From f0a2f9a0386a564db340856ab610715981ee21c3 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Wed, 21 May 2014 06:43:56 +0200 Subject: [PATCH] Fixed problems with returning Activity results Added scopes for querying credentials Extended possibilities of AppTasks Added QueryCredentialsActivity.cs Added missing AboutActivity.cs FileSelectActivity: removed check for savedInstanceState=null. According to stackoverflow, savedInstanceState may be passed when launched from history Passing of protected field lists added to PluginHost to allow full transfer of entries --- src/keepass2android/AboutActivity.cs | 38 ++++ src/keepass2android/EntryActivity.cs | 1 - .../CopyToClipboardPopupMenuIcon.cs | 1 - .../EntryActivityClasses/GotoUrlMenuItem.cs | 2 +- .../OpenBinaryPopupItem.cs | 1 - .../ToggleVisibilityPopupMenuItem.cs | 1 - .../WriteBinaryToFilePopupItem.cs | 1 - src/keepass2android/GroupActivity.cs | 12 -- src/keepass2android/KeePass.cs | 4 + src/keepass2android/PasswordActivity.cs | 8 +- .../QueryCredentialsActivity.cs | 185 +++++++++++++++++ .../Resources/Resource.designer.cs | 196 ++++++++++-------- .../Resources/values/strings.xml | 9 + src/keepass2android/ShareUrlResults.cs | 30 +-- src/keepass2android/app/AppTask.cs | 153 ++++++++++---- .../fileselect/FileSelectActivity.cs | 5 +- src/keepass2android/keepass2android.csproj | 1 + .../pluginhost/PluginArrayAdapter.cs | 1 - .../pluginhost/PluginDatabase.cs | 25 +++ .../pluginhost/PluginDetailsActivity.cs | 3 +- src/keepass2android/pluginhost/PluginHost.cs | 18 +- .../pluginhost/PluginListActivity.cs | 1 - src/keepass2android/search/SearchResults.cs | 11 +- 23 files changed, 517 insertions(+), 190 deletions(-) create mode 100644 src/keepass2android/AboutActivity.cs create mode 100644 src/keepass2android/QueryCredentialsActivity.cs diff --git a/src/keepass2android/AboutActivity.cs b/src/keepass2android/AboutActivity.cs new file mode 100644 index 00000000..3b3926a7 --- /dev/null +++ b/src/keepass2android/AboutActivity.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Android.App; +using Android.Content; +using Android.Content.PM; + +namespace keepass2android +{ + [Activity(Label = "@string/app_name", + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, + Theme = "@style/Base")] + [IntentFilter(new[] { "keepass2android.AboutActivity" }, Categories = new[] { Intent.CategoryDefault })] + public class AboutActivity: Activity, IDialogInterfaceOnDismissListener + { + private AboutDialog _dialog; + + protected override void OnResume() + { + if ((_dialog == null) || (_dialog.IsShowing == false)) + { + if (new ActivityDesign(this).UseDarkTheme) + _dialog = new AboutDialog(this, Android.Resource.Style.ThemeHoloNoActionBarFullscreen); + else + _dialog = new AboutDialog(this, Android.Resource.Style.ThemeHoloLightNoActionBarFullscreen); + _dialog.SetOnDismissListener(this); + _dialog.Show(); + } + base.OnResume(); + } + + public void OnDismiss(IDialogInterface dialog) + { + Finish(); + } + } +} diff --git a/src/keepass2android/EntryActivity.cs b/src/keepass2android/EntryActivity.cs index 4fd5aea9..9161add3 100644 --- a/src/keepass2android/EntryActivity.cs +++ b/src/keepass2android/EntryActivity.cs @@ -38,7 +38,6 @@ using KeePassLib; using KeePassLib.Security; using KeePassLib.Utility; using Keepass2android.Pluginsdk; -using PluginHostTest; using keepass2android.Io; using Uri = Android.Net.Uri; diff --git a/src/keepass2android/EntryActivityClasses/CopyToClipboardPopupMenuIcon.cs b/src/keepass2android/EntryActivityClasses/CopyToClipboardPopupMenuIcon.cs index a1a4207d..5084cfa6 100644 --- a/src/keepass2android/EntryActivityClasses/CopyToClipboardPopupMenuIcon.cs +++ b/src/keepass2android/EntryActivityClasses/CopyToClipboardPopupMenuIcon.cs @@ -1,6 +1,5 @@ using Android.Content; using Android.Graphics.Drawables; -using PluginHostTest; namespace keepass2android { diff --git a/src/keepass2android/EntryActivityClasses/GotoUrlMenuItem.cs b/src/keepass2android/EntryActivityClasses/GotoUrlMenuItem.cs index b1a56d97..468040b8 100644 --- a/src/keepass2android/EntryActivityClasses/GotoUrlMenuItem.cs +++ b/src/keepass2android/EntryActivityClasses/GotoUrlMenuItem.cs @@ -1,5 +1,5 @@ using Android.Graphics.Drawables; -using PluginHostTest; + namespace keepass2android { diff --git a/src/keepass2android/EntryActivityClasses/OpenBinaryPopupItem.cs b/src/keepass2android/EntryActivityClasses/OpenBinaryPopupItem.cs index 38c07867..7ca945ed 100644 --- a/src/keepass2android/EntryActivityClasses/OpenBinaryPopupItem.cs +++ b/src/keepass2android/EntryActivityClasses/OpenBinaryPopupItem.cs @@ -1,5 +1,4 @@ using Android.Graphics.Drawables; -using PluginHostTest; namespace keepass2android { diff --git a/src/keepass2android/EntryActivityClasses/ToggleVisibilityPopupMenuItem.cs b/src/keepass2android/EntryActivityClasses/ToggleVisibilityPopupMenuItem.cs index 54ff1890..b720e263 100644 --- a/src/keepass2android/EntryActivityClasses/ToggleVisibilityPopupMenuItem.cs +++ b/src/keepass2android/EntryActivityClasses/ToggleVisibilityPopupMenuItem.cs @@ -1,5 +1,4 @@ using Android.Graphics.Drawables; -using PluginHostTest; namespace keepass2android { diff --git a/src/keepass2android/EntryActivityClasses/WriteBinaryToFilePopupItem.cs b/src/keepass2android/EntryActivityClasses/WriteBinaryToFilePopupItem.cs index a90fc61b..8169fa39 100644 --- a/src/keepass2android/EntryActivityClasses/WriteBinaryToFilePopupItem.cs +++ b/src/keepass2android/EntryActivityClasses/WriteBinaryToFilePopupItem.cs @@ -1,5 +1,4 @@ using Android.Graphics.Drawables; -using PluginHostTest; namespace keepass2android { diff --git a/src/keepass2android/GroupActivity.cs b/src/keepass2android/GroupActivity.cs index bd898757..e7d1693e 100644 --- a/src/keepass2android/GroupActivity.cs +++ b/src/keepass2android/GroupActivity.cs @@ -167,18 +167,6 @@ namespace keepass2android return cv.OnContextItemSelected(item); } - protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) - { - switch (resultCode) - { - - case Result.Canceled: - break; - default: - base.OnActivityResult(requestCode, resultCode, data); - break; - } - } } } diff --git a/src/keepass2android/KeePass.cs b/src/keepass2android/KeePass.cs index ec22af59..f33cd6ec 100644 --- a/src/keepass2android/KeePass.cs +++ b/src/keepass2android/KeePass.cs @@ -40,6 +40,9 @@ using String = System.String; * While the database is closed, there is only one activity on the stack: Keepass -> FileSelect <-> Password. * After opening an database (in Password), Password is always the root of the stack (exception: after creating a database, * FileSelect is the root without Password being open). + * Another exception: QueryCredentialsActivity is root of the stack if an external app is querying credentials. + * QueryCredentialsActivity checks the plugin access permissions, then launches FileSelectActivity (which starts + * the normal stack.) * * Some possible stacks: * Password -> Group ( -> Group (subgroups) ... ) -> EntryView -> EntryEdit @@ -184,6 +187,7 @@ namespace keepass2android private static String LIB_DALVIK = "libdvm.so"; private static String LIB_ART = "libart.so"; private static String LIB_ART_D = "libartd.so"; + public static string StartWithTask = "keepass2android.ACTION_START_WITH_TASK"; public KeePass() { diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index 27806ecc..e2c0eb23 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -159,11 +159,11 @@ namespace keepass2android Intent i = new Intent(act, typeof(PasswordActivity)); - i.SetFlags(ActivityFlags.ClearTask | ActivityFlags.NewTask); + i.SetFlags(ActivityFlags.ForwardResult); i.PutExtra(KeyFilename, fileName); appTask.ToIntent(i); - act.StartActivityForResult(i, 0); + act.StartActivity(i); } @@ -179,7 +179,7 @@ namespace keepass2android Intent i = new Intent(act, typeof(PasswordActivity)); PutIoConnectionToIntent(ioc, i); - i.SetFlags(ActivityFlags.ClearTask | ActivityFlags.ForwardResult); + i.SetFlags(ActivityFlags.ForwardResult); appTask.ToIntent(i); @@ -355,7 +355,7 @@ namespace keepass2android // and if the activity is not launched from history (i.e. recent tasks) because this would mean that // the Activity was closed already (user cancelling the task or task complete) but is restarted due recent tasks. // Don't re-start the task (especially bad if tak was complete already) - if ((savedInstanceState == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))) + if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)) { AppTask = new NullTask(); } diff --git a/src/keepass2android/QueryCredentialsActivity.cs b/src/keepass2android/QueryCredentialsActivity.cs new file mode 100644 index 00000000..ccbdf2b8 --- /dev/null +++ b/src/keepass2android/QueryCredentialsActivity.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.Content.PM; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using Keepass2android.Pluginsdk; + +namespace keepass2android +{ + [Activity(Label = "@string/app_name", + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, + Theme = "@style/Base")] + [IntentFilter(new[] { Strings.ActionQueryCredentials}, + Categories = new[] { Intent.CategoryDefault })] + [IntentFilter(new[] { Strings.ActionQueryCredentialsForOwnPackage }, + Categories = new[] { Intent.CategoryDefault })] + public class QueryCredentialsActivity : Activity + { + private const int RequestCodePluginAccess = 1; + private const int RequestCodeQuery = 2; + private const string IsRecreate = "isRecreate"; + private const string StartedQuery = "startedQuery"; + private bool _startedQuery; + private string _requiredScope; + private string _requestedUrl; + private string _pluginPackage; + + protected override void OnCreate(Bundle savedInstanceState) + { + base.OnCreate(savedInstanceState); + + //if launched from history, don't re-use the task. Proceed to FileSelect instead. + if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)) + { + Kp2aLog.Log("Forwarding to FileSelect. QueryCredentialsActivity started from history."); + Intent intent = new Intent(this, typeof(FileSelectActivity)); + intent.AddFlags(ActivityFlags.ForwardResult); + StartActivity(intent); + Finish(); + return; + } + + _pluginPackage = null; + if (CallingActivity != null) + _pluginPackage = CallingActivity.PackageName; + if (_pluginPackage == null) + { + Kp2aLog.Log("Couldn't retrieve calling package. Probably activity was started without startActivityForResult()"); + Finish(); + return; + } + if (Intent.Action == Strings.ActionQueryCredentialsForOwnPackage) + { + _requiredScope = Strings.ScopeQueryCredentialsForOwnPackage; + _requestedUrl = "androidapp://" + _pluginPackage; + } + else if (Intent.Action == Strings.ActionQueryCredentials) + { + _requiredScope = Strings.ScopeQueryCredentials; + _requestedUrl = Intent.GetStringExtra(Strings.ExtraQueryString); + } + else + { + Kp2aLog.Log("Invalid action for QueryCredentialsActivity: " + Intent.Action); + SetResult(Result.FirstUser); + Finish(); + return; + } + + //only start the query or request plugin access when creating the first time. + //if we're restarting (after config change or low memory), we will get onActivityResult() later + //which will either start the next activity or finish this one. + if ((savedInstanceState == null) || (savedInstanceState.GetBoolean(IsRecreate, false) == false)) + { + ShowToast(); + + if (new PluginDatabase(this).HasAcceptedScope(_pluginPackage,_requiredScope)) + { + StartQuery(); + } + else + { + RequestPluginAccess(); + } + } + } + + private void ShowToast() + { + string pluginDisplayName = _pluginPackage; + try + { + pluginDisplayName = PackageManager.GetApplicationLabel(PackageManager.GetApplicationInfo(_pluginPackage, 0)); + } + catch (Exception e) + { + Kp2aLog.Log(e.ToString()); + } + if (String.IsNullOrEmpty(_requestedUrl)) + Toast.MakeText(this, GetString(Resource.String.query_credentials, new Java.Lang.Object[] {pluginDisplayName}), ToastLength.Long).Show(); + else + Toast.MakeText(this, + GetString(Resource.String.query_credentials_for_url, + new Java.Lang.Object[] { pluginDisplayName, _requestedUrl }), ToastLength.Long).Show(); ; + } + + private void StartQuery() + { + //launch FileSelectActivity (which is root of the stack (exception: we're even below!)) with the appropriate task. + //will return the results later + Intent i = new Intent(this, typeof (FileSelectActivity)); + //don't show user notifications when an entry is opened. + var task = new SearchUrlTask() {UrlToSearchFor = _requestedUrl, ShowUserNotifications = false}; + task.ToIntent(i); + StartActivityForResult(i, RequestCodeQuery); + _startedQuery = true; + } + + private void RequestPluginAccess() + { + Intent i = new Intent(this, typeof(PluginDetailsActivity)); + i.SetAction(Strings.ActionEditPluginSettings); + i.PutExtra(Strings.ExtraPluginPackage, _pluginPackage); + StartActivityForResult(i, RequestCodePluginAccess); + } + + protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) + { + base.OnActivityResult(requestCode, resultCode, data); + + if (requestCode == RequestCodePluginAccess) + { + if (new PluginDatabase(this).HasAcceptedScope(_pluginPackage, _requiredScope)) + { + //user granted access. Search for the requested credentials: + StartQuery(); + } + else + { + //user didn't grant access + SetResult(Result.Canceled); + Finish(); + } + } + if (requestCode == RequestCodeQuery) + { + if (resultCode == KeePass.ExitCloseAfterTaskComplete) + { + //double check we really have the permission + if (!new PluginDatabase(this).HasAcceptedScope(_pluginPackage, _requiredScope)) + { + Kp2aLog.Log("Ohoh! Scope not available, shouldn't get here. Malicious app somewhere?"); + SetResult(Result.Canceled); + Finish(); + return; + } + //return credentials to caller: + Intent credentialData = new Intent(); + PluginHost.AddEntryToIntent(credentialData, App.Kp2a.GetDb().LastOpenedEntry); + SetResult(Result.Ok, credentialData); + Finish(); + } + else + { + SetResult(Result.Canceled); + Finish(); + } + } + } + + protected override void OnSaveInstanceState(Bundle outState) + { + base.OnSaveInstanceState(outState); + outState.PutBoolean(StartedQuery, _startedQuery); + outState.PutBoolean(IsRecreate, true); + } + } +} \ No newline at end of file diff --git a/src/keepass2android/Resources/Resource.designer.cs b/src/keepass2android/Resources/Resource.designer.cs index bf2fcb93..a674782b 100644 --- a/src/keepass2android/Resources/Resource.designer.cs +++ b/src/keepass2android/Resources/Resource.designer.cs @@ -3431,59 +3431,59 @@ namespace keepass2android // aapt resource value: 0x7f090271 public const int CannotMoveGroupHere = 2131296881; - // aapt resource value: 0x7f0902b2 - public const int CertificateFailure = 2131296946; - - // aapt resource value: 0x7f0902b1 - public const int CertificateWarning = 2131296945; - - // aapt resource value: 0x7f0902c3 - public const int ChangeLog = 2131296963; - - // aapt resource value: 0x7f0902c2 - public const int ChangeLog_0_7 = 2131296962; - - // aapt resource value: 0x7f0902c0 - public const int ChangeLog_0_8 = 2131296960; - - // aapt resource value: 0x7f0902bf - public const int ChangeLog_0_8_1 = 2131296959; - - // aapt resource value: 0x7f0902be - public const int ChangeLog_0_8_2 = 2131296958; - - // aapt resource value: 0x7f0902bd - public const int ChangeLog_0_8_3 = 2131296957; - - // aapt resource value: 0x7f0902bc - public const int ChangeLog_0_8_4 = 2131296956; - - // aapt resource value: 0x7f0902bb - public const int ChangeLog_0_8_5 = 2131296955; - - // aapt resource value: 0x7f0902ba - public const int ChangeLog_0_8_6 = 2131296954; - - // aapt resource value: 0x7f0902b9 - public const int ChangeLog_0_9 = 2131296953; - // aapt resource value: 0x7f0902b8 - public const int ChangeLog_0_9_1 = 2131296952; + public const int CertificateFailure = 2131296952; // aapt resource value: 0x7f0902b7 - public const int ChangeLog_0_9_2 = 2131296951; + public const int CertificateWarning = 2131296951; - // aapt resource value: 0x7f0902b6 - public const int ChangeLog_0_9_3 = 2131296950; + // aapt resource value: 0x7f0902c9 + public const int ChangeLog = 2131296969; - // aapt resource value: 0x7f0902b5 - public const int ChangeLog_0_9_3_r5 = 2131296949; + // aapt resource value: 0x7f0902c8 + public const int ChangeLog_0_7 = 2131296968; + + // aapt resource value: 0x7f0902c6 + public const int ChangeLog_0_8 = 2131296966; + + // aapt resource value: 0x7f0902c5 + public const int ChangeLog_0_8_1 = 2131296965; + + // aapt resource value: 0x7f0902c4 + public const int ChangeLog_0_8_2 = 2131296964; + + // aapt resource value: 0x7f0902c3 + public const int ChangeLog_0_8_3 = 2131296963; + + // aapt resource value: 0x7f0902c2 + public const int ChangeLog_0_8_4 = 2131296962; // aapt resource value: 0x7f0902c1 - public const int ChangeLog_keptDonate = 2131296961; + public const int ChangeLog_0_8_5 = 2131296961; - // aapt resource value: 0x7f0902b4 - public const int ChangeLog_title = 2131296948; + // aapt resource value: 0x7f0902c0 + public const int ChangeLog_0_8_6 = 2131296960; + + // aapt resource value: 0x7f0902bf + public const int ChangeLog_0_9 = 2131296959; + + // aapt resource value: 0x7f0902be + public const int ChangeLog_0_9_1 = 2131296958; + + // aapt resource value: 0x7f0902bd + public const int ChangeLog_0_9_2 = 2131296957; + + // aapt resource value: 0x7f0902bc + public const int ChangeLog_0_9_3 = 2131296956; + + // aapt resource value: 0x7f0902bb + public const int ChangeLog_0_9_3_r5 = 2131296955; + + // aapt resource value: 0x7f0902c7 + public const int ChangeLog_keptDonate = 2131296967; + + // aapt resource value: 0x7f0902ba + public const int ChangeLog_title = 2131296954; // aapt resource value: 0x7f090123 public const int CheckForFileChangesOnSave_key = 2131296547; @@ -3722,17 +3722,29 @@ namespace keepass2android // aapt resource value: 0x7f09025f public const int RestoringRemoteFile = 2131296863; + // aapt resource value: 0x7f0902b1 + public const int SCOPE_CURRENT_ENTRY_explanation = 2131296945; + + // aapt resource value: 0x7f0902b0 + public const int SCOPE_CURRENT_ENTRY_title = 2131296944; + // aapt resource value: 0x7f0902af - public const int SCOPE_CURRENT_ENTRY_explanation = 2131296943; + public const int SCOPE_DATABASE_ACTIONS_explanation = 2131296943; // aapt resource value: 0x7f0902ae - public const int SCOPE_CURRENT_ENTRY_title = 2131296942; + public const int SCOPE_DATABASE_ACTIONS_title = 2131296942; - // aapt resource value: 0x7f0902ad - public const int SCOPE_DATABASE_ACTIONS_explanation = 2131296941; + // aapt resource value: 0x7f0902b3 + public const int SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_explanation = 2131296947; - // aapt resource value: 0x7f0902ac - public const int SCOPE_DATABASE_ACTIONS_title = 2131296940; + // aapt resource value: 0x7f0902b2 + public const int SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_title = 2131296946; + + // aapt resource value: 0x7f0902b5 + public const int SCOPE_QUERY_CREDENTIALS_explanation = 2131296949; + + // aapt resource value: 0x7f0902b4 + public const int SCOPE_QUERY_CREDENTIALS_title = 2131296948; // aapt resource value: 0x7f090210 public const int SaveAttachmentDialog_open = 2131296784; @@ -4247,11 +4259,11 @@ namespace keepass2android // aapt resource value: 0x7f090151 public const int brackets = 2131296593; - // aapt resource value: 0x7f0902c8 - public const int browser_intall_text = 2131296968; + // aapt resource value: 0x7f0902ce + public const int browser_intall_text = 2131296974; - // aapt resource value: 0x7f0902c9 - public const int building_search_idx = 2131296969; + // aapt resource value: 0x7f0902cf + public const int building_search_idx = 2131296975; // aapt resource value: 0x7f09028c public const int button_change_location = 2131296908; @@ -4382,14 +4394,14 @@ namespace keepass2android // aapt resource value: 0x7f090101 public const int db_key = 2131296513; - // aapt resource value: 0x7f0902ca - public const int decrypting_db = 2131296970; + // aapt resource value: 0x7f0902d0 + public const int decrypting_db = 2131296976; - // aapt resource value: 0x7f0902cb - public const int decrypting_entry = 2131296971; + // aapt resource value: 0x7f0902d1 + public const int decrypting_entry = 2131296977; - // aapt resource value: 0x7f0902cc - public const int default_checkbox = 2131296972; + // aapt resource value: 0x7f0902d2 + public const int default_checkbox = 2131296978; // aapt resource value: 0x7f0900f3 public const int default_file_path = 2131296499; @@ -4412,8 +4424,8 @@ namespace keepass2android // aapt resource value: 0x7f090108 public const int design_key = 2131296520; - // aapt resource value: 0x7f0902c4 - public const int design_title = 2131296964; + // aapt resource value: 0x7f0902ca + public const int design_title = 2131296970; // aapt resource value: 0x7f09015e public const int digits = 2131296606; @@ -4445,11 +4457,11 @@ namespace keepass2android // aapt resource value: 0x7f090160 public const int ellipsis = 2131296608; - // aapt resource value: 0x7f0902c6 - public const int enable_plugin_question = 2131296966; + // aapt resource value: 0x7f0902cc + public const int enable_plugin_question = 2131296972; - // aapt resource value: 0x7f0902c5 - public const int enable_plugin_title = 2131296965; + // aapt resource value: 0x7f0902cb + public const int enable_plugin_title = 2131296971; // aapt resource value: 0x7f0901fe public const int enable_quickunlock = 2131296766; @@ -4481,8 +4493,8 @@ namespace keepass2android // aapt resource value: 0x7f090162 public const int entry_accessed = 2131296610; - // aapt resource value: 0x7f0902cd - public const int entry_and_or = 2131296973; + // aapt resource value: 0x7f0902d3 + public const int entry_and_or = 2131296979; // aapt resource value: 0x7f090172 public const int entry_binaries = 2131296626; @@ -4535,8 +4547,8 @@ namespace keepass2android // aapt resource value: 0x7f090294 public const int error_adding_keyfile = 2131296916; - // aapt resource value: 0x7f0902ce - public const int error_arc4 = 2131296974; + // aapt resource value: 0x7f0902d4 + public const int error_arc4 = 2131296980; // aapt resource value: 0x7f090173 public const int error_can_not_handle_uri = 2131296627; @@ -4577,8 +4589,8 @@ namespace keepass2android // aapt resource value: 0x7f09017e public const int error_nopass = 2131296638; - // aapt resource value: 0x7f0902cf - public const int error_out_of_memory = 2131296975; + // aapt resource value: 0x7f0902d5 + public const int error_out_of_memory = 2131296981; // aapt resource value: 0x7f09017f public const int error_pass_gen_type = 2131296639; @@ -4589,8 +4601,8 @@ namespace keepass2android // aapt resource value: 0x7f090181 public const int error_rounds_not_number = 2131296641; - // aapt resource value: 0x7f0902d0 - public const int error_rounds_too_large = 2131296976; + // aapt resource value: 0x7f0902d6 + public const int error_rounds_too_large = 2131296982; // aapt resource value: 0x7f090216 public const int error_string_key = 2131296790; @@ -4607,8 +4619,8 @@ namespace keepass2android // aapt resource value: 0x7f0901d1 public const int export_database_successful = 2131296721; - // aapt resource value: 0x7f0902b3 - public const int export_fileformats_title = 2131296947; + // aapt resource value: 0x7f0902b9 + public const int export_fileformats_title = 2131296953; // aapt resource value: 0x7f090150 public const int export_prefs = 2131296592; @@ -4673,8 +4685,8 @@ namespace keepass2android // aapt resource value: 0x7f090186 public const int generate_password = 2131296646; - // aapt resource value: 0x7f0902b0 - public const int get_regular_version = 2131296944; + // aapt resource value: 0x7f0902b6 + public const int get_regular_version = 2131296950; // aapt resource value: 0x7f090187 public const int group = 2131296647; @@ -4778,11 +4790,11 @@ namespace keepass2android // aapt resource value: 0x7f0901dd public const int insert_element_here = 2131296733; - // aapt resource value: 0x7f0902d1 - public const int install_from_market = 2131296977; + // aapt resource value: 0x7f0902d7 + public const int install_from_market = 2131296983; - // aapt resource value: 0x7f0902d2 - public const int install_from_website = 2131296978; + // aapt resource value: 0x7f0902d8 + public const int install_from_website = 2131296984; // aapt resource value: 0x7f090196 public const int invalid_algorithm = 2131296662; @@ -4991,8 +5003,8 @@ namespace keepass2android // aapt resource value: 0x7f0901ad public const int menu_hide_password = 2131296685; - // aapt resource value: 0x7f0902d3 - public const int menu_homepage = 2131296979; + // aapt resource value: 0x7f0902d9 + public const int menu_homepage = 2131296985; // aapt resource value: 0x7f0901ae public const int menu_lock = 2131296686; @@ -5129,8 +5141,8 @@ namespace keepass2android // aapt resource value: 0x7f0902a8 public const int plugin_enabled = 2131296936; - // aapt resource value: 0x7f0902ab - public const int plugin_enabled_checkbox = 2131296939; + // aapt resource value: 0x7f0902ad + public const int plugin_enabled_checkbox = 2131296941; // aapt resource value: 0x7f0902a5 public const int plugin_packagename = 2131296933; @@ -5141,8 +5153,8 @@ namespace keepass2android // aapt resource value: 0x7f0902a4 public const int plugins = 2131296932; - // aapt resource value: 0x7f0902c7 - public const int plugins_text = 2131296967; + // aapt resource value: 0x7f0902cd + public const int plugins_text = 2131296973; // aapt resource value: 0x7f09008d public const int popular_domain_0 = 2131296397; @@ -5213,6 +5225,12 @@ namespace keepass2android // aapt resource value: 0x7f090219 public const int protection = 2131296793; + // aapt resource value: 0x7f0902ac + public const int query_credentials = 2131296940; + + // aapt resource value: 0x7f0902ab + public const int query_credentials_for_url = 2131296939; + // aapt resource value: 0x7f09004b public const int quick_fixes = 2131296331; diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index 26d546bc..c1a9808c 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -419,6 +419,9 @@ enabled disabled Find plug-ins online + + %1$s is requesting credentials for %2$s. + %1$s is requesting credentials. Please select an entry. Enabled @@ -427,6 +430,12 @@ Current entry data Plugin will receive all data about the current database entry and will be allowed to offer actions and modify the display of it. + + Query own credentials + Plugin will be allowed to query the credentials associated with its own application package. + + Query credentials + Plugin will be allowed to query credentials for deliberate web sites or applications. Get more storage types diff --git a/src/keepass2android/ShareUrlResults.cs b/src/keepass2android/ShareUrlResults.cs index 81974719..8f8b81bd 100644 --- a/src/keepass2android/ShareUrlResults.cs +++ b/src/keepass2android/ShareUrlResults.cs @@ -59,7 +59,8 @@ namespace keepass2android { base.OnCreate(savedInstanceState); - SetResult(KeePass.ExitCloseAfterTaskComplete); + //if user presses back to leave this activity: + SetResult(Result.Canceled); _db = App.Kp2a.GetDb(); if (App.Kp2a.DatabaseIsUnlocked) @@ -77,12 +78,6 @@ namespace keepass2android AppTask.ToBundle(outState); } - public override void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos) - { - base.LaunchActivityForEntry(pwEntry, pos); - Finish(); - } - private void Query(String url) { try @@ -105,13 +100,11 @@ namespace keepass2android } catch (Exception e) { Toast.MakeText(this, e.Message, ToastLength.Long).Show(); + SetResult(Result.Canceled); Finish(); return; } - - - //if there is exactly one match: open the entry if (Group.Entries.Count() == 1) { @@ -153,22 +146,15 @@ namespace keepass2android { createUrlEntry.Visibility = ViewStates.Gone; } - - - } public override bool OnSearchRequested() { - if (base.OnSearchRequested()) - { - Finish(); - return true; - } - else - { - return false; - } + Intent i = new Intent(this, typeof(SearchActivity)); + AppTask.ToIntent(i); + i.SetFlags(ActivityFlags.ForwardResult); + StartActivity(i); + return true; } }} diff --git a/src/keepass2android/app/AppTask.cs b/src/keepass2android/app/AppTask.cs index c1161cd4..387c83f9 100644 --- a/src/keepass2android/app/AppTask.cs +++ b/src/keepass2android/app/AppTask.cs @@ -49,23 +49,23 @@ namespace keepass2android } /// - /// represents data stored in an intent or bundle as extra string array + /// represents data stored in an intent or bundle as extra string array list /// - public class StringArrayExtra : IExtra + public class StringArrayListExtra : IExtra { public string Key { get; set; } - public string[] Value { get; set; } + public IList Value { get; set; } #region IExtra implementation public void ToBundle(Bundle b) { - b.PutStringArray(Key, Value); + b.PutStringArrayList(Key, Value); } public void ToIntent(Intent i) { - i.PutExtra(Key, Value); + i.PutStringArrayListExtra(Key, Value); } #endregion @@ -268,7 +268,9 @@ namespace keepass2android /// /// User is about to search an entry for a given URL /// - public class SearchUrlTask: AppTask + /// Derive from SelectEntryTask. This means that as soon as an Entry is opened, we're returning with + /// ExitAfterTaskComplete. This also allows te specify the flag if we need to display the user notifications. + public class SearchUrlTask: SelectEntryTask { public const String UrlToSearchKey = "UrlToSearch"; @@ -280,18 +282,29 @@ namespace keepass2android public override void Setup(Bundle b) { + base.Setup(b); UrlToSearchFor = b.GetString(UrlToSearchKey); } public override IEnumerable Extras { get { + foreach (IExtra e in base.Extras) + yield return e; yield return new StringExtra { Key=UrlToSearchKey, Value = UrlToSearchFor }; } } public override void AfterUnlockDatabase(PasswordActivity act) { - ShareUrlResults.Launch(act, this); + if (String.IsNullOrEmpty(UrlToSearchFor)) + { + GroupActivity.Launch(act, new SelectEntryTask() { ShowUserNotifications = ShowUserNotifications}); + } + else + { + ShareUrlResults.Launch(act, this); + } + //removed. this causes an issue in the following workflow: //When the user wants to find an entry for a URL but has the wrong database open he needs @@ -308,13 +321,6 @@ namespace keepass2android //act.AppTask = new NullTask(); } - public override void CompleteOnCreateEntryActivity(EntryActivity activity) - { - //show the notifications - activity.StartNotificationsService(true); - //close - activity.CloseAfterTaskComplete(); - } } @@ -324,12 +330,59 @@ namespace keepass2android /// public class SelectEntryTask: AppTask { + public SelectEntryTask() + { + ShowUserNotifications = true; + CloseAfterCreate = true; + } + + public const String ShowUserNotificationsKey = "ShowUserNotifications"; + + public bool ShowUserNotifications { get; set; } + + public const String CloseAfterCreateKey = "CloseAfterCreate"; + + public bool CloseAfterCreate { get; set; } + + + public override void Setup(Bundle b) + { + ShowUserNotifications = GetBoolFromBundle(b, ShowUserNotificationsKey, true); + CloseAfterCreate = GetBoolFromBundle(b, CloseAfterCreateKey, true); + } + + private static bool GetBoolFromBundle(Bundle b, string key, bool defaultValue) + { + bool boolValue; + if (!Boolean.TryParse(b.GetString(key), out boolValue)) + { + boolValue = defaultValue; + } + return boolValue; + } + + public override IEnumerable Extras + { + get + { + yield return new StringExtra { Key = ShowUserNotificationsKey, Value = ShowUserNotifications.ToString() }; + yield return new StringExtra { Key = CloseAfterCreateKey, Value = CloseAfterCreate.ToString() }; + } + } + public override void CompleteOnCreateEntryActivity(EntryActivity activity) { - //show the notifications - activity.StartNotificationsService(true); - //close - activity.CloseAfterTaskComplete(); + if (ShowUserNotifications) + { + //show the notifications + activity.StartNotificationsService(CloseAfterCreate); + + } + if (CloseAfterCreate) + { + //close + activity.CloseAfterTaskComplete(); + } } } @@ -337,7 +390,7 @@ namespace keepass2android /// User is about to select an entry. When selected, ask whether the url he was searching for earlier should be stored /// in the selected entry for later use. /// - public class SelectEntryForUrlTask: AppTask + public class SelectEntryForUrlTask: SelectEntryTask { /// /// default constructor for creating from Bundle @@ -350,10 +403,11 @@ namespace keepass2android public SelectEntryForUrlTask(string url) { UrlToSearchFor = url; + ShowUserNotifications = true; } public const String UrlToSearchKey = "UrlToSearch"; - + public string UrlToSearchFor { get; @@ -362,22 +416,25 @@ namespace keepass2android public override void Setup(Bundle b) { + base.Setup(b); UrlToSearchFor = b.GetString(UrlToSearchKey); } public override IEnumerable Extras { get { + foreach (IExtra e in base.Extras) + yield return e; yield return new StringExtra { Key = UrlToSearchKey, Value = UrlToSearchFor }; } } public override void CompleteOnCreateEntryActivity(EntryActivity activity) { - //if the database is readonly, don't offer to modify the URL - if (App.Kp2a.GetDb().CanWrite == false) + //if the database is readonly (or no URL exists), don't offer to modify the URL + if ((App.Kp2a.GetDb().CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor))) { - ShowNotificationsAndClose(activity); + base.CompleteOnCreateEntryActivity(activity); return; } @@ -386,12 +443,6 @@ namespace keepass2android } - private static void ShowNotificationsAndClose(EntryActivity activity) - { - activity.StartNotificationsService(true); - activity.CloseAfterTaskComplete(); - } - /// /// brings up a dialog asking the user whether he wants to add the given URL to the entry for automatic finding /// @@ -404,17 +455,16 @@ namespace keepass2android builder.SetPositiveButton(activity.GetString(Resource.String.yes), (dlgSender, dlgEvt) => { - activity.AddUrlToEntry(url, () => ShowNotificationsAndClose(activity)); + activity.AddUrlToEntry(url, () => base.CompleteOnCreateEntryActivity(activity)); }); builder.SetNegativeButton(activity.GetString(Resource.String.no), (dlgSender, dlgEvt) => { - ShowNotificationsAndClose(activity); + base.CompleteOnCreateEntryActivity(activity); }); Dialog dialog = builder.Create(); dialog.Show(); - } } @@ -477,17 +527,35 @@ namespace keepass2android /// public const String ProtectedFieldsListKey = Keepass2android.Pluginsdk.Strings.ExtraProtectedFieldsList; + + /// + /// Extra key to specify whether user notifications (e.g. for copy password or keyboard) should be displayed when the entry + /// is selected after creating. + /// + public const String ShowUserNotificationsKey = "ShowUserNotifications"; + + public string Url { get; set; } public string AllFields { get; set; } - public string[] ProtectedFieldsList { get; set; } + public IList ProtectedFieldsList { get; set; } + + public bool ShowUserNotifications { get; set; } + public override void Setup(Bundle b) { + bool showUserNotification; + if (!Boolean.TryParse(b.GetString(ShowUserNotificationsKey), out showUserNotification)) + { + showUserNotification = true; //default to true + } + ShowUserNotifications = showUserNotification; + Url = b.GetString(UrlKey); AllFields = b.GetString(AllFieldsKey); - ProtectedFieldsList = b.GetStringArray(ProtectedFieldsListKey); + ProtectedFieldsList = b.GetStringArrayList(ProtectedFieldsListKey); } public override IEnumerable Extras { @@ -498,7 +566,9 @@ namespace keepass2android if (AllFields != null) yield return new StringExtra { Key = AllFieldsKey, Value = AllFields }; if (ProtectedFieldsList != null) - yield return new StringArrayExtra { Key = ProtectedFieldsListKey, Value = ProtectedFieldsList }; + yield return new StringArrayListExtra { Key = ProtectedFieldsListKey, Value = ProtectedFieldsList }; + + yield return new StringExtra { Key = ShowUserNotificationsKey, Value = ShowUserNotifications.ToString() }; } } @@ -511,17 +581,14 @@ namespace keepass2android } if (AllFields != null) { - IList protectedFieldsKeys = new List(); - if (ProtectedFieldsList != null) - { - protectedFieldsKeys = new Org.Json.JSONArray(ProtectedFieldsList).ToArray(); - } + var allFields = new Org.Json.JSONObject(AllFields); for (var iter = allFields.Keys(); iter.HasNext; ) { string key = iter.Next().ToString(); string value = allFields.Get(key).ToString(); - bool isProtected = protectedFieldsKeys.Contains(key) || key == PwDefs.PasswordField; + bool isProtected = ((ProtectedFieldsList != null) && (ProtectedFieldsList.Contains(key))) + || (key == PwDefs.PasswordField); newEntry.Strings.Set(key, new ProtectedString(isProtected, value)); } @@ -531,7 +598,9 @@ namespace keepass2android public override void AfterAddNewEntry(EntryEditActivity entryEditActivity, PwEntry newEntry) { - EntryActivity.Launch(entryEditActivity, newEntry, -1, new SelectEntryTask(), ActivityFlags.ForwardResult); + EntryActivity.Launch(entryEditActivity, newEntry, -1, + new SelectEntryTask() { ShowUserNotifications = this.ShowUserNotifications}, + ActivityFlags.ForwardResult); //no need to call Finish here, that's done in EntryEditActivity ("closeOrShowError") } diff --git a/src/keepass2android/fileselect/FileSelectActivity.cs b/src/keepass2android/fileselect/FileSelectActivity.cs index 4449c480..4c6b5d81 100644 --- a/src/keepass2android/fileselect/FileSelectActivity.cs +++ b/src/keepass2android/fileselect/FileSelectActivity.cs @@ -26,6 +26,7 @@ using Android.Views; using Android.Widget; using Android.Content.PM; using KeePassLib.Serialization; +using Keepass2android.Pluginsdk; using keepass2android.Io; using Environment = Android.OS.Environment; @@ -42,6 +43,8 @@ namespace keepass2android Label = "@string/kp2a_findUrl", Categories=new[]{Intent.CategoryDefault}, DataMimeType="text/plain")] + [IntentFilter(new[] { Strings.ActionStartWithTask }, + Categories = new[] { Intent.CategoryDefault })] public class FileSelectActivity : ListActivity { private readonly ActivityDesign _design; @@ -84,7 +87,7 @@ namespace keepass2android else { //see PasswordActivity for an explanation - if ((savedInstanceState == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))) + if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)) { AppTask = new NullTask(); } diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index a20eadd9..646ef7cb 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -127,6 +127,7 @@ + diff --git a/src/keepass2android/pluginhost/PluginArrayAdapter.cs b/src/keepass2android/pluginhost/PluginArrayAdapter.cs index 953fa198..4b73ffe8 100644 --- a/src/keepass2android/pluginhost/PluginArrayAdapter.cs +++ b/src/keepass2android/pluginhost/PluginArrayAdapter.cs @@ -6,7 +6,6 @@ using Android.Content; using Android.Views; using System.Collections.Generic; using Android.App; -using PluginHostTest; namespace keepass2android { diff --git a/src/keepass2android/pluginhost/PluginDatabase.cs b/src/keepass2android/pluginhost/PluginDatabase.cs index 0c339848..93098622 100644 --- a/src/keepass2android/pluginhost/PluginDatabase.cs +++ b/src/keepass2android/pluginhost/PluginDatabase.cs @@ -171,5 +171,30 @@ namespace keepass2android var prefs = _ctx.GetSharedPreferences("KP2A.Plugin." + plugin, FileCreationMode.Private); prefs.Edit().Clear().Commit(); } + + /// + /// Checks if the given pluginPackage has been granted the requiredScope + /// + public bool HasAcceptedScope(string pluginPackage, string requiredScope) + { + if (pluginPackage == null) + { + Log.Warn(_tag, "No pluginPackage specified!"); + return false; + } + + var prefs = GetPreferencesForPlugin(pluginPackage); + if (prefs.GetString(_accessToken, null) == null) + { + Log.Info(_tag, "No access token for " + pluginPackage); + return false; + } + if (!AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(requiredScope)) + { + Log.Info(_tag, "Scope " + requiredScope + " not granted for " + pluginPackage); + return false; + } + return true; + } } } \ No newline at end of file diff --git a/src/keepass2android/pluginhost/PluginDetailsActivity.cs b/src/keepass2android/pluginhost/PluginDetailsActivity.cs index fb574e11..cbae8c31 100644 --- a/src/keepass2android/pluginhost/PluginDetailsActivity.cs +++ b/src/keepass2android/pluginhost/PluginDetailsActivity.cs @@ -15,10 +15,9 @@ using Android.Views; using Android.Widget; using Java.Util; using Keepass2android.Pluginsdk; -using keepass2android; using keepass2android.views; -namespace PluginHostTest +namespace keepass2android { [Activity(Label = AppNames.AppName)] [IntentFilter(new[] { Strings.ActionEditPluginSettings }, diff --git a/src/keepass2android/pluginhost/PluginHost.cs b/src/keepass2android/pluginhost/PluginHost.cs index e709f3e6..f70af880 100644 --- a/src/keepass2android/pluginhost/PluginHost.cs +++ b/src/keepass2android/pluginhost/PluginHost.cs @@ -22,7 +22,10 @@ namespace keepass2android private const string _tag = "KP2A_PluginHost"; - private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions, Strings.ScopeCurrentEntry }; + private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions, + Strings.ScopeCurrentEntry, + Strings.ScopeQueryCredentials, + Strings.ScopeQueryCredentialsForOwnPackage}; public static IEnumerable GetAllPlugins(Context ctx) { @@ -154,9 +157,16 @@ namespace keepass2android //add the output string array (placeholders replaced taking into account the db context) Dictionary outputFields = entry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString()); - JSONObject json = new JSONObject(outputFields); - var jsonStr = json.ToString(); - intent.PutExtra(Strings.ExtraEntryOutputData, jsonStr); + JSONObject jsonOutput = new JSONObject(outputFields); + var jsonOutputStr = jsonOutput.ToString(); + intent.PutExtra(Strings.ExtraEntryOutputData, jsonOutputStr); + + JSONArray jsonProtectedFields = new JSONArray( + entry.OutputStrings + .Where(pair => pair.Value.IsProtected) + .Select(pair => pair.Key) + .ToArray()); + intent.PutExtra(Strings.ExtraProtectedFieldsList, jsonProtectedFields.ToString()); intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString()); diff --git a/src/keepass2android/pluginhost/PluginListActivity.cs b/src/keepass2android/pluginhost/PluginListActivity.cs index 7b5a20ba..c83dbe7d 100644 --- a/src/keepass2android/pluginhost/PluginListActivity.cs +++ b/src/keepass2android/pluginhost/PluginListActivity.cs @@ -6,7 +6,6 @@ using Android.Content.PM; using Android.OS; using Android.Widget; using Keepass2android.Pluginsdk; -using PluginHostTest; namespace keepass2android { diff --git a/src/keepass2android/search/SearchResults.cs b/src/keepass2android/search/SearchResults.cs index 6d2926e6..8af7f636 100644 --- a/src/keepass2android/search/SearchResults.cs +++ b/src/keepass2android/search/SearchResults.cs @@ -120,12 +120,11 @@ namespace keepass2android.search public override bool OnSearchRequested() { - if (base.OnSearchRequested()) - { - Finish(); - return true; - } - return false; + Intent i = new Intent(this, typeof(SearchActivity)); + AppTask.ToIntent(i); + i.SetFlags(ActivityFlags.ForwardResult); + StartActivity(i); + return true; } } }