From 84aeb31fd0654dc5283f41a860e9736c26bacc26 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 9 Jul 2013 09:59:17 +0200 Subject: [PATCH] * Introduced IFileStorage interface: Better abstraction than current IOConnection (suitable for cloud support). Currently only implemented by the built-in IOConnection (local/http/ftp) * Implemented Merge functionality for SaveDB. UI is not yet implemented! * Added tests for merge functionality --- src/KeePassLib2Android/Native/NativeLib.cs | 1 - src/KeePassLib2Android/PwDatabase.cs | 54 +++++- .../Serialization/KdbxFile.Read.cs | 12 +- src/Kp2aBusinessLogic/IKp2aApp.cs | 3 + src/Kp2aBusinessLogic/IProgressDialog.cs | 2 +- .../Io/BuiltInFileStorage.cs | 104 +++++++++++ src/Kp2aBusinessLogic/Io/IFileStorage.cs | 82 +++++++++ .../Kp2aBusinessLogic.csproj | 4 +- src/Kp2aBusinessLogic/PreferenceKey.cs | 3 +- ...tatus.cs => ProgressDialogStatusLogger.cs} | 58 +++++- src/Kp2aBusinessLogic/ProgressTask.cs | 7 +- src/Kp2aBusinessLogic/UiStringKey.cs | 15 +- src/Kp2aBusinessLogic/database/Database.cs | 35 ++-- .../database/edit/AddEntry.cs | 27 ++- .../database/edit/AddGroup.cs | 24 ++- .../database/edit/CreateDB.cs | 10 +- .../database/edit/DeleteEntry.cs | 12 +- .../database/edit/DeleteGroup.cs | 8 +- .../database/edit/DeleteRunnable.cs | 6 +- src/Kp2aBusinessLogic/database/edit/LoadDB.cs | 3 +- .../database/edit/OnFinish.cs | 9 +- .../database/edit/RunnableOnFinish.cs | 22 ++- src/Kp2aBusinessLogic/database/edit/SaveDB.cs | 167 ++++++++++++++++-- .../database/edit/SetPassword.cs | 22 ++- .../database/edit/UpdateEntry.cs | 20 ++- src/Kp2aUnitTests/MainActivity.cs | 3 +- src/Kp2aUnitTests/ProgressDialogStub.cs | 4 +- src/Kp2aUnitTests/TestBase.cs | 25 ++- src/Kp2aUnitTests/TestKp2aApp.cs | 45 ++++- src/Kp2aUnitTests/TestLoadDb.cs | 2 +- src/Kp2aUnitTests/TestSaveDb.cs | 167 +++++++++++++++++- src/keepass2android/EntryActivity.cs | 4 +- src/keepass2android/EntryEditActivity.cs | 6 +- src/keepass2android/GroupActivity.cs | 4 +- src/keepass2android/PasswordActivity.cs | 2 +- .../Resources/Resource.designer.cs | 62 +++++-- .../Resources/values/strings.xml | 11 ++ src/keepass2android/SetPasswordDialog.cs | 4 +- src/keepass2android/app/App.cs | 6 + .../fileselect/FileSelectActivity.cs | 6 +- .../settings/AppSettingsActivity.cs | 8 +- .../settings/RoundsPreference.cs | 4 +- 42 files changed, 912 insertions(+), 161 deletions(-) create mode 100644 src/Kp2aBusinessLogic/Io/BuiltInFileStorage.cs create mode 100644 src/Kp2aBusinessLogic/Io/IFileStorage.cs rename src/Kp2aBusinessLogic/{UpdateStatus.cs => ProgressDialogStatusLogger.cs} (61%) diff --git a/src/KeePassLib2Android/Native/NativeLib.cs b/src/KeePassLib2Android/Native/NativeLib.cs index 8cda400c..05a812ff 100644 --- a/src/KeePassLib2Android/Native/NativeLib.cs +++ b/src/KeePassLib2Android/Native/NativeLib.cs @@ -164,7 +164,6 @@ namespace KeePassLib.Native try { - //Kp2aLog.Log("4+1"+new Kp2atest.TestClass().Add1(4)); Com.Keepassdroid.Crypto.Finalkey.NativeFinalKey key = new Com.Keepassdroid.Crypto.Finalkey.NativeFinalKey(); byte[] newKey = key.TransformMasterKey(pKey256, pBuf256, (int)uRounds); diff --git a/src/KeePassLib2Android/PwDatabase.cs b/src/KeePassLib2Android/PwDatabase.cs index 925ab468..1172027c 100644 --- a/src/KeePassLib2Android/PwDatabase.cs +++ b/src/KeePassLib2Android/PwDatabase.cs @@ -562,22 +562,40 @@ namespace KeePassLib /// Open a database. The URL may point to any supported data source. /// /// IO connection to load the database from. - /// Key used to open the specified database. + /// sKey used to open the specified database. /// Logger, which gets all status messages. public void Open(IOConnectionInfo ioSource, CompositeKey pwKey, IStatusLogger slLogger) { - Debug.Assert(ioSource != null); - if(ioSource == null) throw new ArgumentNullException("ioSource"); + Open(IOConnection.OpenRead(ioSource), UrlUtil.StripExtension( + UrlUtil.GetFileName(ioSource.Path)), ioSource, pwKey, slLogger ); + + } + + /// + /// Open a database. The URL may point to any supported data source. + /// + /// IO connection to load the database from. + /// Key used to open the specified database. + /// Logger, which gets all status messages. + public void Open(Stream s, string fileNameWithoutPathAndExt, IOConnectionInfo ioSource, CompositeKey pwKey, + IStatusLogger slLogger) + { + Debug.Assert(s != null); + if (s == null) throw new ArgumentNullException("s"); + Debug.Assert(fileNameWithoutPathAndExt != null); + if (fileNameWithoutPathAndExt == null) throw new ArgumentException("fileNameWithoutPathAndExt"); Debug.Assert(pwKey != null); - if(pwKey == null) throw new ArgumentNullException("pwKey"); + Debug.Assert(ioSource != null); + if (ioSource == null) throw new ArgumentNullException("ioSource"); + + if (pwKey == null) throw new ArgumentNullException("pwKey"); Close(); try { - m_pgRootGroup = new PwGroup(true, true, UrlUtil.StripExtension( - UrlUtil.GetFileName(ioSource.Path)), PwIcon.FolderOpen); + m_pgRootGroup = new PwGroup(true, true, fileNameWithoutPathAndExt, PwIcon.FolderOpen); m_pgRootGroup.IsExpanded = true; m_pwUserKey = pwKey; @@ -587,8 +605,8 @@ namespace KeePassLib KdbxFile kdbx = new KdbxFile(this); kdbx.DetachBinaries = m_strDetachBins; - Stream s = IOConnection.OpenRead(ioSource); kdbx.Load(s, KdbxFormat.Default, slLogger); + s.Close(); m_pbHashOfLastIO = kdbx.HashOfFileOnDisk; @@ -598,13 +616,14 @@ namespace KeePassLib m_bDatabaseOpened = true; m_ioSource = ioSource; } - catch(Exception) + catch (Exception) { Clear(); throw; } } + /// /// Save the currently opened database. The file is written to the location /// it has been opened from. @@ -626,7 +645,7 @@ namespace KeePassLib kdb.Save(s, null, KdbxFormat.Default, slLogger); ft.CommitWrite(); - + m_pbHashOfLastIO = kdb.HashOfFileOnDisk; m_pbHashOfFileOnDisk = kdb.HashOfFileOnDisk; Debug.Assert(m_pbHashOfFileOnDisk != null); @@ -636,6 +655,23 @@ namespace KeePassLib m_bModified = false; } + /// + /// Save the currently opened database. The file is written to the given stream which is expected to be the original location. + /// + /// This allows to save to cloud locations etc. + public void Save(Stream streamOfOriginalLocation, IStatusLogger slLogger) + { + Debug.Assert(ValidateUuidUniqueness()); + Stream s = streamOfOriginalLocation; + KdbxFile kdb = new KdbxFile(this); + kdb.Save(s, null, KdbxFormat.Default, slLogger); + + m_pbHashOfLastIO = kdb.HashOfFileOnDisk; + m_pbHashOfFileOnDisk = kdb.HashOfFileOnDisk; + Debug.Assert(m_pbHashOfFileOnDisk != null); + m_bModified = false; + } + /// /// Save the currently opened database to a different location. If /// is true, the specified diff --git a/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs b/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs index 03456962..37a2873f 100644 --- a/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs +++ b/src/KeePassLib2Android/Serialization/KdbxFile.Read.cs @@ -91,12 +91,18 @@ namespace KeePassLib.Serialization if((sDecrypted == null) || (sDecrypted == hashedStream)) throw new SecurityException(KLRes.CryptoStreamFailed); + if (m_slLogger != null) + m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo); + brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted); byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) throw new InvalidDataException(); + if (m_slLogger != null) + m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo); + for(int iStart = 0; iStart < 32; ++iStart) { if(pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart]) @@ -126,7 +132,8 @@ namespace KeePassLib.Serialization m_pbProtectedStreamKey); } else m_randomStream = null; // No random stream for plain-text files - + if (m_slLogger != null) + m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo); ReadXmlStreamed(readerStream, hashedStream); // ReadXmlDom(readerStream); @@ -312,7 +319,8 @@ namespace KeePassLib.Serialization if(m_pbMasterSeed.Length != 32) throw new FormatException(KLRes.MasterSeedLengthInvalid); ms.Write(m_pbMasterSeed, 0, 32); - + if (m_slLogger != null) + m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo); byte[] pKey32 = m_pwDatabase.MasterKey.GenerateKey32(m_pbTransformSeed, m_pwDatabase.KeyEncryptionRounds).ReadData(); if((pKey32 == null) || (pKey32.Length != 32)) diff --git a/src/Kp2aBusinessLogic/IKp2aApp.cs b/src/Kp2aBusinessLogic/IKp2aApp.cs index b766b110..1951a686 100644 --- a/src/Kp2aBusinessLogic/IKp2aApp.cs +++ b/src/Kp2aBusinessLogic/IKp2aApp.cs @@ -2,6 +2,7 @@ using System; using Android.Content; using Android.OS; using KeePassLib.Serialization; +using keepass2android.Io; namespace keepass2android { @@ -57,5 +58,7 @@ namespace keepass2android Handler UiThreadHandler { get; } IProgressDialog CreateProgressDialog(Context ctx); + IFileStorage GetFileStorage(IOConnectionInfo iocInfo); + } } \ No newline at end of file diff --git a/src/Kp2aBusinessLogic/IProgressDialog.cs b/src/Kp2aBusinessLogic/IProgressDialog.cs index ac74dad0..1035b195 100644 --- a/src/Kp2aBusinessLogic/IProgressDialog.cs +++ b/src/Kp2aBusinessLogic/IProgressDialog.cs @@ -3,7 +3,7 @@ namespace keepass2android public interface IProgressDialog { void SetTitle(string title); - void SetMessage(string getResourceString); + void SetMessage(string resourceString); void Dismiss(); void Show(); } diff --git a/src/Kp2aBusinessLogic/Io/BuiltInFileStorage.cs b/src/Kp2aBusinessLogic/Io/BuiltInFileStorage.cs new file mode 100644 index 00000000..54ebc266 --- /dev/null +++ b/src/Kp2aBusinessLogic/Io/BuiltInFileStorage.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using KeePassLib.Serialization; +using KeePassLib.Utility; + +namespace keepass2android.Io +{ + public class BuiltInFileStorage: IFileStorage + { + public void DeleteFile(IOConnectionInfo ioc) + { + IOConnection.DeleteFile(ioc); + } + + public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion) + { + if (!ioc.IsLocalFile()) + return false; + DateTime previousDate; + if (!DateTime.TryParse(previousFileVersion, out previousDate)) + return false; + return File.GetLastWriteTimeUtc(ioc.Path) > previousDate; + } + + + public string GetCurrentFileVersionFast(IOConnectionInfo ioc) + { + + if (ioc.IsLocalFile()) + { + return File.GetLastWriteTimeUtc(ioc.Path).ToString(CultureInfo.InvariantCulture); + } + else + { + return DateTime.MinValue.ToString(CultureInfo.InvariantCulture); + } + + + } + + public Stream OpenFileForRead(IOConnectionInfo ioc) + { + return IOConnection.OpenRead(ioc); + } + + public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction) + { + return new BuiltInFileTransaction(ioc, useFileTransaction); + } + + public class BuiltInFileTransaction : IWriteTransaction + { + private readonly FileTransactionEx _transaction; + + public BuiltInFileTransaction(IOConnectionInfo ioc, bool useFileTransaction) + { + _transaction = new FileTransactionEx(ioc, useFileTransaction); + } + + public void Dispose() + { + + } + + public Stream OpenFile() + { + return _transaction.OpenWrite(); + } + + public void CommitWrite() + { + _transaction.CommitWrite(); + } + } + + public bool CompleteIoId() + { + throw new NotImplementedException(); + } + + public bool? FileExists() + { + throw new NotImplementedException(); + } + + public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc) + { + return UrlUtil.StripExtension( + UrlUtil.GetFileName(ioc.Path)); + + } + } +} \ No newline at end of file diff --git a/src/Kp2aBusinessLogic/Io/IFileStorage.cs b/src/Kp2aBusinessLogic/Io/IFileStorage.cs new file mode 100644 index 00000000..3a203189 --- /dev/null +++ b/src/Kp2aBusinessLogic/Io/IFileStorage.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using KeePassLib.Keys; +using KeePassLib.Serialization; + +namespace keepass2android.Io +{ + /// + /// Called as a callback from CheckForFileChangeAsync. + /// + /// + /// + public delegate void OnCheckForFileChangeCompleted(IOConnectionInfo ioc, bool fileChanged); + + /// + /// Interface to encapsulate all access to disk or cloud. + /// + /// This interface might be implemented for different cloud storage providers in the future to extend the possibilities of the + /// "built-in" IOConnection class in the Keepass-Lib. + /// Note that it was decided to use the IOConnectionInfo also for cloud storage (unless it turns out that this isn't possible, but + /// with prefixes like dropbox:// it should be). The advantage is that the database for saving recent files etc. will then work without + /// much work to do. Furthermore, the IOConnectionInfo seems generic info to capture all required data, even though it might be nicer to + /// have an IIoStorageId interface in few cases.*/ + public interface IFileStorage + { + /// + /// Deletes the given file. + /// + void DeleteFile(IOConnectionInfo ioc); + + /// + /// Tests whether the file was changed. + /// + /// Note: This function may return false even if the file might have changed. The function + /// should focus on being fast and cheap instead of doing things like hashing or downloading a full file. + /// Returns true if a change was detected, false otherwise. + bool CheckForFileChangeFast(IOConnectionInfo ioc , string previousFileVersion); + + /// + /// Returns a string describing the "version" of the file specified by ioc. + /// + /// This string may have a deliberate value (except null) and should not be used by callers except for passing it to + /// CheckForFileChangeFast(). + /// A string which should not be null. + string GetCurrentFileVersionFast(IOConnectionInfo ioc); + + Stream OpenFileForRead(IOConnectionInfo ioc); + //Stream OpenFileForWrite( IOConnectionInfo ioc, bool useTransaction); + IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction); + + /// + /// brings up a dialog to query credentials or something like this. + /// + /// true if success, false if error or cancelled by user + bool CompleteIoId( /*in/out ioId*/); + + + /// + /// Checks whether the given file exists. + /// + /// true if it exists, false if not. Null if the check couldn't be performed (e.g. because no credentials available or no connection established.) + bool? FileExists( /*ioId*/); + + string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc); + } + + public interface IWriteTransaction: IDisposable + { + Stream OpenFile(); + void CommitWrite(); + } +} \ No newline at end of file diff --git a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj index 1b8192b6..75a2642a 100644 --- a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj +++ b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj @@ -41,6 +41,8 @@ + + @@ -68,7 +70,7 @@ - + diff --git a/src/Kp2aBusinessLogic/PreferenceKey.cs b/src/Kp2aBusinessLogic/PreferenceKey.cs index 6e62362a..83a99b4e 100644 --- a/src/Kp2aBusinessLogic/PreferenceKey.cs +++ b/src/Kp2aBusinessLogic/PreferenceKey.cs @@ -6,6 +6,7 @@ namespace keepass2android public enum PreferenceKey { remember_keyfile, - UseFileTransactions + UseFileTransactions, + CheckForFileChangesOnSave } } \ No newline at end of file diff --git a/src/Kp2aBusinessLogic/UpdateStatus.cs b/src/Kp2aBusinessLogic/ProgressDialogStatusLogger.cs similarity index 61% rename from src/Kp2aBusinessLogic/UpdateStatus.cs rename to src/Kp2aBusinessLogic/ProgressDialogStatusLogger.cs index ab711f93..9a650e05 100644 --- a/src/Kp2aBusinessLogic/UpdateStatus.cs +++ b/src/Kp2aBusinessLogic/ProgressDialogStatusLogger.cs @@ -25,34 +25,54 @@ namespace keepass2android /// /// StatusLogger implementation which shows the progress in a progress dialog /// - public class UpdateStatus: IStatusLogger { + public class ProgressDialogStatusLogger: IStatusLogger { private readonly IProgressDialog _progressDialog; readonly IKp2aApp _app; private readonly Handler _handler; - - public UpdateStatus() { + private string _message = ""; + + public ProgressDialogStatusLogger() { } - public UpdateStatus(IKp2aApp app, Handler handler, IProgressDialog pd) { + public ProgressDialogStatusLogger(IKp2aApp app, Handler handler, IProgressDialog pd) { _app = app; _progressDialog = pd; _handler = handler; } public void UpdateMessage(UiStringKey stringKey) { - if ( _app != null && _progressDialog != null && _handler != null ) { - _handler.Post( () => {_progressDialog.SetMessage(_app.GetResourceString(stringKey));}); - } + if (_app != null) + UpdateMessage(_app.GetResourceString(stringKey)); } public void UpdateMessage (String message) { + _message = message; if ( _app!= null && _progressDialog != null && _handler != null ) { _handler.Post(() => {_progressDialog.SetMessage(message); } ); } } + public void UpdateSubMessage(String submessage) + { + if (_app != null && _progressDialog != null && _handler != null) + { + _handler.Post(() => + { + if (String.IsNullOrEmpty(submessage)) + { + _progressDialog.SetMessage(_message + " (" + submessage + ")"); + } + else + { + _progressDialog.SetMessage(_message); + } + } + ); + } + } + #region IStatusLogger implementation public void StartLogging (string strOperation, bool bWriteOperationToLog) @@ -72,10 +92,32 @@ namespace keepass2android public bool SetText (string strNewText, LogStatusType lsType) { - UpdateMessage(strNewText); + if (strNewText.StartsWith("KP2AKEY_")) + { + UiStringKey key; + if (Enum.TryParse(strNewText.Substring("KP2AKEY_".Length), true, out key)) + { + UpdateMessage(_app.GetResourceString(key), lsType); + return true; + } + } + UpdateMessage(strNewText, lsType); + return true; } + private void UpdateMessage(string message, LogStatusType lsType) + { + if (lsType == LogStatusType.AdditionalInfo) + { + UpdateSubMessage(message); + } + else + { + UpdateMessage(message); + } + } + public bool ContinueWork () { return true; diff --git a/src/Kp2aBusinessLogic/ProgressTask.cs b/src/Kp2aBusinessLogic/ProgressTask.cs index 71e5369f..b26fc667 100644 --- a/src/Kp2aBusinessLogic/ProgressTask.cs +++ b/src/Kp2aBusinessLogic/ProgressTask.cs @@ -32,7 +32,7 @@ namespace keepass2android private readonly IKp2aApp _app; private Thread _thread; - public ProgressTask(IKp2aApp app, Context ctx, RunnableOnFinish task, UiStringKey messageKey) { + public ProgressTask(IKp2aApp app, Context ctx, RunnableOnFinish task) { _task = task; _handler = app.UiThreadHandler; _app = app; @@ -40,11 +40,12 @@ namespace keepass2android // Show process dialog _progressDialog = app.CreateProgressDialog(ctx); _progressDialog.SetTitle(_app.GetResourceString(UiStringKey.progress_title)); - _progressDialog.SetMessage(_app.GetResourceString(messageKey)); + _progressDialog.SetMessage("Initializing..."); // Set code to run when this is finished - _task.SetStatus(new UpdateStatus(_app, _handler, _progressDialog)); _task.OnFinishToRun = new AfterTask(task.OnFinishToRun, _handler, _progressDialog); + _task.SetStatusLogger(new ProgressDialogStatusLogger(_app, _handler, _progressDialog)); + } diff --git a/src/Kp2aBusinessLogic/UiStringKey.cs b/src/Kp2aBusinessLogic/UiStringKey.cs index fd6982c0..534bc637 100644 --- a/src/Kp2aBusinessLogic/UiStringKey.cs +++ b/src/Kp2aBusinessLogic/UiStringKey.cs @@ -17,6 +17,19 @@ namespace keepass2android keyfile_does_not_exist, RecycleBin, progress_create, - loading_database + loading_database, + AddingEntry, + AddingGroup, + DeletingEntry, + DeletingGroup, + SettingPassword, + UndoingChanges, + TransformingKey, + DecodingDatabase, + ParsingDatabase, + CheckingTargetFileForChanges, + TitleSyncQuestion, + MessageSyncQuestions, + SynchronizingDatabase } } \ No newline at end of file diff --git a/src/Kp2aBusinessLogic/database/Database.cs b/src/Kp2aBusinessLogic/database/Database.cs index 99ea6ad9..a5cdf677 100644 --- a/src/Kp2aBusinessLogic/database/Database.cs +++ b/src/Kp2aBusinessLogic/database/Database.cs @@ -17,10 +17,12 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file using System; using System.Collections.Generic; +using System.IO; using Android.Content; using KeePassLib; using KeePassLib.Keys; using KeePassLib.Serialization; +using keepass2android.Io; namespace keepass2android { @@ -34,7 +36,7 @@ namespace keepass2android public PwGroup Root; public PwDatabase KpDatabase; public IOConnectionInfo Ioc { get { return KpDatabase.IOConnectionInfo; } } - public DateTime LastChangeDate; + public string LastFileVersion; public SearchDbHelper SearchHelper; public IDrawableFactory DrawableFactory; @@ -83,15 +85,16 @@ namespace keepass2android public bool DidOpenFileChange() { - if ((Loaded == false) || (Ioc.IsLocalFile() == false)) + if (Loaded == false) { return false; } - return System.IO.File.GetLastWriteTimeUtc(Ioc.Path) > LastChangeDate; + return _app.GetFileStorage(Ioc).CheckForFileChangeFast(Ioc, LastFileVersion); + } - public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, String password, String keyfile, UpdateStatus status) + public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, String password, String keyfile, ProgressDialogStatusLogger status) { PwDatabase pwDatabase = new PwDatabase(); @@ -103,15 +106,17 @@ namespace keepass2android try { compositeKey.AddUserKey(new KcpKeyFile(keyfile)); - } catch (Exception) + } catch (Exception e) { + Kp2aLog.Log(e.ToString()); throw new KeyFileException(); } } try { - pwDatabase.Open(iocInfo, compositeKey, status); + IFileStorage fileStorage = _app.GetFileStorage(iocInfo); + pwDatabase.Open(fileStorage.OpenFileForRead(iocInfo), fileStorage.GetFilenameWithoutPathAndExt(iocInfo), iocInfo, compositeKey, status); } catch (Exception) { @@ -125,14 +130,9 @@ namespace keepass2android else throw; } + status.UpdateSubMessage(""); - if (iocInfo.IsLocalFile()) - { - LastChangeDate = System.IO.File.GetLastWriteTimeUtc(iocInfo.Path); - } else - { - LastChangeDate = DateTime.MinValue; - } + LastFileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo); Root = pwDatabase.RootGroup; PopulateGlobals(Root); @@ -184,9 +184,14 @@ namespace keepass2android public void SaveData(Context ctx) { KpDatabase.UseFileTransactions = _app.GetBooleanPreference(PreferenceKey.UseFileTransactions); - KpDatabase.Save(null); - + using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, KpDatabase.UseFileTransactions)) + { + KpDatabase.Save(trans.OpenFile(), null); + trans.CommitWrite(); + } + } + private void PopulateGlobals (PwGroup currentGroup) { diff --git a/src/Kp2aBusinessLogic/database/edit/AddEntry.cs b/src/Kp2aBusinessLogic/database/edit/AddEntry.cs index 1de447c1..4db60b9a 100644 --- a/src/Kp2aBusinessLogic/database/edit/AddEntry.cs +++ b/src/Kp2aBusinessLogic/database/edit/AddEntry.cs @@ -21,31 +21,38 @@ using KeePassLib; namespace keepass2android { public class AddEntry : RunnableOnFinish { - protected Database Db; + protected Database Db + { + get { return _app.GetDb(); } + } + + private readonly IKp2aApp _app; private readonly PwEntry _entry; private readonly PwGroup _parentGroup; private readonly Context _ctx; - public static AddEntry GetInstance(Context ctx, Database db, PwEntry entry, PwGroup parentGroup, OnFinish finish) { + public static AddEntry GetInstance(Context ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish) { - return new AddEntry(ctx, db, entry, parentGroup, finish); + return new AddEntry(ctx, app, entry, parentGroup, finish); } - protected AddEntry(Context ctx, Database db, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(finish) { + protected AddEntry(Context ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(finish) { _ctx = ctx; _parentGroup = parentGroup; - Db = db; + _app = app; _entry = entry; - OnFinishToRun = new AfterAdd(db, entry, OnFinishToRun); + _onFinishToRun = new AfterAdd(app.GetDb(), entry, OnFinishToRun); } - public override void Run() { + public override void Run() { + StatusLogger.UpdateMessage(UiStringKey.AddingEntry); _parentGroup.AddEntry(_entry, true); // Commit to disk - SaveDb save = new SaveDb(_ctx, Db, OnFinishToRun); + SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun); + save.SetStatusLogger(StatusLogger); save.Run(); } @@ -72,7 +79,9 @@ namespace keepass2android // Add entry to global _db.Entries[_entry.Uuid] = _entry; - } else { + } else + { + StatusLogger.UpdateMessage(UiStringKey.UndoingChanges); //TODO test fail _entry.ParentGroup.Entries.Remove(_entry); } diff --git a/src/Kp2aBusinessLogic/database/edit/AddGroup.cs b/src/Kp2aBusinessLogic/database/edit/AddGroup.cs index b972dd97..92b72b19 100644 --- a/src/Kp2aBusinessLogic/database/edit/AddGroup.cs +++ b/src/Kp2aBusinessLogic/database/edit/AddGroup.cs @@ -23,7 +23,11 @@ namespace keepass2android { public class AddGroup : RunnableOnFinish { - internal Database Db; + internal Database Db + { + get { return _app.GetDb(); } + } + private IKp2aApp _app; private readonly String _name; private readonly int _iconId; internal PwGroup Group; @@ -32,31 +36,32 @@ namespace keepass2android readonly Context _ctx; - public static AddGroup GetInstance(Context ctx, Database db, String name, int iconid, PwGroup parent, OnFinish finish, bool dontSave) { - return new AddGroup(ctx, db, name, iconid, parent, finish, dontSave); + public static AddGroup GetInstance(Context ctx, IKp2aApp app, String name, int iconid, PwGroup parent, OnFinish finish, bool dontSave) { + return new AddGroup(ctx, app, name, iconid, parent, finish, dontSave); } - private AddGroup(Context ctx, Database db, String name, int iconid, PwGroup parent, OnFinish finish, bool dontSave): base(finish) { + private AddGroup(Context ctx, IKp2aApp app, String name, int iconid, PwGroup parent, OnFinish finish, bool dontSave): base(finish) { _ctx = ctx; - Db = db; _name = name; _iconId = iconid; Parent = parent; DontSave = dontSave; - - OnFinishToRun = new AfterAdd(this, OnFinishToRun); + _app = app; + + _onFinishToRun = new AfterAdd(this, OnFinishToRun); } public override void Run() { - + StatusLogger.UpdateMessage(UiStringKey.AddingGroup); // Generate new group Group = new PwGroup(true, true, _name, (PwIcon)_iconId); Parent.AddGroup(Group, true); // Commit to disk - SaveDb save = new SaveDb(_ctx, Db, OnFinishToRun, DontSave); + SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, DontSave); + save.SetStatusLogger(StatusLogger); save.Run(); } @@ -77,6 +82,7 @@ namespace keepass2android // Add group to global list _addGroup.Db.Groups[_addGroup.Group.Uuid] = _addGroup.Group; } else { + StatusLogger.UpdateMessage(UiStringKey.UndoingChanges); _addGroup.Parent.Groups.Remove(_addGroup.Group); } diff --git a/src/Kp2aBusinessLogic/database/edit/CreateDB.cs b/src/Kp2aBusinessLogic/database/edit/CreateDB.cs index 51efa761..ce883733 100644 --- a/src/Kp2aBusinessLogic/database/edit/CreateDB.cs +++ b/src/Kp2aBusinessLogic/database/edit/CreateDB.cs @@ -40,6 +40,7 @@ namespace keepass2android public override void Run() { + StatusLogger.UpdateMessage(UiStringKey.progress_create); Database db = _app.CreateNewDatabase(); db.KpDatabase = new KeePassLib.PwDatabase(); @@ -58,14 +59,15 @@ namespace keepass2android db.SearchHelper = new SearchDbHelper(_app); // Add a couple default groups - AddGroup internet = AddGroup.GetInstance(_ctx, db, "Internet", 1, db.KpDatabase.RootGroup, null, true); + AddGroup internet = AddGroup.GetInstance(_ctx, _app, "Internet", 1, db.KpDatabase.RootGroup, null, true); internet.Run(); - AddGroup email = AddGroup.GetInstance(_ctx, db, "eMail", 19, db.KpDatabase.RootGroup, null, true); + AddGroup email = AddGroup.GetInstance(_ctx, _app, "eMail", 19, db.KpDatabase.RootGroup, null, true); email.Run(); // Commit changes - SaveDb save = new SaveDb(_ctx, db, OnFinishToRun, _dontSave); - OnFinishToRun = null; + SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave); + save.SetStatusLogger(StatusLogger); + _onFinishToRun = null; save.Run(); diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs b/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs index 97a468e4..9ba8c469 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs @@ -48,8 +48,9 @@ namespace keepass2android } } - public override void Run() { - + public override void Run() + { + StatusLogger.UpdateMessage(UiStringKey.DeletingEntry); PwDatabase pd = Db.KpDatabase; PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); @@ -68,7 +69,7 @@ namespace keepass2android PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow); pd.DeletedObjects.Add(pdo); - OnFinishToRun = new ActionOnFinish((success, message) => + _onFinishToRun = new ActionOnFinish((success, message) => { if (success) { @@ -89,7 +90,7 @@ namespace keepass2android pgRecycleBin.AddEntry(pe, true, true); pe.Touch(false); - OnFinishToRun = new ActionOnFinish( (success, message) => + _onFinishToRun = new ActionOnFinish( (success, message) => { if ( success ) { // Mark previous parent dirty @@ -106,7 +107,8 @@ namespace keepass2android } // Commit database - SaveDb save = new SaveDb(Ctx, Db, OnFinishToRun, false); + SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false); + save.SetStatusLogger(StatusLogger); save.Run(); diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs index 8c7bd71a..1586b0df 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs @@ -70,6 +70,7 @@ namespace keepass2android public override void Run() { + StatusLogger.UpdateMessage(UiStringKey.DeletingGroup); //from KP Desktop PwGroup pg = _group; PwGroup pgParent = pg.ParentGroup; @@ -86,7 +87,7 @@ namespace keepass2android PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.Now); pd.DeletedObjects.Add(pdo); - OnFinishToRun = new AfterDeletePermanently(OnFinishToRun, App, _group); + _onFinishToRun = new AfterDeletePermanently(OnFinishToRun, App, _group); } else // Recycle { @@ -95,7 +96,7 @@ namespace keepass2android pgRecycleBin.AddGroup(pg, true, true); pg.Touch(false); - OnFinishToRun = new ActionOnFinish((success, message) => + _onFinishToRun = new ActionOnFinish((success, message) => { if ( success ) { // Mark new parent (Recycle bin) dirty @@ -113,7 +114,8 @@ namespace keepass2android } // Save - SaveDb save = new SaveDb(Ctx, Db, OnFinishToRun, DontSave); + SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, DontSave); + save.SetStatusLogger(StatusLogger); save.Run(); } diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs index 3b07a551..cab35523 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs @@ -109,12 +109,12 @@ namespace keepass2android (dlgSender, dlgEvt) => { DeletePermanently = true; - ProgressTask pt = new ProgressTask(App, Ctx, this, UiStringKey.saving_database); + ProgressTask pt = new ProgressTask(App, Ctx, this); pt.Run(); }, (dlgSender, dlgEvt) => { DeletePermanently = false; - ProgressTask pt = new ProgressTask(App, Ctx, this, UiStringKey.saving_database); + ProgressTask pt = new ProgressTask(App, Ctx, this); pt.Run(); }, (dlgSender, dlgEvt) => {}, @@ -124,7 +124,7 @@ namespace keepass2android } else { - ProgressTask pt = new ProgressTask(App, Ctx, this, UiStringKey.saving_database); + ProgressTask pt = new ProgressTask(App, Ctx, this); pt.Run(); } } diff --git a/src/Kp2aBusinessLogic/database/edit/LoadDB.cs b/src/Kp2aBusinessLogic/database/edit/LoadDB.cs index 378a4614..239c04a9 100644 --- a/src/Kp2aBusinessLogic/database/edit/LoadDB.cs +++ b/src/Kp2aBusinessLogic/database/edit/LoadDB.cs @@ -43,7 +43,8 @@ namespace keepass2android { try { - _app.GetDb().LoadData (_app, _ioc, _pass, _key, Status); + StatusLogger.UpdateMessage(UiStringKey.loading_database); + _app.GetDb().LoadData (_app, _ioc, _pass, _key, StatusLogger); SaveFileData (_ioc, _key); } catch (KeyFileException) { diff --git a/src/Kp2aBusinessLogic/database/edit/OnFinish.cs b/src/Kp2aBusinessLogic/database/edit/OnFinish.cs index 8fd26f4f..a23926a0 100644 --- a/src/Kp2aBusinessLogic/database/edit/OnFinish.cs +++ b/src/Kp2aBusinessLogic/database/edit/OnFinish.cs @@ -29,6 +29,13 @@ namespace keepass2android protected OnFinish BaseOnFinish; protected Handler Handler; + private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired + + public ProgressDialogStatusLogger StatusLogger + { + get { return _statusLogger; } + set { _statusLogger = value; } + } protected OnFinish() { } @@ -47,7 +54,7 @@ namespace keepass2android BaseOnFinish = finish; Handler = null; } - + public void SetResult(bool success, String message) { Success = success; Message = message; diff --git a/src/Kp2aBusinessLogic/database/edit/RunnableOnFinish.cs b/src/Kp2aBusinessLogic/database/edit/RunnableOnFinish.cs index f899519c..6a5e4da5 100644 --- a/src/Kp2aBusinessLogic/database/edit/RunnableOnFinish.cs +++ b/src/Kp2aBusinessLogic/database/edit/RunnableOnFinish.cs @@ -21,13 +21,19 @@ namespace keepass2android public abstract class RunnableOnFinish { - public OnFinish OnFinishToRun; - public UpdateStatus Status; + protected OnFinish _onFinishToRun; + public ProgressDialogStatusLogger StatusLogger = new ProgressDialogStatusLogger(); //default: empty but not null protected RunnableOnFinish(OnFinish finish) { - OnFinishToRun = finish; + _onFinishToRun = finish; } - + + public OnFinish OnFinishToRun + { + get { return _onFinishToRun; } + set { _onFinishToRun = value; } + } + protected void Finish(bool result, String message) { if ( OnFinishToRun != null ) { OnFinishToRun.SetResult(result, message); @@ -42,8 +48,12 @@ namespace keepass2android } } - public void SetStatus(UpdateStatus status) { - Status = status; + public void SetStatusLogger(ProgressDialogStatusLogger status) { + if (OnFinishToRun != null) + { + OnFinishToRun.StatusLogger = status; + } + StatusLogger = status; } abstract public void Run(); diff --git a/src/Kp2aBusinessLogic/database/edit/SaveDB.cs b/src/Kp2aBusinessLogic/database/edit/SaveDB.cs index c98fe438..a3b76089 100644 --- a/src/Kp2aBusinessLogic/database/edit/SaveDB.cs +++ b/src/Kp2aBusinessLogic/database/edit/SaveDB.cs @@ -14,26 +14,40 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file You should have received a copy of the GNU General Public License along with Keepass2Android. If not, see . */ + using System; +using System.IO; +using System.Security.Cryptography; using Android.Content; +using Android.OS; +using Java.Lang; +using KeePassLib; +using KeePassLib.Serialization; +using KeePassLib.Utility; +using keepass2android.Io; +using Debug = System.Diagnostics.Debug; +using Exception = System.Exception; namespace keepass2android { public class SaveDb : RunnableOnFinish { - private readonly Database _db; + private readonly IKp2aApp _app; private readonly bool _dontSave; private readonly Context _ctx; - - public SaveDb(Context ctx, Database db, OnFinish finish, bool dontSave): base(finish) { + private Thread _workerThread; + + public SaveDb(Context ctx, IKp2aApp app, OnFinish finish, bool dontSave): base(finish) { _ctx = ctx; - _db = db; + _app = app; _dontSave = dontSave; } - public SaveDb(Context ctx, Database db, OnFinish finish):base(finish) { + public SaveDb(Context ctx, IKp2aApp app, OnFinish finish) + : base(finish) + { _ctx = ctx; - _db = db; + _app = app; _dontSave = false; } @@ -42,10 +56,67 @@ namespace keepass2android { if (! _dontSave) { - try { - _db.SaveData (_ctx); - if (_db.Ioc.IsLocalFile()) - _db.LastChangeDate = System.IO.File.GetLastWriteTimeUtc(_db.Ioc.Path); + try + { + StatusLogger.UpdateMessage(UiStringKey.saving_database); + IOConnectionInfo ioc = _app.GetDb().Ioc; + IFileStorage fileStorage = _app.GetFileStorage(ioc); + + if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave)) + || (_app.GetDb().KpDatabase.HashOfFileOnDisk == null)) //first time saving + { + PerformSaveWithoutCheck(fileStorage, ioc); + Finish(true); + return; + } + + + + if (fileStorage.CheckForFileChangeFast(ioc, _app.GetDb().LastFileVersion) //first try to use the fast change detection + || (FileHashChanged(ioc, _app.GetDb().KpDatabase.HashOfFileOnDisk))) //if that fails, hash the file and compare: + { + //ask user... + _app.AskYesNoCancel(UiStringKey.TitleSyncQuestion, UiStringKey.MessageSyncQuestions, + //yes = sync + (sender, args) => + { + Action runHandler = () => + { + //note: when synced, the file might be downloaded once again from the server. Caching the data + //in the hashing function would solve this but increases complexity. I currently assume the files are + //small. + StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase)); + MergeIn(fileStorage, ioc); + PerformSaveWithoutCheck(fileStorage, ioc); + Finish(true); + }; + RunInWorkerThread(runHandler); + }, + //no = overwrite + (sender, args) => + { + RunInWorkerThread( () => + { + PerformSaveWithoutCheck(fileStorage, ioc); + Finish(true); + }); + }, + //cancel + (sender, args) => + { + RunInWorkerThread(() => Finish(false)); + }, + _ctx + ); + + + } + else + { + PerformSaveWithoutCheck(fileStorage, ioc); + Finish(true); + } + } catch (Exception e) { /* TODO KPDesktop: * catch(Exception exSave) @@ -54,13 +125,85 @@ namespace keepass2android bSuccess = false; } */ - Finish (false, e.Message); + Finish (false, e.ToString()); return; } } - Finish(true); + } + + private void RunInWorkerThread(Action runHandler) + { + try + { + _workerThread = new Thread(runHandler); + _workerThread.Run(); + } + catch (Exception e) + { + Kp2aLog.Log("Error in worker thread of SaveDb: "+e); + Finish(false, e.Message); + } + + } + + public void JoinWorkerThread() + { + if (_workerThread != null) + _workerThread.Join(); + } + + private void MergeIn(IFileStorage fileStorage, IOConnectionInfo ioc) + { + PwDatabase pwImp = new PwDatabase(); + PwDatabase pwDatabase = _app.GetDb().KpDatabase; + pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey); + pwImp.MemoryProtection = pwDatabase.MemoryProtection.CloneDeep(); + pwImp.MasterKey = pwDatabase.MasterKey; + KdbxFile kdbx = new KdbxFile(pwImp); + kdbx.Load(fileStorage.OpenFileForRead(ioc), KdbxFormat.Default, null); + + pwDatabase.MergeIn(pwImp, PwMergeMethod.Synchronize, null); + + } + + private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc) + { + _app.GetDb().SaveData(_ctx); + _app.GetDb().LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc); + } + + public byte[] HashFile(IOConnectionInfo iocFile) + { + if (iocFile == null) { Debug.Assert(false); return null; } // Assert only + + Stream sIn; + try + { + sIn = _app.GetFileStorage(iocFile).OpenFileForRead(iocFile); + if (sIn == null) throw new FileNotFoundException(); + } + catch (Exception) { return null; } + + byte[] pbHash; + try + { + SHA256Managed sha256 = new SHA256Managed(); + pbHash = sha256.ComputeHash(sIn); + } + catch (Exception) { Debug.Assert(false); sIn.Close(); return null; } + + sIn.Close(); + return pbHash; + } + + private bool FileHashChanged(IOConnectionInfo ioc, byte[] hashOfFileOnDisk) + { + StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.CheckingTargetFileForChanges)); + return !MemUtil.ArraysEqual(HashFile(ioc), hashOfFileOnDisk); + } + } diff --git a/src/Kp2aBusinessLogic/database/edit/SetPassword.cs b/src/Kp2aBusinessLogic/database/edit/SetPassword.cs index ca238321..521414c3 100644 --- a/src/Kp2aBusinessLogic/database/edit/SetPassword.cs +++ b/src/Kp2aBusinessLogic/database/edit/SetPassword.cs @@ -25,21 +25,23 @@ namespace keepass2android private readonly String _password; private readonly String _keyfile; - private readonly Database _db; + private readonly IKp2aApp _app; private readonly bool _dontSave; private readonly Context _ctx; - public SetPassword(Context ctx, Database db, String password, String keyfile, OnFinish finish): base(finish) { + public SetPassword(Context ctx, IKp2aApp app, String password, String keyfile, OnFinish finish): base(finish) { _ctx = ctx; - _db = db; + _app = app; _password = password; _keyfile = keyfile; _dontSave = false; } - - public SetPassword(Context ctx, Database db, String password, String keyfile, OnFinish finish, bool dontSave): base(finish) { + + public SetPassword(Context ctx, IKp2aApp app, String password, String keyfile, OnFinish finish, bool dontSave) + : base(finish) + { _ctx = ctx; - _db = db; + _app = app; _password = password; _keyfile = keyfile; _dontSave = dontSave; @@ -48,7 +50,8 @@ namespace keepass2android public override void Run () { - PwDatabase pm = _db.KpDatabase; + StatusLogger.UpdateMessage(UiStringKey.SettingPassword); + PwDatabase pm = _app.GetDb().KpDatabase; CompositeKey newKey = new CompositeKey (); if (String.IsNullOrEmpty (_password) == false) { newKey.AddUserKey (new KcpPassword (_password)); @@ -69,8 +72,9 @@ namespace keepass2android pm.MasterKey = newKey; // Save Database - OnFinishToRun = new AfterSave(previousKey, previousMasterKeyChanged, pm, OnFinishToRun); - SaveDb save = new SaveDb(_ctx, _db, OnFinishToRun, _dontSave); + _onFinishToRun = new AfterSave(previousKey, previousMasterKeyChanged, pm, OnFinishToRun); + SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave); + save.SetStatusLogger(StatusLogger); save.Run(); } diff --git a/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs b/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs index 40f7bf4a..b4850566 100644 --- a/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs +++ b/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs @@ -22,32 +22,33 @@ namespace keepass2android { public class UpdateEntry : RunnableOnFinish { - private readonly Database _db; + private readonly IKp2aApp _app; private readonly Context _ctx; - public UpdateEntry(Context ctx, Database db, PwEntry oldE, PwEntry newE, OnFinish finish):base(finish) { + public UpdateEntry(Context ctx, IKp2aApp app, PwEntry oldE, PwEntry newE, OnFinish finish):base(finish) { _ctx = ctx; - _db = db; + _app = app; - OnFinishToRun = new AfterUpdate(oldE, newE, db, finish); + _onFinishToRun = new AfterUpdate(oldE, newE, app, finish); } public override void Run() { // Commit to disk - SaveDb save = new SaveDb(_ctx, _db, OnFinishToRun); + SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun); + save.SetStatusLogger(StatusLogger); save.Run(); } private class AfterUpdate : OnFinish { private readonly PwEntry _backup; private readonly PwEntry _updatedEntry; - private readonly Database _db; + private readonly IKp2aApp _app; - public AfterUpdate(PwEntry backup, PwEntry updatedEntry, Database db, OnFinish finish):base(finish) { + public AfterUpdate(PwEntry backup, PwEntry updatedEntry, IKp2aApp app, OnFinish finish):base(finish) { _backup = backup; _updatedEntry = updatedEntry; - _db = db; + _app = app; } public override void Run() { @@ -65,11 +66,12 @@ namespace keepass2android if ( parent != null ) { // Mark parent group dirty - _db.Dirty.Add(parent); + _app.GetDb().Dirty.Add(parent); } } } else { + StatusLogger.UpdateMessage(UiStringKey.UndoingChanges); // If we fail to save, back out changes to global structure //TODO test fail _updatedEntry.AssignProperties(_backup, false, true, false); diff --git a/src/Kp2aUnitTests/MainActivity.cs b/src/Kp2aUnitTests/MainActivity.cs index e67cbb0b..d1370c27 100644 --- a/src/Kp2aUnitTests/MainActivity.cs +++ b/src/Kp2aUnitTests/MainActivity.cs @@ -20,7 +20,8 @@ namespace Kp2aUnitTests // Run all tests from this assembly runner.AddTests(Assembly.GetExecutingAssembly()); //runner.AddTests(new List { typeof(TestSaveDb)}); - //runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadWithPasswordOnly"));}} + //runner.AddTests(typeof(TestSaveDb).GetMethod("TestLoadEditSaveWithSyncConflict")); + //runner.AddTests(typeof(TestSaveDb).GetMethod("TestLoadEditSave")); return runner; } } diff --git a/src/Kp2aUnitTests/ProgressDialogStub.cs b/src/Kp2aUnitTests/ProgressDialogStub.cs index ef68a6c5..e31fee1d 100644 --- a/src/Kp2aUnitTests/ProgressDialogStub.cs +++ b/src/Kp2aUnitTests/ProgressDialogStub.cs @@ -12,9 +12,9 @@ namespace Kp2aUnitTests } - public void SetMessage(string getResourceString) + public void SetMessage(string resourceString) { - + Kp2aLog.Log("Progress message: " + resourceString); } public void Dismiss() diff --git a/src/Kp2aUnitTests/TestBase.cs b/src/Kp2aUnitTests/TestBase.cs index 8e7f9340..328fa767 100644 --- a/src/Kp2aUnitTests/TestBase.cs +++ b/src/Kp2aUnitTests/TestBase.cs @@ -74,7 +74,7 @@ namespace Kp2aUnitTests }) ); - ProgressTask pt = new ProgressTask(app, Application.Context, task, UiStringKey.loading_database); + ProgressTask pt = new ProgressTask(app, Application.Context, task); pt.Run(); pt.JoinWorkerThread(); Assert.IsTrue(loadSuccesful); @@ -83,16 +83,27 @@ namespace Kp2aUnitTests protected void SaveDatabase(IKp2aApp app) { - bool saveSuccesful = false; - SaveDb save = new SaveDb(Application.Context, app.GetDb(), new ActionOnFinish((success, message) => - { - saveSuccesful = success; - }), false); - save.Run(); + bool saveSuccesful = TrySaveDatabase(app); Assert.IsTrue(saveSuccesful); } + public static bool TrySaveDatabase(IKp2aApp app) + { + bool saveSuccesful = false; + SaveDb save = new SaveDb(Application.Context, app, new ActionOnFinish((success, message) => + { + saveSuccesful = success; + if (!success) + { + Kp2aLog.Log("Error during TestBase.SaveDatabase: " + message); + } + }), false); + save.Run(); + save.JoinWorkerThread(); + return saveSuccesful; + } + protected IKp2aApp SetupAppWithDefaultDatabase() { IKp2aApp app = new TestKp2aApp(); diff --git a/src/Kp2aUnitTests/TestKp2aApp.cs b/src/Kp2aUnitTests/TestKp2aApp.cs index 88fab8a3..365a9355 100644 --- a/src/Kp2aUnitTests/TestKp2aApp.cs +++ b/src/Kp2aUnitTests/TestKp2aApp.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using Android.Content; using Android.OS; using KeePassLib.Serialization; using keepass2android; +using keepass2android.Io; namespace Kp2aUnitTests { @@ -11,7 +13,14 @@ namespace Kp2aUnitTests /// internal class TestKp2aApp : IKp2aApp { + internal enum YesNoCancelResult + { + Yes, No, Cancel + } + private Database _db; + private YesNoCancelResult _yesNoCancelResult = YesNoCancelResult.Yes; + private Dictionary _preferences = new Dictionary(); public void SetShutdown() { @@ -43,13 +52,32 @@ namespace Kp2aUnitTests public bool GetBooleanPreference(PreferenceKey key) { + if (_preferences.ContainsKey(key)) + return _preferences[key]; return true; } + public UiStringKey? LastYesNoCancelQuestionTitle { get; set; } + public void AskYesNoCancel(UiStringKey titleKey, UiStringKey messageKey, EventHandler yesHandler, EventHandler noHandler, EventHandler cancelHandler, Context ctx) { - yesHandler(null, null); + LastYesNoCancelQuestionTitle = titleKey; + switch (_yesNoCancelResult) + { + case YesNoCancelResult.Yes: + yesHandler(null, null); + break; + case YesNoCancelResult.No: + noHandler(null, null); + break; + case YesNoCancelResult.Cancel: + cancelHandler(null, null); + break; + default: + throw new Exception("unexpected case!"); + } + } public Handler UiThreadHandler { @@ -59,5 +87,20 @@ namespace Kp2aUnitTests { return new ProgressDialogStub(); } + + public IFileStorage GetFileStorage(IOConnectionInfo iocInfo) + { + return new BuiltInFileStorage(); + } + + public void SetYesNoCancelResult(YesNoCancelResult yesNoCancelResult) + { + _yesNoCancelResult = yesNoCancelResult; + } + + public void SetPreference(PreferenceKey key, bool value) + { + _preferences[key] = value; + } } } \ No newline at end of file diff --git a/src/Kp2aUnitTests/TestLoadDb.cs b/src/Kp2aUnitTests/TestLoadDb.cs index 96f4d2c3..d7e2402b 100644 --- a/src/Kp2aUnitTests/TestLoadDb.cs +++ b/src/Kp2aUnitTests/TestLoadDb.cs @@ -26,7 +26,7 @@ namespace Kp2aUnitTests loadSuccesful = success; }) ); - ProgressTask pt = new ProgressTask(app, Application.Context, task, UiStringKey.loading_database); + ProgressTask pt = new ProgressTask(app, Application.Context, task); Android.Util.Log.Debug("KP2ATest", "Running ProgressTask"); pt.Run(); pt.JoinWorkerThread(); diff --git a/src/Kp2aUnitTests/TestSaveDb.cs b/src/Kp2aUnitTests/TestSaveDb.cs index a07eb665..58c704d9 100644 --- a/src/Kp2aUnitTests/TestSaveDb.cs +++ b/src/Kp2aUnitTests/TestSaveDb.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Security.Cryptography; using System.Text; using Android.App; using Android.OS; using KeePassLib; +using KeePassLib.Keys; using KeePassLib.Serialization; using KeePassLib.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -23,6 +25,7 @@ namespace Kp2aUnitTests { //create the default database: IKp2aApp app = SetupAppWithDefaultDatabase(); + IOConnection.DeleteFile(new IOConnectionInfo { Path = DefaultFilename }); //save it and reload it so we have a base version SaveDatabase(app); app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); @@ -56,9 +59,14 @@ namespace Kp2aUnitTests //save the database from app 1: SaveDatabase(app); + ((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.Yes); + //save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync: SaveDatabase(app2); + //make sure the right question was asked + Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle); + //add group 2 to app 1: app.GetDb().KpDatabase.RootGroup.AddGroup(group2, true); @@ -68,7 +76,162 @@ namespace Kp2aUnitTests //ensure the sync was successful: AssertDatabasesAreEqual(app.GetDb().KpDatabase, resultApp.GetDb().KpDatabase); - Assert.IsTrue(false, "todo: test for sync question, test overwrite or cancel!"); + } + + + [TestMethod] + public void TestLoadEditSaveWithSyncOverwrite() + { + //create the default database: + IKp2aApp app = SetupAppWithDefaultDatabase(); + //save it and reload it so we have a base version + SaveDatabase(app); + app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + //load it once again: + IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + + //modify the database by adding a group in both databases: + app.GetDb().KpDatabase.RootGroup.AddGroup(new PwGroup(true, true, "TestGroup", PwIcon.Apple), true); + var group2 = new PwGroup(true, true, "TestGroup2", PwIcon.Energy); + app2.GetDb().KpDatabase.RootGroup.AddGroup(group2, true); + //save the database from app 1: + SaveDatabase(app); + + //the user clicks the "no" button when asked if the sync should be performed -> overwrite expected! + ((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.No); + + //save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync: + SaveDatabase(app2); + + //make sure the right question was asked + Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle); + + //load database to a new app instance: + IKp2aApp resultApp = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + + //ensure the sync was NOT performed (overwrite expected!): + AssertDatabasesAreEqual(app2.GetDb().KpDatabase, resultApp.GetDb().KpDatabase); + + } + + + [TestMethod] + public void TestLoadEditSaveWithSyncOverwriteBecauseOfNoCheck() + { + //create the default database: + IKp2aApp app = SetupAppWithDefaultDatabase(); + //save it and reload it so we have a base version + SaveDatabase(app); + app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + //load it once again: + IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + + //modify the database by adding a group in both databases: + app.GetDb().KpDatabase.RootGroup.AddGroup(new PwGroup(true, true, "TestGroup", PwIcon.Apple), true); + var group2 = new PwGroup(true, true, "TestGroup2", PwIcon.Energy); + app2.GetDb().KpDatabase.RootGroup.AddGroup(group2, true); + //save the database from app 1: + SaveDatabase(app); + + //the user doesn't want to perform check for file change: + ((TestKp2aApp) app2).SetPreference(PreferenceKey.CheckForFileChangesOnSave, false); + + //save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync: + SaveDatabase(app2); + + //make sure no question was asked + Assert.AreEqual(null, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle); + + //load database to a new app instance: + IKp2aApp resultApp = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + + //ensure the sync was NOT performed (overwrite expected!): + AssertDatabasesAreEqual(app2.GetDb().KpDatabase, resultApp.GetDb().KpDatabase); + + } + + [TestMethod] + public void TestLoadEditSaveWithSyncCancel() + { + //create the default database: + IKp2aApp app = SetupAppWithDefaultDatabase(); + //save it and reload it so we have a base version + SaveDatabase(app); + app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + //load it once again: + IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + + //modify the database by adding a group in both databases: + app.GetDb().KpDatabase.RootGroup.AddGroup(new PwGroup(true, true, "TestGroup", PwIcon.Apple), true); + var group2 = new PwGroup(true, true, "TestGroup2", PwIcon.Energy); + app2.GetDb().KpDatabase.RootGroup.AddGroup(group2, true); + //save the database from app 1: + SaveDatabase(app); + + //the user clicks the "cancel" button when asked if the sync should be performed + ((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.Cancel); + + //save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync: + Assert.AreEqual(false, TrySaveDatabase(app2)); + + //make sure the right question was asked + Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle); + + //load database to a new app instance: + IKp2aApp resultApp = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + + //ensure the sync was NOT performed (cancel expected!): + AssertDatabasesAreEqual(app.GetDb().KpDatabase, resultApp.GetDb().KpDatabase); + + } + + + [TestMethod] + public void TestLoadEditSaveWithSyncConflict() + { + + //create the default database: + IKp2aApp app = SetupAppWithDefaultDatabase(); + //save it and reload it so we have a base version + SaveDatabase(app); + app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + //load it once again: + IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile); + + //modify the database by renaming the same group in both databases: + app.GetDb().KpDatabase.RootGroup.Groups.Single(g => g.Name == "Internet").Name += "abc"; + app2.GetDb().KpDatabase.RootGroup.Groups.Single(g => g.Name == "Internet").Name += "abcde"; + //app1 also changes the master password: + var compositeKey = app.GetDb().KpDatabase.MasterKey; + compositeKey.RemoveUserKey(compositeKey.GetUserKey(typeof (KcpPassword))); + compositeKey.AddUserKey(new KcpPassword("abc")); + + //save the database from app 1: + SaveDatabase(app); + + + ((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.Yes); + + //save the database from app 2: This save operation must fail because the target file cannot be loaded: + Assert.IsFalse(TrySaveDatabase(app2)); + + //make sure the right question was asked + Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle); + + } + + + + [TestMethod] + public void TestSaveAsWhenReadOnly() + { + Assert.Fail("TODO: Test "); + } + + [TestMethod] + public void TestSaveAsWhenSyncError() + { + Assert.Fail("TODO: Test "); } [TestMethod] @@ -119,4 +282,6 @@ namespace Kp2aUnitTests return sOutput.Text; } } + + } diff --git a/src/keepass2android/EntryActivity.cs b/src/keepass2android/EntryActivity.cs index 5e49f64b..eb52839c 100644 --- a/src/keepass2android/EntryActivity.cs +++ b/src/keepass2android/EntryActivity.cs @@ -123,8 +123,8 @@ namespace keepass2android Entry.Expires = true; Entry.Touch(true); RequiresRefresh(); - UpdateEntry update = new UpdateEntry(this, App.Kp2a.GetDb(), backupEntry, Entry, null); - ProgressTask pt = new ProgressTask(App.Kp2a, this, update, UiStringKey.saving_database); + UpdateEntry update = new UpdateEntry(this, App.Kp2a, backupEntry, Entry, null); + ProgressTask pt = new ProgressTask(App.Kp2a, this, update); pt.Run(); } FillData(false); diff --git a/src/keepass2android/EntryEditActivity.cs b/src/keepass2android/EntryEditActivity.cs index 03a3745d..9fd89b12 100644 --- a/src/keepass2android/EntryEditActivity.cs +++ b/src/keepass2android/EntryEditActivity.cs @@ -383,11 +383,11 @@ namespace keepass2android },closeOrShowError); if ( State.IsNew ) { - runnable = AddEntry.GetInstance(this, App.Kp2a.GetDb(), newEntry, State.ParentGroup, afterAddEntry); + runnable = AddEntry.GetInstance(this, App.Kp2a, newEntry, State.ParentGroup, afterAddEntry); } else { - runnable = new UpdateEntry(this, App.Kp2a.GetDb(), initialEntry, newEntry, closeOrShowError); + runnable = new UpdateEntry(this, App.Kp2a, initialEntry, newEntry, closeOrShowError); } - ProgressTask pt = new ProgressTask(App.Kp2a, act, runnable, UiStringKey.saving_database); + ProgressTask pt = new ProgressTask(App.Kp2a, act, runnable); pt.Run(); diff --git a/src/keepass2android/GroupActivity.cs b/src/keepass2android/GroupActivity.cs index 4eec2ca2..44cf124e 100644 --- a/src/keepass2android/GroupActivity.cs +++ b/src/keepass2android/GroupActivity.cs @@ -177,8 +177,8 @@ namespace keepass2android int groupIconId = data.Extras.GetInt(GroupEditActivity.KeyIconId); GroupBaseActivity act = this; Handler handler = new Handler(); - AddGroup task = AddGroup.GetInstance(this, App.Kp2a.GetDb(), groupName, groupIconId, Group, new RefreshTask(handler, this), false); - ProgressTask pt = new ProgressTask(App.Kp2a, act, task, UiStringKey.saving_database); + AddGroup task = AddGroup.GetInstance(this, App.Kp2a, groupName, groupIconId, Group, new RefreshTask(handler, this), false); + ProgressTask pt = new ProgressTask(App.Kp2a, act, task); pt.Run(); break; diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index 31bf4587..21738fbb 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -365,7 +365,7 @@ namespace keepass2android Handler handler = new Handler(); LoadDb task = new LoadDb(App.Kp2a, _ioConnection, pass, key, new AfterLoad(handler, this)); - ProgressTask pt = new ProgressTask(App.Kp2a, this, task, UiStringKey.loading_database); + ProgressTask pt = new ProgressTask(App.Kp2a, this, task); pt.Run(); }; diff --git a/src/keepass2android/Resources/Resource.designer.cs b/src/keepass2android/Resources/Resource.designer.cs index ec16ac2f..bd47f390 100644 --- a/src/keepass2android/Resources/Resource.designer.cs +++ b/src/keepass2android/Resources/Resource.designer.cs @@ -1230,6 +1230,12 @@ namespace keepass2android // aapt resource value: 0x7f080033 public const int AboutText = 2131230771; + // aapt resource value: 0x7f08011e + public const int AddingEntry = 2131231006; + + // aapt resource value: 0x7f08011f + public const int AddingGroup = 2131231007; + // aapt resource value: 0x7f080114 public const int AskDeletePermanentlyEntry = 2131230996; @@ -1278,29 +1284,32 @@ namespace keepass2android // aapt resource value: 0x7f0800f5 public const int BinaryDirectory_title = 2131230965; - // aapt resource value: 0x7f080125 - public const int ChangeLog = 2131231013; + // aapt resource value: 0x7f08012f + public const int ChangeLog = 2131231023; - // aapt resource value: 0x7f080124 - public const int ChangeLog_0_7 = 2131231012; + // aapt resource value: 0x7f08012e + public const int ChangeLog_0_7 = 2131231022; - // aapt resource value: 0x7f080122 - public const int ChangeLog_0_8 = 2131231010; + // aapt resource value: 0x7f08012c + public const int ChangeLog_0_8 = 2131231020; - // aapt resource value: 0x7f080121 - public const int ChangeLog_0_8_1 = 2131231009; + // aapt resource value: 0x7f08012b + public const int ChangeLog_0_8_1 = 2131231019; - // aapt resource value: 0x7f080120 - public const int ChangeLog_0_8_2 = 2131231008; + // aapt resource value: 0x7f08012a + public const int ChangeLog_0_8_2 = 2131231018; - // aapt resource value: 0x7f08011f - public const int ChangeLog_0_8_3 = 2131231007; + // aapt resource value: 0x7f080129 + public const int ChangeLog_0_8_3 = 2131231017; - // aapt resource value: 0x7f080123 - public const int ChangeLog_keptDonate = 2131231011; + // aapt resource value: 0x7f08012d + public const int ChangeLog_keptDonate = 2131231021; - // aapt resource value: 0x7f08011e - public const int ChangeLog_title = 2131231006; + // aapt resource value: 0x7f080128 + public const int ChangeLog_title = 2131231016; + + // aapt resource value: 0x7f080127 + public const int CheckingTargetFileForChanges = 2131231015; // aapt resource value: 0x7f080048 public const int ClearClipboard = 2131230792; @@ -1311,6 +1320,15 @@ namespace keepass2android // aapt resource value: 0x7f080034 public const int CreditsText = 2131230772; + // aapt resource value: 0x7f080125 + public const int DecodingDatabase = 2131231013; + + // aapt resource value: 0x7f080120 + public const int DeletingEntry = 2131231008; + + // aapt resource value: 0x7f080121 + public const int DeletingGroup = 2131231009; + // aapt resource value: 0x7f080081 public const int FileNotFound = 2131230849; @@ -1335,6 +1353,9 @@ namespace keepass2android // aapt resource value: 0x7f08010c public const int OpenKp2aKeyboardAutomatically_title = 2131230988; + // aapt resource value: 0x7f080126 + public const int ParsingDatabase = 2131231014; + // aapt resource value: 0x7f080023 public const int QuickUnlockDefaultEnabled_key = 2131230755; @@ -1389,6 +1410,9 @@ namespace keepass2android // aapt resource value: 0x7f0800fb public const int SaveAttachment_doneMessage = 2131230971; + // aapt resource value: 0x7f080122 + public const int SettingPassword = 2131231010; + // aapt resource value: 0x7f080109 public const int ShowCopyToClipboardNotification_summary = 2131230985; @@ -1422,9 +1446,15 @@ namespace keepass2android // aapt resource value: 0x7f0800de public const int TanExpiresOnUse_title = 2131230942; + // aapt resource value: 0x7f080124 + public const int TransformingKey = 2131231012; + // aapt resource value: 0x7f08002b public const int TranslationURL = 2131230763; + // aapt resource value: 0x7f080123 + public const int UndoingChanges = 2131231011; + // aapt resource value: 0x7f080026 public const int UsageCount_key = 2131230758; diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index f1398952..9e907d92 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -240,6 +240,17 @@ Suggest or vote for improvements Rate this app Translate KP2A + Adding entry… + Adding group… + Deleting entry… + Deleting group… + Setting password… + Undoing changes… + Transforming master key… + Decoding database… + Parsing database… + Checking target file for changes… + Change log Version 0.8.3\n * Username/TAN index displayed in entry list (see settings)\n diff --git a/src/keepass2android/SetPasswordDialog.cs b/src/keepass2android/SetPasswordDialog.cs index 059a0088..86371b28 100644 --- a/src/keepass2android/SetPasswordDialog.cs +++ b/src/keepass2android/SetPasswordDialog.cs @@ -71,8 +71,8 @@ namespace keepass2android } - SetPassword sp = new SetPassword(Context, App.Kp2a.GetDb(), pass, keyfile, new AfterSave(this, _finish, new Handler())); - ProgressTask pt = new ProgressTask(App.Kp2a, Context, sp, UiStringKey.saving_database); + SetPassword sp = new SetPassword(Context, App.Kp2a, pass, keyfile, new AfterSave(this, _finish, new Handler())); + ProgressTask pt = new ProgressTask(App.Kp2a, Context, sp); pt.Run(); }; diff --git a/src/keepass2android/app/App.cs b/src/keepass2android/app/App.cs index a450637e..29e7c909 100644 --- a/src/keepass2android/app/App.cs +++ b/src/keepass2android/app/App.cs @@ -22,6 +22,7 @@ using Android.OS; using Android.Runtime; using KeePassLib.Serialization; using Android.Preferences; +using keepass2android.Io; namespace keepass2android { @@ -223,6 +224,11 @@ namespace keepass2android return new RealProgressDialog(ctx); } + public IFileStorage GetFileStorage(IOConnectionInfo iocInfo) + { + return new BuiltInFileStorage(); + } + internal void OnTerminate() { diff --git a/src/keepass2android/fileselect/FileSelectActivity.cs b/src/keepass2android/fileselect/FileSelectActivity.cs index 5533e0b9..882b7fcf 100644 --- a/src/keepass2android/fileselect/FileSelectActivity.cs +++ b/src/keepass2android/fileselect/FileSelectActivity.cs @@ -170,8 +170,7 @@ namespace keepass2android CreateDb create = new CreateDb(App.Kp2a, this, IOConnectionInfo.FromPath(filename), collectPassword, true); ProgressTask createTask = new ProgressTask( App.Kp2a, - this, create, - UiStringKey.progress_create); + this, create); createTask.Run(); @@ -313,7 +312,8 @@ namespace keepass2android GroupActivity.Launch(_activity, _activity.AppTask); } else { - IOConnection.DeleteFile(_ioc); + App.Kp2a.GetFileStorage(_ioc).DeleteFile(_ioc); + } } } diff --git a/src/keepass2android/settings/AppSettingsActivity.cs b/src/keepass2android/settings/AppSettingsActivity.cs index 2f617f99..6f2741b7 100644 --- a/src/keepass2android/settings/AppSettingsActivity.cs +++ b/src/keepass2android/settings/AppSettingsActivity.cs @@ -73,7 +73,7 @@ namespace keepass2android String previousUsername = db.KpDatabase.DefaultUserName; db.KpDatabase.DefaultUserName = e.NewValue.ToString(); - SaveDb save = new SaveDb(this, App.Kp2a.GetDb(), new ActionOnFinish( (success, message) => + SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) => { if (!success) { @@ -82,7 +82,7 @@ namespace keepass2android Toast.MakeText(this, message, ToastLength.Long).Show(); } })); - ProgressTask pt = new ProgressTask(App.Kp2a, this, save, UiStringKey.saving_database); + ProgressTask pt = new ProgressTask(App.Kp2a, this, save); pt.Run(); }; @@ -95,7 +95,7 @@ namespace keepass2android String previousName = db.KpDatabase.Name; db.KpDatabase.Name = e.NewValue.ToString(); - SaveDb save = new SaveDb(this, App.Kp2a.GetDb(), new ActionOnFinish( (success, message) => + SaveDb save = new SaveDb(this, App.Kp2a, new ActionOnFinish( (success, message) => { if (!success) { @@ -104,7 +104,7 @@ namespace keepass2android Toast.MakeText(this, message, ToastLength.Long).Show(); } })); - ProgressTask pt = new ProgressTask(App.Kp2a, this, save, UiStringKey.saving_database); + ProgressTask pt = new ProgressTask(App.Kp2a, this, save); pt.Run(); }; diff --git a/src/keepass2android/settings/RoundsPreference.cs b/src/keepass2android/settings/RoundsPreference.cs index 2f0abf58..9bd50e56 100644 --- a/src/keepass2android/settings/RoundsPreference.cs +++ b/src/keepass2android/settings/RoundsPreference.cs @@ -80,8 +80,8 @@ namespace keepass2android.settings PwDatabase.KeyEncryptionRounds = rounds; Handler handler = new Handler(); - SaveDb save = new SaveDb(Context, App.Kp2a.GetDb(), new AfterSave(Context, handler, oldRounds, this)); - ProgressTask pt = new ProgressTask(App.Kp2a, Context, save, UiStringKey.saving_database); + SaveDb save = new SaveDb(Context, App.Kp2a, new AfterSave(Context, handler, oldRounds, this)); + ProgressTask pt = new ProgressTask(App.Kp2a, Context, save); pt.Run(); }