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