Compare commits
	
		
			7 Commits
		
	
	
		
			v1.09e-r4
			...
			v1.09e-r5b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					554f88c723 | ||
| 
						 | 
					4cd32d30c6 | ||
| 
						 | 
					d0da83182f | ||
| 
						 | 
					ec5f26e0cd | ||
| 
						 | 
					6110166af8 | ||
| 
						 | 
					6f10a04589 | ||
| 
						 | 
					e0c003fcb2 | 
							
								
								
									
										12
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Makefile
									
									
									
									
									
								
							@@ -314,6 +314,18 @@ clean_KP2AKdbLibrary:
 | 
			
		||||
	cd src/java/KP2AKdbLibrary && $(GRADLEW) clean
 | 
			
		||||
clean_PluginQR:
 | 
			
		||||
	cd src/java/PluginQR && $(GRADLEW) clean
 | 
			
		||||
clean_rm:
 | 
			
		||||
	rm -rf src/*/obj
 | 
			
		||||
	rm -rf src/*/bin
 | 
			
		||||
	rm -rf src/java/*/app/build
 | 
			
		||||
	rm -rf src/java/argon2/obj
 | 
			
		||||
	rm -rf src/java/argon2/libs
 | 
			
		||||
	rm -rf src/packages
 | 
			
		||||
	rm -rf src/java/KP2AKdbLibrary/app/.cxx
 | 
			
		||||
	rm -rf src/java/KP2ASoftkeyboard_AS/app/.cxx
 | 
			
		||||
	rm -rf src/SamsungPass/Xamarin.SamsungPass/SamsungPass/bin
 | 
			
		||||
	rm -rf src/SamsungPass/Xamarin.SamsungPass/SamsungPass/obj
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
# https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore-troubleshooting#other-potential-conditions
 | 
			
		||||
clean_nuget:
 | 
			
		||||
 
 | 
			
		||||
@@ -134,10 +134,10 @@ namespace Kp2aAutofillParser
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class FilledAutofillFieldCollection<FieldT> where FieldT:InputField
 | 
			
		||||
    {
 | 
			
		||||
        public Dictionary<string, FilledAutofillField<FieldT>> HintMap { get; }
 | 
			
		||||
        public Dictionary<string, FilledAutofillField> HintMap { get; }
 | 
			
		||||
        public string DatasetName { get; set; }
 | 
			
		||||
 | 
			
		||||
        public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField<FieldT>> hintMap, string datasetName = "")
 | 
			
		||||
        public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField> hintMap, string datasetName = "")
 | 
			
		||||
        {
 | 
			
		||||
            //recreate hint map making sure we compare case insensitive
 | 
			
		||||
            HintMap = BuildHintMap();
 | 
			
		||||
@@ -149,9 +149,9 @@ namespace Kp2aAutofillParser
 | 
			
		||||
        public FilledAutofillFieldCollection() : this(BuildHintMap())
 | 
			
		||||
        { }
 | 
			
		||||
 | 
			
		||||
        private static Dictionary<string, FilledAutofillField<FieldT>> BuildHintMap()
 | 
			
		||||
        private static Dictionary<string, FilledAutofillField> BuildHintMap()
 | 
			
		||||
        {
 | 
			
		||||
            return new Dictionary<string, FilledAutofillField<FieldT>>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
            return new Dictionary<string, FilledAutofillField>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@@ -159,7 +159,7 @@ namespace Kp2aAutofillParser
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>The add.</returns>
 | 
			
		||||
        /// <param name="filledAutofillField">Filled autofill field.</param>
 | 
			
		||||
        public void Add(FilledAutofillField<FieldT> filledAutofillField)
 | 
			
		||||
        public void Add(FilledAutofillField filledAutofillField)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (string hint in filledAutofillField.AutofillHints)
 | 
			
		||||
            {
 | 
			
		||||
@@ -572,7 +572,7 @@ namespace Kp2aAutofillParser
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class FilledAutofillField<FieldT> where FieldT : InputField
 | 
			
		||||
    public class FilledAutofillField
 | 
			
		||||
    {
 | 
			
		||||
        private string[] _autofillHints;
 | 
			
		||||
        public string TextValue { get; set; }
 | 
			
		||||
@@ -611,13 +611,13 @@ namespace Kp2aAutofillParser
 | 
			
		||||
        public FilledAutofillField()
 | 
			
		||||
        { }
 | 
			
		||||
 | 
			
		||||
        public FilledAutofillField(FieldT inputField)
 | 
			
		||||
        public FilledAutofillField(InputField inputField) 
 | 
			
		||||
            : this(inputField, inputField.AutofillHints)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public FilledAutofillField(FieldT inputField, string[] hints)
 | 
			
		||||
        public FilledAutofillField(InputField inputField, string[] hints)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(hints);
 | 
			
		||||
@@ -665,6 +665,7 @@ namespace Kp2aAutofillParser
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            AutofillHints = AutofillHintsHelper.ConvertToCanonicalLowerCaseHints(hintList.ToArray()).ToArray();
 | 
			
		||||
            inputField.FillFilledAutofillValue(this);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
@@ -679,7 +680,7 @@ namespace Kp2aAutofillParser
 | 
			
		||||
            if (this == obj) return true;
 | 
			
		||||
            if (obj == null || GetType() != obj.GetType()) return false;
 | 
			
		||||
 | 
			
		||||
            FilledAutofillField<FieldT> that = (FilledAutofillField<FieldT>)obj;
 | 
			
		||||
            FilledAutofillField that = (FilledAutofillField)obj;
 | 
			
		||||
 | 
			
		||||
            if (!TextValue?.Equals(that.TextValue) ?? that.TextValue != null)
 | 
			
		||||
                return false;
 | 
			
		||||
@@ -717,6 +718,8 @@ namespace Kp2aAutofillParser
 | 
			
		||||
        public string HtmlInfoTag { get; set; }
 | 
			
		||||
        public string HtmlInfoTypeAttribute { get; set; }
 | 
			
		||||
 | 
			
		||||
        public abstract void FillFilledAutofillValue(FilledAutofillField filledField);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,9 @@ namespace Kp2aAutofillParserTest
 | 
			
		||||
        class TestInputField: InputField
 | 
			
		||||
        {
 | 
			
		||||
            public string[] ExpectedAssignedHints { get; set; }
 | 
			
		||||
            public override void FillFilledAutofillValue(FilledAutofillField filledField)
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import com.jcraft.jsch.UserInfo;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
public class SftpStorage extends JavaFileStorageBase {
 | 
			
		||||
 | 
			
		||||
@@ -323,6 +324,8 @@ public class SftpStorage extends JavaFileStorageBase {
 | 
			
		||||
		jsch = new JSch();
 | 
			
		||||
		ConnectionInfo ci = splitStringToConnectionInfo(filename);
 | 
			
		||||
 | 
			
		||||
		Log.d("KP2AJFS", "init SFTP");
 | 
			
		||||
 | 
			
		||||
		String base_dir = getBaseDir();
 | 
			
		||||
		jsch.setKnownHosts(base_dir + "/known_hosts");
 | 
			
		||||
 | 
			
		||||
@@ -340,7 +343,9 @@ public class SftpStorage extends JavaFileStorageBase {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Log.e("KP2AJFS[thread]", "getting session...");
 | 
			
		||||
		Session session = jsch.getSession(ci.username, ci.host, ci.port);
 | 
			
		||||
		Log.e("KP2AJFS", "creating SftpUserInfo");
 | 
			
		||||
		UserInfo ui = new SftpUserInfo(ci.password,_appContext);
 | 
			
		||||
		session.setUserInfo(ui);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,6 @@ public class SftpUserInfo implements UserInfo {
 | 
			
		||||
				builder.setContentText("SFTP prompt");
 | 
			
		||||
				builder.setSmallIcon(R.drawable.ic_logo_green_foreground);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				Handler h = new Handler() {
 | 
			
		||||
					public void handleMessage(Message M) {
 | 
			
		||||
						msg.copyFrom(M);
 | 
			
		||||
@@ -51,8 +50,12 @@ public class SftpUserInfo implements UserInfo {
 | 
			
		||||
				intent.putExtra("keepass2android.sftp.prompt", text);
 | 
			
		||||
				intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				Log.e("KP2AJFS[thread]", "built after 2023-03-14");
 | 
			
		||||
 | 
			
		||||
				int flags = 0;
 | 
			
		||||
				if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
 | 
			
		||||
					Log.e("KP2AJFS[thread]", "Setting mutable flag...");
 | 
			
		||||
					flags |= PendingIntent.FLAG_MUTABLE;
 | 
			
		||||
				}
 | 
			
		||||
				PendingIntent contentIntent = PendingIntent.getActivity(_appContext, 0, intent, flags);
 | 
			
		||||
 
 | 
			
		||||
@@ -530,7 +530,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
 | 
			
		||||
     *         parameters.
 | 
			
		||||
     */
 | 
			
		||||
    private MatrixCursor doRetrieveFileInfo(Uri uri) {
 | 
			
		||||
    	Log.d(CLASSNAME, "retrieve file info "+uri.toString());
 | 
			
		||||
 | 
			
		||||
        MatrixCursor matrixCursor = BaseFileProviderUtils.newBaseFileCursor();
 | 
			
		||||
 | 
			
		||||
        String filename = extractFile(uri);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 | 
			
		||||
			android:versionCode="192" 
 | 
			
		||||
			android:versionName="1.09e-r4" 
 | 
			
		||||
			android:versionCode="194" 
 | 
			
		||||
			android:versionName="1.09e-r5b" 
 | 
			
		||||
			package="keepass2android.keepass2android" 
 | 
			
		||||
			xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
			android:installLocation="auto">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 | 
			
		||||
			android:versionCode="192" 
 | 
			
		||||
			android:versionName="1.09e-r4" 
 | 
			
		||||
			android:versionCode="193" 
 | 
			
		||||
			android:versionName="1.09e-r5" 
 | 
			
		||||
			package="keepass2android.keepass2android_nonet"
 | 
			
		||||
			xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
			android:installLocation="auto">
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@ namespace keepass2android.services.AutofillBase
 | 
			
		||||
            {
 | 
			
		||||
                foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection.GetFieldsForHint(hint))
 | 
			
		||||
                {
 | 
			
		||||
                    FilledAutofillField<ViewNodeInputField> filledAutofillField;
 | 
			
		||||
                    FilledAutofillField filledAutofillField;
 | 
			
		||||
                    if (!filledAutofillFieldCollection.HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ namespace keepass2android.services.AutofillBase
 | 
			
		||||
        [JsonIgnore]
 | 
			
		||||
        public AssistStructure.ViewNode ViewNode { get; set; }
 | 
			
		||||
 | 
			
		||||
        public void FillFilledAutofillValue(FilledAutofillField<ViewNodeInputField> filledField)
 | 
			
		||||
        public override void FillFilledAutofillValue(FilledAutofillField filledField)
 | 
			
		||||
        {
 | 
			
		||||
            AutofillValue autofillValue = ViewNode.AutofillValue;
 | 
			
		||||
            if (autofillValue != null)
 | 
			
		||||
@@ -159,8 +159,12 @@ namespace keepass2android.services.AutofillBase
 | 
			
		||||
 | 
			
		||||
        protected override AutofillTargetId Parse(bool forFill, bool isManualRequest, AutofillView<ViewNodeInputField> autofillView)
 | 
			
		||||
        {
 | 
			
		||||
            if (autofillView == null)
 | 
			
		||||
                Kp2aLog.Log("Received null autofill view!");
 | 
			
		||||
            var result = base.Parse(forFill, isManualRequest, autofillView);
 | 
			
		||||
 | 
			
		||||
            Kp2aLog.Log("Parsing done");
 | 
			
		||||
 | 
			
		||||
            if (forFill)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var p in FieldsMappedToHints)
 | 
			
		||||
@@ -168,8 +172,9 @@ namespace keepass2android.services.AutofillBase
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                ClientFormData = new FilledAutofillFieldCollection<ViewNodeInputField>();
 | 
			
		||||
                foreach (var p in FieldsMappedToHints)
 | 
			
		||||
                    ClientFormData.Add(new FilledAutofillField<ViewNodeInputField>(p.Key, p.Value));
 | 
			
		||||
                    ClientFormData.Add(new FilledAutofillField(p.Key, p.Value));
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -61,8 +61,8 @@ namespace keepass2android.services.Kp2aAutofill
 | 
			
		||||
            foreach (string key in pwEntryOutput.OutputStrings.GetKeys())
 | 
			
		||||
            {
 | 
			
		||||
                
 | 
			
		||||
                FilledAutofillField<ViewNodeInputField> field =
 | 
			
		||||
                    new FilledAutofillField<ViewNodeInputField>
 | 
			
		||||
                FilledAutofillField field =
 | 
			
		||||
                    new FilledAutofillField
 | 
			
		||||
                    {
 | 
			
		||||
                        AutofillHints = GetCanonicalHintsFromKp2aField(key).ToArray(),
 | 
			
		||||
                        TextValue = pwEntryOutput.OutputStrings.ReadSafe(key)
 | 
			
		||||
@@ -73,8 +73,8 @@ namespace keepass2android.services.Kp2aAutofill
 | 
			
		||||
            if (IsCreditCard(pwEntry, context) && pwEntry.Expires)
 | 
			
		||||
            {
 | 
			
		||||
                DateTime expTime = pwEntry.ExpiryTime;
 | 
			
		||||
                FilledAutofillField<ViewNodeInputField> field =
 | 
			
		||||
                    new FilledAutofillField<ViewNodeInputField>
 | 
			
		||||
                FilledAutofillField field =
 | 
			
		||||
                    new FilledAutofillField
 | 
			
		||||
                    {
 | 
			
		||||
                        AutofillHints = new[] {View.AutofillHintCreditCardExpirationDate},
 | 
			
		||||
                        DateValue = (long) (1000 * TimeUtil.SerializeUnix(expTime))
 | 
			
		||||
@@ -82,7 +82,7 @@ namespace keepass2android.services.Kp2aAutofill
 | 
			
		||||
                fieldCollection.Add(field);
 | 
			
		||||
 | 
			
		||||
                field =
 | 
			
		||||
                    new FilledAutofillField<ViewNodeInputField>
 | 
			
		||||
                    new FilledAutofillField
 | 
			
		||||
                    {
 | 
			
		||||
                        AutofillHints = new[] {View.AutofillHintCreditCardExpirationDay},
 | 
			
		||||
                        TextValue = expTime.Day.ToString()
 | 
			
		||||
@@ -90,7 +90,7 @@ namespace keepass2android.services.Kp2aAutofill
 | 
			
		||||
                fieldCollection.Add(field);
 | 
			
		||||
 | 
			
		||||
                field =
 | 
			
		||||
                    new FilledAutofillField<ViewNodeInputField>
 | 
			
		||||
                    new FilledAutofillField
 | 
			
		||||
                    {
 | 
			
		||||
                        AutofillHints = new[] {View.AutofillHintCreditCardExpirationMonth},
 | 
			
		||||
                        TextValue = expTime.Month.ToString()
 | 
			
		||||
@@ -98,7 +98,7 @@ namespace keepass2android.services.Kp2aAutofill
 | 
			
		||||
                fieldCollection.Add(field);
 | 
			
		||||
 | 
			
		||||
                field =
 | 
			
		||||
                    new FilledAutofillField<ViewNodeInputField>
 | 
			
		||||
                    new FilledAutofillField
 | 
			
		||||
                    {
 | 
			
		||||
                        AutofillHints = new[] {View.AutofillHintCreditCardExpirationYear},
 | 
			
		||||
                        TextValue = expTime.Year.ToString()
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// FilledAutofillFieldCollection is the model that holds all of the data on a client app's page,
 | 
			
		||||
    /// plus the dataset name associated with it.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class FilledAutofillFieldCollection<FieldT> where FieldT:InputField
 | 
			
		||||
    {
 | 
			
		||||
        public Dictionary<string, FilledAutofillField<FieldT>> HintMap { get; }
 | 
			
		||||
        public string DatasetName { get; set; }
 | 
			
		||||
 | 
			
		||||
        public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField<FieldT>> 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<string, FilledAutofillField<FieldT>> BuildHintMap()
 | 
			
		||||
        {
 | 
			
		||||
            return new Dictionary<string, FilledAutofillField<FieldT>>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Adds a filledAutofillField to the collection, indexed by all of its hints.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>The add.</returns>
 | 
			
		||||
        /// <param name="filledAutofillField">Filled autofill field.</param>
 | 
			
		||||
        public void Add(FilledAutofillField<FieldT> filledAutofillField)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (string hint in filledAutofillField.AutofillHints)
 | 
			
		||||
            {
 | 
			
		||||
                if (AutofillHintsHelper.IsSupportedHint(hint))
 | 
			
		||||
                {
 | 
			
		||||
                    HintMap.TryAdd(hint, filledAutofillField);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 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.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns><c>true</c>, if with hints was helpsed, <c>false</c> otherwise.</returns>
 | 
			
		||||
        /// <param name="autofillHints">Autofill hints.</param>
 | 
			
		||||
        public bool HelpsWithHints(List<string> 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<string> _allSupportedHints = new HashSet<string>(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<HashSet<string>> partitionsOfCanonicalHints = new List<HashSet<string>>()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            new HashSet<string>(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<string>(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<string>(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<string, string> hintToCanonicalReplacement = new Dictionary<string, string>(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<string>();
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// transforms hints by replacing some W3cHints by their Android counterparts and transforming everything to lowercase
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static List<string> ConvertToCanonicalHints(string[] supportedHints)
 | 
			
		||||
        {
 | 
			
		||||
            List<string> result = new List<string>();
 | 
			
		||||
            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<FieldT> FilterForPartition<FieldT>(FilledAutofillFieldCollection<FieldT> autofillFields, int partitionIndex) where FieldT: InputField
 | 
			
		||||
        {
 | 
			
		||||
            FilledAutofillFieldCollection<FieldT> filteredCollection =
 | 
			
		||||
                new FilledAutofillFieldCollection<FieldT> { 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<FieldT> FilterForPartition<FieldT>(FilledAutofillFieldCollection<FieldT> filledAutofillFieldCollection, List<string> 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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// This enum represents the Android.Text.InputTypes values. For testability, this is duplicated here.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    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<FieldT> 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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// returns the autofill hints for the filled field. These are always lowercased for simpler string comparison.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
	    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<string> hintList = new List<string>();
 | 
			
		||||
 | 
			
		||||
            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<FieldT> that = (FilledAutofillField<FieldT>)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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 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
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    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<TField> where TField : InputField
 | 
			
		||||
    {
 | 
			
		||||
        public List<TField> InputFields { get; set; } = new List<TField>();
 | 
			
		||||
 | 
			
		||||
        public string PackageId { get; set; } = null;
 | 
			
		||||
        public string WebDomain { get; set; } = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public interface ILogger
 | 
			
		||||
    {
 | 
			
		||||
        void Log(string x);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class StructureParserBase<FieldT> where FieldT: InputField
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ILogger _log;
 | 
			
		||||
        private readonly IKp2aDigitalAssetLinksDataSource _digitalAssetLinksDataSource;
 | 
			
		||||
 | 
			
		||||
        private readonly List<string> _autofillHintsForLogin = new List<string>
 | 
			
		||||
            {
 | 
			
		||||
                AutofillHintsHelper.AutofillHintPassword,
 | 
			
		||||
                AutofillHintsHelper.AutofillHintUsername,
 | 
			
		||||
                AutofillHintsHelper.AutofillHintEmailAddress
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        public string PackageId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Dictionary<FieldT, string[]> FieldsMappedToHints = new Dictionary<FieldT, string[]>();
 | 
			
		||||
 | 
			
		||||
        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; }
 | 
			
		||||
 | 
			
		||||
            /// <summary>
 | 
			
		||||
            /// 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.
 | 
			
		||||
            /// </summary>
 | 
			
		||||
            public bool IncompatiblePackageAndDomain { get; set; }
 | 
			
		||||
 | 
			
		||||
            public string DomainOrPackage
 | 
			
		||||
            {
 | 
			
		||||
                get
 | 
			
		||||
                {
 | 
			
		||||
                    return WebDomain ?? PackageNameWithPseudoSchema;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AutofillTargetId ParseForFill(bool isManual, AutofillView<FieldT> autofillView)
 | 
			
		||||
        {
 | 
			
		||||
            return Parse(true, isManual, autofillView);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AutofillTargetId ParseForSave(AutofillView<FieldT> autofillView)
 | 
			
		||||
        {
 | 
			
		||||
            return Parse(false, true, autofillView);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Traverse AssistStructure and add ViewNode metadata to a flat list.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>The parse.</returns>
 | 
			
		||||
        /// <param name="forFill">If set to <c>true</c> for fill.</param>
 | 
			
		||||
        /// <param name="isManualRequest"></param>
 | 
			
		||||
        protected virtual AutofillTargetId Parse(bool forFill, bool isManualRequest, AutofillView<FieldT> 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<string> 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<FieldT> 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<FieldT> 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<string> _passwordHints = new HashSet<string> { "password", "passwort"
 | 
			
		||||
            /*, "passwordAuto", "pswd"*/ };
 | 
			
		||||
        private static bool HasPasswordHint(InputField f)
 | 
			
		||||
        {
 | 
			
		||||
            return IsAny(f.IdEntry, _passwordHints) ||
 | 
			
		||||
                   IsAny(f.Hint, _passwordHints);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static readonly HashSet<string> _usernameHints = new HashSet<string> { "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<string> 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")
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>netstandard2.1</TargetFramework>
 | 
			
		||||
    <Nullable>enable</Nullable>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
		Reference in New Issue
	
	Block a user