* 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);
//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)

View File

@ -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)

View File

@ -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)
{

View File

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

View File

@ -23,7 +23,7 @@ namespace Kp2aUnitTests
//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(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"));

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<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>
</manifest>

View File

@ -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()
{

View File

@ -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)

View File

@ -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)
{

View File

@ -529,6 +529,9 @@
<string name="ChangeLog_title">Change log</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">
Version 0.9.7b\n

View File

@ -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<string, Dialog, bool>) null,
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),
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();

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);
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();
};