diff --git a/src/keepass2android/services/Kp2aAutofillParser/AutofillParser.cs b/src/keepass2android/services/Kp2aAutofillParser/AutofillParser.cs deleted file mode 100644 index 2824d35be..000000000 --- a/src/keepass2android/services/Kp2aAutofillParser/AutofillParser.cs +++ /dev/null @@ -1,956 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Newtonsoft.Json; -using Formatting = System.Xml.Formatting; - -namespace Kp2aAutofillParser -{ - public class W3cHints - { - - // Supported W3C autofill tokens (https://html.spec.whatwg.org/multipage/forms.html#autofill) - public const string HONORIFIC_PREFIX = "honorific-prefix"; - public const string NAME = "name"; - public const string GIVEN_NAME = "given-name"; - public const string ADDITIONAL_NAME = "additional-name"; - public const string FAMILY_NAME = "family-name"; - public const string HONORIFIC_SUFFIX = "honorific-suffix"; - public const string USERNAME = "username"; - public const string NEW_PASSWORD = "new-password"; - public const string CURRENT_PASSWORD = "current-password"; - public const string ORGANIZATION_TITLE = "organization-title"; - public const string ORGANIZATION = "organization"; - public const string STREET_ADDRESS = "street-address"; - public const string ADDRESS_LINE1 = "address-line1"; - public const string ADDRESS_LINE2 = "address-line2"; - public const string ADDRESS_LINE3 = "address-line3"; - public const string ADDRESS_LEVEL4 = "address-level4"; - public const string ADDRESS_LEVEL3 = "address-level3"; - public const string ADDRESS_LEVEL2 = "address-level2"; - public const string ADDRESS_LEVEL1 = "address-level1"; - public const string COUNTRY = "country"; - public const string COUNTRY_NAME = "country-name"; - public const string POSTAL_CODE = "postal-code"; - public const string CC_NAME = "cc-name"; - public const string CC_GIVEN_NAME = "cc-given-name"; - public const string CC_ADDITIONAL_NAME = "cc-additional-name"; - public const string CC_FAMILY_NAME = "cc-family-name"; - public const string CC_NUMBER = "cc-number"; - public const string CC_EXPIRATION = "cc-exp"; - public const string CC_EXPIRATION_MONTH = "cc-exp-month"; - public const string CC_EXPIRATION_YEAR = "cc-exp-year"; - public const string CC_CSC = "cc-csc"; - public const string CC_TYPE = "cc-type"; - public const string TRANSACTION_CURRENCY = "transaction-currency"; - public const string TRANSACTION_AMOUNT = "transaction-amount"; - public const string LANGUAGE = "language"; - public const string BDAY = "bday"; - public const string BDAY_DAY = "bday-day"; - public const string BDAY_MONTH = "bday-month"; - public const string BDAY_YEAR = "bday-year"; - public const string SEX = "sex"; - public const string URL = "url"; - public const string PHOTO = "photo"; - // Optional W3C prefixes - public const string PREFIX_SECTION = "section-"; - public const string SHIPPING = "shipping"; - public const string BILLING = "billing"; - // W3C prefixes below... - public const string PREFIX_HOME = "home"; - public const string PREFIX_WORK = "work"; - public const string PREFIX_FAX = "fax"; - public const string PREFIX_PAGER = "pager"; - // ... require those suffix - public const string TEL = "tel"; - public const string TEL_COUNTRY_CODE = "tel-country-code"; - public const string TEL_NATIONAL = "tel-national"; - public const string TEL_AREA_CODE = "tel-area-code"; - public const string TEL_LOCAL = "tel-local"; - public const string TEL_LOCAL_PREFIX = "tel-local-prefix"; - public const string TEL_LOCAL_SUFFIX = "tel-local-suffix"; - public const string TEL_EXTENSION = "tel_extension"; - public const string EMAIL = "email"; - public const string IMPP = "impp"; - - private W3cHints() - { - } - - - - public static bool isW3cSectionPrefix(string hint) - { - return hint.ToLower().StartsWith(W3cHints.PREFIX_SECTION); - } - - public static bool isW3cAddressType(string hint) - { - switch (hint.ToLower()) - { - case W3cHints.SHIPPING: - case W3cHints.BILLING: - return true; - } - return false; - } - - public static bool isW3cTypePrefix(string hint) - { - switch (hint.ToLower()) - { - case W3cHints.PREFIX_WORK: - case W3cHints.PREFIX_FAX: - case W3cHints.PREFIX_HOME: - case W3cHints.PREFIX_PAGER: - return true; - } - return false; - } - - public static bool isW3cTypeHint(string hint) - { - switch (hint.ToLower()) - { - case W3cHints.TEL: - case W3cHints.TEL_COUNTRY_CODE: - case W3cHints.TEL_NATIONAL: - case W3cHints.TEL_AREA_CODE: - case W3cHints.TEL_LOCAL: - case W3cHints.TEL_LOCAL_PREFIX: - case W3cHints.TEL_LOCAL_SUFFIX: - case W3cHints.TEL_EXTENSION: - case W3cHints.EMAIL: - case W3cHints.IMPP: - return true; - } - return false; - } - } - /// - /// FilledAutofillFieldCollection is the model that holds all of the data on a client app's page, - /// plus the dataset name associated with it. - /// - public class FilledAutofillFieldCollection where FieldT:InputField - { - public Dictionary> HintMap { get; } - public string DatasetName { get; set; } - - public FilledAutofillFieldCollection(Dictionary> hintMap, string datasetName = "") - { - //recreate hint map making sure we compare case insensitive - HintMap = BuildHintMap(); - foreach (var p in hintMap) - HintMap.Add(p.Key, p.Value); - DatasetName = datasetName; - } - - public FilledAutofillFieldCollection() : this(BuildHintMap()) - { } - - private static Dictionary> BuildHintMap() - { - return new Dictionary>(StringComparer.OrdinalIgnoreCase); - } - - /// - /// Adds a filledAutofillField to the collection, indexed by all of its hints. - /// - /// The add. - /// Filled autofill field. - public void Add(FilledAutofillField filledAutofillField) - { - foreach (string hint in filledAutofillField.AutofillHints) - { - if (AutofillHintsHelper.IsSupportedHint(hint)) - { - HintMap.TryAdd(hint, filledAutofillField); - } - } - - } - - - - - /// - /// Takes in a list of autofill hints (`autofillHints`), usually associated with a View or set of - /// Views. Returns whether any of the filled fields on the page have at least 1 of these - /// `autofillHint`s. - /// - /// true, if with hints was helpsed, false otherwise. - /// Autofill hints. - public bool HelpsWithHints(List autofillHints) - { - for (int i = 0; i < autofillHints.Count; i++) - { - var autofillHint = autofillHints[i]; - if (HintMap.ContainsKey(autofillHint) && !HintMap[autofillHint].IsNull()) - { - return true; - } - } - return false; - } - } - public class AutofillHintsHelper - { - public const string AutofillHint2faAppOtp = "2faAppOTPCode"; - public const string AutofillHintBirthDateDay = "birthDateDay"; - public const string AutofillHintBirthDateFull = "birthDateFull"; - public const string AutofillHintBirthDateMonth = "birthDateMonth"; - public const string AutofillHintBirthDateYear = "birthDateYear"; - public const string AutofillHintCreditCardExpirationDate = "creditCardExpirationDate"; - public const string AutofillHintCreditCardExpirationDay = "creditCardExpirationDay"; - public const string AutofillHintCreditCardExpirationMonth = "creditCardExpirationMonth"; - public const string AutofillHintCreditCardExpirationYear = "creditCardExpirationYear"; - public const string AutofillHintCreditCardNumber = "creditCardNumber"; - public const string AutofillHintCreditCardSecurityCode = "creditCardSecurityCode"; - public const string AutofillHintEmailAddress = "emailAddress"; - public const string AutofillHintEmailOtp = "emailOTPCode"; - public const string AutofillHintGender = "gender"; - public const string AutofillHintName = "name"; - public const string AutofillHintNewPassword = "newPassword"; - public const string AutofillHintNewUsername = "newUsername"; - public const string AutofillHintNotApplicable = "notApplicable"; - public const string AutofillHintPassword = "password"; - public const string AutofillHintPersonName = "personName"; - public const string AutofillHintPersonNameFAMILY = "personFamilyName"; - public const string AutofillHintPersonNameGIVEN = "personGivenName"; - public const string AutofillHintPersonNameMIDDLE = "personMiddleName"; - public const string AutofillHintPersonNameMIDDLE_INITIAL = "personMiddleInitial"; - public const string AutofillHintPersonNamePREFIX = "personNamePrefix"; - public const string AutofillHintPersonNameSUFFIX = "personNameSuffix"; - public const string AutofillHintPhone = "phone"; - public const string AutofillHintPhoneContryCode = "phoneCountryCode"; - public const string AutofillHintPostalAddressAPT_NUMBER = "aptNumber"; - public const string AutofillHintPostalAddressCOUNTRY = "addressCountry"; - public const string AutofillHintPostalAddressDEPENDENT_LOCALITY = "dependentLocality"; - public const string AutofillHintPostalAddressEXTENDED_ADDRESS = "extendedAddress"; - public const string AutofillHintPostalAddressEXTENDED_POSTAL_CODE = "extendedPostalCode"; - public const string AutofillHintPostalAddressLOCALITY = "addressLocality"; - public const string AutofillHintPostalAddressREGION = "addressRegion"; - public const string AutofillHintPostalAddressSTREET_ADDRESS = "streetAddress"; - public const string AutofillHintPostalCode = "postalCode"; - public const string AutofillHintPromoCode = "promoCode"; - public const string AutofillHintSMS_OTP = "smsOTPCode"; - public const string AutofillHintUPI_VPA = "upiVirtualPaymentAddress"; - public const string AutofillHintUsername = "username"; - public const string AutofillHintWifiPassword = "wifiPassword"; - public const string AutofillHintPhoneNational = "phoneNational"; - public const string AutofillHintPhoneNumber = "phoneNumber"; - public const string AutofillHintPhoneNumberDevice = "phoneNumberDevice"; - public const string AutofillHintPostalAddress = "postalAddress"; - - private static readonly HashSet _allSupportedHints = new HashSet(StringComparer.OrdinalIgnoreCase) - { - AutofillHintCreditCardExpirationDate, - AutofillHintCreditCardExpirationDay, - AutofillHintCreditCardExpirationMonth, - AutofillHintCreditCardExpirationYear, - AutofillHintCreditCardNumber, - AutofillHintCreditCardSecurityCode, - AutofillHintEmailAddress, - AutofillHintPhone, - AutofillHintName, - AutofillHintPassword, - AutofillHintPostalAddress, - AutofillHintPostalCode, - AutofillHintUsername, - W3cHints.HONORIFIC_PREFIX, - W3cHints.NAME, - W3cHints.GIVEN_NAME, - W3cHints.ADDITIONAL_NAME, - W3cHints.FAMILY_NAME, - W3cHints.HONORIFIC_SUFFIX, - W3cHints.USERNAME, - W3cHints.NEW_PASSWORD, - W3cHints.CURRENT_PASSWORD, - W3cHints.ORGANIZATION_TITLE, - W3cHints.ORGANIZATION, - W3cHints.STREET_ADDRESS, - W3cHints.ADDRESS_LINE1, - W3cHints.ADDRESS_LINE2, - W3cHints.ADDRESS_LINE3, - W3cHints.ADDRESS_LEVEL4, - W3cHints.ADDRESS_LEVEL3, - W3cHints.ADDRESS_LEVEL2, - W3cHints.ADDRESS_LEVEL1, - W3cHints.COUNTRY, - W3cHints.COUNTRY_NAME, - W3cHints.POSTAL_CODE, - W3cHints.CC_NAME, - W3cHints.CC_GIVEN_NAME, - W3cHints.CC_ADDITIONAL_NAME, - W3cHints.CC_FAMILY_NAME, - W3cHints.CC_NUMBER, - W3cHints.CC_EXPIRATION, - W3cHints.CC_EXPIRATION_MONTH, - W3cHints.CC_EXPIRATION_YEAR, - W3cHints.CC_CSC, - W3cHints.CC_TYPE, - W3cHints.TRANSACTION_CURRENCY, - W3cHints.TRANSACTION_AMOUNT, - W3cHints.LANGUAGE, - W3cHints.BDAY, - W3cHints.BDAY_DAY, - W3cHints.BDAY_MONTH, - W3cHints.BDAY_YEAR, - W3cHints.SEX, - W3cHints.URL, - W3cHints.PHOTO, - W3cHints.TEL, - W3cHints.TEL_COUNTRY_CODE, - W3cHints.TEL_NATIONAL, - W3cHints.TEL_AREA_CODE, - W3cHints.TEL_LOCAL, - W3cHints.TEL_LOCAL_PREFIX, - W3cHints.TEL_LOCAL_SUFFIX, - W3cHints.TEL_EXTENSION, - W3cHints.EMAIL, - W3cHints.IMPP, - }; - - private static readonly List> partitionsOfCanonicalHints = new List>() - { - - new HashSet(StringComparer.OrdinalIgnoreCase) - { - AutofillHintEmailAddress, - AutofillHintPhone, - AutofillHintName, - AutofillHintPassword, - AutofillHintUsername, - W3cHints.HONORIFIC_PREFIX, - W3cHints.NAME, - W3cHints.GIVEN_NAME, - W3cHints.ADDITIONAL_NAME, - W3cHints.FAMILY_NAME, - W3cHints.HONORIFIC_SUFFIX, - W3cHints.ORGANIZATION_TITLE, - W3cHints.ORGANIZATION, - W3cHints.LANGUAGE, - W3cHints.BDAY, - W3cHints.BDAY_DAY, - W3cHints.BDAY_MONTH, - W3cHints.BDAY_YEAR, - W3cHints.SEX, - W3cHints.URL, - W3cHints.PHOTO, - W3cHints.TEL, - W3cHints.TEL_COUNTRY_CODE, - W3cHints.TEL_NATIONAL, - W3cHints.TEL_AREA_CODE, - W3cHints.TEL_LOCAL, - W3cHints.TEL_LOCAL_PREFIX, - W3cHints.TEL_LOCAL_SUFFIX, - W3cHints.TEL_EXTENSION, - W3cHints.IMPP, - }, - new HashSet(StringComparer.OrdinalIgnoreCase) - { - AutofillHintPostalAddress, - AutofillHintPostalCode, - - W3cHints.STREET_ADDRESS, - W3cHints.ADDRESS_LINE1, - W3cHints.ADDRESS_LINE2, - W3cHints.ADDRESS_LINE3, - W3cHints.ADDRESS_LEVEL4, - W3cHints.ADDRESS_LEVEL3, - W3cHints.ADDRESS_LEVEL2, - W3cHints.ADDRESS_LEVEL1, - W3cHints.COUNTRY, - W3cHints.COUNTRY_NAME - }, - new HashSet(StringComparer.OrdinalIgnoreCase) - { - AutofillHintCreditCardExpirationDate, - AutofillHintCreditCardExpirationDay, - AutofillHintCreditCardExpirationMonth, - AutofillHintCreditCardExpirationYear, - AutofillHintCreditCardNumber, - AutofillHintCreditCardSecurityCode, - - W3cHints.CC_NAME, - W3cHints.CC_GIVEN_NAME, - W3cHints.CC_ADDITIONAL_NAME, - W3cHints.CC_FAMILY_NAME, - W3cHints.CC_TYPE, - W3cHints.TRANSACTION_CURRENCY, - W3cHints.TRANSACTION_AMOUNT, - }, - - }; - - private static readonly Dictionary hintToCanonicalReplacement = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - {W3cHints.EMAIL, AutofillHintEmailAddress}, - {W3cHints.USERNAME, AutofillHintUsername}, - {W3cHints.CURRENT_PASSWORD, AutofillHintPassword}, - {W3cHints.NEW_PASSWORD, AutofillHintPassword}, - {W3cHints.CC_EXPIRATION_MONTH, AutofillHintCreditCardExpirationMonth }, - {W3cHints.CC_EXPIRATION_YEAR, AutofillHintCreditCardExpirationYear }, - {W3cHints.CC_EXPIRATION, AutofillHintCreditCardExpirationDate }, - {W3cHints.CC_NUMBER, AutofillHintCreditCardNumber }, - {W3cHints.CC_CSC, AutofillHintCreditCardSecurityCode }, - {W3cHints.POSTAL_CODE, AutofillHintPostalCode }, - - - }; - - public static bool IsSupportedHint(string hint) - { - return _allSupportedHints.Contains(hint); - } - - - public static string[] FilterForSupportedHints(string[] hints) - { - if (hints == null) - return Array.Empty(); - var filteredHints = new string[hints.Length]; - int i = 0; - foreach (var hint in hints) - { - if (IsSupportedHint(hint)) - { - filteredHints[i++] = hint; - } - - } - var finalFilteredHints = new string[i]; - Array.Copy(filteredHints, 0, finalFilteredHints, 0, i); - return finalFilteredHints; - } - - - - /// - /// transforms hints by replacing some W3cHints by their Android counterparts and transforming everything to lowercase - /// - public static List ConvertToCanonicalHints(string[] supportedHints) - { - List result = new List(); - foreach (string hint in supportedHints) - { - string canonicalHint; - if (!hintToCanonicalReplacement.TryGetValue(hint, out canonicalHint)) - canonicalHint = hint; - result.Add(canonicalHint.ToLower()); - } - return result; - - } - - public static int GetPartitionIndex(string hint) - { - for (int i = 0; i < partitionsOfCanonicalHints.Count; i++) - { - if (partitionsOfCanonicalHints[i].Contains(hint)) - { - return i; - } - } - return -1; - } - - public static FilledAutofillFieldCollection FilterForPartition(FilledAutofillFieldCollection autofillFields, int partitionIndex) where FieldT: InputField - { - FilledAutofillFieldCollection filteredCollection = - new FilledAutofillFieldCollection { DatasetName = autofillFields.DatasetName }; - - if (partitionIndex == -1) - return filteredCollection; - - foreach (var field in autofillFields.HintMap.Values.Distinct()) - { - foreach (var hint in field.AutofillHints) - { - if (GetPartitionIndex(hint) == partitionIndex) - { - filteredCollection.Add(field); - break; - } - } - } - - return filteredCollection; - } - - public static FilledAutofillFieldCollection FilterForPartition(FilledAutofillFieldCollection filledAutofillFieldCollection, List autofillFieldsFocusedAutofillCanonicalHints) where FieldT: InputField - { - - //only apply partition data if we have FocusedAutofillCanonicalHints. This may be empty on buggy Firefox. - if (autofillFieldsFocusedAutofillCanonicalHints.Any()) - { - int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFieldsFocusedAutofillCanonicalHints.FirstOrDefault()); - return AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, partitionIndex); - } - - return filledAutofillFieldCollection; - } - } - /// - /// This enum represents the Android.Text.InputTypes values. For testability, this is duplicated here. - /// - public enum InputTypes - { - ClassDatetime = 4, - ClassNumber = 2, - ClassPhone = 3, - ClassText = 1, - DatetimeVariationDate = 16, - DatetimeVariationNormal = 0, - DatetimeVariationTime = 32, - MaskClass = 15, - MaskFlags = 16773120, - MaskVariation = 4080, - Null = 0, - NumberFlagDecimal = 8192, - NumberFlagSigned = 4096, - NumberVariationNormal = 0, - NumberVariationPassword = 16, - TextFlagAutoComplete = 65536, - TextFlagAutoCorrect = 32768, - TextFlagCapCharacters = 4096, - TextFlagCapSentences = 16384, - TextFlagCapWords = 8192, - TextFlagEnableTextConversionSuggestions = 1048576, - TextFlagImeMultiLine = 262144, - TextFlagMultiLine = 131072, - TextFlagNoSuggestions = 524288, - TextVariationEmailAddress = 32, - TextVariationEmailSubject = 48, - TextVariationFilter = 176, - TextVariationLongMessage = 80, - TextVariationNormal = 0, - TextVariationPassword = 128, - TextVariationPersonName = 96, - TextVariationPhonetic = 192, - TextVariationPostalAddress = 112, - TextVariationShortMessage = 64, - TextVariationUri = 16, - TextVariationVisiblePassword = 144, - TextVariationWebEditText = 160, - TextVariationWebEmailAddress = 208, - TextVariationWebPassword = 224 - } - - public interface IKp2aDigitalAssetLinksDataSource - { - bool IsTrustedApp(string packageName); - bool IsTrustedLink(string domain, string targetPackage); - bool IsEnabled(); - - } - - class TimeUtil - { - private static DateTime? m_dtUnixRoot = null; - public static DateTime ConvertUnixTime(double dtUnix) - { - try - { - if (!m_dtUnixRoot.HasValue) - m_dtUnixRoot = (new DateTime(1970, 1, 1, 0, 0, 0, 0, - DateTimeKind.Utc)).ToLocalTime(); - - return m_dtUnixRoot.Value.AddSeconds(dtUnix); - } - catch (Exception) { Debug.Assert(false); } - - return DateTime.UtcNow; - } - } - - public class FilledAutofillField where FieldT : InputField - { - private string[] _autofillHints; - public string TextValue { get; set; } - public long? DateValue { get; set; } - public bool? ToggleValue { get; set; } - - public string ValueToString() - { - if (DateValue != null) - { - return TimeUtil.ConvertUnixTime((long)DateValue / 1000.0).ToLongDateString(); - } - if (ToggleValue != null) - return ToggleValue.ToString(); - return TextValue; - } - - /// - /// returns the autofill hints for the filled field. These are always lowercased for simpler string comparison. - /// - public string[] AutofillHints - { - get - { - return _autofillHints; - } - set - { - _autofillHints = value; - for (int i = 0; i < _autofillHints.Length; i++) - _autofillHints[i] = _autofillHints[i].ToLower(); - } - } - - - public FilledAutofillField() - { } - - public FilledAutofillField(FieldT inputField) - : this(inputField, inputField.AutofillHints) - { - - } - - public FilledAutofillField(FieldT inputField, string[] hints) - { - - string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(hints); - List hintList = new List(); - - string nextHint = null; - for (int i = 0; i < rawHints.Length; i++) - { - string hint = rawHints[i]; - if (i < rawHints.Length - 1) - { - nextHint = rawHints[i + 1]; - } - // First convert the compound W3C autofill hints - if (W3cHints.isW3cSectionPrefix(hint) && i < rawHints.Length - 1) - { - hint = rawHints[++i]; - - if (i < rawHints.Length - 1) - { - nextHint = rawHints[i + 1]; - } - } - if (W3cHints.isW3cTypePrefix(hint) && nextHint != null && W3cHints.isW3cTypeHint(nextHint)) - { - hint = nextHint; - i++; - - } - if (W3cHints.isW3cAddressType(hint) && nextHint != null) - { - hint = nextHint; - i++; - - } - - // Then check if the "actual" hint is supported. - if (AutofillHintsHelper.IsSupportedHint(hint)) - { - hintList.Add(hint); - } - else - { - - } - } - AutofillHints = AutofillHintsHelper.ConvertToCanonicalHints(hintList.ToArray()).ToArray(); - - - } - - public bool IsNull() - { - return TextValue == null && DateValue == null && ToggleValue == null; - } - - public override bool Equals(object obj) - { - if (this == obj) return true; - if (obj == null || GetType() != obj.GetType()) return false; - - FilledAutofillField that = (FilledAutofillField)obj; - - if (!TextValue?.Equals(that.TextValue) ?? that.TextValue != null) - return false; - if (DateValue != null ? !DateValue.Equals(that.DateValue) : that.DateValue != null) - return false; - return ToggleValue != null ? ToggleValue.Equals(that.ToggleValue) : that.ToggleValue == null; - } - - public override int GetHashCode() - { - unchecked - { - var result = TextValue != null ? TextValue.GetHashCode() : 0; - result = 31 * result + (DateValue != null ? DateValue.GetHashCode() : 0); - result = 31 * result + (ToggleValue != null ? ToggleValue.GetHashCode() : 0); - return result; - } - } - } - - /// - /// Base class for everything that is a input field which might (or might not) be autofilled. - /// For testability, this is independent from Android classes like ViewNode - /// - public abstract class InputField - { - public string IdEntry { get; set; } - public string Hint { get; set; } - public string ClassName { get; set; } - public string[] AutofillHints { get; set; } - public bool IsFocused { get; set; } - - public InputTypes InputType { get; set; } - - public string HtmlInfoTag { get; set; } - public string HtmlInfoTypeAttribute { get; set; } - - } - - public class AutofillView where TField : InputField - { - public List InputFields { get; set; } = new List(); - - public string PackageId { get; set; } = null; - public string WebDomain { get; set; } = null; - } - - public interface ILogger - { - void Log(string x); - } - - public class StructureParserBase where FieldT: InputField - { - private readonly ILogger _log; - private readonly IKp2aDigitalAssetLinksDataSource _digitalAssetLinksDataSource; - - private readonly List _autofillHintsForLogin = new List - { - AutofillHintsHelper.AutofillHintPassword, - AutofillHintsHelper.AutofillHintUsername, - AutofillHintsHelper.AutofillHintEmailAddress - }; - - public string PackageId { get; set; } - - public Dictionary FieldsMappedToHints = new Dictionary(); - - public StructureParserBase(ILogger logger, IKp2aDigitalAssetLinksDataSource digitalAssetLinksDataSource) - { - _log = logger; - _digitalAssetLinksDataSource = digitalAssetLinksDataSource; - } - - public class AutofillTargetId - { - public string PackageName { get; set; } - - public string PackageNameWithPseudoSchema - { - get { return AndroidAppScheme + PackageName; } - } - - public const string AndroidAppScheme = "androidapp://"; - - public string WebDomain { get; set; } - - /// - /// If PackageName and WebDomain are not compatible (by DAL or because PackageName is a trusted browser in which case we treat all domains as "compatible" - /// we need to issue a warning. If we would fill credentials for the package, a malicious website could try to get credentials for the app. - /// If we would fill credentials for the domain, a malicious app could get credentials for the domain. - /// - public bool IncompatiblePackageAndDomain { get; set; } - - public string DomainOrPackage - { - get - { - return WebDomain ?? PackageNameWithPseudoSchema; - } - } - } - - public AutofillTargetId ParseForFill(bool isManual, AutofillView autofillView) - { - return Parse(true, isManual, autofillView); - } - - public AutofillTargetId ParseForSave(AutofillView autofillView) - { - return Parse(false, true, autofillView); - } - - /// - /// Traverse AssistStructure and add ViewNode metadata to a flat list. - /// - /// The parse. - /// If set to true for fill. - /// - protected virtual AutofillTargetId Parse(bool forFill, bool isManualRequest, AutofillView autofillView) - { - AutofillTargetId result = new AutofillTargetId() - { - PackageName = autofillView.PackageId, - WebDomain = autofillView.WebDomain - }; - - _log.Log("parsing autofillStructure..."); - - //TODO remove from production - _log.Log("will log the autofillStructure..."); - string debugInfo = JsonConvert.SerializeObject(autofillView, Newtonsoft.Json.Formatting.Indented); - _log.Log("will log the autofillStructure... size is " + debugInfo.Length); - _log.Log("This is the autofillStructure: \n\n " + debugInfo); - - //go through each input field and determine username/password fields. - //Depending on the target this can require more or less heuristics. - // * if there is a valid & supported autofill hint, we assume that all fields which should be filled do have an appropriate Autofill hint - // * if there is no such autofill hint, we use IsPassword to - - HashSet autofillHintsOfAllFields = autofillView.InputFields.Where(f => f.AutofillHints != null) - .SelectMany(f => f.AutofillHints).ToHashSet(); - bool hasLoginAutofillHints = autofillHintsOfAllFields.Intersect(_autofillHintsForLogin).Any(); - - if (hasLoginAutofillHints) - { - foreach (var viewNode in autofillView.InputFields) - { - string[] viewHints = viewNode.AutofillHints; - if (viewHints == null) - continue; - if (viewHints.Intersect(_autofillHintsForLogin).Any()) - { - FieldsMappedToHints.Add(viewNode, viewHints); - } - - } - } - else - { - //determine password fields, first by type, then by hint: - List passwordFields = autofillView.InputFields.Where(f => IsEditText(f) && IsPassword(f)).ToList(); - if (!passwordFields.Any()) - { - passwordFields = autofillView.InputFields.Where(f => IsEditText(f) && HasPasswordHint(f)).ToList(); - } - - //determine username fields. Try by hint, if that fails use the one before the password - List usernameFields = autofillView.InputFields.Where(f => IsEditText(f) && HasUsernameHint(f)).ToList(); - if (!usernameFields.Any()) - { - foreach (var passwordField in passwordFields) - { - var lastInputBeforePassword = autofillView.InputFields - .TakeWhile(f => IsEditText(f) && f != passwordField && !passwordFields.Contains(f)).LastOrDefault(); - if (lastInputBeforePassword != null) - usernameFields.Add(lastInputBeforePassword); - } - - } - - //for "heuristic determination" we demand that one of the filled fields is focused: - if (passwordFields.Concat(usernameFields).Any(f => f.IsFocused)) - { - foreach (var uf in usernameFields) - FieldsMappedToHints.Add(uf, new string[] { AutofillHintsHelper.AutofillHintUsername }); - foreach (var pf in passwordFields) - FieldsMappedToHints.Add(pf, new string[] { AutofillHintsHelper.AutofillHintPassword }); - } - } - - - if (!string.IsNullOrEmpty(autofillView.WebDomain) && _digitalAssetLinksDataSource.IsEnabled()) - { - result.IncompatiblePackageAndDomain = !_digitalAssetLinksDataSource.IsTrustedLink(autofillView.WebDomain, result.PackageName); - if (result.IncompatiblePackageAndDomain) - { - _log.Log($"DAL verification failed for {result.PackageName}/{result.WebDomain}"); - } - } - else - { - result.IncompatiblePackageAndDomain = false; - } - return result; - } - - private bool IsEditText(FieldT f) - { - return (f.ClassName == "android.widget.EditText" - || f.ClassName == "android.widget.AutoCompleteTextView" - || f.HtmlInfoTag == "input"); - } - - private static readonly HashSet _passwordHints = new HashSet { "password", "passwort" - /*, "passwordAuto", "pswd"*/ }; - private static bool HasPasswordHint(InputField f) - { - return IsAny(f.IdEntry, _passwordHints) || - IsAny(f.Hint, _passwordHints); - } - - private static readonly HashSet _usernameHints = new HashSet { "email", "e-mail", "username" }; - - private static bool HasUsernameHint(InputField f) - { - return IsAny(f.IdEntry, _usernameHints) || - IsAny(f.Hint, _usernameHints); - } - - private static bool IsAny(string value, IEnumerable terms) - { - if (string.IsNullOrWhiteSpace(value)) - { - return false; - } - var lowerValue = value.ToLowerInvariant(); - return terms.Any(t => lowerValue == t); - } - - private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass) - { - if (!InputTypes.MaskClass.HasFlag(inputTypeClass)) - throw new Exception("invalid inputTypeClass"); - return (((int)inputType) & (int)InputTypes.MaskClass) == (int)(inputTypeClass); - } - private static bool IsInputTypeVariation(InputTypes inputType, InputTypes inputTypeVariation) - { - if (!InputTypes.MaskVariation.HasFlag(inputTypeVariation)) - throw new Exception("invalid inputTypeVariation"); - return (((int)inputType) & (int)InputTypes.MaskVariation) == (int)(inputTypeVariation); - } - - private static bool IsPassword(InputField f) - { - InputTypes inputType = f.InputType; - - return - (!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) && - (!f.Hint?.ToLowerInvariant().Contains("search") ?? true) && - ( - (IsInputTypeClass(inputType, InputTypes.ClassText) - && - ( - IsInputTypeVariation(inputType, InputTypes.TextVariationPassword) - || IsInputTypeVariation(inputType, InputTypes.TextVariationVisiblePassword) - || IsInputTypeVariation(inputType, InputTypes.TextVariationWebPassword) - ) - ) - || (f.AutofillHints != null && f.AutofillHints.First() == "passwordAuto") - || (f.HtmlInfoTypeAttribute == "password") - ); - } - - - - - - - } -} diff --git a/src/keepass2android/services/Kp2aAutofillParser/Kp2aAutofillParser.csproj b/src/keepass2android/services/Kp2aAutofillParser/Kp2aAutofillParser.csproj deleted file mode 100644 index 375762cdd..000000000 --- a/src/keepass2android/services/Kp2aAutofillParser/Kp2aAutofillParser.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - netstandard2.1 - enable - - - - - - -