From 56b0cb02ac91cd32b060d6ea100285ad0f0e5c86 Mon Sep 17 00:00:00 2001 From: Aptivi CEO Date: Fri, 11 Aug 2023 17:54:32 +0300 Subject: [PATCH] add - doc - Added full support for MeCard We've added support for MeCard --- We've added full support for MeCard by parsing it and converting it to its VCard 3.0 equivalent. --- Type: add Breaking: False Doc Required: True Part: 1/1 --- VisualCard.ShowContacts/Program.cs | 13 ++- VisualCard/Converters/MeCard.cs | 145 +++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 VisualCard/Converters/MeCard.cs diff --git a/VisualCard.ShowContacts/Program.cs b/VisualCard.ShowContacts/Program.cs index b68ed3b..1d177c5 100644 --- a/VisualCard.ShowContacts/Program.cs +++ b/VisualCard.ShowContacts/Program.cs @@ -45,17 +45,26 @@ static void Main(string[] args) bool save = args.Contains("-save"); bool dbg = args.Contains("-debug"); bool android = args.Contains("-android"); + bool mecard = args.Contains("-mecard"); // If debug, wait for debugger if (dbg) Debugger.Launch(); + // If mecard, get a MeCard string + string meCardString = ""; + if (mecard) + meCardString = args[^1]; + // Initialize stopwatch Stopwatch elapsed = new(); elapsed.Start(); // Get parsers - List ContactParsers = android ? AndroidContactsDb.GetContactsFromDb(args[0]) : CardTools.GetCardParsers(args[0]); + List ContactParsers = + android ? AndroidContactsDb.GetContactsFromDb(args[0]) : + mecard ? MeCard.GetContactsFromMeCardString(meCardString) : + CardTools.GetCardParsers(args[0]); List Contacts = new(); // Parse all contacts @@ -170,4 +179,4 @@ static void Main(string[] args) } } } -} \ No newline at end of file +} diff --git a/VisualCard/Converters/MeCard.cs b/VisualCard/Converters/MeCard.cs new file mode 100644 index 0000000..63b2b2f --- /dev/null +++ b/VisualCard/Converters/MeCard.cs @@ -0,0 +1,145 @@ +/* + * MIT License + * + * Copyright (c) 2021-2022 Aptivi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +using Microsoft.Data.Sqlite; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System; +using VisualCard.Parsers; +using System.Linq; + +namespace VisualCard.Converters +{ + /// + /// MeCard string generator and converter + /// + public static class MeCard + { + /// + /// Gets all contacts from a MeCard string + /// + /// MeCard string + /// + /// + /// + public static List GetContactsFromMeCardString(string meCardString) + { + // Check to see if the MeCard string is valid + if (string.IsNullOrWhiteSpace(meCardString)) + throw new InvalidDataException("MeCard string should not be empty."); + if (!meCardString.StartsWith("MECARD:") && !meCardString.EndsWith(";;")) + throw new InvalidDataException("This string doesn't represent a valid MeCard contact."); + + // Now, parse it. + List cardParsers; + try + { + // Split the meCard string from the beginning and the ending + meCardString = meCardString.Substring(meCardString.IndexOf(":") + 1, meCardString.IndexOf(";;") - (meCardString.IndexOf(":") + 1)); + + // Split the values from the semicolons + var values = meCardString.Split(';'); + string fullName = ""; + + // Replace all the commas found with semicolons if possible + for (int i = 0; i < values.Length; i++) + { + string value = values[i]; + + // "SOUND:" here is actually just a Kana name, so blacklist it. + if (value.StartsWith("SOUND:")) + continue; + + // Now, replace all the commas in Name and Address with the semicolons. + if (value.StartsWith("N:") || value.StartsWith("ADR:")) + values[i] = value.Replace(",", ";"); + + // Build a full name + if (value.StartsWith("N:")) + { + var nameSplits = value.Substring(2).Split(','); + fullName = $"{nameSplits[1]} {nameSplits[0]}"; + } + + // As VisualCard doesn't support ADR: yet, why don't we just make it ADR; until we improve type detecion? + if (value.StartsWith("ADR:")) + values[i] = $"ADR;TYPE=HOME{values[i].Substring(3)}"; + + // MeCard stores birthday date in this format: + // 19700310 + // Digit 1-4: Year + // Digit 5-6: Month + // Digit 7-8: Day + // However, .NET's DateTime doesn't support it, so we verify the number, split it, and make an appropriate string for this. + if (value.StartsWith("BDAY:")) + { + int birthNum = int.Parse(value.Substring(5)); + var birthDigits = GetDigits(birthNum).ToList(); + int birthYear = (birthDigits[0] * 1000) + (birthDigits[1] * 100) + (birthDigits[2] * 10) + birthDigits[3]; + int birthMonth = (birthDigits[4] * 10) + birthDigits[5]; + int birthDay = (birthDigits[6] * 10) + birthDigits[7]; + var birthDate = new DateTime(birthYear, birthMonth, birthDay); + values[i] = $"BDAY:{birthDate:g}"; + } + } + + // Install the values! + var masterContactBuilder = new StringBuilder( + $""" + BEGIN:VCARD + VERSION:3.0 + {string.Join(Environment.NewLine, values)} + FN:{fullName} + END:VCARD + """ + ); + + // Now, invoke VisualCard to give us the card parsers + cardParsers = CardTools.GetCardParsersFromString(masterContactBuilder.ToString()); + } + catch (Exception ex) + { + throw new InvalidDataException("The MeCard contact string is not valid.", ex); + } + + return cardParsers; + } + + private static IEnumerable GetDigits(int num) + { + int individualFactor = 0; + int tennerFactor = Convert.ToInt32(Math.Pow(10, num.ToString().Length)); + while (tennerFactor > 1) + { + num -= tennerFactor * individualFactor; + tennerFactor /= 10; + individualFactor = num / tennerFactor; + yield return individualFactor; + } + } + + } +}