From ccea8de8cf49343dd4190a77ac755d233bb4e528 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Fri, 5 Jan 2024 18:39:49 -0700 Subject: [PATCH] Removed updated and created front data Helper-2023-12-21 -> Helper-2024-01-05 --- .kanbn/index.md | 34 - .vscode/launch.json | 16 +- Day/Helper-2023-12-21.cs | 295 -------- Day/Helper-2024-01-05.cs | 1347 +++++++++++++++++++++++++++++++++++++ Day/HelperDay.cs | 4 +- Helpers/HelperMarkdown.cs | 28 +- 6 files changed, 1352 insertions(+), 372 deletions(-) delete mode 100644 .kanbn/index.md delete mode 100644 Day/Helper-2023-12-21.cs create mode 100644 Day/Helper-2024-01-05.cs diff --git a/.kanbn/index.md b/.kanbn/index.md deleted file mode 100644 index 4dc9d80..0000000 --- a/.kanbn/index.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -startedColumns: - - 'In Progress' -completedColumns: - - Done -dateFormat: mm/dd -taskTemplate: '^+^_${overdue ? ''^R'' : ''''}${name}^: ${relations ? (''\n^-^/^g'' + relations.reduce((accumulator, currentValue) => accumulator.task + currentValue.task + '' '', '''')) : ''''}' -created: "2023-10-01T18:07:00.085Z" -updated: "2023-10-01T18:07:00.085Z" -type: "Kanban" ---- - -# File File Helper - -## Backlog - -- [markdown-links-to-json](tasks/markdown-links-to-json.md) -- [markdown-to-json](tasks/markdown-to-json.md) - -## Todo - -- [find-orphan-links](tasks/find-orphan-links.md) - -## In Progress - -## Done - -- [break-circular-references](tasks/break-circular-references.md) -- [populate-a-collection-of-file-info-front-matter-and-first-indent-of-1](tasks/populate-a-collection-of-file-info-front-matter-and-first-indent-of-1.md) -- [use-humanizer-on-title-of-links](tasks/use-humanizer-on-title-of-links.md) -- [convert-files-to-slug-name](tasks/convert-files-to-slug-name.md) -- [convert-wiki-links-to-markdown-links](tasks/convert-wiki-links-to-markdown-links.md) -- [add-update-date-in-front-matter](tasks/add-update-date-in-front-matter.md) -- [relative-path-to-relative-to-content](tasks/relative-path-to-relative-to-content.md) diff --git a/.vscode/launch.json b/.vscode/launch.json index d7ba071..5971abe 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -24,18 +24,4 @@ "request": "attach" } ] -} -// dotnet build -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R45-PLC\PollPath" -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R47-PLC\PollPath" -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R49-PLC\PollPath" -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R51-PLC\PollPath" -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R70-PLC\PollPath" -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R72-PLC\PollPath" -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R73-PLC\PollPath" -// dotnet run "s" "\\messv02ecc1.ec.local\EC_EDA\Staging\Traces\HTR-PLC\R74-PLC\PollPath" -// dotnet run "s" "C:/Users/phares/.nuget/packages" -// dotnet run "s" "D:/ProgramData/EC_EAFRepository/nupkg" -// dotnet run "s" "D:/Baget/packages" -// dotnet run "s" "\\messdv002.na.infineon.com\Candela\BaGet\packages" -// dotnet run "s" "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/.kanbn" \ No newline at end of file +} \ No newline at end of file diff --git a/Day/Helper-2023-12-21.cs b/Day/Helper-2023-12-21.cs deleted file mode 100644 index 2dfeeb9..0000000 --- a/Day/Helper-2023-12-21.cs +++ /dev/null @@ -1,295 +0,0 @@ -using Microsoft.Extensions.Logging; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Xml.Linq; - -namespace File_Folder_Helper.Day; - -internal static partial class Helper20231221 -{ - - // 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 static Func OtherFields() - { - return x => - { - string? key = x.Element("Key")?.Value; - return key != "Title" && key != "Notes" && key != "UserName" && key != "Password" && - key != "URL" && !_BlacklistedFields.Contains(key); - }; - } - - 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")] string? Id, - [property: JsonPropertyName("name")] string? Name - ); - - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(Folder))] - private partial class FolderSourceGenerationContext : JsonSerializerContext - { - } - - private record Item( - [property: JsonPropertyName("revisionDate")] string? RevisionDate, - [property: JsonPropertyName("creationDate")] string? CreationDate, - [property: JsonPropertyName("folderId")] string? FolderId, - [property: JsonPropertyName("type")] int Type, - [property: JsonPropertyName("name")] string? Name, - [property: JsonPropertyName("notes")] string? Notes, - [property: JsonPropertyName("fields")] IReadOnlyList? Fields, - [property: JsonPropertyName("login")] Login Login - ); - - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(Item))] - private partial class ItemSourceGenerationContext : JsonSerializerContext - { - } - - private record Login( - [property: JsonPropertyName("uris")] IReadOnlyList 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 Folders, - [property: JsonPropertyName("items")] IReadOnlyList 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 - ); - - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(Uri))] - private partial class UriSourceGenerationContext : JsonSerializerContext - { - } - - private static Item? GetEntry(string folderId, XElement entry) - { - Item? result; - XElement[] stringFields = entry.Elements("String").ToArray(); - string? name = stringFields.Where(x => x.Element("Key")?.Value == "Title").Select(x => x.Element("Value")?.Value).FirstOrDefault(); - if (_BlacklistedFields.Contains(name)) - result = null; - else - { - XElement[] timesFields = entry.Elements("Times").ToArray(); - string? creationTime = timesFields.Elements("CreationTime").FirstOrDefault()?.Value; - string? revisionDate = timesFields.Elements("LastModificationTime").FirstOrDefault()?.Value; - string? uri = stringFields.Where(x => x.Element("Key")?.Value == "URL").Select(x => x.Element("Value")?.Value).FirstOrDefault(); - string? notes = stringFields.Where(x => x.Element("Key")?.Value == "Notes").Select(x => x.Element("Value")?.Value).FirstOrDefault(); - string? username = stringFields.Where(x => x.Element("Key")?.Value == "UserName").Select(x => x.Element("Value")?.Value).FirstOrDefault(); - string? password = stringFields.Where(x => x.Element("Key")?.Value == "Password").Select(x => x.Element("Value")?.Value).FirstOrDefault(); - string? host = string.IsNullOrEmpty(uri) || !uri.Contains(':') ? null : new System.Uri(uri).Host; - Login login = new(new Uri[] { new(uri, host) }, username, password); - List itemFields = stringFields.Where(OtherFields()).Select(x => new Field(x.Element("Key")?.Value, x.Element("Value")?.Value, 0)).ToList(); - result = new(revisionDate, - creationTime, - folderId, - 1, - name, - string.IsNullOrEmpty(notes) ? null : notes, - itemFields.Count > 0 ? itemFields : null, - login); - } - return result; - } - - private static List Filter(List items) - { - List results = []; - string key; - Item result; - Login login; - string? uri; - string? host; - string? name; - string? folderId; - string? username; - List? check; - string? creationTime; - string? revisionDate; - List notes = []; - string? password = null; - string?[] checkPasswords; - Dictionary> keyValuePairs = []; - foreach (Item item in items) - { - key = string.Concat(item.Login.Username, '-', string.Join('-', item.Login.Uris.Select(l => l.Host))); - if (!keyValuePairs.TryGetValue(key, out check)) - { - keyValuePairs.Add(key, []); - if (!keyValuePairs.TryGetValue(key, out check)) - throw new NotSupportedException(); - } - check.Add(item); - } - foreach (KeyValuePair> 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]); - else - { - uri = null; - host = null; - name = null; - notes.Clear(); - folderId = null; - username = null; - creationTime = null; - revisionDate = null; - notes.Add("Unset Password"); - 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; - } - name = item.Name; - folderId = item.FolderId; - username = item.Login.Username; - creationTime = item.CreationDate; - revisionDate = item.RevisionDate; - notes.Add($"{item.Login.Password} on {item.RevisionDate}"); - } - login = new(new Uri[] { new(uri, host) }, username, password); - result = new(revisionDate, - creationTime, - folderId, - 1, - name, - string.Join(Environment.NewLine, notes), - null, - login); - results.Add(result); - } - } - } - results = (from l in results orderby l.Login.Uris[0].Host, l.Login.Username select l).ToList(); - return results; - } - - private static void SaveTabSeparatedValueFiles(List items, string xmlFile) - { - List lines = []; - foreach (Item item in items) - lines.Add($"{item.Login.Uris[0].Host}\t{item.Login.Username}\t{item.Login.Password}"); - File.WriteAllLines(Path.ChangeExtension(xmlFile, ".tvs"), lines); - } - - internal static void ConvertKeePassExport(ILogger logger, List args) - { - Root root; - Item? item; - string json; - string folderId; - string groupKey; - List items; - XElement element; - string newGroupKey; - XDocument xDocument; - List folders; - string childGroupName; - string outputPath = args[0]; - string newExtension = args[3]; - IEnumerable childEntries; - IEnumerable childElements; - Dictionary namesToIds; - Queue> groupsToProcess; - string[] xmlFiles = Directory.GetFiles(args[0], args[2]); - foreach (string xmlFile in xmlFiles) - { - xDocument = XDocument.Load(xmlFile); - if (xDocument.Root is null) - throw new Exception("Root element missing"); - items = []; - folders = []; - namesToIds = []; - groupsToProcess = []; - logger.LogInformation($"Loaded XML {xmlFile}.", xmlFile); - groupsToProcess.Enqueue(new KeyValuePair("", xDocument.Root.Descendants("Group").First())); - while (groupsToProcess.TryDequeue(out KeyValuePair valuePair)) - { - groupKey = valuePair.Key; - element = valuePair.Value; - folderId = Guid.NewGuid().ToString(); - childElements = element.Elements("Group"); - folders.Add(new Folder(folderId, groupKey)); - foreach (XElement childElement in childElements) - { - childGroupName = (childElement.Element("Name")?.Value) ?? throw new Exception("Found group with no name, malformed file"); - if (_BlacklistedFolders.Contains(childGroupName)) - childGroupName = ""; - newGroupKey = $"{groupKey}/{childGroupName}"; - if (groupKey == "") - newGroupKey = childGroupName; - logger.LogInformation($"Found group '{newGroupKey}'"); - groupsToProcess.Enqueue(new KeyValuePair(newGroupKey, childElement)); - } - childEntries = element.Elements("Entry"); - foreach (XElement entry in childEntries) - { - item = GetEntry(folderId, entry); - if (item is null) - continue; - items.Add(item); - } - } - items = Filter(items); - root = new(folders, items); - logger.LogInformation("Serializing output file..."); - json = JsonSerializer.Serialize(root, RootSourceGenerationContext.Default.Root); - logger.LogInformation("Writing output file..."); - File.WriteAllText(Path.ChangeExtension(xmlFile, newExtension), json); - SaveTabSeparatedValueFiles(items, xmlFile); - } - logger.LogInformation("Done!"); - } - -} \ No newline at end of file diff --git a/Day/Helper-2024-01-05.cs b/Day/Helper-2024-01-05.cs new file mode 100644 index 0000000..9d4f526 --- /dev/null +++ b/Day/Helper-2024-01-05.cs @@ -0,0 +1,1347 @@ +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.Day; + +#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? Fields, + [property: JsonPropertyName("login")] Login Login + ); + + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(Item))] + private partial class ItemSourceGenerationContext : JsonSerializerContext + { + } + + private record Login( + [property: JsonPropertyName("uris")] IReadOnlyList 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 Folders, + [property: JsonPropertyName("items")] IReadOnlyList 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 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 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 itemFields = (from l in keePassFileGroupEntryStrings select new Field(l.Key, l.Value.Value, 0)).ToList(); + Login login = new(new Uri[] { 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; + } + + internal 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) ? "-" : string.Join('-', from l in value select l); + // string.IsNullOrEmpty(value) ? "-" : string.Join('-', from l in value orderby random.Next() select l); + string.IsNullOrEmpty(value) ? "-" : GetDeterministicHashCode(System.Text.Encoding.ASCII.GetBytes(value)).ToString(); + + private static List Filter(string xmlFile, ILogger logger, List items) + { + List results = []; + int? port; + string key; + Item result; + Login login; + string? uri; + string? host; + string? name; + Guid? folderId; + string? username; + List? check; + string? folderName; + DateTime? creationTime; + string joinedHostPorts; + DateTime? revisionDate; + List notes = []; + string? password = null; + string?[] checkPasswords; + Dictionary> 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> 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[] { 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 items, string xmlFile) + { + List 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); + } + + 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 @this, bool throwExceptions) + { + KeePassFile? result; + try + { + Type type = typeof(KeePassFile); + Stream stream = ToStream(@this.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 GetItems(Guid folderId, string folderName, KeePassFileGroup keePassFileGroup) + { + List 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 List GetItems(KeePassFileRoot keePassFileRoot) + { + List 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 GetFolders(List items) + { + List results = []; + Folder folder; + List 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; + } + + internal static void ConvertKeePassExport(ILogger logger, List args) + { + Root root; + string xml; + string json; + List items; + List folders; + KeePassFile? keePassFile; + List 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!"); + } + +} \ No newline at end of file diff --git a/Day/HelperDay.cs b/Day/HelperDay.cs index c4a675b..610c7c5 100644 --- a/Day/HelperDay.cs +++ b/Day/HelperDay.cs @@ -30,10 +30,10 @@ internal static class HelperDay Day.Helper20231205.SplitMarkdownFile(logger, args); else if (args[1] == "Day-Helper-2023-12-12") Day.Helper20231212.SplitJsonFile(logger, args); - else if (args[1] == "Day-Helper-2023-12-21") - Day.Helper20231221.ConvertKeePassExport(logger, args); else if (args[1] == "Day-Helper-2023-12-22") Day.Helper20231222.ConvertId(logger, args); + else if (args[1] == "Day-Helper-2024-01-05") + Day.Helper20240105.ConvertKeePassExport(logger, args); else throw new Exception(appSettings.Company); } diff --git a/Helpers/HelperMarkdown.cs b/Helpers/HelperMarkdown.cs index 8de04dc..b574675 100644 --- a/Helpers/HelperMarkdown.cs +++ b/Helpers/HelperMarkdown.cs @@ -1282,12 +1282,7 @@ internal static partial class HelperMarkdown string h1Line; string[] lines; string typeLine; - string createdLine; - string updatedLine; - DateTime creationDateTime; MarkdownFile markdownFile; - string createdLineCompare; - string updatedLineCompare; foreach (KeyValuePair relativeTo in relativeToCollection) { if (relativeTo.Value.Lines.Length == 0) @@ -1296,13 +1291,8 @@ internal static partial class HelperMarkdown lines = relativeTo.Value.Lines; markdownFile = relativeTo.Value.MarkdownFile; results.AddRange(lines); - creationDateTime = markdownFile.CreationDateTime > markdownFile.LastWriteDateTime ? markdownFile.LastWriteDateTime : markdownFile.CreationDateTime; typeLine = $"type: \"{appSettings.DefaultNoteType}\""; h1Line = $"# {markdownFile.FileNameWithoutExtension}"; - createdLineCompare = $"created: \"{creationDateTime.ToUniversalTime():yyyy-MM-dd}T"; - createdLine = $"created: \"{creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\""; - updatedLineCompare = $"updated: \"{markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-dd}T"; - updatedLine = $"updated: \"{markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\""; if (markdownFile.LineNumber.FrontMatterYamlEnd is null) { if (markdownFile.LineNumber.H1 is null) @@ -1312,8 +1302,6 @@ internal static partial class HelperMarkdown results.Insert(0, string.Empty); } results.Insert(0, "---"); - results.Insert(0, updatedLine); - results.Insert(0, createdLine); results.Insert(0, typeLine); results.Insert(0, "---"); } @@ -1327,20 +1315,8 @@ internal static partial class HelperMarkdown } if (markdownFile.LineNumber.Type is null) results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, typeLine); - if (markdownFile.LineNumber.Updated is null) - results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine); - else - { - if (results[markdownFile.LineNumber.Updated.Value].Contains('$')) - continue; - if (results[markdownFile.LineNumber.Updated.Value][..updatedLineCompare.Length] == updatedLineCompare) - continue; - results[markdownFile.LineNumber.Updated.Value] = updatedLine; - } - if (markdownFile.LineNumber.Created is null) - results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, createdLine); - else if (results[markdownFile.LineNumber.Created.Value][..createdLineCompare.Length] != createdLineCompare) - results[markdownFile.LineNumber.Created.Value] = createdLine; + if (markdownFile.LineNumber.H1 is not null && markdownFile.LineNumber.Type is not null) + continue; } File.WriteAllLines(markdownFile.File, results); File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);