From ed15af3f8f1b53e2e3d19e1844ec250a104cfd48 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Wed, 17 Jun 2015 06:33:29 +0200 Subject: [PATCH] * check if entered URI looks like a directory (not a file) and issue warning in SelectStorageLocationActivity * check if URI is directory and refuse to delete in IOConnection --- .../Serialization/IOConnection.cs | 5 ++ .../Io/AndroidContentStorage.cs | 51 ++++++++++++------- .../SelectStorageLocationActivityBase.cs | 14 ++++- src/Kp2aUnitTests/Kp2aUnitTests.csproj | 5 +- src/Kp2aUnitTests/MainActivity.cs | 4 +- .../Properties/AndroidManifest.xml | 2 +- .../TestSelectStorageLocation.cs | 50 +++++++++++++++++- src/keepass2android/CreateDatabaseActivity.cs | 2 +- src/keepass2android/ExportDatabaseActivity.cs | 2 +- .../Resources/values/strings.xml | 3 ++ .../SelectStorageLocationActivity.cs | 18 +++++-- src/keepass2android/Utils/Util.cs | 6 +-- 12 files changed, 127 insertions(+), 35 deletions(-) diff --git a/src/KeePassLib2Android/Serialization/IOConnection.cs b/src/KeePassLib2Android/Serialization/IOConnection.cs index aff3ad2d..575bbf45 100644 --- a/src/KeePassLib2Android/Serialization/IOConnection.cs +++ b/src/KeePassLib2Android/Serialization/IOConnection.cs @@ -519,6 +519,11 @@ namespace KeePassLib.Serialization { RaiseIOAccessPreEvent(ioc, IOAccessType.Delete); + //in case a user entered a directory instead of a filename, make sure we're never + //deleting their whole WebDAV/FTP content + if (ioc.Path.EndsWith("/")) + throw new IOException("Delete file does not expect directory URIs."); + if(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; } #if (!KeePassLibSD && !KeePassRT) diff --git a/src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs b/src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs index d56d96c6..6d24fa10 100644 --- a/src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs +++ b/src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs @@ -9,7 +9,10 @@ using KeePassLib.Serialization; namespace keepass2android.Io { - //TODOC,TOTEST, TODO: unimplemented methods? + /** FileStorage to work with content URIs + * Supports both "old" system where data is available only temporarily as + * well as the SAF system. Assumes that persistable permissions are "taken" by + * the activity which receives OnActivityResult from the system file picker.*/ public class AndroidContentStorage: IFileStorage { private readonly Context _ctx; @@ -135,27 +138,39 @@ namespace keepass2android.Io private bool TryGetDisplayName(IOConnectionInfo ioc, ref string displayName) { var uri = Android.Net.Uri.Parse(ioc.Path); - var cursor = _ctx.ContentResolver.Query(uri, null, null, null, null, null); - try { - if (cursor != null && cursor.MoveToFirst()) + var cursor = _ctx.ContentResolver.Query(uri, null, null, null, null, null); + try { - displayName = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName)); - if (!string.IsNullOrEmpty(displayName)) - { - return true; - } - - } - } - finally - { - if (cursor != null) - cursor.Close(); - } - return false; + if (cursor != null && cursor.MoveToFirst()) + { + displayName = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName)); + if (!string.IsNullOrEmpty(displayName)) + { + return true; + } + + } + } + finally + { + if (cursor != null) + cursor.Close(); + } + + return false; + } + catch (Exception e) + { + Kp2aLog.Log(e.ToString()); + + return false; + } + + + } public string CreateFilePath(string parent, string newFilename) diff --git a/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs b/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs index 09d212ff..9e94df4a 100644 --- a/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs +++ b/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs @@ -258,21 +258,31 @@ namespace keepass2android protected abstract void StartFileChooser(string path, int requestCode, bool isForSave); - protected bool OnOpenButton(String fileName, int requestCode) + protected bool OnOpenButton(string fileName, int requestCode, Action dismissDialog) { - IOConnectionInfo ioc = new IOConnectionInfo { Path = fileName }; + int lastSlashPos = fileName.LastIndexOf('/'); + int lastDotPos = fileName.LastIndexOf('.'); + if (lastSlashPos >= lastDotPos) //no dot after last slash or == in case neither / nor . + { + ShowFilenameWarning(fileName, () => { IocSelected(ioc, requestCode); dismissDialog(); }, () => { /* don't do anything, leave dialog open, let user try again*/ }); + //signal that the dialog should be kept open + return false; + } + IocSelected(ioc, requestCode); return true; } + protected abstract void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect); + protected virtual void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc) { diff --git a/src/Kp2aUnitTests/Kp2aUnitTests.csproj b/src/Kp2aUnitTests/Kp2aUnitTests.csproj index a1b94ac4..a631e05d 100644 --- a/src/Kp2aUnitTests/Kp2aUnitTests.csproj +++ b/src/Kp2aUnitTests/Kp2aUnitTests.csproj @@ -14,12 +14,13 @@ Resources\Resource.Designer.cs Off Properties\AndroidManifest.xml - v4.2 + v4.4 armeabi,armeabi-v7a,x86 + true @@ -101,7 +102,7 @@ KeePassLib2Android - {53A9CB7F-6553-4BC0-B56B-9410BB2E59AA} + {53a9cb7f-6553-4bc0-b56b-9410bb2e59aa} Kp2aBusinessLogic diff --git a/src/Kp2aUnitTests/MainActivity.cs b/src/Kp2aUnitTests/MainActivity.cs index a5ba6de9..5d080071 100644 --- a/src/Kp2aUnitTests/MainActivity.cs +++ b/src/Kp2aUnitTests/MainActivity.cs @@ -23,7 +23,7 @@ namespace Kp2aUnitTests //runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly")); - //runner.AddTests(new List { typeof(TestSelectStorageLocation) }); + runner.AddTests(new List { typeof(TestSelectStorageLocation) }); //runner.AddTests(new List { typeof(TestSynchronizeCachedDatabase)}); //runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure")); //runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure")); @@ -36,7 +36,7 @@ namespace Kp2aUnitTests //runner.AddTests(typeof(TestSaveDb).GetMethod("TestLoadAndSave_TestIdenticalFiles_kdb")); //runner.AddTests(typeof(TestSaveDb).GetMethod("TestCreateSaveAndLoad_TestIdenticalFiles_kdb")); - runner.AddTests(typeof(TestSaveDb).GetMethod("TestSaveTwice_kdb")); + // runner.AddTests(typeof(TestSaveDb).GetMethod("TestSaveTwice_kdb")); //runner.AddTests(typeof(TestLoadDb).GetMethod("LoadAndSaveFromRemote1And1Ftp")); //runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdbpWithPasswordOnly")); diff --git a/src/Kp2aUnitTests/Properties/AndroidManifest.xml b/src/Kp2aUnitTests/Properties/AndroidManifest.xml index 5a67745d..36b14c48 100644 --- a/src/Kp2aUnitTests/Properties/AndroidManifest.xml +++ b/src/Kp2aUnitTests/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Kp2aUnitTests/TestSelectStorageLocation.cs b/src/Kp2aUnitTests/TestSelectStorageLocation.cs index 9b0d1ab6..b8b35f67 100644 --- a/src/Kp2aUnitTests/TestSelectStorageLocation.cs +++ b/src/Kp2aUnitTests/TestSelectStorageLocation.cs @@ -225,6 +225,12 @@ namespace Kp2aUnitTests _userAction = new SelectFileAction(isForSave, browseRequestCode, protocolId, this); } + protected override void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect) + { + _userAction = new ShowAlertDialogAction("filenameWarning", delegate { onUserWantsToContinue(); }, + delegate { onUserWantsToCorrect(); }); + } + public void HandleActivityResult(int requestCode, Result resultCode, Intent data) { OnActivityResult(requestCode, resultCode, data); @@ -281,7 +287,7 @@ namespace Kp2aUnitTests private void PressOpenButton(string path, int browseRequestCode) { - OnOpenButton(path, browseRequestCode); + OnOpenButton(path, browseRequestCode, () => { }); } @@ -523,6 +529,48 @@ namespace Kp2aUnitTests } + [TestMethod] + public void TestManualSelectWithDirectoryCancel() + { + var testee = CreateTestee(); + var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; + action.ReturnProtocol("ftp"); + + Assert.IsNull(testee._result); //no result yet + + var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; + string path = "ftp://crocoll.net/"; + action2.PerformManualFileSelect(path); + + Assert.IsNull(testee._result); + + var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction) testee._userAction; + action3.Ok(); + Assert.IsTrue((bool)testee._result); + Assert.AreEqual(testee._resultIoc.Path, path); + + } + + + [TestMethod] + public void TestManualSelectWithDirectory() + { + var testee = CreateTestee(); + var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction; + action.ReturnProtocol("ftp"); + + Assert.IsNull(testee._result); //no result yet + + var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction; + string path = "ftp://crocoll.net/"; + action2.PerformManualFileSelect(path); + + Assert.IsNull(testee._result); + + var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction; + action3.Cancel(); + + } [TestMethod] public void TestCancelManualSelect() { diff --git a/src/keepass2android/CreateDatabaseActivity.cs b/src/keepass2android/CreateDatabaseActivity.cs index 8907edca..6e19fe25 100644 --- a/src/keepass2android/CreateDatabaseActivity.cs +++ b/src/keepass2android/CreateDatabaseActivity.cs @@ -441,7 +441,7 @@ namespace keepass2android return filename; } - private bool OnCreateButton(string filename) + private bool OnCreateButton(string filename, Dialog dialog) { // Make sure file name exists if (filename.Length == 0) diff --git a/src/keepass2android/ExportDatabaseActivity.cs b/src/keepass2android/ExportDatabaseActivity.cs index ad942c5b..481e423f 100644 --- a/src/keepass2android/ExportDatabaseActivity.cs +++ b/src/keepass2android/ExportDatabaseActivity.cs @@ -144,7 +144,7 @@ namespace keepass2android get { return 0; } } - private bool OnCreateButton(string filename) + private bool OnCreateButton(string filename, Dialog dialog) { if (filename.Length == 0) { diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index 01b20299..cb95eac9 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -529,6 +529,9 @@ Change log Please note! This is a preview release and might come with some flaws! If you experience *anything* unexpected, please let me know (on Codeplex or by email). + + Continue + The URI you have entered does not look like a filename. Are you sure this is a valid file? Version 0.9.7b\n diff --git a/src/keepass2android/SelectStorageLocationActivity.cs b/src/keepass2android/SelectStorageLocationActivity.cs index 782477b2..38ed009b 100644 --- a/src/keepass2android/SelectStorageLocationActivity.cs +++ b/src/keepass2android/SelectStorageLocationActivity.cs @@ -100,9 +100,9 @@ namespace keepass2android if (defaultPath.StartsWith("sftp://")) Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel); else - //todo oncreate nur wenn for save? - Util.ShowFilenameDialog(this, filename => OnOpenButton(filename, browseRequestCode), - filename => OnOpenButton(filename, browseRequestCode), + Util.ShowFilenameDialog(this, + !isForSave ? delegate(string filename, Dialog dialog) { return OnOpenButton(filename, browseRequestCode, dialog.Dismiss); } : (Func) null, + isForSave ? delegate(string filename, Dialog dialog) { return OnOpenButton(filename, browseRequestCode, dialog.Dismiss); } : (Func) null, ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url), browseRequestCode); }); @@ -224,9 +224,19 @@ namespace keepass2android } + protected override void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect) + { + new AlertDialog.Builder(this) + .SetPositiveButton(Resource.String.Continue, delegate { onUserWantsToContinue(); } ) + .SetMessage(Resource.String.NoFilenameWarning) + .SetCancelable(false) + .SetNegativeButton(Android.Resource.String.Cancel, delegate { onUserWantsToCorrect(); }) + .Create() + .Show(); + + } - public void OnDismiss(IDialogInterface dialog) { // ReturnCancel(); diff --git a/src/keepass2android/Utils/Util.cs b/src/keepass2android/Utils/Util.cs index 9f389ef1..aa3130ab 100644 --- a/src/keepass2android/Utils/Util.cs +++ b/src/keepass2android/Utils/Util.cs @@ -335,7 +335,7 @@ namespace keepass2android } } - public static void ShowFilenameDialog(Activity activity, FileSelectedHandler onOpen, FileSelectedHandler onCreate, Action onCancel, bool showBrowseButton, string defaultFilename, string detailsText, int requestCodeBrowse) + public static void ShowFilenameDialog(Activity activity, Func onOpen, Func onCreate, Action onCancel, bool showBrowseButton, string defaultFilename, string detailsText, int requestCodeBrowse) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.SetView(activity.LayoutInflater.Inflate(Resource.Layout.file_selection_filename, null)); @@ -362,7 +362,7 @@ namespace keepass2android openButton.Click += (sender, args) => { String fileName = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text; - if (onOpen(fileName)) + if (onOpen(fileName, dialog)) dialog.Dismiss(); }; @@ -371,7 +371,7 @@ namespace keepass2android createButton.Click += (sender, args) => { String fileName = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text; - if (onCreate(fileName)) + if (onCreate(fileName, dialog)) dialog.Dismiss(); };