1345 lines
34 KiB
C#
1345 lines
34 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using System.ComponentModel;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
using System.Xml.Serialization;
|
|
|
|
namespace File_Folder_Helper.ADO2024.PI1;
|
|
|
|
#pragma warning disable IDE1006, CS8618
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
[XmlRoot(Namespace = "", IsNullable = false)]
|
|
public partial class KeePassFile
|
|
{
|
|
|
|
private KeePassFileMeta metaField;
|
|
|
|
private KeePassFileRoot rootField;
|
|
|
|
public KeePassFileMeta Meta
|
|
{
|
|
get => metaField;
|
|
set => metaField = value;
|
|
}
|
|
|
|
public KeePassFileRoot Root
|
|
{
|
|
get => rootField;
|
|
set => rootField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileMeta
|
|
{
|
|
|
|
private string generatorField;
|
|
|
|
private DateTime settingsChangedField;
|
|
|
|
private object databaseNameField;
|
|
|
|
private DateTime databaseNameChangedField;
|
|
|
|
private object databaseDescriptionField;
|
|
|
|
private DateTime databaseDescriptionChangedField;
|
|
|
|
private object defaultUserNameField;
|
|
|
|
private DateTime defaultUserNameChangedField;
|
|
|
|
private ushort maintenanceHistoryDaysField;
|
|
|
|
private object colorField;
|
|
|
|
private DateTime masterKeyChangedField;
|
|
|
|
private sbyte masterKeyChangeRecField;
|
|
|
|
private sbyte masterKeyChangeForceField;
|
|
|
|
private KeePassFileMetaMemoryProtection memoryProtectionField;
|
|
|
|
private KeePassFileMetaIcon[] customIconsField;
|
|
|
|
private string recycleBinEnabledField;
|
|
|
|
private string recycleBinUUIDField;
|
|
|
|
private DateTime recycleBinChangedField;
|
|
|
|
private string entryTemplatesGroupField;
|
|
|
|
private DateTime entryTemplatesGroupChangedField;
|
|
|
|
private byte historyMaxItemsField;
|
|
|
|
private uint historyMaxSizeField;
|
|
|
|
private string lastSelectedGroupField;
|
|
|
|
private string lastTopVisibleGroupField;
|
|
|
|
private object binariesField;
|
|
|
|
private object customDataField;
|
|
|
|
public string Generator
|
|
{
|
|
get => generatorField;
|
|
set => generatorField = value;
|
|
}
|
|
|
|
public DateTime SettingsChanged
|
|
{
|
|
get => settingsChangedField;
|
|
set => settingsChangedField = value;
|
|
}
|
|
|
|
public object DatabaseName
|
|
{
|
|
get => databaseNameField;
|
|
set => databaseNameField = value;
|
|
}
|
|
|
|
public DateTime DatabaseNameChanged
|
|
{
|
|
get => databaseNameChangedField;
|
|
set => databaseNameChangedField = value;
|
|
}
|
|
|
|
public object DatabaseDescription
|
|
{
|
|
get => databaseDescriptionField;
|
|
set => databaseDescriptionField = value;
|
|
}
|
|
|
|
public DateTime DatabaseDescriptionChanged
|
|
{
|
|
get => databaseDescriptionChangedField;
|
|
set => databaseDescriptionChangedField = value;
|
|
}
|
|
|
|
public object DefaultUserName
|
|
{
|
|
get => defaultUserNameField;
|
|
set => defaultUserNameField = value;
|
|
}
|
|
|
|
public DateTime DefaultUserNameChanged
|
|
{
|
|
get => defaultUserNameChangedField;
|
|
set => defaultUserNameChangedField = value;
|
|
}
|
|
|
|
public ushort MaintenanceHistoryDays
|
|
{
|
|
get => maintenanceHistoryDaysField;
|
|
set => maintenanceHistoryDaysField = value;
|
|
}
|
|
|
|
public object Color
|
|
{
|
|
get => colorField;
|
|
set => colorField = value;
|
|
}
|
|
|
|
public DateTime MasterKeyChanged
|
|
{
|
|
get => masterKeyChangedField;
|
|
set => masterKeyChangedField = value;
|
|
}
|
|
|
|
public sbyte MasterKeyChangeRec
|
|
{
|
|
get => masterKeyChangeRecField;
|
|
set => masterKeyChangeRecField = value;
|
|
}
|
|
|
|
public sbyte MasterKeyChangeForce
|
|
{
|
|
get => masterKeyChangeForceField;
|
|
set => masterKeyChangeForceField = value;
|
|
}
|
|
|
|
public KeePassFileMetaMemoryProtection MemoryProtection
|
|
{
|
|
get => memoryProtectionField;
|
|
set => memoryProtectionField = value;
|
|
}
|
|
|
|
[XmlArrayItem("Icon", IsNullable = false)]
|
|
public KeePassFileMetaIcon[] CustomIcons
|
|
{
|
|
get => customIconsField;
|
|
set => customIconsField = value;
|
|
}
|
|
|
|
public string RecycleBinEnabled
|
|
{
|
|
get => recycleBinEnabledField;
|
|
set => recycleBinEnabledField = value;
|
|
}
|
|
|
|
public string RecycleBinUUID
|
|
{
|
|
get => recycleBinUUIDField;
|
|
set => recycleBinUUIDField = value;
|
|
}
|
|
|
|
public DateTime RecycleBinChanged
|
|
{
|
|
get => recycleBinChangedField;
|
|
set => recycleBinChangedField = value;
|
|
}
|
|
|
|
public string EntryTemplatesGroup
|
|
{
|
|
get => entryTemplatesGroupField;
|
|
set => entryTemplatesGroupField = value;
|
|
}
|
|
|
|
public DateTime EntryTemplatesGroupChanged
|
|
{
|
|
get => entryTemplatesGroupChangedField;
|
|
set => entryTemplatesGroupChangedField = value;
|
|
}
|
|
|
|
public byte HistoryMaxItems
|
|
{
|
|
get => historyMaxItemsField;
|
|
set => historyMaxItemsField = value;
|
|
}
|
|
|
|
public uint HistoryMaxSize
|
|
{
|
|
get => historyMaxSizeField;
|
|
set => historyMaxSizeField = value;
|
|
}
|
|
|
|
public string LastSelectedGroup
|
|
{
|
|
get => lastSelectedGroupField;
|
|
set => lastSelectedGroupField = value;
|
|
}
|
|
|
|
public string LastTopVisibleGroup
|
|
{
|
|
get => lastTopVisibleGroupField;
|
|
set => lastTopVisibleGroupField = value;
|
|
}
|
|
|
|
public object Binaries
|
|
{
|
|
get => binariesField;
|
|
set => binariesField = value;
|
|
}
|
|
|
|
public object CustomData
|
|
{
|
|
get => customDataField;
|
|
set => customDataField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileMetaMemoryProtection
|
|
{
|
|
|
|
private string protectTitleField;
|
|
|
|
private string protectUserNameField;
|
|
|
|
private string protectPasswordField;
|
|
|
|
private string protectURLField;
|
|
|
|
private string protectNotesField;
|
|
|
|
public string ProtectTitle
|
|
{
|
|
get => protectTitleField;
|
|
set => protectTitleField = value;
|
|
}
|
|
|
|
public string ProtectUserName
|
|
{
|
|
get => protectUserNameField;
|
|
set => protectUserNameField = value;
|
|
}
|
|
|
|
public string ProtectPassword
|
|
{
|
|
get => protectPasswordField;
|
|
set => protectPasswordField = value;
|
|
}
|
|
|
|
public string ProtectURL
|
|
{
|
|
get => protectURLField;
|
|
set => protectURLField = value;
|
|
}
|
|
|
|
public string ProtectNotes
|
|
{
|
|
get => protectNotesField;
|
|
set => protectNotesField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileMetaIcon
|
|
{
|
|
|
|
private string uUIDField;
|
|
|
|
private string dataField;
|
|
|
|
private string nameField;
|
|
|
|
private DateTime lastModificationTimeField;
|
|
|
|
private bool lastModificationTimeFieldSpecified;
|
|
|
|
public string UUID
|
|
{
|
|
get => uUIDField;
|
|
set => uUIDField = value;
|
|
}
|
|
|
|
public string Data
|
|
{
|
|
get => dataField;
|
|
set => dataField = value;
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get => nameField;
|
|
set => nameField = value;
|
|
}
|
|
|
|
public DateTime LastModificationTime
|
|
{
|
|
get => lastModificationTimeField;
|
|
set => lastModificationTimeField = value;
|
|
}
|
|
|
|
[XmlIgnore()]
|
|
public bool LastModificationTimeSpecified
|
|
{
|
|
get => lastModificationTimeFieldSpecified;
|
|
set => lastModificationTimeFieldSpecified = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileRoot
|
|
{
|
|
|
|
private KeePassFileGroup[] groupField;
|
|
|
|
private KeePassFileGroupEntry[] entryField;
|
|
|
|
private KeePassFileRootDeletedObject[] deletedObjectsField;
|
|
|
|
[XmlElement("Group")]
|
|
public KeePassFileGroup[] Group
|
|
{
|
|
get => groupField;
|
|
set => groupField = value;
|
|
}
|
|
|
|
[XmlElement("Entry")]
|
|
public KeePassFileGroupEntry[] Entry
|
|
{
|
|
get => entryField;
|
|
set => entryField = value;
|
|
}
|
|
|
|
[XmlArrayItem("DeletedObject", IsNullable = false)]
|
|
public KeePassFileRootDeletedObject[] DeletedObjects
|
|
{
|
|
get => deletedObjectsField;
|
|
set => deletedObjectsField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroup
|
|
{
|
|
|
|
private string uUIDField;
|
|
|
|
private string nameField;
|
|
|
|
private object notesField;
|
|
|
|
private byte iconIDField;
|
|
|
|
private KeePassFileGroupTimes timesField;
|
|
|
|
private string isExpandedField;
|
|
|
|
private object defaultAutoTypeSequenceField;
|
|
|
|
private string enableAutoTypeField;
|
|
|
|
private string enableSearchingField;
|
|
|
|
private KeePassFileGroup[] groupField;
|
|
|
|
private KeePassFileGroupEntry[] entryField;
|
|
|
|
public string UUID
|
|
{
|
|
get => uUIDField;
|
|
set => uUIDField = value;
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get => nameField;
|
|
set => nameField = value;
|
|
}
|
|
|
|
public object Notes
|
|
{
|
|
get => notesField;
|
|
set => notesField = value;
|
|
}
|
|
|
|
public byte IconID
|
|
{
|
|
get => iconIDField;
|
|
set => iconIDField = value;
|
|
}
|
|
|
|
public KeePassFileGroupTimes Times
|
|
{
|
|
get => timesField;
|
|
set => timesField = value;
|
|
}
|
|
|
|
public string IsExpanded
|
|
{
|
|
get => isExpandedField;
|
|
set => isExpandedField = value;
|
|
}
|
|
|
|
public object DefaultAutoTypeSequence
|
|
{
|
|
get => defaultAutoTypeSequenceField;
|
|
set => defaultAutoTypeSequenceField = value;
|
|
}
|
|
|
|
public string EnableAutoType
|
|
{
|
|
get => enableAutoTypeField;
|
|
set => enableAutoTypeField = value;
|
|
}
|
|
|
|
public string EnableSearching
|
|
{
|
|
get => enableSearchingField;
|
|
set => enableSearchingField = value;
|
|
}
|
|
|
|
[XmlElement("Group")]
|
|
public KeePassFileGroup[] Group
|
|
{
|
|
get => groupField;
|
|
set => groupField = value;
|
|
}
|
|
|
|
[XmlElement("Entry")]
|
|
public KeePassFileGroupEntry[] Entry
|
|
{
|
|
get => entryField;
|
|
set => entryField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupTimes
|
|
{
|
|
|
|
private DateTime creationTimeField;
|
|
|
|
private DateTime lastModificationTimeField;
|
|
|
|
private DateTime expiryTimeField;
|
|
|
|
private string expiresField;
|
|
|
|
public DateTime CreationTime
|
|
{
|
|
get => creationTimeField;
|
|
set => creationTimeField = value;
|
|
}
|
|
|
|
public DateTime LastModificationTime
|
|
{
|
|
get => lastModificationTimeField;
|
|
set => lastModificationTimeField = value;
|
|
}
|
|
|
|
public DateTime ExpiryTime
|
|
{
|
|
get => expiryTimeField;
|
|
set => expiryTimeField = value;
|
|
}
|
|
|
|
public string Expires
|
|
{
|
|
get => expiresField;
|
|
set => expiresField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntry
|
|
{
|
|
|
|
private string uUIDField;
|
|
|
|
private byte iconIDField;
|
|
|
|
private object foregroundColorField;
|
|
|
|
private object backgroundColorField;
|
|
|
|
private object overrideURLField;
|
|
|
|
private object tagsField;
|
|
|
|
private KeePassFileGroupEntryTimes timesField;
|
|
|
|
private KeePassFileGroupEntryString[] stringField;
|
|
|
|
private KeePassFileGroupEntryAutoType autoTypeField;
|
|
|
|
private KeePassFileGroupEntryEntry[] historyField;
|
|
|
|
public string UUID
|
|
{
|
|
get => uUIDField;
|
|
set => uUIDField = value;
|
|
}
|
|
|
|
public byte IconID
|
|
{
|
|
get => iconIDField;
|
|
set => iconIDField = value;
|
|
}
|
|
|
|
public object ForegroundColor
|
|
{
|
|
get => foregroundColorField;
|
|
set => foregroundColorField = value;
|
|
}
|
|
|
|
public object BackgroundColor
|
|
{
|
|
get => backgroundColorField;
|
|
set => backgroundColorField = value;
|
|
}
|
|
|
|
public object OverrideURL
|
|
{
|
|
get => overrideURLField;
|
|
set => overrideURLField = value;
|
|
}
|
|
|
|
public object Tags
|
|
{
|
|
get => tagsField;
|
|
set => tagsField = value;
|
|
}
|
|
|
|
public KeePassFileGroupEntryTimes Times
|
|
{
|
|
get => timesField;
|
|
set => timesField = value;
|
|
}
|
|
|
|
[XmlElement("String")]
|
|
public KeePassFileGroupEntryString[] String
|
|
{
|
|
get => stringField;
|
|
set => stringField = value;
|
|
}
|
|
|
|
public KeePassFileGroupEntryAutoType AutoType
|
|
{
|
|
get => autoTypeField;
|
|
set => autoTypeField = value;
|
|
}
|
|
|
|
[XmlArrayItem("Entry", IsNullable = false)]
|
|
public KeePassFileGroupEntryEntry[] History
|
|
{
|
|
get => historyField;
|
|
set => historyField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryTimes
|
|
{
|
|
|
|
private DateTime creationTimeField;
|
|
|
|
private DateTime lastModificationTimeField;
|
|
|
|
private DateTime expiryTimeField;
|
|
|
|
private string expiresField;
|
|
|
|
public DateTime CreationTime
|
|
{
|
|
get => creationTimeField;
|
|
set => creationTimeField = value;
|
|
}
|
|
|
|
public DateTime LastModificationTime
|
|
{
|
|
get => lastModificationTimeField;
|
|
set => lastModificationTimeField = value;
|
|
}
|
|
|
|
public DateTime ExpiryTime
|
|
{
|
|
get => expiryTimeField;
|
|
set => expiryTimeField = value;
|
|
}
|
|
|
|
public string Expires
|
|
{
|
|
get => expiresField;
|
|
set => expiresField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryString
|
|
{
|
|
|
|
private string keyField;
|
|
|
|
private KeePassFileGroupEntryStringValue valueField;
|
|
|
|
public string Key
|
|
{
|
|
get => keyField;
|
|
set => keyField = value;
|
|
}
|
|
|
|
public KeePassFileGroupEntryStringValue Value
|
|
{
|
|
get => valueField;
|
|
set => valueField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryStringValue
|
|
{
|
|
|
|
private string protectInMemoryField;
|
|
|
|
private string valueField;
|
|
|
|
[XmlAttribute()]
|
|
public string ProtectInMemory
|
|
{
|
|
get => protectInMemoryField;
|
|
set => protectInMemoryField = value;
|
|
}
|
|
|
|
[XmlText()]
|
|
public string Value
|
|
{
|
|
get => valueField;
|
|
set => valueField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryAutoType
|
|
{
|
|
|
|
private string enabledField;
|
|
|
|
private byte dataTransferObfuscationField;
|
|
|
|
public string Enabled
|
|
{
|
|
get => enabledField;
|
|
set => enabledField = value;
|
|
}
|
|
|
|
public byte DataTransferObfuscation
|
|
{
|
|
get => dataTransferObfuscationField;
|
|
set => dataTransferObfuscationField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryEntry
|
|
{
|
|
|
|
private string uUIDField;
|
|
|
|
private byte iconIDField;
|
|
|
|
private object foregroundColorField;
|
|
|
|
private object backgroundColorField;
|
|
|
|
private object overrideURLField;
|
|
|
|
private object tagsField;
|
|
|
|
private KeePassFileGroupEntryEntryTimes timesField;
|
|
|
|
private KeePassFileGroupEntryEntryString[] stringField;
|
|
|
|
private KeePassFileGroupEntryEntryAutoType autoTypeField;
|
|
|
|
public string UUID
|
|
{
|
|
get => uUIDField;
|
|
set => uUIDField = value;
|
|
}
|
|
|
|
public byte IconID
|
|
{
|
|
get => iconIDField;
|
|
set => iconIDField = value;
|
|
}
|
|
|
|
public object ForegroundColor
|
|
{
|
|
get => foregroundColorField;
|
|
set => foregroundColorField = value;
|
|
}
|
|
|
|
public object BackgroundColor
|
|
{
|
|
get => backgroundColorField;
|
|
set => backgroundColorField = value;
|
|
}
|
|
|
|
public object OverrideURL
|
|
{
|
|
get => overrideURLField;
|
|
set => overrideURLField = value;
|
|
}
|
|
|
|
public object Tags
|
|
{
|
|
get => tagsField;
|
|
set => tagsField = value;
|
|
}
|
|
|
|
public KeePassFileGroupEntryEntryTimes Times
|
|
{
|
|
get => timesField;
|
|
set => timesField = value;
|
|
}
|
|
|
|
[XmlElement("String")]
|
|
public KeePassFileGroupEntryEntryString[] String
|
|
{
|
|
get => stringField;
|
|
set => stringField = value;
|
|
}
|
|
|
|
public KeePassFileGroupEntryEntryAutoType AutoType
|
|
{
|
|
get => autoTypeField;
|
|
set => autoTypeField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryEntryTimes
|
|
{
|
|
|
|
private DateTime creationTimeField;
|
|
|
|
private DateTime lastModificationTimeField;
|
|
|
|
private DateTime expiryTimeField;
|
|
|
|
private string expiresField;
|
|
|
|
public DateTime CreationTime
|
|
{
|
|
get => creationTimeField;
|
|
set => creationTimeField = value;
|
|
}
|
|
|
|
public DateTime LastModificationTime
|
|
{
|
|
get => lastModificationTimeField;
|
|
set => lastModificationTimeField = value;
|
|
}
|
|
|
|
public DateTime ExpiryTime
|
|
{
|
|
get => expiryTimeField;
|
|
set => expiryTimeField = value;
|
|
}
|
|
|
|
public string Expires
|
|
{
|
|
get => expiresField;
|
|
set => expiresField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryEntryString
|
|
{
|
|
|
|
private string keyField;
|
|
|
|
private KeePassFileGroupEntryEntryStringValue valueField;
|
|
|
|
public string Key
|
|
{
|
|
get => keyField;
|
|
set => keyField = value;
|
|
}
|
|
|
|
public KeePassFileGroupEntryEntryStringValue Value
|
|
{
|
|
get => valueField;
|
|
set => valueField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryEntryStringValue
|
|
{
|
|
|
|
private string protectInMemoryField;
|
|
|
|
private string valueField;
|
|
|
|
[XmlAttribute()]
|
|
public string ProtectInMemory
|
|
{
|
|
get => protectInMemoryField;
|
|
set => protectInMemoryField = value;
|
|
}
|
|
|
|
[XmlText()]
|
|
public string Value
|
|
{
|
|
get => valueField;
|
|
set => valueField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileGroupEntryEntryAutoType
|
|
{
|
|
|
|
private string enabledField;
|
|
|
|
private byte dataTransferObfuscationField;
|
|
|
|
public string Enabled
|
|
{
|
|
get => enabledField;
|
|
set => enabledField = value;
|
|
}
|
|
|
|
public byte DataTransferObfuscation
|
|
{
|
|
get => dataTransferObfuscationField;
|
|
set => dataTransferObfuscationField = value;
|
|
}
|
|
}
|
|
|
|
[Serializable()]
|
|
[DesignerCategory("code")]
|
|
[XmlType(AnonymousType = true)]
|
|
public partial class KeePassFileRootDeletedObject
|
|
{
|
|
|
|
private string uUIDField;
|
|
|
|
private DateTime deletionTimeField;
|
|
|
|
public string UUID
|
|
{
|
|
get => uUIDField;
|
|
set => uUIDField = value;
|
|
}
|
|
|
|
public DateTime DeletionTime
|
|
{
|
|
get => deletionTimeField;
|
|
set => deletionTimeField = value;
|
|
}
|
|
}
|
|
|
|
#pragma warning restore IDE1006, CS8618
|
|
|
|
internal static partial class Helper20240105
|
|
{
|
|
|
|
// Folders with these names will be put in the root instead.
|
|
private static readonly string[] _BlacklistedFolders =
|
|
[
|
|
"KeePassHttp Passwords",
|
|
"KeePassXC-Browser Passwords"
|
|
];
|
|
|
|
private static readonly string[] _BlacklistedFields = [
|
|
"KeePassXC-Browser Settings",
|
|
"KeePassHttp Settings"
|
|
];
|
|
|
|
private record Field(
|
|
[property: JsonPropertyName("name")] string? Name,
|
|
[property: JsonPropertyName("value")] string? Value,
|
|
[property: JsonPropertyName("type")] int? Type
|
|
);
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
[JsonSerializable(typeof(Field))]
|
|
private partial class FieldSourceGenerationContext : JsonSerializerContext
|
|
{
|
|
}
|
|
|
|
private record Folder(
|
|
[property: JsonPropertyName("id")] Guid? Id,
|
|
[property: JsonPropertyName("name")] string? Name
|
|
);
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
[JsonSerializable(typeof(Folder))]
|
|
private partial class FolderSourceGenerationContext : JsonSerializerContext
|
|
{
|
|
}
|
|
|
|
private record Item(
|
|
[property: JsonPropertyName("revisionDate")] DateTime? RevisionDate,
|
|
[property: JsonPropertyName("creationDate")] DateTime? CreationDate,
|
|
[property: JsonPropertyName("folderId")] Guid? FolderId,
|
|
[property: JsonPropertyName("folderName")] string? FolderName,
|
|
[property: JsonPropertyName("type")] int Type,
|
|
[property: JsonPropertyName("name")] string? Name,
|
|
[property: JsonPropertyName("notes")] string? Notes,
|
|
[property: JsonPropertyName("fields")] IReadOnlyList<Field>? Fields,
|
|
[property: JsonPropertyName("login")] Login Login
|
|
);
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
[JsonSerializable(typeof(Item))]
|
|
private partial class ItemSourceGenerationContext : JsonSerializerContext
|
|
{
|
|
}
|
|
|
|
private record Login(
|
|
[property: JsonPropertyName("uris")] IReadOnlyList<Uri> Uris,
|
|
[property: JsonPropertyName("username")] string? Username,
|
|
[property: JsonPropertyName("password")] string? Password
|
|
);
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
[JsonSerializable(typeof(Login))]
|
|
private partial class LoginSourceGenerationContext : JsonSerializerContext
|
|
{
|
|
}
|
|
|
|
private record Root(
|
|
[property: JsonPropertyName("folders")] IReadOnlyList<Folder> Folders,
|
|
[property: JsonPropertyName("items")] IReadOnlyList<Item> Items
|
|
);
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
[JsonSerializable(typeof(Root))]
|
|
private partial class RootSourceGenerationContext : JsonSerializerContext
|
|
{
|
|
}
|
|
|
|
private record Uri(
|
|
[property: JsonPropertyName("uri")] string? Value,
|
|
[property: JsonPropertyName("host")] string? Host,
|
|
[property: JsonPropertyName("port")] int? Port
|
|
);
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
[JsonSerializable(typeof(Uri))]
|
|
private partial class UriSourceGenerationContext : JsonSerializerContext
|
|
{
|
|
}
|
|
|
|
private static int GetDeterministicHashCode(byte[] value)
|
|
{
|
|
int result;
|
|
unchecked
|
|
{
|
|
int hash1 = (5381 << 16) + 5381;
|
|
int hash2 = hash1;
|
|
for (int i = 0; i < value.Length; i += 2)
|
|
{
|
|
hash1 = ((hash1 << 5) + hash1) ^ value[i];
|
|
if (i == value.Length - 1)
|
|
break;
|
|
hash2 = ((hash2 << 5) + hash2) ^ value[i + 1];
|
|
}
|
|
result = hash1 + (hash2 * 1566083941);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static string? ConvertPassword(string? value) =>
|
|
string.IsNullOrEmpty(value) ? "-" : GetDeterministicHashCode(System.Text.Encoding.ASCII.GetBytes(value)).ToString();
|
|
|
|
private static Item? GetEntry(Guid folderId, string folderName, KeePassFileGroupEntry keePassFileGroupEntry)
|
|
{
|
|
Item? result;
|
|
string? name = keePassFileGroupEntry.String.Where(x => x.Key == "Title").Select(x => x.Value.Value).FirstOrDefault();
|
|
if (_BlacklistedFields.Contains(name))
|
|
result = null;
|
|
else
|
|
{
|
|
int? port;
|
|
string? host;
|
|
string? uri = null;
|
|
string? notes = null;
|
|
string? password = null;
|
|
string? username = null;
|
|
DateTime creationTime = keePassFileGroupEntry.Times.CreationTime;
|
|
DateTime revisionDate = keePassFileGroupEntry.Times.LastModificationTime;
|
|
List<KeePassFileGroupEntryString> keePassFileGroupEntryStrings = [];
|
|
foreach (KeePassFileGroupEntryString keePassFileGroupEntryString in keePassFileGroupEntry.String)
|
|
{
|
|
if (keePassFileGroupEntryString.Key == "Title")
|
|
continue;
|
|
if (_BlacklistedFields.Contains(keePassFileGroupEntryString.Key))
|
|
continue;
|
|
if (keePassFileGroupEntryString.Key == "URL")
|
|
uri = keePassFileGroupEntryString.Value.Value;
|
|
else if (keePassFileGroupEntryString.Key == "Notes")
|
|
notes = keePassFileGroupEntryString.Value.Value;
|
|
else if (keePassFileGroupEntryString.Key == "UserName")
|
|
username = keePassFileGroupEntryString.Value.Value;
|
|
else if (keePassFileGroupEntryString.Key == "Password")
|
|
password = ConvertPassword(keePassFileGroupEntryString.Value.Value);
|
|
else if (keePassFileGroupEntryString.Key == "URL")
|
|
uri = keePassFileGroupEntryString.Value.Value;
|
|
else
|
|
keePassFileGroupEntryStrings.Add(keePassFileGroupEntryString);
|
|
}
|
|
if (string.IsNullOrEmpty(uri) || !uri.Contains(':'))
|
|
{
|
|
host = null;
|
|
port = null;
|
|
}
|
|
else
|
|
{
|
|
host = new System.Uri(uri).Host;
|
|
port = new System.Uri(uri).Port;
|
|
}
|
|
List<Field> itemFields = (from l in keePassFileGroupEntryStrings select new Field(l.Key, l.Value.Value, 0)).ToList();
|
|
Login login = new([new(uri, host, port)], username, password);
|
|
result = new(revisionDate,
|
|
creationTime,
|
|
folderId,
|
|
folderName,
|
|
1,
|
|
name,
|
|
string.IsNullOrEmpty(notes) ? null : notes,
|
|
itemFields.Count > 0 ? itemFields : null,
|
|
login);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static List<Item> GetItems(Guid folderId, string folderName, KeePassFileGroup keePassFileGroup)
|
|
{
|
|
List<Item> results = [];
|
|
if (keePassFileGroup.Entry is not null)
|
|
{
|
|
Item? item;
|
|
foreach (KeePassFileGroupEntry keePassFileGroupEntry in keePassFileGroup.Entry)
|
|
{
|
|
item = GetEntry(folderId, folderName, keePassFileGroupEntry);
|
|
if (item is null)
|
|
continue;
|
|
results.Add(item);
|
|
}
|
|
}
|
|
if (keePassFileGroup.Group is not null)
|
|
{
|
|
foreach (KeePassFileGroup keePassFileGroupInner in keePassFileGroup.Group)
|
|
{
|
|
folderId = Guid.NewGuid();
|
|
results.AddRange(GetItems(folderId, $"{folderName}/{keePassFileGroupInner.Name}", keePassFileGroupInner));
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static MemoryStream ToStream(string @this)
|
|
{
|
|
MemoryStream memoryStream = new();
|
|
StreamWriter streamWriter = new(memoryStream);
|
|
streamWriter.Write(@this);
|
|
streamWriter.Flush();
|
|
memoryStream.Position = 0;
|
|
return memoryStream;
|
|
}
|
|
|
|
private static KeePassFile? ParseXML(string value, bool throwExceptions)
|
|
{
|
|
KeePassFile? result;
|
|
try
|
|
{
|
|
Type type = typeof(KeePassFile);
|
|
Stream stream = ToStream(value.Trim());
|
|
XmlReader xmlReader = XmlReader.Create(stream, new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
|
|
#pragma warning disable IL2026
|
|
XmlSerializer xmlSerializer = new(type, type.GetNestedTypes());
|
|
result = xmlSerializer.Deserialize(xmlReader) as KeePassFile;
|
|
#pragma warning restore IL2026
|
|
stream.Dispose();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (throwExceptions)
|
|
throw;
|
|
result = null;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static List<Item> GetItems(KeePassFileRoot keePassFileRoot)
|
|
{
|
|
List<Item> results = [];
|
|
Guid folderId = Guid.NewGuid();
|
|
string folderName = string.Empty;
|
|
if (keePassFileRoot.Entry is not null)
|
|
{
|
|
Item? item;
|
|
foreach (KeePassFileGroupEntry keePassFileGroupEntry in keePassFileRoot.Entry)
|
|
{
|
|
item = GetEntry(folderId, folderName, keePassFileGroupEntry);
|
|
if (item is null)
|
|
continue;
|
|
results.Add(item);
|
|
}
|
|
}
|
|
if (keePassFileRoot.Group is not null)
|
|
{
|
|
foreach (KeePassFileGroup keePassFileGroup in keePassFileRoot.Group)
|
|
{
|
|
folderId = Guid.NewGuid();
|
|
folderName = keePassFileGroup.Name;
|
|
results.AddRange(GetItems(folderId, folderName, keePassFileGroup));
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static List<Folder> GetFolders(List<Item> items)
|
|
{
|
|
List<Folder> results = [];
|
|
Folder folder;
|
|
List<string> distinct = [];
|
|
foreach (Item item in items)
|
|
{
|
|
if (item.FolderName is null)
|
|
throw new NullReferenceException(nameof(item.FolderName));
|
|
if (distinct.Contains(item.FolderName))
|
|
continue;
|
|
distinct.Add(item.FolderName);
|
|
folder = new(item.FolderId, item.FolderName);
|
|
results.Add(folder);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static List<Item> Filter(string xmlFile, ILogger<Worker> logger, List<Item> items)
|
|
{
|
|
List<Item> results = [];
|
|
int? port;
|
|
string key;
|
|
Item result;
|
|
Login login;
|
|
string? uri;
|
|
string? host;
|
|
string? name;
|
|
Guid? folderId;
|
|
string? username;
|
|
List<Item>? check;
|
|
string? folderName;
|
|
DateTime? creationTime;
|
|
string joinedHostPorts;
|
|
DateTime? revisionDate;
|
|
List<string> notes = [];
|
|
string? password = null;
|
|
string?[] checkPasswords;
|
|
Dictionary<string, List<Item>> keyValuePairs = [];
|
|
foreach (Item item in items)
|
|
{
|
|
joinedHostPorts = string.Join('-', item.Login.Uris.Select(l => $"{l.Host}:{l.Port}"));
|
|
key = string.Concat(item.Login.Username, '-', string.IsNullOrEmpty(joinedHostPorts) ? Guid.NewGuid().ToString() : joinedHostPorts);
|
|
if (!keyValuePairs.TryGetValue(key, out check))
|
|
{
|
|
keyValuePairs.Add(key, []);
|
|
if (!keyValuePairs.TryGetValue(key, out check))
|
|
throw new NotSupportedException();
|
|
}
|
|
check.Add(item);
|
|
}
|
|
foreach (KeyValuePair<string, List<Item>> keyValuePair in keyValuePairs)
|
|
{
|
|
if (keyValuePair.Value.Count == 1)
|
|
results.AddRange(keyValuePair.Value);
|
|
else
|
|
{
|
|
checkPasswords = keyValuePair.Value.Select(l => l.Login.Password).Distinct().ToArray();
|
|
if (checkPasswords.Length == 1)
|
|
{
|
|
results.Add(keyValuePair.Value[0]);
|
|
logger.LogInformation("Filtered XML duplicate Password [{keyValuePair.Key}] <{xmlFile}>.", keyValuePair.Key, xmlFile);
|
|
}
|
|
else
|
|
{
|
|
uri = null;
|
|
host = null;
|
|
name = null;
|
|
port = null;
|
|
notes.Clear();
|
|
folderId = null;
|
|
username = null;
|
|
folderName = null;
|
|
creationTime = null;
|
|
revisionDate = null;
|
|
notes.Add("Unset Password");
|
|
logger.LogInformation("Filtered XML Unset Password [{keyValuePair.Key}] <{xmlFile}>.", keyValuePair.Key, xmlFile);
|
|
foreach (Item item in from l in keyValuePair.Value orderby l.RevisionDate, l.Login.Password?.Length descending select l)
|
|
{
|
|
if (item.Login.Uris.Count == 1)
|
|
{
|
|
uri = item.Login.Uris[0].Value;
|
|
host = item.Login.Uris[0].Host;
|
|
port = item.Login.Uris[0].Port;
|
|
}
|
|
name = item.Name;
|
|
folderId = item.FolderId;
|
|
folderName = item.FolderName;
|
|
username = item.Login.Username;
|
|
creationTime = item.CreationDate;
|
|
revisionDate = item.RevisionDate;
|
|
notes.Add($"{item.Login.Password} on {item.RevisionDate}");
|
|
}
|
|
login = new([new(uri, host, port)], username, password);
|
|
result = new(revisionDate,
|
|
creationTime,
|
|
folderId,
|
|
folderName,
|
|
1,
|
|
name,
|
|
string.Join(Environment.NewLine, notes),
|
|
null,
|
|
login);
|
|
results.Add(result);
|
|
}
|
|
}
|
|
}
|
|
if (results.Count != items.Count)
|
|
logger.LogInformation("Filtered XML {results.Count} != {items.Count} <{xmlFile}>.", results.Count, items.Count, xmlFile);
|
|
results = (from l in results orderby l.Login.Uris[0].Host, l.Login.Username select l).ToList();
|
|
return results;
|
|
}
|
|
|
|
private static void SaveTabSeparatedValueFiles(List<Item> items, string xmlFile)
|
|
{
|
|
List<string> lines = [];
|
|
foreach (Item item in items)
|
|
lines.Add($"{item.Login.Uris[0].Host}\t{item.Login.Uris[0].Port}\t{item.Login.Username}\t{item.Login.Password}");
|
|
File.WriteAllLines(Path.ChangeExtension(xmlFile, ".tvs"), lines);
|
|
}
|
|
|
|
internal static void ConvertKeePassExport(ILogger<Worker> logger, List<string> args)
|
|
{
|
|
Root root;
|
|
string xml;
|
|
string json;
|
|
List<Item> items;
|
|
List<Folder> folders;
|
|
KeePassFile? keePassFile;
|
|
List<Item> filteredItems;
|
|
string newExtension = args[3];
|
|
string[] xmlFiles = Directory.GetFiles(args[0], args[2]);
|
|
foreach (string xmlFile in xmlFiles)
|
|
{
|
|
xml = XDocument.Load(xmlFile).ToString();
|
|
keePassFile = ParseXML(xml, throwExceptions: true);
|
|
if (keePassFile is null)
|
|
throw new NullReferenceException(nameof(keePassFile));
|
|
logger.LogInformation("Loaded XML <{xmlFile}>.", xmlFile);
|
|
items = GetItems(keePassFile.Root);
|
|
folders = GetFolders(items);
|
|
filteredItems = Filter(xmlFile, logger, items);
|
|
logger.LogInformation("Save generic output file...");
|
|
SaveTabSeparatedValueFiles(filteredItems, xmlFile);
|
|
root = new(folders, filteredItems);
|
|
logger.LogInformation("Serializing output file...");
|
|
json = JsonSerializer.Serialize(root, RootSourceGenerationContext.Default.Root);
|
|
logger.LogInformation("Writing output file...");
|
|
File.WriteAllText(Path.ChangeExtension(xmlFile, newExtension), json);
|
|
}
|
|
logger.LogInformation("Done!");
|
|
}
|
|
|
|
} |