* 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
This commit is contained in:
Philipp Crocoll 2015-06-17 06:33:29 +02:00
parent 6c3795ff1a
commit ed15af3f8f
12 changed files with 127 additions and 35 deletions

View File

@ -519,6 +519,11 @@ namespace KeePassLib.Serialization
{ {
RaiseIOAccessPreEvent(ioc, IOAccessType.Delete); 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(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; }
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassRT)

View File

@ -9,7 +9,10 @@ using KeePassLib.Serialization;
namespace keepass2android.Io 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 public class AndroidContentStorage: IFileStorage
{ {
private readonly Context _ctx; private readonly Context _ctx;
@ -135,10 +138,12 @@ namespace keepass2android.Io
private bool TryGetDisplayName(IOConnectionInfo ioc, ref string displayName) private bool TryGetDisplayName(IOConnectionInfo ioc, ref string displayName)
{ {
var uri = Android.Net.Uri.Parse(ioc.Path); var uri = Android.Net.Uri.Parse(ioc.Path);
var cursor = _ctx.ContentResolver.Query(uri, null, null, null, null, null);
try try
{ {
var cursor = _ctx.ContentResolver.Query(uri, null, null, null, null, null);
try
{
if (cursor != null && cursor.MoveToFirst()) if (cursor != null && cursor.MoveToFirst())
{ {
displayName = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName)); displayName = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName));
@ -156,6 +161,16 @@ namespace keepass2android.Io
} }
return false; return false;
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
return false;
}
} }
public string CreateFilePath(string parent, string newFilename) public string CreateFilePath(string parent, string newFilename)

View File

@ -258,21 +258,31 @@ namespace keepass2android
protected abstract void StartFileChooser(string path, int requestCode, bool isForSave); 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 IOConnectionInfo ioc = new IOConnectionInfo
{ {
Path = fileName 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); IocSelected(ioc, requestCode);
return true; return true;
} }
protected abstract void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect);
protected virtual void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc) protected virtual void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc)
{ {

View File

@ -14,12 +14,13 @@
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile> <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<TargetFrameworkVersion>v4.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis> <AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
<AndroidStoreUncompressedFileExtensions /> <AndroidStoreUncompressedFileExtensions />
<MandroidI18n /> <MandroidI18n />
<JavaMaximumHeapSize /> <JavaMaximumHeapSize />
<JavaOptions /> <JavaOptions />
<AndroidUseLatestPlatformSdk />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -101,7 +102,7 @@
<Name>KeePassLib2Android</Name> <Name>KeePassLib2Android</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\Kp2aBusinessLogic\Kp2aBusinessLogic.csproj"> <ProjectReference Include="..\Kp2aBusinessLogic\Kp2aBusinessLogic.csproj">
<Project>{53A9CB7F-6553-4BC0-B56B-9410BB2E59AA}</Project> <Project>{53a9cb7f-6553-4bc0-b56b-9410bb2e59aa}</Project>
<Name>Kp2aBusinessLogic</Name> <Name>Kp2aBusinessLogic</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj"> <ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">

View File

@ -23,7 +23,7 @@ namespace Kp2aUnitTests
//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly")); //runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly"));
//runner.AddTests(new List<Type> { typeof(TestSelectStorageLocation) }); runner.AddTests(new List<Type> { typeof(TestSelectStorageLocation) });
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)}); //runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)});
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure")); //runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure"));
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure")); //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("TestLoadAndSave_TestIdenticalFiles_kdb"));
//runner.AddTests(typeof(TestSaveDb).GetMethod("TestCreateSaveAndLoad_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("LoadAndSaveFromRemote1And1Ftp"));
//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdbpWithPasswordOnly")); //runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdbpWithPasswordOnly"));

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" />
<application></application> <application></application>
</manifest> </manifest>

View File

@ -225,6 +225,12 @@ namespace Kp2aUnitTests
_userAction = new SelectFileAction(isForSave, browseRequestCode, protocolId, this); _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) public void HandleActivityResult(int requestCode, Result resultCode, Intent data)
{ {
OnActivityResult(requestCode, resultCode, data); OnActivityResult(requestCode, resultCode, data);
@ -281,7 +287,7 @@ namespace Kp2aUnitTests
private void PressOpenButton(string path, int browseRequestCode) 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] [TestMethod]
public void TestCancelManualSelect() public void TestCancelManualSelect()
{ {

View File

@ -441,7 +441,7 @@ namespace keepass2android
return filename; return filename;
} }
private bool OnCreateButton(string filename) private bool OnCreateButton(string filename, Dialog dialog)
{ {
// Make sure file name exists // Make sure file name exists
if (filename.Length == 0) if (filename.Length == 0)

View File

@ -144,7 +144,7 @@ namespace keepass2android
get { return 0; } get { return 0; }
} }
private bool OnCreateButton(string filename) private bool OnCreateButton(string filename, Dialog dialog)
{ {
if (filename.Length == 0) if (filename.Length == 0)
{ {

View File

@ -530,6 +530,9 @@
<string name="PreviewWarning">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).</string> <string name="PreviewWarning">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).</string>
<string name="Continue">Continue</string>
<string name="NoFilenameWarning">The URI you have entered does not look like a filename. Are you sure this is a valid file?</string>
<string name="ChangeLog_0_9_7b"> <string name="ChangeLog_0_9_7b">
Version 0.9.7b\n Version 0.9.7b\n
* updated translations\n * updated translations\n

View File

@ -100,9 +100,9 @@ namespace keepass2android
if (defaultPath.StartsWith("sftp://")) if (defaultPath.StartsWith("sftp://"))
Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel); Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel);
else else
//todo oncreate nur wenn for save? Util.ShowFilenameDialog(this,
Util.ShowFilenameDialog(this, filename => OnOpenButton(filename, browseRequestCode), !isForSave ? delegate(string filename, Dialog dialog) { return OnOpenButton(filename, browseRequestCode, dialog.Dismiss); } : (Func<string, Dialog, bool>) null,
filename => OnOpenButton(filename, browseRequestCode), isForSave ? delegate(string filename, Dialog dialog) { return OnOpenButton(filename, browseRequestCode, dialog.Dismiss); } : (Func<string, Dialog, bool>) null,
ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url), ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
browseRequestCode); browseRequestCode);
}); });
@ -224,7 +224,17 @@ 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) public void OnDismiss(IDialogInterface dialog)

View File

@ -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<string, Dialog, bool> onOpen, Func<string, Dialog, bool> onCreate, Action onCancel, bool showBrowseButton, string defaultFilename, string detailsText, int requestCodeBrowse)
{ {
AlertDialog.Builder builder = new AlertDialog.Builder(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.SetView(activity.LayoutInflater.Inflate(Resource.Layout.file_selection_filename, null)); builder.SetView(activity.LayoutInflater.Inflate(Resource.Layout.file_selection_filename, null));
@ -362,7 +362,7 @@ namespace keepass2android
openButton.Click += (sender, args) => openButton.Click += (sender, args) =>
{ {
String fileName = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text; String fileName = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text;
if (onOpen(fileName)) if (onOpen(fileName, dialog))
dialog.Dismiss(); dialog.Dismiss();
}; };
@ -371,7 +371,7 @@ namespace keepass2android
createButton.Click += (sender, args) => createButton.Click += (sender, args) =>
{ {
String fileName = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text; String fileName = ((EditText)dialog.FindViewById(Resource.Id.file_filename)).Text;
if (onCreate(fileName)) if (onCreate(fileName, dialog))
dialog.Dismiss(); dialog.Dismiss();
}; };