diff --git a/src/KeePassLib2Android/PwDatabase.cs b/src/KeePassLib2Android/PwDatabase.cs index 4b4deb02..19158f15 100644 --- a/src/KeePassLib2Android/PwDatabase.cs +++ b/src/KeePassLib2Android/PwDatabase.cs @@ -485,12 +485,6 @@ namespace KeePassLib set { m_pbHashOfLastIO = value; } } - public bool UseFileTransactions - { - get { return m_bUseFileTransactions; } - set { m_bUseFileTransactions = value; } - } - public bool UseFileLocks { get { return m_bUseFileLocks; } diff --git a/src/KeePassLib2Android/Serialization/IOConnectionInfo.cs b/src/KeePassLib2Android/Serialization/IOConnectionInfo.cs index b887d6ab..96401093 100644 --- a/src/KeePassLib2Android/Serialization/IOConnectionInfo.cs +++ b/src/KeePassLib2Android/Serialization/IOConnectionInfo.cs @@ -360,5 +360,12 @@ namespace KeePassLib.Serialization m_ioCredProtMode = IOCredProtMode.None; } } + + public bool IsSameFileAs(IOConnectionInfo other) + { + if (other == null) + return false; + return Path == other.Path && UserName == other.UserName; + } } } diff --git a/src/Kp2aBusinessLogic/IKp2aApp.cs b/src/Kp2aBusinessLogic/IKp2aApp.cs index 1c81bc50..72e94697 100644 --- a/src/Kp2aBusinessLogic/IKp2aApp.cs +++ b/src/Kp2aBusinessLogic/IKp2aApp.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using Android.App; @@ -33,26 +34,37 @@ namespace keepass2android /// This also contains methods which are UI specific and should be replacable for testing. public interface IKp2aApp : ICertificateValidationHandler { - /// - /// Locks the currently open database, quicklocking if available (unless false is passed for allowQuickUnlock) - /// - void LockDatabase(bool allowQuickUnlock = true); + /// + /// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock) + /// + void Lock(bool allowQuickUnlock); - /// - /// Loads the specified data as the currently open database, as unlocked. - /// - void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, - ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat); - /// - /// Returns the current database - /// - Database GetDb(); + /// + /// Loads the specified data as the currently open database, as unlocked. + /// + Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat); - /// - /// Tell the app that the file from ioc was opened with keyfile. - /// - void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = ""); + + HashSet DirtyGroups { get; } + + void MarkAllGroupsAsDirty(); + + /// + /// Returns the current database + /// + Database CurrentDb { get; } + + IEnumerable OpenDatabases { get; } + void CloseDatabase(Database db); + + Database FindDatabaseForGroupId(PwUuid groupKey); + Database FindDatabaseForEntryId(PwUuid entryId); + + /// + /// Tell the app that the file from ioc was opened with keyfile. + /// + void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = ""); /// /// Creates a new database and returns it @@ -111,6 +123,10 @@ namespace keepass2android bool CheckForDuplicateUuids { get; } #if !NoNet ICertificateErrorHandler CertificateErrorHandler { get; } + + + #endif - } + + } } \ No newline at end of file diff --git a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj index dde58ccc..6893ee31 100644 --- a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj +++ b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj @@ -90,8 +90,9 @@ + - + diff --git a/src/Kp2aBusinessLogic/PwGroupEqualityFromIdComparer.cs b/src/Kp2aBusinessLogic/PwGroupEqualityFromIdComparer.cs index 31d8951f..010dd5b0 100644 --- a/src/Kp2aBusinessLogic/PwGroupEqualityFromIdComparer.cs +++ b/src/Kp2aBusinessLogic/PwGroupEqualityFromIdComparer.cs @@ -23,7 +23,7 @@ namespace keepass2android /// /// EqualityComparer implementation to compare PwGroups based on their Id /// - class PwGroupEqualityFromIdComparer: IEqualityComparer + public class PwGroupEqualityFromIdComparer: IEqualityComparer { #region IEqualityComparer implementation public bool Equals (PwGroup x, PwGroup y) diff --git a/src/Kp2aBusinessLogic/UiStringKey.cs b/src/Kp2aBusinessLogic/UiStringKey.cs index 3f8ab026..d78a1e8f 100644 --- a/src/Kp2aBusinessLogic/UiStringKey.cs +++ b/src/Kp2aBusinessLogic/UiStringKey.cs @@ -86,6 +86,11 @@ namespace keepass2android ReadOnlyReason_PreKitKat, ReadOnlyReason_ReadOnlyFlag, ReadOnlyReason_ReadOnlyKitKat, - ReadOnlyReason_LocalBackup + ReadOnlyReason_LocalBackup, + UpdatingTemplateIds, + ChangleLegacyTemplateIds_Message, + ChangleLegacyTemplateIds_Title, + Ok, + cancel } } diff --git a/src/Kp2aBusinessLogic/database/CheckDatabaseForChanges.cs b/src/Kp2aBusinessLogic/database/CheckDatabaseForChanges.cs index d9ceef73..518a4abb 100644 --- a/src/Kp2aBusinessLogic/database/CheckDatabaseForChanges.cs +++ b/src/Kp2aBusinessLogic/database/CheckDatabaseForChanges.cs @@ -29,7 +29,7 @@ namespace keepass2android { try { - IOConnectionInfo ioc = _app.GetDb().Ioc; + IOConnectionInfo ioc = _app.CurrentDb.Ioc; IFileStorage fileStorage = _app.GetFileStorage(ioc); if (fileStorage is CachingFileStorage) { @@ -49,7 +49,7 @@ namespace keepass2android hashingRemoteStream.CopyTo(remoteData); hashingRemoteStream.Close(); - if (!MemUtil.ArraysEqual(_app.GetDb().KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash)) + if (!MemUtil.ArraysEqual(_app.CurrentDb.KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash)) { _app.TriggerReload(_context); Finish(true); diff --git a/src/Kp2aBusinessLogic/database/Database.cs b/src/Kp2aBusinessLogic/database/Database.cs index 4c4afe90..6758c238 100644 --- a/src/Kp2aBusinessLogic/database/Database.cs +++ b/src/Kp2aBusinessLogic/database/Database.cs @@ -38,7 +38,6 @@ namespace keepass2android public Dictionary Groups = new Dictionary(new PwUuidEqualityComparer()); public Dictionary Entries = new Dictionary(new PwUuidEqualityComparer()); - public HashSet Dirty = new HashSet(new PwGroupEqualityFromIdComparer()); public PwGroup Root; public PwDatabase KpDatabase; public IOConnectionInfo Ioc @@ -49,11 +48,6 @@ namespace keepass2android } } - /// - /// Information about the last opened entry. Includes the entry but also transformed fields. - /// - public PwEntryOutput LastOpenedEntry { get; set; } - /// /// if an OTP key was used, this property tells the location of the OTP auxiliary file. /// Must be set after loading. @@ -74,16 +68,11 @@ namespace keepass2android CanWrite = true; //default } - private bool _reloadRequested; - private IDatabaseFormat _databaseFormat = new KdbxDatabaseFormat(KdbxFormat.Default); + private IDatabaseFormat _databaseFormat = new KdbxDatabaseFormat(KdbxFormat.Default); - public bool ReloadRequested - { - get { return _reloadRequested; } - set { _reloadRequested = value; } - } + public bool ReloadRequested { get; set; } - public bool DidOpenFileChange() + public bool DidOpenFileChange() { return _app.GetFileStorage(Ioc).CheckForFileChangeFast(Ioc, LastFileVersion); } @@ -195,8 +184,7 @@ namespace keepass2android public void SaveData() { - KpDatabase.UseFileTransactions = _app.GetBooleanPreference(PreferenceKey.UseFileTransactions); - using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, KpDatabase.UseFileTransactions)) + using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions))) { DatabaseFormat.Save(KpDatabase, trans.OpenFile()); @@ -243,14 +231,6 @@ namespace keepass2android PopulateGlobals(currentGroup, _app.CheckForDuplicateUuids); } - public void MarkAllGroupsAsDirty() { - foreach ( PwGroup group in Groups.Values ) { - Dirty.Add(group); - } - - - } - } diff --git a/src/Kp2aBusinessLogic/database/PwEntryOutput.cs b/src/Kp2aBusinessLogic/database/PwEntryOutput.cs index 26d0ea18..295b9a0e 100644 --- a/src/Kp2aBusinessLogic/database/PwEntryOutput.cs +++ b/src/Kp2aBusinessLogic/database/PwEntryOutput.cs @@ -14,13 +14,13 @@ namespace keepass2android public class PwEntryOutput { private readonly PwEntry _entry; - private readonly PwDatabase _db; + private readonly Database _db; private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary(); /// /// Constructs the PwEntryOutput by replacing the placeholders /// - public PwEntryOutput(PwEntry entry, PwDatabase db) + public PwEntryOutput(PwEntry entry, Database db) { _entry = entry; _db = db; @@ -34,7 +34,7 @@ namespace keepass2android string GetStringAndReplacePlaceholders(string key) { String value = Entry.Strings.ReadSafe(key); - value = SprEngine.Compile(value, new SprContext(Entry, _db, SprCompileFlags.All)); + value = SprEngine.Compile(value, new SprContext(Entry, _db.KpDatabase, SprCompileFlags.All)); return value; } diff --git a/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs b/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs index bd8c7ca1..13976d10 100644 --- a/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs +++ b/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs @@ -26,7 +26,7 @@ namespace keepass2android { try { - IOConnectionInfo ioc = _app.GetDb().Ioc; + IOConnectionInfo ioc = _app.CurrentDb.Ioc; IFileStorage fileStorage = _app.GetFileStorage(ioc); if (!(fileStorage is CachingFileStorage)) { @@ -70,10 +70,10 @@ namespace keepass2android Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully)); } _saveDb = null; - }), false, remoteData); + }), _app.CurrentDb, false, remoteData); _saveDb.Run(); - _app.GetDb().MarkAllGroupsAsDirty(); + _app.MarkAllGroupsAsDirty(); } else { diff --git a/src/Kp2aBusinessLogic/database/edit/AddEntry.cs b/src/Kp2aBusinessLogic/database/edit/AddEntry.cs index 7cfcfda4..e7181fef 100644 --- a/src/Kp2aBusinessLogic/database/edit/AddEntry.cs +++ b/src/Kp2aBusinessLogic/database/edit/AddEntry.cs @@ -24,7 +24,7 @@ namespace keepass2android public class AddEntry : RunnableOnFinish { protected Database Db { - get { return _app.GetDb(); } + get { return _app.CurrentDb; } } private readonly IKp2aApp _app; @@ -43,7 +43,7 @@ namespace keepass2android _app = app; _entry = entry; - _onFinishToRun = new AfterAdd(ctx, app.GetDb(), entry, OnFinishToRun); + _onFinishToRun = new AfterAdd(ctx, app.CurrentDb, entry, app,OnFinishToRun); } @@ -60,7 +60,7 @@ namespace keepass2android // Commit to disk - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun); + SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun); save.SetStatusLogger(StatusLogger); save.Run(); } @@ -68,12 +68,13 @@ namespace keepass2android private class AfterAdd : OnFinish { private readonly Database _db; private readonly PwEntry _entry; + private readonly IKp2aApp _app; - public AfterAdd(Activity activity, Database db, PwEntry entry, OnFinish finish):base(activity, finish) { + public AfterAdd(Activity activity, Database db, PwEntry entry, IKp2aApp app, OnFinish finish):base(activity, finish) { _db = db; _entry = entry; - - } + _app = app; + } @@ -83,7 +84,7 @@ namespace keepass2android PwGroup parent = _entry.ParentGroup; // Mark parent group dirty - _db.Dirty.Add(parent); + _app.DirtyGroups.Add(parent); // Add entry to global _db.Entries[_entry.Uuid] = _entry; diff --git a/src/Kp2aBusinessLogic/database/edit/AddGroup.cs b/src/Kp2aBusinessLogic/database/edit/AddGroup.cs index f052dcfb..5ea12475 100644 --- a/src/Kp2aBusinessLogic/database/edit/AddGroup.cs +++ b/src/Kp2aBusinessLogic/database/edit/AddGroup.cs @@ -26,9 +26,12 @@ namespace keepass2android public class AddGroup : RunnableOnFinish { internal Database Db { - get { return _app.GetDb(); } + get { return _app.CurrentDb; } } - private IKp2aApp _app; + + public IKp2aApp App { get => _app; } + + private IKp2aApp _app; private readonly String _name; private readonly int _iconId; private readonly PwUuid _groupCustomIconId; @@ -69,7 +72,7 @@ namespace keepass2android Parent.AddGroup(Group, true); // Commit to disk - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, DontSave); + SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, DontSave); save.SetStatusLogger(StatusLogger); save.Run(); } @@ -86,7 +89,7 @@ namespace keepass2android if ( Success ) { // Mark parent group dirty - _addGroup.Db.Dirty.Add(_addGroup.Parent); + _addGroup.App.DirtyGroups.Add(_addGroup.Parent); // Add group to global list _addGroup.Db.Groups[_addGroup.Group.Uuid] = _addGroup.Group; diff --git a/src/Kp2aBusinessLogic/database/edit/AddTemplateEntries.cs b/src/Kp2aBusinessLogic/database/edit/AddTemplateEntries.cs index c2b39b63..3d5b5289 100644 --- a/src/Kp2aBusinessLogic/database/edit/AddTemplateEntries.cs +++ b/src/Kp2aBusinessLogic/database/edit/AddTemplateEntries.cs @@ -28,7 +28,7 @@ namespace keepass2android { public class AddTemplateEntries : RunnableOnFinish { - class TemplateEntry + public class TemplateEntry { public UiStringKey Title { get; set; } public PwIcon Icon { get; set; } @@ -47,11 +47,12 @@ namespace keepass2android void AddToEntry(IKp2aApp app, PwEntry entry, int position); } - internal enum FieldType + public enum FieldType { Inline, ProtectedInline } - internal enum SpecialFieldKey + + public enum SpecialFieldKey { ExpDate, OverrideUrl, Tags } @@ -125,7 +126,7 @@ namespace keepass2android protected Database Db { - get { return _app.GetDb(); } + get { return _app.CurrentDb; } } private readonly IKp2aApp _app; @@ -140,7 +141,7 @@ namespace keepass2android //_onFinishToRun = new AfterAdd(this, OnFinishToRun); } - static readonly List TemplateEntries = new List() + public static readonly List TemplateEntries = new List() { new TemplateEntry() { @@ -285,12 +286,23 @@ namespace keepass2android }; - public static bool ContainsAllTemplates(IKp2aApp app) + public static bool ContainsAllTemplates(Database db) { - return TemplateEntries.All(t => app.GetDb().Entries.ContainsKey(t.Uuid)); + return TemplateEntries.All(t => + { + string hexId = t.Uuid.ToHexString(); + + return db.Entries.Any(kvp => kvp.Key.Equals(t.Uuid) || + kvp.Value.Strings.ReadSafe(TemplateIdStringKey) == hexId); + }); } - public override void Run() { + public static string TemplateIdStringKey + { + get { return "KP2A_TemplateId"; } + } + + public override void Run() { StatusLogger.UpdateMessage(UiStringKey.AddingEntry); List addedEntries; @@ -298,10 +310,10 @@ namespace keepass2android if (addedEntries.Any()) { - _app.GetDb().Dirty.Add(templateGroup); + _app.DirtyGroups.Add(templateGroup); // Commit to disk - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun); + SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun); save.SetStatusLogger(StatusLogger); save.Run(); } @@ -315,37 +327,38 @@ namespace keepass2android } PwGroup templateGroup; - if (!_app.GetDb().Groups.TryGetValue(_app.GetDb().KpDatabase.EntryTemplatesGroup, out templateGroup)) + if (!_app.CurrentDb.Groups.TryGetValue(_app.CurrentDb.KpDatabase.EntryTemplatesGroup, out templateGroup)) { //create template group templateGroup = new PwGroup(true, true, _app.GetResourceString(UiStringKey.TemplateGroupName), PwIcon.Folder); - _app.GetDb().KpDatabase.RootGroup.AddGroup(templateGroup, true); - _app.GetDb().KpDatabase.EntryTemplatesGroup = templateGroup.Uuid; - _app.GetDb().KpDatabase.EntryTemplatesGroupChanged = DateTime.Now; - _app.GetDb().Dirty.Add(_app.GetDb().KpDatabase.RootGroup); - _app.GetDb().Groups[templateGroup.Uuid] = templateGroup; + _app.CurrentDb.KpDatabase.RootGroup.AddGroup(templateGroup, true); + _app.CurrentDb.KpDatabase.EntryTemplatesGroup = templateGroup.Uuid; + _app.CurrentDb.KpDatabase.EntryTemplatesGroupChanged = DateTime.Now; + _app.DirtyGroups.Add(_app.CurrentDb.KpDatabase.RootGroup); + _app.CurrentDb.Groups[templateGroup.Uuid] = templateGroup; } addedEntries = new List(); foreach (var template in TemplateEntries) { - if (_app.GetDb().Entries.ContainsKey(template.Uuid)) + if (_app.CurrentDb.Entries.ContainsKey(template.Uuid)) continue; PwEntry entry = CreateEntry(template); templateGroup.AddEntry(entry, true); addedEntries.Add(entry); - _app.GetDb().Entries[entry.Uuid] = entry; + _app.CurrentDb.Entries[entry.Uuid] = entry; } return templateGroup; } private PwEntry CreateEntry(TemplateEntry template) { - PwEntry entry = new PwEntry(false, true); - entry.Uuid = template.Uuid; + PwEntry entry = new PwEntry(true, true); + entry.IconId = template.Icon; entry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, _app.GetResourceString(template.Title))); entry.Strings.Set("_etm_template", new ProtectedString(false, "1")); + entry.Strings.Set(TemplateIdStringKey, new ProtectedString(false, template.Uuid.ToHexString())); int position = 0; foreach (var field in template.Fields) { @@ -373,8 +386,12 @@ namespace keepass2android base.Run(); } } - - + + + public static bool IsTemplateId(PwUuid pwUuid) + { + return TemplateEntries.Any(te => te.Uuid.Equals(pwUuid)); + } } } diff --git a/src/Kp2aBusinessLogic/database/edit/ChangeTemplateIds.cs b/src/Kp2aBusinessLogic/database/edit/ChangeTemplateIds.cs new file mode 100644 index 00000000..c081668d --- /dev/null +++ b/src/Kp2aBusinessLogic/database/edit/ChangeTemplateIds.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +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; +using KeePassLib.Security; + +namespace keepass2android.database.edit +{ + public class ChangeTemplateIds: RunnableOnFinish + { + private readonly IKp2aApp _app; + private readonly Database _db; + private string _etmTemplateUuid { get { return "_etm_template_uuid"; } } + public static string TemplateIdStringKey + { + get { return "KP2A_TemplateId"; } + } + + public ChangeTemplateIds(Activity activeActivity, IKp2aApp app, Database db, OnFinish finish) : base(activeActivity, finish) + { + _app = app; + _db = db; + } + + public override void Run() + { + StatusLogger.UpdateMessage(UiStringKey.UpdatingTemplateIds); + Dictionary uuidMap = new Dictionary(); + foreach (var templateEntry in AddTemplateEntries.TemplateEntries) + { + PwEntry entry; + if (_db.Entries.TryGetValue(templateEntry.Uuid, out entry)) + { + PwUuid oldUuid = entry.Uuid; + entry.Uuid = new PwUuid(true); + uuidMap[oldUuid.ToHexString()] = entry.Uuid.ToHexString(); + entry.Strings.Set(TemplateIdStringKey,new ProtectedString(false, oldUuid.ToHexString())); + _db.Entries.Remove(oldUuid); + _db.Entries[entry.Uuid] = entry; + } + } + foreach (var entry in _db.Entries.Values) + { + string templateUuid = entry.Strings.ReadSafe(_etmTemplateUuid); + if (templateUuid != null) + { + string newTemplateUuid; + if (uuidMap.TryGetValue(templateUuid, out newTemplateUuid)) + { + entry.Strings.Set(_etmTemplateUuid, new ProtectedString(false, newTemplateUuid)); + } + } + } + + if (uuidMap.Any()) + { + SaveDb save = new SaveDb( ActiveActivity, _app, _db, OnFinishToRun); + save.SetStatusLogger(StatusLogger); + save.Run(); + } + else + { + OnFinishToRun?.Run(); + } + + } + } +} \ No newline at end of file diff --git a/src/Kp2aBusinessLogic/database/edit/CreateDB.cs b/src/Kp2aBusinessLogic/database/edit/CreateDB.cs index 495e1c41..0d3dbb92 100644 --- a/src/Kp2aBusinessLogic/database/edit/CreateDB.cs +++ b/src/Kp2aBusinessLogic/database/edit/CreateDB.cs @@ -84,7 +84,7 @@ namespace keepass2android addTemplates.AddTemplates(out addedEntries); // Commit changes - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave); + SaveDb save = new SaveDb(_ctx, _app, db, 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 047e0d5c..c56f42d4 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs @@ -31,7 +31,7 @@ namespace keepass2android public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) { Ctx = activiy; - Db = app.GetDb(); + Db = app.FindDatabaseForEntryId(entry.Uuid); _entry = entry; } @@ -40,7 +40,7 @@ namespace keepass2android { get { - return App.GetDb().DatabaseFormat.CanRecycle && CanRecycleGroup(_entry.ParentGroup); + return Db.DatabaseFormat.CanRecycle && CanRecycleGroup(_entry.ParentGroup); } } diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs index a0f3c414..338704d4 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs @@ -47,7 +47,7 @@ namespace keepass2android */ private void SetMembers(Activity activity, IKp2aApp app, PwGroup group, bool dontSave) { - base.SetMembers(activity, app.GetDb()); + base.SetMembers(activity, app.FindDatabaseForGroupId(group.Uuid)); _group = group; DontSave = dontSave; @@ -58,7 +58,7 @@ namespace keepass2android { get { - return App.GetDb().DatabaseFormat.CanRecycle && CanRecycleGroup(_group); + return Db.DatabaseFormat.CanRecycle && CanRecycleGroup(_group); } } diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteMultipleItems.cs b/src/Kp2aBusinessLogic/database/edit/DeleteMultipleItemsFromOneDatabase.cs similarity index 89% rename from src/Kp2aBusinessLogic/database/edit/DeleteMultipleItems.cs rename to src/Kp2aBusinessLogic/database/edit/DeleteMultipleItemsFromOneDatabase.cs index 02f5bcfa..ac21700d 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteMultipleItems.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteMultipleItemsFromOneDatabase.cs @@ -7,12 +7,12 @@ using KeePassLib.Interfaces; namespace keepass2android { - public class DeleteMultipleItems : DeleteRunnable + public class DeleteMultipleItemsFromOneDatabase : DeleteRunnable { private readonly List _elementsToDelete; private readonly bool _canRecycle; - public DeleteMultipleItems(Activity activity, Database db, List elementsToDelete, OnFinish finish, IKp2aApp app) + public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List elementsToDelete, OnFinish finish, IKp2aApp app) : base(activity, finish, app) { _elementsToDelete = elementsToDelete; @@ -26,7 +26,7 @@ namespace keepass2android private bool DetermineCanRecycle() { Android.Util.Log.Debug("KP2A", "CanRecycle?"); - if (!App.GetDb().DatabaseFormat.CanRecycle) + if (!Db.DatabaseFormat.CanRecycle) { Android.Util.Log.Debug("KP2A", "CanRecycle? No because of DB format."); return false; diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs index 582aa1de..558196c6 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs @@ -130,6 +130,7 @@ namespace keepass2android DeletePermanently = true; ProgressTask pt = new ProgressTask(App, Ctx, this); pt.Run(); + }, (dlgSender, dlgEvt) => { @@ -215,11 +216,11 @@ namespace keepass2android if (success) { foreach (var g in touchedGroups) - Db.Dirty.Add(g); + App.DirtyGroups.Add(g); foreach (var g in permanentlyDeletedGroups) { - //remove groups from global lists if present there - Db.Dirty.Remove(g); + //remove groups from global lists if present there + App.DirtyGroups.Remove(g); Db.Groups.Remove(g.Uuid); } @@ -227,12 +228,12 @@ namespace keepass2android else { // Let's not bother recovering from a failure to save. It is too much work. - App.LockDatabase(false); + App.Lock(false); } }, OnFinishToRun); // Commit database - SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false); + SaveDb save = new SaveDb(Ctx, App, Db, OnFinishToRun, false); save.SetStatusLogger(StatusLogger); save.Run(); diff --git a/src/Kp2aBusinessLogic/database/edit/EditGroup.cs b/src/Kp2aBusinessLogic/database/edit/EditGroup.cs index fb596011..15e2361c 100644 --- a/src/Kp2aBusinessLogic/database/edit/EditGroup.cs +++ b/src/Kp2aBusinessLogic/database/edit/EditGroup.cs @@ -26,9 +26,12 @@ namespace keepass2android public class EditGroup : RunnableOnFinish { internal Database Db { - get { return _app.GetDb(); } + get { return _app.FindDatabaseForGroupId(Group.Uuid); } } - private IKp2aApp _app; + + public IKp2aApp App { get => _app; } + + private IKp2aApp _app; private readonly String _name; private readonly PwIcon _iconId; private readonly PwUuid _customIconId; @@ -57,7 +60,7 @@ namespace keepass2android Group.Touch(true); // Commit to disk - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun); + SaveDb save = new SaveDb(_ctx, _app, Db, OnFinishToRun); save.SetStatusLogger(StatusLogger); save.Run(); } @@ -76,10 +79,10 @@ namespace keepass2android if ( Success ) { // Mark parent group dirty - _editGroup.Db.Dirty.Add(_editGroup.Group.ParentGroup); + _editGroup.App.DirtyGroups.Add(_editGroup.Group.ParentGroup); } else { - _editGroup._app.LockDatabase(false); + _editGroup._app.Lock(false); } base.Run(); diff --git a/src/Kp2aBusinessLogic/database/edit/LoadDB.cs b/src/Kp2aBusinessLogic/database/edit/LoadDB.cs index 553d3e94..ea3c3b03 100644 --- a/src/Kp2aBusinessLogic/database/edit/LoadDB.cs +++ b/src/Kp2aBusinessLogic/database/edit/LoadDB.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Android.App; +using keepass2android.database.edit; using KeePassLib; using KeePassLib.Keys; using KeePassLib.Serialization; @@ -47,7 +48,8 @@ namespace keepass2android _rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile); } - + + protected bool success = false; public override void Run() { @@ -78,6 +80,10 @@ namespace keepass2android //ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess: _format = new KdbxDatabaseFormat(KdbpFile.GetFormatToUse(_ioc)); TryLoad(databaseStream); + + + + success = true; } catch (Exception e) { @@ -125,7 +131,7 @@ namespace keepass2android /// public Exception Exception { get; set; } - private void TryLoad(MemoryStream databaseStream) + Database TryLoad(MemoryStream databaseStream) { //create a copy of the stream so we can try again if we get an exception which indicates we should change parameters //This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors. @@ -138,19 +144,89 @@ namespace keepass2android //now let's go: try { - _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format); + Database newDb = _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format); Kp2aLog.Log("LoadDB OK"); //make sure the stored access time for the actual file is more recent than that of its backup Thread.Sleep(10); SaveFileData(_ioc, _keyfileOrProvider); - Finish(true, _format.SuccessMessage); + + bool hasLegacyTemplateIds = false; + //make sure we never have entries with same Uuids + foreach (var entryKey in newDb.Entries.Keys) + { + foreach (Database otherDb in _app.OpenDatabases) + { + if (otherDb == newDb) + continue; + if (otherDb.Entries.ContainsKey(entryKey)) + { + if (AddTemplateEntries.IsTemplateId(entryKey)) + { + hasLegacyTemplateIds = true; + } + else + { + _app.CloseDatabase(newDb); + throw new Exception("Database contains entry id " + entryKey.ToHexString() + "(" + + newDb.Entries[entryKey].Strings.ReadSafe(PwDefs.TitleField) + + ") which is already contained in " + + _app.GetFileStorage(otherDb.Ioc).GetDisplayName(otherDb.Ioc) + + "! Please close the other database before opening this one."); + } + } + } + } + + + foreach (var groupKey in newDb.Groups.Keys) + { + foreach (Database otherDb in _app.OpenDatabases) + { + if (otherDb == newDb) + continue; + if (otherDb.Groups.ContainsKey(groupKey)) + { + throw new Exception("Database contains group id " + groupKey.ToHexString() + "(" + + newDb.Groups[groupKey].Name + ") which is already contained in " + + _app.GetFileStorage(otherDb.Ioc).GetDisplayName(otherDb.Ioc) + + "! Please close the other database before opening this one."); + } + } + } + + if (hasLegacyTemplateIds) + { + _app.AskYesNoCancel(UiStringKey.ChangleLegacyTemplateIds_Title, UiStringKey.ChangleLegacyTemplateIds_Message,UiStringKey.Ok, UiStringKey.cancel, + /*yes*/ + (sender, args) => + { + ChangeTemplateIds cti = new ChangeTemplateIds(ActiveActivity, _app, newDb, new ActionOnFinish(ActiveActivity, (b, message, activity) => Finish(b, message))); + cti.Run(); + }, + /*no*/ + (sender, args) => + { + _app.CloseDatabase(newDb); + Finish(false); + }, + null, + ActiveActivity + + ); + + + + } + else + Finish(true, _format.SuccessMessage); + return newDb; } catch (OldFormatException) { _format = new KdbDatabaseFormat(_app); - TryLoad(databaseStream); + return TryLoad(databaseStream); } catch (InvalidCompositeKeyException) { @@ -162,7 +238,7 @@ namespace keepass2android //retry without password: _compositeKey.RemoveUserKey(passwordKey); //retry: - TryLoad(databaseStream); + return TryLoad(databaseStream); } else throw; } diff --git a/src/Kp2aBusinessLogic/database/edit/MoveElements.cs b/src/Kp2aBusinessLogic/database/edit/MoveElements.cs index c5aacf5c..5be1ce26 100644 --- a/src/Kp2aBusinessLogic/database/edit/MoveElements.cs +++ b/src/Kp2aBusinessLogic/database/edit/MoveElements.cs @@ -53,8 +53,8 @@ namespace keepass2android.database.edit foreach (var elementToMove in _elementsToMove) { - _app.GetDb().Dirty.Add(elementToMove.ParentGroup); - + _app.DirtyGroups.Add(elementToMove.ParentGroup); + //TODO is this safe when transferring between databases? PwGroup pgParent = elementToMove.ParentGroup; if (pgParent != _targetGroup) { @@ -87,12 +87,15 @@ namespace keepass2android.database.edit { if (!success) { // Let's not bother recovering from a failure. - _app.LockDatabase(false); + _app.Lock(false); } }, OnFinishToRun); + //Unchecked + //TODO save the right database + // Save - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, false); + SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, false); save.SetStatusLogger(StatusLogger); save.Run(); } diff --git a/src/Kp2aBusinessLogic/database/edit/SaveDB.cs b/src/Kp2aBusinessLogic/database/edit/SaveDB.cs index ae2035a3..cdd345de 100644 --- a/src/Kp2aBusinessLogic/database/edit/SaveDB.cs +++ b/src/Kp2aBusinessLogic/database/edit/SaveDB.cs @@ -34,7 +34,8 @@ namespace keepass2android public class SaveDb : RunnableOnFinish { private readonly IKp2aApp _app; - private readonly bool _dontSave; + private readonly Database _db; + private readonly bool _dontSave; /// /// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync @@ -43,9 +44,10 @@ namespace keepass2android private readonly Context _ctx; private Thread _workerThread; - public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave) + public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish, bool dontSave) : base(ctx, finish) { + _db = db; _ctx = ctx; _app = app; _dontSave = dontSave; @@ -59,21 +61,23 @@ namespace keepass2android /// /// /// Stream for reading the data from the (changed) original location - public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave, Stream streamForOrigFile) + public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, Database db, bool dontSave, Stream streamForOrigFile) : base(ctx, finish) { + _db = db; _ctx = ctx; _app = app; _dontSave = dontSave; _streamForOrigFile = streamForOrigFile; } - public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish) + public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish) : base(ctx, finish) { _ctx = ctx; _app = app; - _dontSave = false; + _db = db; + _dontSave = false; } @@ -84,7 +88,7 @@ namespace keepass2android { try { - if (_app.GetDb().CanWrite == false) + if (_db.CanWrite == false) { //this should only happen if there is a problem in the UI so that the user sees an edit interface. Finish(false,"Cannot save changes. File is read-only!"); @@ -92,13 +96,13 @@ namespace keepass2android } StatusLogger.UpdateMessage(UiStringKey.saving_database); - IOConnectionInfo ioc = _app.GetDb().Ioc; + IOConnectionInfo ioc = _db.Ioc; IFileStorage fileStorage = _app.GetFileStorage(ioc); if (_streamForOrigFile == null) { if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave)) - || (_app.GetDb().KpDatabase.HashOfFileOnDisk == null)) //first time saving + || (_db.KpDatabase.HashOfFileOnDisk == null)) //first time saving { PerformSaveWithoutCheck(fileStorage, ioc); Finish(true); @@ -109,8 +113,8 @@ namespace keepass2android if ( (_streamForOrigFile != null) - || fileStorage.CheckForFileChangeFast(ioc, _app.GetDb().LastFileVersion) //first try to use the fast change detection - || (FileHashChanged(ioc, _app.GetDb().KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare: + || fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion) //first try to use the fast change detection + || (FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare: ) { @@ -217,13 +221,13 @@ namespace keepass2android StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase)); PwDatabase pwImp = new PwDatabase(); - PwDatabase pwDatabase = _app.GetDb().KpDatabase; + PwDatabase pwDatabase = _db.KpDatabase; pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey); pwImp.MemoryProtection = pwDatabase.MemoryProtection.CloneDeep(); pwImp.MasterKey = pwDatabase.MasterKey; var stream = GetStreamForBaseFile(fileStorage, ioc); - _app.GetDb().DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null); + _db.DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null); pwDatabase.MergeIn(pwImp, PwMergeMethod.Synchronize, null); @@ -249,8 +253,8 @@ namespace keepass2android private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc) { StatusLogger.UpdateSubMessage(""); - _app.GetDb().SaveData(); - _app.GetDb().LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc); + _db.SaveData(); + _db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc); } public byte[] HashOriginalFile(IOConnectionInfo iocFile) diff --git a/src/Kp2aBusinessLogic/database/edit/SetPassword.cs b/src/Kp2aBusinessLogic/database/edit/SetPassword.cs index a3869ef8..36fcad55 100644 --- a/src/Kp2aBusinessLogic/database/edit/SetPassword.cs +++ b/src/Kp2aBusinessLogic/database/edit/SetPassword.cs @@ -52,7 +52,7 @@ namespace keepass2android public override void Run () { StatusLogger.UpdateMessage(UiStringKey.SettingPassword); - PwDatabase pm = _app.GetDb().KpDatabase; + PwDatabase pm = _app.CurrentDb.KpDatabase; CompositeKey newKey = new CompositeKey (); if (String.IsNullOrEmpty (_password) == false) { newKey.AddUserKey (new KcpPassword (_password)); @@ -74,7 +74,7 @@ namespace keepass2android // Save Database _onFinishToRun = new AfterSave(ActiveActivity, previousKey, previousMasterKeyChanged, pm, OnFinishToRun); - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave); + SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, _dontSave); save.SetStatusLogger(StatusLogger); save.Run(); } diff --git a/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs b/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs index 4bed5cbf..bec284c6 100644 --- a/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs +++ b/src/Kp2aBusinessLogic/database/edit/UpdateEntry.cs @@ -36,7 +36,7 @@ namespace keepass2android public override void Run() { // Commit to disk - SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun); + SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun); save.SetStatusLogger(StatusLogger); save.Run(); } @@ -59,7 +59,7 @@ namespace keepass2android if ( parent != null ) { // Mark parent group dirty - _app.GetDb().Dirty.Add(parent); + _app.DirtyGroups.Add(parent); } diff --git a/src/keepass2android/AppKilledInfo.cs b/src/keepass2android/AppKilledInfo.cs index 2b411eca..48a846dc 100644 --- a/src/keepass2android/AppKilledInfo.cs +++ b/src/keepass2android/AppKilledInfo.cs @@ -26,7 +26,7 @@ namespace keepass2android b.SetMessage(Resource.String.killed_by_os); b.SetPositiveButton(Android.Resource.String.Ok, delegate { - Intent i = new Intent(this, typeof(FileSelectActivity)); + Intent i = new Intent(this, typeof(SelectCurrentDbActivity)); i.AddFlags(ActivityFlags.ClearTask | ActivityFlags.NewTask); StartActivity(i); diff --git a/src/keepass2android/EntryActivity.cs b/src/keepass2android/EntryActivity.cs index 04455c71..d236bcf6 100644 --- a/src/keepass2android/EntryActivity.cs +++ b/src/keepass2android/EntryActivity.cs @@ -91,7 +91,7 @@ namespace keepass2android public class EntryActivity : LockCloseActivity { public const String KeyEntry = "entry"; - public const String KeyRefreshPos = "refresh_pos"; + public const String KeyRefreshPos = "refresh_pos"; public const String KeyCloseAfterCreate = "close_after_create"; public const String KeyGroupFullPath = "groupfullpath_key"; @@ -107,6 +107,11 @@ namespace keepass2android i.PutExtra(KeyEntry, pw.Uuid.ToHexString()); i.PutExtra(KeyRefreshPos, pos); + if (!App.Kp2a.CurrentDb.Entries.ContainsKey(pw.Uuid)) + { + App.Kp2a.CurrentDb = App.Kp2a.FindDatabaseForEntryId(pw.Uuid); + } + if (flags != null) i.SetFlags((ActivityFlags) flags); @@ -167,7 +172,7 @@ namespace keepass2android protected void SetupEditButtons() { View edit = FindViewById(Resource.Id.entry_edit); - if (App.Kp2a.GetDb().CanWrite) + if (App.Kp2a.CurrentDb.CanWrite) { edit.Visibility = ViewStates.Visible; edit.Click += (sender, e) => @@ -265,9 +270,9 @@ namespace keepass2android //update the Entry output in the App database and notify the CopyToClipboard service - if (App.Kp2a.GetDb()?.LastOpenedEntry != null) + if (App.Kp2a.LastOpenedEntry != null) { - App.Kp2a.GetDb().LastOpenedEntry.OutputStrings.Set(key, new ProtectedString(isProtected, value)); + App.Kp2a.LastOpenedEntry.OutputStrings.Set(key, new ProtectedString(isProtected, value)); Intent updateKeyboardIntent = new Intent(this, typeof(CopyToClipboardService)); updateKeyboardIntent.SetAction(Intents.UpdateKeyboard); updateKeyboardIntent.PutExtra(KeyEntry, Entry.Uuid.ToHexString()); @@ -323,7 +328,7 @@ namespace keepass2android i.SetPackage(pluginPackage); i.PutExtra(Strings.ExtraActionData, bundleExtra); i.PutExtra(Strings.ExtraSender, PackageName); - PluginHost.AddEntryToIntent(i, App.Kp2a.GetDb().LastOpenedEntry); + PluginHost.AddEntryToIntent(i, App.Kp2a.LastOpenedEntry); var menuOption = new PluginMenuOption() { @@ -389,7 +394,7 @@ namespace keepass2android SetEntryView(); - Database db = App.Kp2a.GetDb(); + Database db = App.Kp2a.CurrentDb; // Likely the app has been killed exit the activity if (db == null || (App.Kp2a.QuickLocked)) { @@ -428,7 +433,7 @@ namespace keepass2android SetupEditButtons(); - App.Kp2a.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.GetDb().KpDatabase); + App.Kp2a.LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.CurrentDb); _pluginActionReceiver = new PluginActionReceiver(this); RegisterReceiver(_pluginActionReceiver, new IntentFilter(Strings.ActionAddEntryAction)); @@ -508,8 +513,8 @@ namespace keepass2android ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings); bool hasExtras = false; IEditMode editMode = new DefaultEdit(); - if (KpEntryTemplatedEdit.IsTemplated(App.Kp2a.GetDb(), this.Entry)) - editMode = new KpEntryTemplatedEdit(App.Kp2a.GetDb(), this.Entry); + if (KpEntryTemplatedEdit.IsTemplated(App.Kp2a.CurrentDb, this.Entry)) + editMode = new KpEntryTemplatedEdit(App.Kp2a.CurrentDb, this.Entry); foreach (var key in editMode.SortExtraFieldKeys(Entry.Strings.GetKeys().Where(key=> !PwDefs.IsStandardField(key)))) { if (editMode.IsVisible(key)) @@ -936,7 +941,7 @@ namespace keepass2android private void PopulateStandardText(List viewIds, int containerViewId, String key) { String value = Entry.Strings.ReadSafe(key); - value = SprEngine.Compile(value, new SprContext(Entry, App.Kp2a.GetDb().KpDatabase, SprCompileFlags.All)); + value = SprEngine.Compile(value, new SprContext(Entry, App.Kp2a.CurrentDb.KpDatabase, SprCompileFlags.All)); PopulateText(viewIds, containerViewId, value); _stringViews.Add(key, new StandardStringView(viewIds, containerViewId, this)); } @@ -1012,7 +1017,7 @@ namespace keepass2android { ((IOfflineSwitchable)fileStorage).IsOffline = false; } - using (var writeTransaction = fileStorage.OpenWriteTransaction(_targetIoc, _app.GetDb().KpDatabase.UseFileTransactions)) + using (var writeTransaction = fileStorage.OpenWriteTransaction(_targetIoc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions))) { Stream sOut = writeTransaction.OpenFile(); @@ -1173,7 +1178,7 @@ namespace keepass2android return true; case Resource.Id.menu_lock: - App.Kp2a.LockDatabase(); + App.Kp2a.Lock(); return true; case Android.Resource.Id.Home: //Currently the action bar only displays the home button when we come from a previous activity. @@ -1264,7 +1269,7 @@ namespace keepass2android public void AddEntryToIntent(Intent intent) { - PluginHost.AddEntryToIntent(intent, App.Kp2a.GetDb().LastOpenedEntry); + PluginHost.AddEntryToIntent(intent, App.Kp2a.LastOpenedEntry); } public void CloseAfterTaskComplete() diff --git a/src/keepass2android/EntryEditActivity.cs b/src/keepass2android/EntryEditActivity.cs index 7f60bcd4..ff0121ec 100644 --- a/src/keepass2android/EntryEditActivity.cs +++ b/src/keepass2android/EntryEditActivity.cs @@ -120,7 +120,7 @@ namespace keepass2android } else { - Database db = App.Kp2a.GetDb(); + Database db = App.Kp2a.CurrentDb; App.Kp2a.EntryEditActivityState = new EntryEditActivityState(); ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this); @@ -199,7 +199,7 @@ namespace keepass2android if (State.SelectedIcon) { - App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iconButton, this, App.Kp2a.GetDb().KpDatabase, (PwIcon)State.SelectedIconId, State.SelectedCustomIconId, false); + App.Kp2a.CurrentDb.DrawableFactory.AssignDrawableTo(iconButton, this, App.Kp2a.CurrentDb.KpDatabase, (PwIcon)State.SelectedIconId, State.SelectedCustomIconId, false); } iconButton.Click += (sender, evt) => { UpdateEntryFromUi(State.Entry); @@ -371,7 +371,7 @@ namespace keepass2android private void SetAddExtraStringEnabled() { - if (!App.Kp2a.GetDb().DatabaseFormat.CanHaveCustomFields) + if (!App.Kp2a.CurrentDb.DatabaseFormat.CanHaveCustomFields) ((Button)FindViewById(Resource.Id.add_advanced)).Visibility = ViewStates.Gone; } @@ -393,7 +393,7 @@ namespace keepass2android void SaveEntry() { - Database db = App.Kp2a.GetDb(); + Database db = App.Kp2a.CurrentDb; EntryEditActivity act = this; if (!ValidateBeforeSaving()) @@ -498,7 +498,7 @@ namespace keepass2android void UpdateEntryFromUi(PwEntry entry) { - Database db = App.Kp2a.GetDb(); + Database db = App.Kp2a.CurrentDb; EntryEditActivity act = this; entry.Strings.Set(PwDefs.TitleField, new ProtectedString(db.KpDatabase.MemoryProtection.ProtectTitle, @@ -753,7 +753,7 @@ namespace keepass2android String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password"); byte[] password = StrUtil.Utf8.GetBytes(generatedPassword); - State.Entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(App.Kp2a.GetDb().KpDatabase.MemoryProtection.ProtectPassword, + State.Entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(App.Kp2a.CurrentDb.KpDatabase.MemoryProtection.ProtectPassword, password)); MemUtil.ZeroByteArray(password); @@ -792,7 +792,7 @@ namespace keepass2android { String key = pair.Key; String label = key; - if ((String.IsNullOrEmpty(label) || (!App.Kp2a.GetDb().DatabaseFormat.SupportsAttachmentKeys))) + if ((String.IsNullOrEmpty(label) || (!App.Kp2a.CurrentDb.DatabaseFormat.SupportsAttachmentKeys))) { label = ""; } @@ -820,7 +820,7 @@ namespace keepass2android addBinaryButton.Enabled = true; - if (!App.Kp2a.GetDb().DatabaseFormat.CanHaveMultipleAttachments) + if (!App.Kp2a.CurrentDb.DatabaseFormat.CanHaveMultipleAttachments) addBinaryButton.Enabled = !State.Entry.Binaries.Any(); addBinaryButton.Click += (sender, e) => { @@ -921,7 +921,7 @@ namespace keepass2android { if (_additionalKeys == null) { - _additionalKeys = App.Kp2a.GetDb().Entries + _additionalKeys = App.Kp2a.CurrentDb.Entries .Select(kvp => kvp.Value) .SelectMany(x => x.Strings.GetKeys().Where(k => !PwDefs.IsStandardField(k))) .Where(k => (k != null) && !k.StartsWith("_etm_") ) @@ -999,7 +999,7 @@ namespace keepass2android { _editModeHiddenViews = new List(); ImageButton currIconButton = (ImageButton) FindViewById(Resource.Id.icon_button); - App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(currIconButton, this, App.Kp2a.GetDb().KpDatabase, State.Entry.IconId, State.Entry.CustomIconUuid, false); + App.Kp2a.CurrentDb.DrawableFactory.AssignDrawableTo(currIconButton, this, App.Kp2a.CurrentDb.KpDatabase, State.Entry.IconId, State.Entry.CustomIconUuid, false); PopulateText(Resource.Id.entry_title, State.Entry.Strings.ReadSafe (PwDefs.TitleField)); PopulateText(Resource.Id.entry_user_name, State.Entry.Strings.ReadSafe (PwDefs.UserNameField)); @@ -1028,7 +1028,7 @@ namespace keepass2android PopulateBinaries(); - if (App.Kp2a.GetDb().DatabaseFormat.SupportsOverrideUrl) + if (App.Kp2a.CurrentDb.DatabaseFormat.SupportsOverrideUrl) { PopulateText(Resource.Id.entry_override_url, State.Entry.OverrideUrl); } @@ -1037,7 +1037,7 @@ namespace keepass2android FindViewById(Resource.Id.entry_override_url_container).Visibility = ViewStates.Gone; } - if (App.Kp2a.GetDb().DatabaseFormat.SupportsTags) + if (App.Kp2a.CurrentDb.DatabaseFormat.SupportsTags) { PopulateText(Resource.Id.entry_tags, StrUtil.TagsToString(State.Entry.Tags, true)); } diff --git a/src/keepass2android/ExportDatabaseActivity.cs b/src/keepass2android/ExportDatabaseActivity.cs index cb46349d..dd043f53 100644 --- a/src/keepass2android/ExportDatabaseActivity.cs +++ b/src/keepass2android/ExportDatabaseActivity.cs @@ -107,7 +107,7 @@ namespace keepass2android public override void Run() { StatusLogger.UpdateMessage(UiStringKey.exporting_database); - var pd = _app.GetDb().KpDatabase; + var pd = _app.CurrentDb.KpDatabase; PwExportInfo pwInfo = new PwExportInfo(pd.RootGroup, pd, true); try @@ -117,7 +117,7 @@ namespace keepass2android { ((IOfflineSwitchable) fileStorage).IsOffline = false; } - using (var writeTransaction = fileStorage.OpenWriteTransaction(_targetIoc, _app.GetDb().KpDatabase.UseFileTransactions)) + using (var writeTransaction = fileStorage.OpenWriteTransaction(_targetIoc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions))) { Stream sOut = writeTransaction.OpenFile(); _fileFormat.Export(pwInfo, sOut, new NullStatusLogger()); diff --git a/src/keepass2android/FingerprintSetupActivity.cs b/src/keepass2android/FingerprintSetupActivity.cs index 48ebeddf..fb3ffa9b 100644 --- a/src/keepass2android/FingerprintSetupActivity.cs +++ b/src/keepass2android/FingerprintSetupActivity.cs @@ -64,7 +64,7 @@ namespace keepass2android SetContentView(Resource.Layout.fingerprint_setup); Enum.TryParse( - PreferenceManager.GetDefaultSharedPreferences(this).GetString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, ""), + PreferenceManager.GetDefaultSharedPreferences(this).GetString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, ""), out _unlockMode); _fpIcon = FindViewById(Resource.Id.fingerprint_icon); @@ -174,7 +174,7 @@ namespace keepass2android string CurrentPreferenceKey { - get { return App.Kp2a.GetDb().CurrentFingerprintPrefKey; } + get { return App.Kp2a.CurrentDb.CurrentFingerprintPrefKey; } } private void StoreUnlockMode() @@ -188,13 +188,13 @@ namespace keepass2android { if (_unlockMode == FingerprintUnlockMode.FullUnlock) { - var userKey = App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(); + var userKey = App.Kp2a.CurrentDb.KpDatabase.MasterKey.GetUserKey(); _enc.StoreEncrypted(userKey != null ? userKey.Password.ReadString() : "", CurrentPreferenceKey, edit); } else _enc.StoreEncrypted("QuickUnlock" /*some dummy data*/, CurrentPreferenceKey, edit); } - edit.PutString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, _unlockMode.ToString()); + edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, _unlockMode.ToString()); edit.Commit(); } @@ -261,7 +261,7 @@ namespace keepass2android UpdateKeyboardCheckboxVisibility(); ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit(); - edit.PutString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, _unlockMode.ToString()); + edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, _unlockMode.ToString()); edit.Commit(); return; } diff --git a/src/keepass2android/GroupActivity.cs b/src/keepass2android/GroupActivity.cs index fd841fc0..b8841e20 100644 --- a/src/keepass2android/GroupActivity.cs +++ b/src/keepass2android/GroupActivity.cs @@ -69,15 +69,6 @@ namespace keepass2android public static void Launch (Activity act, PwGroup g, AppTask appTask) { - // Need to use PwDatabase since group may be null - PwDatabase db = App.Kp2a.GetDb().KpDatabase; - - if (db == null) { - // Reached if db is null - Log.Debug (Tag, "Tried to launch with null db"); - return; - } - Intent i = new Intent(act, typeof(GroupActivity)); if ( g != null ) { @@ -105,10 +96,15 @@ namespace keepass2android protected override bool AddEntryEnabled { - get { return App.Kp2a.GetDb().CanWrite && ((this.Group.ParentGroup != null) || App.Kp2a.GetDb().DatabaseFormat.CanHaveEntriesInRootGroup); } + get { return App.Kp2a.CurrentDb.CanWrite && ((this.Group.ParentGroup != null) || App.Kp2a.CurrentDb.DatabaseFormat.CanHaveEntriesInRootGroup); } } - private class TemplateListAdapter : ArrayAdapter + protected override bool AddGroupEnabled + { + get { return App.Kp2a.CurrentDb.CanWrite; } + } + + private class TemplateListAdapter : ArrayAdapter { public TemplateListAdapter(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer) { @@ -149,8 +145,7 @@ namespace keepass2android int size = (int)(Util.convertDpToPixel(Util.convertDpToPixel(20, Context), Context)); var bmp = Bitmap.CreateScaledBitmap( - Util.DrawableToBitmap(App.Kp2a.GetDb() - .DrawableFactory.GetIconDrawable(Context, App.Kp2a.GetDb().KpDatabase, templateEntry.IconId, PwUuid.Zero, false)), + Util.DrawableToBitmap(App.Kp2a.CurrentDb .DrawableFactory.GetIconDrawable(Context, App.Kp2a.CurrentDb.KpDatabase, templateEntry.IconId, PwUuid.Zero, false)), size, size, true); @@ -193,7 +188,7 @@ namespace keepass2android PwUuid id = RetrieveGroupId (intent); - Database db = App.Kp2a.GetDb(); + Database db = App.Kp2a.CurrentDb; if (id == null) { Group = db.Root; } else { @@ -221,8 +216,8 @@ namespace keepass2android View addEntry = FindViewById (Resource.Id.fabAddNewEntry); addEntry.Click += (sender, e) => { - if (App.Kp2a.GetDb().DatabaseFormat.SupportsTemplates && - !AddTemplateEntries.ContainsAllTemplates(App.Kp2a) && + if (App.Kp2a.CurrentDb.DatabaseFormat.SupportsTemplates && + !AddTemplateEntries.ContainsAllTemplates(App.Kp2a.CurrentDb) && PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(Askaddtemplates, true)) { App.Kp2a.AskYesNoCancel(UiStringKey.AskAddTemplatesTitle, UiStringKey.AskAddTemplatesMessage,UiStringKey.yes, UiStringKey.no, @@ -256,14 +251,14 @@ namespace keepass2android FragmentManager.FindFragmentById(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group); Log.Warn(Tag, "Finished creating group"); - var ioc = App.Kp2a.GetDb().Ioc; + var ioc = App.Kp2a.CurrentDb.Ioc; OptionalOut reason = new OptionalOut(); if (App.Kp2a.GetFileStorage(ioc).IsReadOnly(ioc, reason)) { bool hasShownReadOnlyReason = PreferenceManager.GetDefaultSharedPreferences(this) - .GetBoolean(App.Kp2a.GetDb().IocAsHexString() + "_readonlyreason", false); + .GetBoolean(App.Kp2a.CurrentDb.IocAsHexString() + "_readonlyreason", false); if (!hasShownReadOnlyReason) { var b = new AlertDialog.Builder(this); @@ -273,7 +268,7 @@ namespace keepass2android (sender, args) => { PreferenceManager.GetDefaultSharedPreferences(this). - Edit().PutBoolean(App.Kp2a.GetDb().IocAsHexString() + "_readonlyreason", true).Commit(); + Edit().PutBoolean(App.Kp2a.CurrentDb.IocAsHexString() + "_readonlyreason", true).Commit(); }); b.Show(); } @@ -287,11 +282,11 @@ namespace keepass2android defaultTemplate.IconId = PwIcon.Key; defaultTemplate.Strings.Set(PwDefs.TitleField, new ProtectedString(false, GetString(Resource.String.DefaultTemplate))); List templates = new List() {defaultTemplate}; - if ((!PwUuid.Zero.Equals(App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup)) - && (App.Kp2a.GetDb().KpDatabase.RootGroup.FindGroup(App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup, true) != null)) + if ((!PwUuid.Zero.Equals(App.Kp2a.CurrentDb.KpDatabase.EntryTemplatesGroup)) + && (App.Kp2a.CurrentDb.KpDatabase.RootGroup.FindGroup(App.Kp2a.CurrentDb.KpDatabase.EntryTemplatesGroup, true) != null)) { templates.AddRange( - App.Kp2a.GetDb().Groups[App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup].Entries.OrderBy( + App.Kp2a.CurrentDb.Groups[App.Kp2a.CurrentDb.KpDatabase.EntryTemplatesGroup].Entries.OrderBy( entr => entr.Strings.ReadSafe(PwDefs.TitleField))); } if (templates.Count > 1) @@ -315,8 +310,13 @@ namespace keepass2android ClickView cv = (ClickView) acmi.TargetView; cv.OnCreateMenu(menu, menuInfo); } - - public override void OnBackPressed() + + public override bool EntriesBelongToCurrentDatabaseOnly + { + get { return true; } + } + + public override void OnBackPressed() { base.OnBackPressed(); //if ((Group != null) && (Group.ParentGroup != null)) diff --git a/src/keepass2android/GroupBaseActivity.cs b/src/keepass2android/GroupBaseActivity.cs index ab6675f3..44ba11af 100644 --- a/src/keepass2android/GroupBaseActivity.cs +++ b/src/keepass2android/GroupBaseActivity.cs @@ -95,11 +95,11 @@ namespace keepass2android protected virtual bool AddGroupEnabled { - get { return App.Kp2a.GetDb().CanWrite; } + get { return false; } } protected virtual bool AddEntryEnabled { - get { return App.Kp2a.GetDb().CanWrite; } + get { return false; } } public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry) @@ -186,7 +186,7 @@ namespace keepass2android else { PwUuid groupUuid = new PwUuid(MemUtil.HexStringToByteArray(strGroupUuid)); - task = new EditGroup(this, App.Kp2a, groupName, (PwIcon)groupIconId, groupCustomIconId, App.Kp2a.GetDb().Groups[groupUuid], + task = new EditGroup(this, App.Kp2a, groupName, (PwIcon)groupIconId, groupCustomIconId, App.Kp2a.FindGroup(groupUuid), new RefreshTask(handler, this)); } ProgressTask pt = new ProgressTask(App.Kp2a, act, task); @@ -258,7 +258,7 @@ namespace keepass2android { FingerprintUnlockMode um; - Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(App.Kp2a.GetDb().Ioc), ""), out um); + Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(App.Kp2a.CurrentDb.Ioc), ""), out um); bool isFingerprintEnabled = (um == FingerprintUnlockMode.FullUnlock); string masterKeyKey = "MasterKey" + isFingerprintEnabled; @@ -267,11 +267,11 @@ namespace keepass2android List applicableInfoTextKeys = new List { masterKeyKey }; - if (App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc).UserShouldBackup) + if (App.Kp2a.GetFileStorage(App.Kp2a.CurrentDb.Ioc).UserShouldBackup) { applicableInfoTextKeys.Add(backupKey); } - if (App.Kp2a.GetDb().Entries.Count > 15) + if (App.Kp2a.CurrentDb.Entries.Count > 15) { applicableInfoTextKeys.Add(emergencyKey); } @@ -378,10 +378,9 @@ namespace keepass2android public void RefreshIfDirty() { - Database db = App.Kp2a.GetDb(); - if (db.Dirty.Contains(Group)) + if (App.Kp2a.DirtyGroups.Contains(Group)) { - db.Dirty.Remove(Group); + App.Kp2a.DirtyGroups.Remove(Group); ListAdapter.NotifyDataSetChanged(); } @@ -407,7 +406,7 @@ namespace keepass2android AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); // Likely the app has been killed exit the activity - if (App.Kp2a.GetDb() == null) + if (App.Kp2a.CurrentDb== null) { Finish(); return; @@ -458,7 +457,7 @@ namespace keepass2android { FindViewById(Resource.Id.hide_fingerprint_info).Click += (sender, args) => { - _prefs.Edit().PutBoolean(fingerprintinfohidden_prefskey + App.Kp2a.GetDb().CurrentFingerprintPrefKey, true).Commit(); + _prefs.Edit().PutBoolean(fingerprintinfohidden_prefskey + App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, true).Commit(); UpdateFingerprintInfo(); }; } @@ -627,7 +626,7 @@ namespace keepass2android { bool canShowFingerprintInfo = false; - bool disabledForDatabase = _prefs.GetBoolean(fingerprintinfohidden_prefskey + App.Kp2a.GetDb().CurrentFingerprintPrefKey, false); + bool disabledForDatabase = _prefs.GetBoolean(fingerprintinfohidden_prefskey + App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, false); bool disabledForAll = _prefs.GetBoolean(fingerprintinfohidden_prefskey, false); if (!disabledForAll && !disabledForDatabase) { @@ -636,7 +635,7 @@ namespace keepass2android if (fpModule.FingerprintManager != null && fpModule.FingerprintManager.IsHardwareDetected) { FingerprintUnlockMode um; - Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(App.Kp2a.GetDb().Ioc), ""), out um); + Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(App.Kp2a.CurrentDb.Ioc), ""), out um); canShowFingerprintInfo = um == FingerprintUnlockMode.Disabled; } } @@ -667,8 +666,7 @@ namespace keepass2android private void InsertElements() { MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask; - IEnumerable elementsToMove = - moveElementsTask.Uuids.Select(uuid => App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(uuid, true, null)); + IEnumerable elementsToMove = moveElementsTask.Uuids.Select(uuid => App.Kp2a.FindStructureItem(uuid)); @@ -690,7 +688,7 @@ namespace keepass2android { String name = Group.Name; String titleText; - bool clickable = (Group != null) && (Group.IsVirtual == false) && (Group.ParentGroup != null); + bool clickable = (Group != null) && (Group.IsVirtual == false) && ((Group.ParentGroup != null) || App.Kp2a.OpenDatabases.Count() > 1); if (!String.IsNullOrEmpty(name)) { titleText = name; @@ -715,9 +713,7 @@ namespace keepass2android { if (Group != null) { - Drawable drawable = App.Kp2a.GetDb().DrawableFactory.GetIconDrawable(this, App.Kp2a.GetDb().KpDatabase, Group.IconId, Group.CustomIconUuid, true); SupportActionBar.SetDisplayShowHomeEnabled(true); - //SupportActionBar.SetIcon(drawable); } } @@ -740,7 +736,8 @@ namespace keepass2android var cursor = _suggestionsAdapter.Cursor; cursor.MoveToPosition(position); string entryIdAsHexString = cursor.GetString(cursor.GetColumnIndexOrThrow(SearchManager.SuggestColumnIntentDataId)); - EntryActivity.Launch(_activity, App.Kp2a.GetDb().Entries[new PwUuid(MemUtil.HexStringToByteArray(entryIdAsHexString))], -1, _activity.AppTask); + var entryId = new PwUuid(MemUtil.HexStringToByteArray(entryIdAsHexString)); + EntryActivity.Launch(_activity, App.Kp2a.FindDatabaseForEntryId(entryId).Entries[entryId], -1, _activity.AppTask); return true; } @@ -830,35 +827,34 @@ namespace keepass2android { if (_syncItem != null) { - if (App.Kp2a.GetDb().Ioc.IsLocalFile()) + if (((App.Kp2a.OpenDatabases.Count() == 1) || (EntriesBelongToCurrentDatabaseOnly)) + && App.Kp2a.CurrentDb.Ioc.IsLocalFile()) _syncItem.SetVisible(false); else _syncItem.SetVisible(!App.Kp2a.OfflineMode); } - if (App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc) is IOfflineSwitchable) + if (((App.Kp2a.OpenDatabases.Count() == 1) || (EntriesBelongToCurrentDatabaseOnly)) + && (App.Kp2a.GetFileStorage(App.Kp2a.CurrentDb.Ioc) is IOfflineSwitchable)) { - if (_offlineItem != null) - _offlineItem.SetVisible(App.Kp2a.OfflineMode == false); - if (_onlineItem != null) - _onlineItem.SetVisible(App.Kp2a.OfflineMode); + _offlineItem?.SetVisible(App.Kp2a.OfflineMode == false); + _onlineItem?.SetVisible(App.Kp2a.OfflineMode); } else { - if (_offlineItem != null) - _offlineItem.SetVisible(false); - if (_onlineItem != null) - _onlineItem.SetVisible(false); - + _offlineItem?.SetVisible(false); + _onlineItem?.SetVisible(false); } } catch (Exception e) { - Kp2aLog.LogUnexpectedError(new Exception("Cannot UpdateOfflineModeMenu " + (App.Kp2a == null) + " " + ((App.Kp2a == null) || (App.Kp2a.GetDb() == null)) + " " + (((App.Kp2a == null) || (App.Kp2a.GetDb() == null) || (App.Kp2a.GetDb().Ioc == null)) + " " + (_syncItem != null) + " " + (_offlineItem != null) + " " + (_onlineItem != null)))); + Kp2aLog.LogUnexpectedError(new Exception("Cannot UpdateOfflineModeMenu " + (App.Kp2a == null) + " " + ((App.Kp2a == null) || (App.Kp2a.CurrentDb== null)) + " " + (((App.Kp2a == null) || (App.Kp2a.CurrentDb== null) || (App.Kp2a.CurrentDb.Ioc == null)) + " " + (_syncItem != null) + " " + (_offlineItem != null) + " " + (_onlineItem != null)))); } } + public abstract bool EntriesBelongToCurrentDatabaseOnly { get; } + public override bool OnPrepareOptionsMenu(IMenu menu) { @@ -880,7 +876,7 @@ namespace keepass2android case Resource.Id.menu_donate: return Util.GotoDonateUrl(this); case Resource.Id.menu_lock: - App.Kp2a.LockDatabase(); + App.Kp2a.Lock(); return true; case Resource.Id.menu_search: @@ -906,8 +902,10 @@ namespace keepass2android UpdateOfflineModeMenu(); Synchronize(); return true; - - + case Resource.Id.menu_open_other_db: + AppTask.SetActivityResult(this, KeePass.ExitLoadAnotherDb); + Finish(); + return true; case Resource.Id.menu_sort: ChangeSort(); return true; @@ -960,7 +958,7 @@ namespace keepass2android private void Synchronize() { - var filestorage = App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc); + var filestorage = App.Kp2a.GetFileStorage(App.Kp2a.CurrentDb.Ioc); RunnableOnFinish task; OnFinish onFinish = new ActionOnFinish(this, (success, message, activity) => { @@ -971,9 +969,9 @@ namespace keepass2android BaseAdapter adapter = (BaseAdapter)((GroupBaseActivity)activity)?.ListAdapter; adapter?.NotifyDataSetChanged(); - if (App.Kp2a.GetDb().OtpAuxFileIoc != null) + if (App.Kp2a.CurrentDb.OtpAuxFileIoc != null) { - var task2 = new SyncOtpAuxFile(this, App.Kp2a.GetDb().OtpAuxFileIoc); + var task2 = new SyncOtpAuxFile(this, App.Kp2a.CurrentDb.OtpAuxFileIoc); new ProgressTask(App.Kp2a, activity, task2).Run(true); } }); @@ -1022,10 +1020,10 @@ namespace keepass2android ActivityCompat.InvalidateOptionsMenu(this); // Mark all groups as dirty now to refresh them on load - Database db = App.Kp2a.GetDb(); - db.MarkAllGroupsAsDirty(); + + App.Kp2a.MarkAllGroupsAsDirty(); // We'll manually refresh this group so we can remove it - db.Dirty.Remove(Group); + App.Kp2a.DirtyGroups.Remove(Group); // Tell the adapter to refresh it's list @@ -1082,7 +1080,7 @@ namespace keepass2android Toast.MakeText(ActiveActivity, "Unrecoverable error: " + Message, ToastLength.Long).Show(); }); - App.Kp2a.LockDatabase(false); + App.Kp2a.Lock(false); } } @@ -1133,9 +1131,9 @@ namespace keepass2android MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask; foreach (var uuid in moveElementsTask.Uuids) { - IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(uuid, true, null); + IStructureItem elementToMove = App.Kp2a.FindStructureItem(uuid); if (elementToMove.ParentGroup != Group) - App.Kp2a.GetDb().Dirty.Add(elementToMove.ParentGroup); + App.Kp2a.DirtyGroups.Add(elementToMove.ParentGroup); } } catch (Exception e) @@ -1167,7 +1165,7 @@ namespace keepass2android base.OnActivityCreated(savedInstanceState); ListView.SetMultiChoiceModeListener(this); - if (App.Kp2a.GetDb().CanWrite) + if (App.Kp2a.OpenDatabases.Any(db => db.CanWrite)) { ListView.ChoiceMode = ChoiceMode.MultipleModal; @@ -1221,13 +1219,10 @@ namespace keepass2android { case Resource.Id.menu_delete: - - DeleteMultipleItems task = new DeleteMultipleItems((GroupBaseActivity)Activity, App.Kp2a.GetDb(), checkedItems, - new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a); - task.Start(); + DeleteMultipleItems((GroupBaseActivity)Activity, checkedItems, new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a); break; case Resource.Id.menu_move: - var navMove = new NavigateToFolderAndLaunchMoveElementTask(checkedItems.First().ParentGroup, checkedItems.Select(i => i.Uuid).ToList(), ((GroupBaseActivity)Activity).IsSearchResult); + var navMove = new NavigateToFolderAndLaunchMoveElementTask(App.Kp2a.CurrentDb, checkedItems.First().ParentGroup, checkedItems.Select(i => i.Uuid).ToList(), ((GroupBaseActivity)Activity).IsSearchResult); ((GroupBaseActivity)Activity).StartTask(navMove); break; case Resource.Id.menu_copy: @@ -1240,7 +1235,7 @@ namespace keepass2android break; case Resource.Id.menu_navigate: - NavigateToFolder navNavigate = new NavigateToFolder(checkedItems.First().ParentGroup, true); + NavigateToFolder navNavigate = new NavigateToFolder(App.Kp2a.CurrentDb, checkedItems.First().ParentGroup, true); ((GroupBaseActivity)Activity).StartTask(navNavigate); break; case Resource.Id.menu_edit: @@ -1378,6 +1373,66 @@ namespace keepass2android { return IsOnlyOneItemChecked() && !IsOnlyOneGroupChecked(); } + + + public void DeleteMultipleItems(GroupBaseActivity activity, List checkedItems, OnFinish onFinish, Kp2aApp app) + { + if (checkedItems.Any() == false) + return; + //sort checkedItems by database + List>> itemsForDatabases = + new List>>(); + foreach (var item in checkedItems) + { + var db = app.FindDatabaseForEntryId(item.Uuid) ?? app.FindDatabaseForGroupId(item.Uuid); + if (db != null) + { + bool foundDatabase = false; + foreach (var listEntry in itemsForDatabases) + { + if (listEntry.Key == db) + { + foundDatabase = true; + listEntry.Value.Add(item); + break; + } + } + if (!foundDatabase) + { + itemsForDatabases.Add(new KeyValuePair>(db, new List { item })); + } + } + + } + + int dbIndex = 0; + Action action = null; + action = (success, message, activeActivity) => + { + if (success) + { + dbIndex++; + if (dbIndex == itemsForDatabases.Count) + { + onFinish.SetResult(true); + onFinish.Run(); + return; + } + new DeleteMultipleItemsFromOneDatabase(activity, itemsForDatabases[dbIndex].Key, + itemsForDatabases[dbIndex].Value, new ActionOnFinish(activeActivity, (b, s, activity1) => action(b, s, activity1)), app) + .Start(); + } + else + { + onFinish.SetResult(false, message, null); + } + }; + + new DeleteMultipleItemsFromOneDatabase(activity, itemsForDatabases[dbIndex].Key, + itemsForDatabases[dbIndex].Value, new ActionOnFinish(activity, (b, s, activity1) => action(b, s, activity1)), app) + .Start(); + } + } } diff --git a/src/keepass2android/GroupEditActivity.cs b/src/keepass2android/GroupEditActivity.cs index ae548a29..90516808 100644 --- a/src/keepass2android/GroupEditActivity.cs +++ b/src/keepass2android/GroupEditActivity.cs @@ -90,7 +90,7 @@ namespace keepass2android }; _selectedIconId = (int) PwIcon.FolderOpen; - iconButton.SetImageDrawable(App.Kp2a.GetDb().DrawableFactory.GetIconDrawable(this, App.Kp2a.GetDb().KpDatabase, (PwIcon)_selectedIconId, null, true)); + iconButton.SetImageDrawable(App.Kp2a.CurrentDb.DrawableFactory.GetIconDrawable(this, App.Kp2a.CurrentDb.KpDatabase, (PwIcon)_selectedIconId, null, true)); Button okButton = (Button)FindViewById (Resource.Id.ok); okButton.Click += (sender, e) => { @@ -118,12 +118,12 @@ namespace keepass2android if (Intent.HasExtra(KeyGroupUuid)) { string groupUuid = Intent.Extras.GetString(KeyGroupUuid); - _groupToEdit = App.Kp2a.GetDb().Groups[new PwUuid(MemUtil.HexStringToByteArray(groupUuid))]; + _groupToEdit = App.Kp2a.CurrentDb.Groups[new PwUuid(MemUtil.HexStringToByteArray(groupUuid))]; _selectedIconId = (int) _groupToEdit.IconId; _selectedCustomIconId = _groupToEdit.CustomIconUuid; TextView nameField = (TextView)FindViewById(Resource.Id.group_name); nameField.Text = _groupToEdit.Name; - App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iconButton, this, App.Kp2a.GetDb().KpDatabase, _groupToEdit.IconId, _groupToEdit.CustomIconUuid, false); + App.Kp2a.CurrentDb.DrawableFactory.AssignDrawableTo(iconButton, this, App.Kp2a.CurrentDb.KpDatabase, _groupToEdit.IconId, _groupToEdit.CustomIconUuid, false); SetTitle(Resource.String.edit_group_title); } else @@ -156,7 +156,7 @@ namespace keepass2android _selectedCustomIconId = PwUuid.Zero; ImageButton currIconButton = (ImageButton) FindViewById(Resource.Id.icon_button); - App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(currIconButton, this, App.Kp2a.GetDb().KpDatabase, (PwIcon) _selectedIconId, _selectedCustomIconId, false); + App.Kp2a.CurrentDb.DrawableFactory.AssignDrawableTo(currIconButton, this, App.Kp2a.CurrentDb.KpDatabase, (PwIcon) _selectedIconId, _selectedCustomIconId, false); break; } } diff --git a/src/keepass2android/IconPickerActivity.cs b/src/keepass2android/IconPickerActivity.cs index 706fd725..e3b3239d 100644 --- a/src/keepass2android/IconPickerActivity.cs +++ b/src/keepass2android/IconPickerActivity.cs @@ -51,7 +51,7 @@ namespace keepass2android SetContentView(Resource.Layout.icon_picker); GridView currIconGridView = (GridView)FindViewById(Resource.Id.IconGridView); - currIconGridView.Adapter = new ImageAdapter(this, App.Kp2a.GetDb().KpDatabase); + currIconGridView.Adapter = new ImageAdapter(this, App.Kp2a.CurrentDb.KpDatabase); currIconGridView.ItemClick += (sender, e) => { @@ -134,7 +134,7 @@ namespace keepass2android bitmap.Compress(Bitmap.CompressFormat.Png, 90, ms); PwCustomIcon pwci = new PwCustomIcon(new PwUuid(true), ms.ToArray()); - App.Kp2a.GetDb().KpDatabase.CustomIcons.Add(pwci); + App.Kp2a.CurrentDb.KpDatabase.CustomIcons.Add(pwci); } var gridView = ((GridView)FindViewById(Resource.Id.IconGridView)); @@ -198,8 +198,7 @@ namespace keepass2android if (position < (int)PwIcon.Count) { tv.Text = "" + position; - var drawable = App.Kp2a.GetDb() - .DrawableFactory.GetIconDrawable(_act, App.Kp2a.GetDb().KpDatabase, (KeePassLib.PwIcon) position, null, false); + var drawable = App.Kp2a.CurrentDb .DrawableFactory.GetIconDrawable(_act, App.Kp2a.CurrentDb.KpDatabase, (KeePassLib.PwIcon) position, null, false); drawable = new BitmapDrawable(Util.DrawableToBitmap(drawable)); iv.SetImageDrawable(drawable); //App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iv, _act, App.Kp2a.GetDb().KpDatabase, (KeePassLib.PwIcon) position, null, false); diff --git a/src/keepass2android/ImageViewActivity.cs b/src/keepass2android/ImageViewActivity.cs index f7b4433c..b6470920 100644 --- a/src/keepass2android/ImageViewActivity.cs +++ b/src/keepass2android/ImageViewActivity.cs @@ -317,7 +317,7 @@ namespace keepass2android SetContentView(Resource.Layout.ImageViewActivity); var uuid = new PwUuid(MemUtil.HexStringToByteArray(Intent.GetStringExtra("EntryId"))); string key = Intent.GetStringExtra("EntryKey"); - var binary = App.Kp2a.GetDb().Entries[uuid].Binaries.Get(key); + var binary = App.Kp2a.FindDatabaseForEntryId(uuid).Entries[uuid].Binaries.Get(key); SupportActionBar.Title = key; byte[] pbdata = binary.ReadData(); diff --git a/src/keepass2android/KeePass.cs b/src/keepass2android/KeePass.cs index 68b57e10..c9099215 100644 --- a/src/keepass2android/KeePass.cs +++ b/src/keepass2android/KeePass.cs @@ -38,8 +38,8 @@ using String = System.String; * Keepass2Android comprises quite a number of different activities and entry points: The app can be started * using the launcher icon (-> Activity "Keepass"), or by sending a URL (-> FileSelect), opening a .kdb(x)-file (->Password), * swiping a YubikeyNEO (NfcOtpActivity). - * While the database is closed, there is only one activity on the stack: Keepass -> FileSelect <-> Password. - * After opening an database (in Password), Password is always the root of the stack (exception: after creating a database, + * There is either only the KeePass activity on stack (no db loaded then) or the first activity + * After opening a database (in Password), Password is always the root of the stack (exception: after creating a database, * FileSelect is the root without Password being open). * Another exception: QueryCredentialsActivity is root of the stack if an external app is querying credentials. * QueryCredentialsActivity checks the plugin access permissions, then launches FileSelectActivity (which starts @@ -86,8 +86,10 @@ namespace keepass2android public const Result ExitClose = Result.FirstUser + 7; public const Result ExitFileStorageSelectionOk = Result.FirstUser + 8; public const Result ResultOkPasswordGenerator = Result.FirstUser + 9; + public const Result ExitLoadAnotherDb = Result.FirstUser + 10; + - public const string TagsKey = "@tags"; + public const string TagsKey = "@tags"; public const string OverrideUrlKey = "@override"; public const string ExpDateKey = "@exp_date"; diff --git a/src/keepass2android/KpEntryTemplatedEdit.cs b/src/keepass2android/KpEntryTemplatedEdit.cs index 2979f5cb..c06e98d6 100644 --- a/src/keepass2android/KpEntryTemplatedEdit.cs +++ b/src/keepass2android/KpEntryTemplatedEdit.cs @@ -38,7 +38,7 @@ namespace keepass2android return res; } - private const string EtmTemplateUuid = "_etm_template_uuid"; + public const string EtmTemplateUuid = "_etm_template_uuid"; private const string EtmTitle = "_etm_title_"; private readonly Database _db; private readonly PwEntry _entry; diff --git a/src/keepass2android/LifecycleAwareActivity.cs b/src/keepass2android/LifecycleAwareActivity.cs index 114c3f02..8ee63560 100644 --- a/src/keepass2android/LifecycleAwareActivity.cs +++ b/src/keepass2android/LifecycleAwareActivity.cs @@ -51,7 +51,7 @@ namespace keepass2android { base.OnResume(); Kp2aLog.Log(ClassName+".OnResume"); - if (App.Kp2a.GetDb() == null) + if (App.Kp2a.CurrentDb== null) { Kp2aLog.Log(" DB null"); } diff --git a/src/keepass2android/LockCloseActivity.cs b/src/keepass2android/LockCloseActivity.cs index 8864d1f2..09053369 100644 --- a/src/keepass2android/LockCloseActivity.cs +++ b/src/keepass2android/LockCloseActivity.cs @@ -63,7 +63,7 @@ namespace keepass2android } - _ioc = App.Kp2a.GetDb().Ioc; + _ioc = App.Kp2a.CurrentDb.Ioc; if (Intent.GetBooleanExtra(NoLockCheck, false)) return; @@ -106,10 +106,13 @@ namespace keepass2android if (Intent.GetBooleanExtra(NoLockCheck, false)) return; - if (TimeoutHelper.CheckShutdown(this, _ioc)) - return; + if (TimeoutHelper.CheckDbChanged(this, _ioc)) + { + Finish(); + return; + } - //todo: it seems like OnResume can be called after dismissing a dialog, e.g. the Delete-permanently-Dialog. + //todo: it seems like OnResume can be called after dismissing a dialog, e.g. the Delete-permanently-Dialog. //in this case the following check might run in parallel with the check performed during the SaveDb check (triggered after the //aforementioned dialog is closed) which can cause odd behavior. However, this is a rare case and hard to resolve so this is currently //accepted. (If the user clicks cancel on the reload-dialog, everything will work.) diff --git a/src/keepass2android/LockCloseListActivity.cs b/src/keepass2android/LockCloseListActivity.cs index b5a4635b..8d13291b 100644 --- a/src/keepass2android/LockCloseListActivity.cs +++ b/src/keepass2android/LockCloseListActivity.cs @@ -51,7 +51,7 @@ namespace keepass2android Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure); } - _ioc = App.Kp2a.GetDb().Ioc; + _ioc = App.Kp2a.CurrentDb.Ioc; _intentReceiver = new LockCloseListActivityBroadcastReceiver(this); IntentFilter filter = new IntentFilter(); @@ -72,11 +72,14 @@ namespace keepass2android { base.OnResume(); _design.ReapplyTheme(); - - if (TimeoutHelper.CheckShutdown(this, _ioc)) - return; - - //todo: see LockCloseActivity.OnResume + + if (TimeoutHelper.CheckDbChanged(this, _ioc)) + { + Finish(); + return; + } + + //todo: see LockCloseActivity.OnResume App.Kp2a.CheckForOpenFileChanged(this); } diff --git a/src/keepass2android/LockingActivity.cs b/src/keepass2android/LockingActivity.cs index f92e0726..37dc4eee 100644 --- a/src/keepass2android/LockingActivity.cs +++ b/src/keepass2android/LockingActivity.cs @@ -43,7 +43,7 @@ namespace keepass2android { base.OnStart(); - var xcKey = App.Kp2a.GetDb()?.KpDatabase.MasterKey.GetUserKey(); + var xcKey = App.Kp2a.CurrentDb?.KpDatabase.MasterKey.GetUserKey(); if (xcKey != null) { xcKey.Activity = this; @@ -58,7 +58,7 @@ namespace keepass2android protected override void OnStop() { base.OnStop(); - var xcKey = App.Kp2a.GetDb()?.KpDatabase.MasterKey.GetUserKey(); + var xcKey = App.Kp2a.CurrentDb?.KpDatabase.MasterKey.GetUserKey(); if (xcKey != null) { //don't store a pointer to this activity in the static database object to avoid memory leak diff --git a/src/keepass2android/LockingClosePreferenceActivity.cs b/src/keepass2android/LockingClosePreferenceActivity.cs index eef52df4..ba2988e0 100644 --- a/src/keepass2android/LockingClosePreferenceActivity.cs +++ b/src/keepass2android/LockingClosePreferenceActivity.cs @@ -32,7 +32,7 @@ namespace keepass2android protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); - _ioc = App.Kp2a.GetDb().Ioc; + _ioc = App.Kp2a.CurrentDb.Ioc; _intentReceiver = new LockingClosePreferenceActivityBroadcastReceiver(this); @@ -43,8 +43,12 @@ namespace keepass2android protected override void OnResume() { base.OnResume(); - - TimeoutHelper.CheckShutdown(this, _ioc); + + if (TimeoutHelper.CheckDbChanged(this, _ioc)) + { + Finish(); + return; + } } diff --git a/src/keepass2android/NfcOtpActivity.cs b/src/keepass2android/NfcOtpActivity.cs index 45ab7000..0c73c7a1 100644 --- a/src/keepass2android/NfcOtpActivity.cs +++ b/src/keepass2android/NfcOtpActivity.cs @@ -93,15 +93,7 @@ namespace keepass2android return; } - if (App.Kp2a.GetDb() != null) - { - Toast.MakeText(this, GetString(Resource.String.otp_discarded_because_db_open), ToastLength.Long).Show(); - } - else - { - StartActivity(i); - } - + StartActivity(i); Finish(); } diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index 5d87c676..554105b6 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -737,12 +737,6 @@ namespace keepass2android } } - if (App.Kp2a.GetDb()?.Ioc != null && App.Kp2a.GetDb().Ioc.GetDisplayName() != _ioConnection.GetDisplayName()) - { - // A different database is currently loaded, unload it before loading the new one requested - App.Kp2a.LockDatabase(false); - } - SetContentView(Resource.Layout.password); InitializeToolbar(); @@ -806,6 +800,14 @@ namespace keepass2android if ((int)Build.VERSION.SdkInt >= 23) RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FingerprintPermissionRequestCode); + + + var matchingOpenDb = App.Kp2a.OpenDatabases.FirstOrDefault(db => db.Ioc.IsSameFileAs(_ioConnection)); + if (matchingOpenDb != null) + { + App.Kp2a.CurrentDb = matchingOpenDb; + Finish(); + } } @@ -1473,9 +1475,7 @@ namespace keepass2android : new LoadDb(this, App.Kp2a, _ioConnection, _loadDbFileTask, compositeKey, GetKeyProviderString(), onFinish); _loadDbFileTask = null; // prevent accidental re-use - SetNewDefaultFile(); - - new ProgressTask(App.Kp2a, this, task).Run(); + new ProgressTask(App.Kp2a, this, task).Run(); } catch (Exception e) { @@ -1596,35 +1596,7 @@ namespace keepass2android base.OnPause(); } - private void SetNewDefaultFile() - { -//Don't allow the current file to be the default if we don't have stored credentials - bool makeFileDefault; - if ((_ioConnection.IsLocalFile() == false) && (_ioConnection.CredSaveMode != IOCredSaveMode.SaveCred)) - { - makeFileDefault = false; - } - else - { - makeFileDefault = true; - } - String newDefaultFileName; - - if (makeFileDefault) - { - newDefaultFileName = _ioConnection.Path; - } - else - { - newDefaultFileName = ""; - } - - ISharedPreferencesEditor editor = _prefs.Edit(); - editor.PutString(KeyDefaultFilename, newDefaultFileName); - EditorCompat.Apply(editor); - } - - protected override void OnStart() + protected override void OnStart() { base.OnStart(); _starting = true; @@ -1815,34 +1787,27 @@ namespace keepass2android //use !performingLoad to make sure we're not already loading the database (after ActivityResult from File-Prepare-Activity; this would cause _loadDbFileTask to exist when we reload later!) if ( !IsFinishing && !_performingLoad) { - if (App.Kp2a.DatabaseIsUnlocked) - { - Finish(); - } - else if (App.Kp2a.QuickUnlockEnabled && App.Kp2a.QuickLocked) - { - Finish(); - } - else - { - // OnResume is run every time the activity comes to the foreground. This code should only run when the activity is started (OnStart), but must - // be run in OnResume rather than OnStart so that it always occurrs after OnActivityResult (when re-creating a killed activity, OnStart occurs before OnActivityResult) - if (_starting) - { + + + // OnResume is run every time the activity comes to the foreground. This code should only run when the activity is started (OnStart), but must + // be run in OnResume rather than OnStart so that it always occurrs after OnActivityResult (when re-creating a killed activity, OnStart occurs before OnActivityResult) + if (_starting) + { - _starting = false; + _starting = false; - //database not yet loaded. + //database not yet loaded. + + //check if pre-loading is enabled but wasn't started yet: + if (_loadDbFileTask == null && + _prefs.GetBoolean(GetString(Resource.String.PreloadDatabaseEnabled_key), true)) + { + // Create task to kick off file loading while the user enters the password + _loadDbFileTask = Task.Factory.StartNew(PreloadDbFile); + _loadDbTaskOffline = App.Kp2a.OfflineMode; + } + } - //check if pre-loading is enabled but wasn't started yet: - if (_loadDbFileTask == null && _prefs.GetBoolean(GetString(Resource.String.PreloadDatabaseEnabled_key), true)) - { - // Create task to kick off file loading while the user enters the password - _loadDbFileTask = Task.Factory.StartNew(PreloadDbFile); - _loadDbTaskOffline = App.Kp2a.OfflineMode; - } - } - } } if (compositeKeyForImmediateLoad != null) @@ -2049,6 +2014,7 @@ namespace keepass2android _act.BroadcastOpenDatabase(); _act.InvalidCompositeKeyCount = 0; _act.LoadingErrorCount = 0; + _act.Finish(); GC.Collect(); // Ensure temporary memory used while loading is collected @@ -2193,7 +2159,7 @@ namespace keepass2android if (!OathHotpKeyProv.CreateAuxFile(_act._otpInfo, ctx, _act._otpAuxIoc)) Toast.MakeText(_act, _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile), ToastLength.Long).Show(); - App.Kp2a.GetDb().OtpAuxFileIoc = _act._otpAuxIoc; + } catch (Exception e) { @@ -2206,7 +2172,14 @@ namespace keepass2android base.Run(); - } + if (success) + { + App.Kp2a.CurrentDb.OtpAuxFileIoc = _act._otpAuxIoc; + } + + + } + } private class PasswordActivityBroadcastReceiver : BroadcastReceiver { diff --git a/src/keepass2android/QueryCredentialsActivity.cs b/src/keepass2android/QueryCredentialsActivity.cs index e2c0c53d..fcc9ecc7 100644 --- a/src/keepass2android/QueryCredentialsActivity.cs +++ b/src/keepass2android/QueryCredentialsActivity.cs @@ -49,8 +49,8 @@ namespace keepass2android //if launched from history, don't re-use the task. Proceed to FileSelect instead. if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)) { - Kp2aLog.Log("Forwarding to FileSelect. QueryCredentialsActivity started from history."); - Intent intent = new Intent(this, typeof(FileSelectActivity)); + Kp2aLog.Log("Forwarding to SelectCurrentDbActivity. QueryCredentialsActivity started from history."); + Intent intent = new Intent(this, typeof(SelectCurrentDbActivity)); intent.AddFlags(ActivityFlags.ForwardResult); StartActivity(intent); Finish(); @@ -135,9 +135,9 @@ namespace keepass2android private void StartQuery() { - //launch FileSelectActivity (which is root of the stack (exception: we're even below!)) with the appropriate task. - //will return the results later - Intent i = new Intent(this, typeof (FileSelectActivity)); + //launch SelectCurrentDbActivity (which is root of the stack (exception: we're even below!)) with the appropriate task. + //will return the results later + Intent i = new Intent(this, typeof (SelectCurrentDbActivity)); //don't show user notifications when an entry is opened. var task = new SearchUrlTask() {UrlToSearchFor = _requestedUrl, ShowUserNotifications = false}; task.ToIntent(i); @@ -185,7 +185,7 @@ namespace keepass2android } //return credentials to caller: Intent credentialData = new Intent(); - PluginHost.AddEntryToIntent(credentialData, App.Kp2a.GetDb().LastOpenedEntry); + PluginHost.AddEntryToIntent(credentialData, App.Kp2a.LastOpenedEntry); credentialData.PutExtra(Strings.ExtraQueryString,_requestedUrl); SetResult(Result.Ok, credentialData); Finish(); diff --git a/src/keepass2android/QuickUnlock.cs b/src/keepass2android/QuickUnlock.cs index 5bcc3e75..05a83c01 100644 --- a/src/keepass2android/QuickUnlock.cs +++ b/src/keepass2android/QuickUnlock.cs @@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. */ using System; +using System.Linq; using Android; using Android.App; using Android.Content; @@ -64,7 +65,7 @@ namespace keepass2android Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure); } - _ioc = App.Kp2a.GetDb().Ioc; + _ioc = App.Kp2a.GetDbForQuickUnlock().Ioc; if (_ioc == null) { @@ -81,10 +82,10 @@ namespace keepass2android var collapsingToolbar = FindViewById(Resource.Id.collapsing_toolbar); collapsingToolbar.SetTitle(GetString(Resource.String.QuickUnlock_prefs)); - if (App.Kp2a.GetDb().KpDatabase.Name != "") + if (App.Kp2a.GetDbForQuickUnlock().KpDatabase.Name != "") { FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Visible; - ((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetDb().KpDatabase.Name; + ((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetDbForQuickUnlock().KpDatabase.Name; } else { @@ -134,7 +135,7 @@ namespace keepass2android btnLock.Text = btnLock.Text.Replace("ß", "ss"); btnLock.Click += (object sender, EventArgs e) => { - App.Kp2a.LockDatabase(false); + App.Kp2a.Lock(false); Finish(); }; pwd.EditorAction += (sender, args) => @@ -244,7 +245,7 @@ namespace keepass2android try { FingerprintUnlockMode um; - Enum.TryParse(PreferenceManager.GetDefaultSharedPreferences(this).GetString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, ""), out um); + Enum.TryParse(PreferenceManager.GetDefaultSharedPreferences(this).GetString(App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintModePrefKey, ""), out um); btn.Visibility = (um != FingerprintUnlockMode.Disabled) ? ViewStates.Visible : ViewStates.Gone; if (um == FingerprintUnlockMode.Disabled) @@ -258,10 +259,10 @@ namespace keepass2android FingerprintModule fpModule = new FingerprintModule(this); Kp2aLog.Log("fpModule.FingerprintManager.IsHardwareDetected=" + fpModule.FingerprintManager.IsHardwareDetected); if (fpModule.FingerprintManager.IsHardwareDetected) //see FingerprintSetupActivity - _fingerprintIdentifier = new FingerprintDecryption(fpModule, App.Kp2a.GetDb().CurrentFingerprintPrefKey, this, - App.Kp2a.GetDb().CurrentFingerprintPrefKey); + _fingerprintIdentifier = new FingerprintDecryption(fpModule, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey, this, + App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey); } - if ((_fingerprintIdentifier == null) && (!FingerprintDecryption.IsSetUp(this, App.Kp2a.GetDb().CurrentFingerprintPrefKey))) + if ((_fingerprintIdentifier == null) && (!FingerprintDecryption.IsSetUp(this, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey))) { try { @@ -321,15 +322,7 @@ namespace keepass2android _fingerprintIdentifier = null; } - private void ClearFingerprintUnlockData() - { - ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit(); - edit.PutString(App.Kp2a.GetDb().CurrentFingerprintPrefKey, ""); - edit.PutString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, FingerprintUnlockMode.Disabled.ToString()); - edit.Commit(); - } - - private void OnUnlock(int quickUnlockLength, EditText pwd) + private void OnUnlock(int quickUnlockLength, EditText pwd) { var expectedPasswordPart = ExpectedPasswordPart; if (pwd.Text == expectedPasswordPart) @@ -340,7 +333,7 @@ namespace keepass2android else { Kp2aLog.Log("QuickUnlock not successful!"); - App.Kp2a.LockDatabase(false); + App.Kp2a.Lock(false); Toast.MakeText(this, GetString(Resource.String.QuickUnlock_fail), ToastLength.Long).Show(); } Finish(); @@ -350,7 +343,7 @@ namespace keepass2android { get { - KcpPassword kcpPassword = (KcpPassword) App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof (KcpPassword)); + KcpPassword kcpPassword = (KcpPassword) App.Kp2a.GetDbForQuickUnlock().KpDatabase.MasterKey.GetUserKey(typeof (KcpPassword)); String password = kcpPassword.Password.ReadString(); var passwordStringInfo = new System.Globalization.StringInfo(password); @@ -427,7 +420,7 @@ namespace keepass2android private void CheckIfUnloaded() { - if (App.Kp2a.GetDb() == null) + if (App.Kp2a.OpenDatabases.Any() == false) { Finish(); } diff --git a/src/keepass2android/Resources/layout/open_db_selection.xml b/src/keepass2android/Resources/layout/open_db_selection.xml new file mode 100644 index 00000000..38627f90 --- /dev/null +++ b/src/keepass2android/Resources/layout/open_db_selection.xml @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/src/keepass2android/Resources/menu/group.xml b/src/keepass2android/Resources/menu/group.xml index 9a9aa6ee..0239564e 100644 --- a/src/keepass2android/Resources/menu/group.xml +++ b/src/keepass2android/Resources/menu/group.xml @@ -57,11 +57,16 @@ app:showAsAction="never" /> - - - + + + + diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index 0da7b0dd..89213480 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -63,8 +63,9 @@ Unlock database Brackets Cancel + Ok Clipboard cleared. - + Clipboard timeout @@ -210,7 +211,7 @@ Don\'t search backup and recycle bin entries Omit \'Backup\' and Recycle Bin group from search results KeePass database filename - Enter database password + Enter databoase password Select master key type: Creating new database… Create database @@ -430,6 +431,10 @@ Rename Failed to add file attachment. Recycle Bin + + Updating template entries + + Do you want to delete this entry permanently? Press No to recycle. Do you want to delete this group permanently? Press No to recycle. Do you want to delete the selected elements permanently? Press No to recycle. @@ -731,6 +736,12 @@ + Open another database… + Select database + + Identical template entry ids + Your database contains template entries with the same UUIDs as template entries in another open database. In order to use both databases simultaneously, Keepass2Android will modify the IDs of the database you are about to open. + Database unlocked Notification about the database being unlocked diff --git a/src/keepass2android/SelectCurrentDbActivity.cs b/src/keepass2android/SelectCurrentDbActivity.cs index 2fdabc1f..7bf00057 100644 --- a/src/keepass2android/SelectCurrentDbActivity.cs +++ b/src/keepass2android/SelectCurrentDbActivity.cs @@ -6,25 +6,155 @@ using System.Text; using Android.App; using Android.Content; using Android.Content.PM; +using Android.Graphics; +using Android.Graphics.Drawables; using Android.OS; using Android.Preferences; using Android.Runtime; +using Android.Support.V7.App; +using Android.Text; +using Android.Util; using Android.Views; using Android.Widget; using keepass2android.Io; using keepass2android.Utils; -using KeeChallenge; using KeePassLib.Keys; using KeePassLib.Serialization; +using Object = Java.Lang.Object; namespace keepass2android { [Activity(Label = AppNames.AppName, MainLauncher = false, Theme = "@style/MyTheme_Blue", LaunchMode = LaunchMode.SingleInstance)] - public class SelectCurrentDbActivity : Activity + public class SelectCurrentDbActivity : AppCompatActivity { + public class OpenDatabaseAdapter : BaseAdapter + { + + private readonly SelectCurrentDbActivity _context; + internal List _displayedDatabases; + + public OpenDatabaseAdapter(SelectCurrentDbActivity context) + { + _context = context; + Update(); + + } + + public override Object GetItem(int position) + { + return position; + } + + public override long GetItemId(int position) + { + return position; + } + + + + public static float convertDpToPixel(float dp, Context context) + { + return Util.convertDpToPixel(dp, context); + } + + + public override View GetView(int position, View convertView, ViewGroup parent) + { + + Button btn; + + if (convertView == null) + { + // if it's not recycled, initialize some attributes + + btn = new Button(_context); + btn.LayoutParameters = new GridView.LayoutParams((int) convertDpToPixel(140, _context), + (int) convertDpToPixel(150, _context)); + btn.SetBackgroundResource(Resource.Drawable.storagetype_button_bg); + btn.SetPadding((int) convertDpToPixel(4, _context), + (int) convertDpToPixel(20, _context), + (int) convertDpToPixel(4, _context), + (int) convertDpToPixel(4, _context)); + btn.SetTextSize(ComplexUnitType.Sp, 11); + btn.SetTextColor(new Color(115, 115, 115)); + btn.SetSingleLine(false); + btn.Gravity = GravityFlags.Center; + btn.Click += (sender, args) => + { + int pos; + int.TryParse(((Button) sender).Tag.ToString(), out pos); + if (pos < _displayedDatabases.Count) + _context.OnItemSelected(_displayedDatabases[pos]); + else + { + _context.OnOpenOther(); + } + }; + } + else + { + btn = (Button) convertView; + } + + btn.Tag = position.ToString(); + + string displayName; + Drawable drawable; + if (position < _displayedDatabases.Count) + { + var db = _displayedDatabases[position]; + drawable = App.Kp2a.GetResourceDrawable("ic_storage_" + Util.GetProtocolId(db.Ioc)); + displayName = db.KpDatabase.Name; + displayName += "\n" + App.Kp2a.GetFileStorage(db.Ioc).GetDisplayName(db.Ioc); + } + else + { + displayName = _context.GetString(Resource.String.start_open_file); + drawable = App.Kp2a.GetResourceDrawable("ic_nav_changedb"); + } + + var str = new SpannableString(displayName); + + btn.TextFormatted = str; + //var drawable = ContextCompat.GetDrawable(context, Resource.Drawable.Icon); + btn.SetCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null); + + return btn; + } + + public override int Count + { + get { return _displayedDatabases.Count+1; } + } + + public void Update() + { + _displayedDatabases = App.Kp2a.OpenDatabases.ToList(); + } + } + + private void OnOpenOther() + { + StartFileSelect(); + } + + private void OnItemSelected(Database selectedDatabase) + { + App.Kp2a.CurrentDb = selectedDatabase; + AppTask.LaunchFirstGroupActivity(this); + } + protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); + SetContentView(Resource.Layout.open_db_selection); + + var toolbar = FindViewById(Resource.Id.mytoolbar); + + SetSupportActionBar(toolbar); + + SupportActionBar.Title = GetString(Resource.String.select_database); + if ((AppTask == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))) { AppTask = new NullTask(); @@ -33,6 +163,12 @@ namespace keepass2android { AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); } + + _adapter = new OpenDatabaseAdapter(this); + var gridView = FindViewById(Resource.Id.gridview); + gridView.ItemClick += (sender, args) => OnItemSelected(_adapter._displayedDatabases[args.Position]); + gridView.Adapter = _adapter; + } @@ -44,54 +180,78 @@ namespace keepass2android protected override void OnResume() { base.OnResume(); - if (!IsFinishing) + if (!IsFinishing && !LaunchingPasswordActivity) { - if (App.Kp2a.GetDb() == null) + if (App.Kp2a.OpenDatabases.Any() == false) { - // Load default database - ISharedPreferences prefs = Android.Preferences.PreferenceManager.GetDefaultSharedPreferences(this); - String defaultFileName = prefs.GetString(PasswordActivity.KeyDefaultFilename, ""); - - if (defaultFileName.Length > 0) - { - try - { - PasswordActivity.Launch(this, LoadIoc(defaultFileName), AppTask); - return; - } - catch (Exception e) - { - Toast.MakeText(this, e.Message, ToastLength.Long); - // Ignore exception - } - } - - Intent intent = new Intent(this, typeof(FileSelectActivity)); - AppTask.ToIntent(intent); - intent.AddFlags(ActivityFlags.ForwardResult); - StartActivity(intent); + StartFileSelect(); return; } + if (_loadAnotherDatabase) + { + StartFileSelect(); + _loadAnotherDatabase = false; + return; + } //database loaded if (App.Kp2a.QuickLocked) { var i = new Intent(this, typeof(QuickUnlock)); - Util.PutIoConnectionToIntent(App.Kp2a.GetDb().Ioc, i); + Util.PutIoConnectionToIntent(App.Kp2a.GetDbForQuickUnlock().Ioc, i); Kp2aLog.Log("Starting QuickUnlock"); StartActivityForResult(i, 0); + return; } - else + + //database(s) unlocked + if (App.Kp2a.OpenDatabases.Count() == 1) { AppTask.LaunchFirstGroupActivity(this); + return; } - + + //more than one database open or user requested to load another db. Don't launch another activity. + _adapter.Update(); + _adapter.NotifyDataSetChanged(); + + } base.OnResume(); } + protected override void OnPause() + { + LaunchingPasswordActivity = false; + base.OnPause(); + } + + private void StartFileSelect() + { + Intent intent = new Intent(this, typeof(FileSelectActivity)); + AppTask.ToIntent(intent); + intent.AddFlags(ActivityFlags.ForwardResult); + StartActivity(intent); + } + internal AppTask AppTask; + private bool _loadAnotherDatabase; + private OpenDatabaseAdapter _adapter; + + public override void OnBackPressed() + { + base.OnBackPressed(); + if (PreferenceManager.GetDefaultSharedPreferences(this) + .GetBoolean(GetString(Resource.String.LockWhenNavigateBack_key), false)) + { + App.Kp2a.Lock(); + } + //by leaving the app with the back button, the user probably wants to cancel the task + //The activity might be resumed (through Android's recent tasks list), then use a NullTask: + AppTask = new NullTask(); + Finish(); + } protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { @@ -104,15 +264,10 @@ namespace keepass2android switch (resultCode) { case KeePass.ExitNormal: // Returned to this screen using the Back key - if (PreferenceManager.GetDefaultSharedPreferences(this) - .GetBoolean(GetString(Resource.String.LockWhenNavigateBack_key), false)) + if (App.Kp2a.OpenDatabases.Count() == 1) { - App.Kp2a.LockDatabase(); + OnBackPressed(); } - //by leaving the app with the back button, the user probably wants to cancel the task - //The activity might be resumed (through Android's recent tasks list), then use a NullTask: - AppTask = new NullTask(); - Finish(); break; case KeePass.ExitLock: // The database has already been locked, and the quick unlock screen will be shown if appropriate @@ -128,18 +283,21 @@ namespace keepass2android break; case KeePass.ExitReloadDb: - if (App.Kp2a.GetDb() != null) + if (App.Kp2a.CurrentDb!= null) { //remember the composite key for reloading: - var compositeKey = App.Kp2a.GetDb().KpDatabase.MasterKey; - var ioc = App.Kp2a.GetDb().Ioc; + var compositeKey = App.Kp2a.CurrentDb.KpDatabase.MasterKey; + var ioc = App.Kp2a.CurrentDb.Ioc; //lock the database: - App.Kp2a.LockDatabase(false); + App.Kp2a.CloseDatabase(App.Kp2a.CurrentDb); LaunchPasswordActivityForReload(ioc, compositeKey); } + break; + case KeePass.ExitLoadAnotherDb: + _loadAnotherDatabase = true; break; } @@ -147,7 +305,10 @@ namespace keepass2android private void LaunchPasswordActivityForReload(IOConnectionInfo ioc, CompositeKey compositeKey) { + LaunchingPasswordActivity = true; PasswordActivity.Launch(this, ioc, AppTask, compositeKey); } + + public bool LaunchingPasswordActivity { get; set; } } } \ No newline at end of file diff --git a/src/keepass2android/SetPasswordDialog.cs b/src/keepass2android/SetPasswordDialog.cs index 3615a31f..025388d8 100644 --- a/src/keepass2android/SetPasswordDialog.cs +++ b/src/keepass2android/SetPasswordDialog.cs @@ -104,13 +104,13 @@ namespace keepass2android _finish.Filename = _dlg.Keyfile; } FingerprintUnlockMode um; - Enum.TryParse(PreferenceManager.GetDefaultSharedPreferences(_dlg.Context).GetString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, ""), out um); + Enum.TryParse(PreferenceManager.GetDefaultSharedPreferences(_dlg.Context).GetString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, ""), out um); if (um == FingerprintUnlockMode.FullUnlock) { ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(_dlg.Context).Edit(); - edit.PutString(App.Kp2a.GetDb().CurrentFingerprintPrefKey, ""); - edit.PutString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, FingerprintUnlockMode.Disabled.ToString()); + edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintPrefKey, ""); + edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, FingerprintUnlockMode.Disabled.ToString()); edit.Commit(); Toast.MakeText(_dlg.Context, Resource.String.fingerprint_reenable, ToastLength.Long).Show(); diff --git a/src/keepass2android/ShareUrlResults.cs b/src/keepass2android/ShareUrlResults.cs index 32a4f37d..0a4f6a5f 100644 --- a/src/keepass2android/ShareUrlResults.cs +++ b/src/keepass2android/ShareUrlResults.cs @@ -26,6 +26,7 @@ using Android.Views; using Android.Widget; using Android.Content.PM; using Android.Preferences; +using KeePassLib; using KeePassLib.Utility; namespace keepass2android @@ -63,9 +64,6 @@ namespace keepass2android } - private Database _db; - - public override bool IsSearchResult { get { return true; } @@ -77,9 +75,7 @@ namespace keepass2android //if user presses back to leave this activity: SetResult(Result.Canceled); - - _db = App.Kp2a.GetDb(); - + UpdateBottomBarElementVisibility(Resource.Id.select_other_entry, true); UpdateBottomBarElementVisibility(Resource.Id.add_url_entry, true); @@ -107,24 +103,41 @@ namespace keepass2android { try { - //first: search for exact url - Group = _db.SearchForExactUrl(url); - if (!url.StartsWith("androidapp://")) - { - //if no results, search for host (e.g. "accounts.google.com") - if (!Group.Entries.Any()) - Group = _db.SearchForHost(url, false); - //if still no results, search for host, allowing subdomains ("www.google.com" in entry is ok for "accounts.google.com" in search (but not the other way around) - if (!Group.Entries.Any()) - Group = _db.SearchForHost(url, true); - - } - //if no results returned up to now, try to search through other fields as well: - if (!Group.Entries.Any()) - Group = _db.SearchForText(url); - //search for host as text - if (!Group.Entries.Any()) - Group = _db.SearchForText(UrlUtil.GetHost(url.Trim())); + foreach (var db in App.Kp2a.OpenDatabases) + { + PwGroup resultsForThisDb; + //first: search for exact url + resultsForThisDb = db.SearchForExactUrl(url); + if (!url.StartsWith("androidapp://")) + { + //if no results, search for host (e.g. "accounts.google.com") + if (!resultsForThisDb.Entries.Any()) + resultsForThisDb = db.SearchForHost(url, false); + //if still no results, search for host, allowing subdomains ("www.google.com" in entry is ok for "accounts.google.com" in search (but not the other way around) + if (!resultsForThisDb.Entries.Any()) + resultsForThisDb = db.SearchForHost(url, true); + + } + //if no results returned up to now, try to search through other fields as well: + if (!resultsForThisDb.Entries.Any()) + resultsForThisDb = db.SearchForText(url); + //search for host as text + if (!resultsForThisDb.Entries.Any()) + resultsForThisDb = db.SearchForText(UrlUtil.GetHost(url.Trim())); + + if (Group == null) + { + Group = resultsForThisDb; + } + else + { + foreach (var entry in resultsForThisDb.Entries) + { + Group.AddEntry(entry, false,false); + } + } + + } } catch (Exception e) { @@ -164,7 +177,7 @@ namespace keepass2android View createUrlEntry = FindViewById (Resource.Id.add_url_entry); - if (App.Kp2a.GetDb().CanWrite) + if (App.Kp2a.OpenDatabases.Any(db => db.CanWrite)) { createUrlEntry.Visibility = ViewStates.Visible; createUrlEntry.Click += (sender, e) => @@ -194,5 +207,10 @@ namespace keepass2android { get { return Resource.Layout.searchurlresults; } } + + public override bool EntriesBelongToCurrentDatabaseOnly + { + get { return false; } + } }} diff --git a/src/keepass2android/Totp/Kp2aTotp.cs b/src/keepass2android/Totp/Kp2aTotp.cs index 9a67b71c..462a5575 100644 --- a/src/keepass2android/Totp/Kp2aTotp.cs +++ b/src/keepass2android/Totp/Kp2aTotp.cs @@ -13,10 +13,12 @@ namespace keepass2android readonly ITotpPluginAdapter[] _pluginAdapters = new ITotpPluginAdapter[] { new TrayTotpPluginAdapter(), new KeeOtpPluginAdapter(), new KeeWebOtpPluginAdapter() }; public void OnOpenEntry() - { - foreach (ITotpPluginAdapter adapter in _pluginAdapters) + { + if (App.Kp2a.LastOpenedEntry == null) + return; + foreach (ITotpPluginAdapter adapter in _pluginAdapters) { - TotpData totpData = adapter.GetTotpData(App.Kp2a.GetDb().LastOpenedEntry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString()), Application.Context, false); + TotpData totpData = adapter.GetTotpData(App.Kp2a.LastOpenedEntry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString()), Application.Context, false); if (totpData.IsTotpEnry) { new UpdateTotpTimerTask(Application.Context, adapter).Run(); diff --git a/src/keepass2android/Totp/UpdateTotpTimerTask.cs b/src/keepass2android/Totp/UpdateTotpTimerTask.cs index 840d82a2..cefdca4d 100644 --- a/src/keepass2android/Totp/UpdateTotpTimerTask.cs +++ b/src/keepass2android/Totp/UpdateTotpTimerTask.cs @@ -27,10 +27,10 @@ namespace PluginTOTP { try { - if (App.Kp2a.GetDb().LastOpenedEntry == null) + if (App.Kp2a.LastOpenedEntry == null) return; //DB was locked - Dictionary entryFields = App.Kp2a.GetDb().LastOpenedEntry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString()); + Dictionary entryFields = App.Kp2a.LastOpenedEntry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString()); //mute warnings to avoid repeated display of the toasts TotpData totpData = _adapter.GetTotpData(entryFields, _context, true /*mute warnings*/); if (totpData.IsTotpEnry) @@ -58,10 +58,10 @@ namespace PluginTOTP private void UpdateEntryData(string totp) { //update the Entry output in the App database and notify the CopyToClipboard service - App.Kp2a.GetDb().LastOpenedEntry.OutputStrings.Set(_totp, new ProtectedString(true, totp)); + App.Kp2a.LastOpenedEntry.OutputStrings.Set(_totp, new ProtectedString(true, totp)); Intent updateKeyboardIntent = new Intent(_context, typeof(CopyToClipboardService)); updateKeyboardIntent.SetAction(Intents.UpdateKeyboard); - updateKeyboardIntent.PutExtra("entry", App.Kp2a.GetDb().LastOpenedEntry.Uuid.ToHexString()); + updateKeyboardIntent.PutExtra("entry", App.Kp2a.LastOpenedEntry.Uuid.ToHexString()); _context.StartService(updateKeyboardIntent); } @@ -73,7 +73,7 @@ namespace PluginTOTP i.SetPackage(_context.PackageName); i.PutExtra(Strings.ExtraSender, _context.PackageName); i.PutExtra(Strings.ExtraFieldValue, totp); - i.PutExtra(Strings.ExtraEntryId, App.Kp2a.GetDb().LastOpenedEntry.Entry.Uuid.ToHexString()); + i.PutExtra(Strings.ExtraEntryId, App.Kp2a.LastOpenedEntry.Entry.Uuid.ToHexString()); i.PutExtra(Strings.ExtraFieldId, _totp); i.PutExtra(Strings.ExtraFieldProtected, true); diff --git a/src/keepass2android/Utils/Util.cs b/src/keepass2android/Utils/Util.cs index 26b15360..68f36a00 100644 --- a/src/keepass2android/Utils/Util.cs +++ b/src/keepass2android/Utils/Util.cs @@ -46,20 +46,20 @@ namespace keepass2android public const String KeyServercredmode = "serverCredRememberMode"; - public static void PutIoConnectionToIntent(IOConnectionInfo ioc, Intent i) + public static void PutIoConnectionToIntent(IOConnectionInfo ioc, Intent i, string prefix="") { - i.PutExtra(KeyFilename, ioc.Path); - i.PutExtra(KeyServerusername, ioc.UserName); - i.PutExtra(KeyServerpassword, ioc.Password); - i.PutExtra(KeyServercredmode, (int)ioc.CredSaveMode); + i.PutExtra(prefix+KeyFilename, ioc.Path); + i.PutExtra(prefix + KeyServerusername, ioc.UserName); + i.PutExtra(prefix + KeyServerpassword, ioc.Password); + i.PutExtra(prefix + KeyServercredmode, (int)ioc.CredSaveMode); } - public static void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent i) + public static void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent i, string prefix="") { - ioc.Path = i.GetStringExtra(KeyFilename); - ioc.UserName = i.GetStringExtra(KeyServerusername) ?? ""; - ioc.Password = i.GetStringExtra(KeyServerpassword) ?? ""; - ioc.CredSaveMode = (IOCredSaveMode)i.GetIntExtra(KeyServercredmode, (int)IOCredSaveMode.NoSave); + ioc.Path = i.GetStringExtra(prefix + KeyFilename); + ioc.UserName = i.GetStringExtra(prefix + KeyServerusername) ?? ""; + ioc.Password = i.GetStringExtra(prefix + KeyServerpassword) ?? ""; + ioc.CredSaveMode = (IOCredSaveMode)i.GetIntExtra(prefix + KeyServercredmode, (int)IOCredSaveMode.NoSave); } public static Bitmap DrawableToBitmap(Drawable drawable) @@ -558,6 +558,15 @@ namespace keepass2android int width = (int)(0.9 * context.Resources.GetDimension(Android.Resource.Dimension.NotificationLargeIconWidth)); return Bitmap.CreateScaledBitmap(unscaled, width, height, true); } + + public static string GetProtocolId(IOConnectionInfo ioc) + { + string displayPath = App.Kp2a.GetFileStorage(ioc).GetDisplayName(ioc); + int protocolSeparatorPos = displayPath.IndexOf("://", StringComparison.Ordinal); + string protocolId = protocolSeparatorPos < 0 ? + "file" : displayPath.Substring(0, protocolSeparatorPos); + return protocolId; + } } } diff --git a/src/keepass2android/app/App.cs b/src/keepass2android/app/App.cs index f016a048..9679dba3 100644 --- a/src/keepass2android/app/App.cs +++ b/src/keepass2android/app/App.cs @@ -18,6 +18,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Security; using Android.App; using Android.Content; @@ -40,6 +41,7 @@ using TwofishCipher; using Keepass2android.Pluginsdk; using keepass2android.Io; using keepass2android.addons.OtpKeyProv; +using keepass2android.database.edit; using KeePassLib.Interfaces; using KeePassLib.Utility; #if !NoNet @@ -100,19 +102,19 @@ namespace keepass2android /// public class Kp2aApp: IKp2aApp, ICacheSupervisor { - public void LockDatabase(bool allowQuickUnlock = true) - { - if (GetDb() != null) + public void Lock(bool allowQuickUnlock = true) + { + if (OpenDatabases.Any()) { if (QuickUnlockEnabled && allowQuickUnlock && - GetDb().KpDatabase.MasterKey.ContainsType(typeof(KcpPassword)) && - !((KcpPassword)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpPassword))).Password.IsEmpty) + GetDbForQuickUnlock().KpDatabase.MasterKey.ContainsType(typeof(KcpPassword)) && + !((KcpPassword)App.Kp2a.GetDbForQuickUnlock().KpDatabase.MasterKey.GetUserKey(typeof(KcpPassword))).Password.IsEmpty) { if (!QuickLocked) { Kp2aLog.Log("QuickLocking database"); QuickLocked = true; - GetDb().LastOpenedEntry = null; + LastOpenedEntry = null; BroadcastDatabaseAction(Application.Context, Strings.ActionLockDatabase); } else @@ -126,8 +128,10 @@ namespace keepass2android BroadcastDatabaseAction(Application.Context, Strings.ActionCloseDatabase); - // Couldn't quick-lock, so unload database instead - _db = null; + // Couldn't quick-lock, so unload database(s) instead + _openDatabases.Clear(); + _currentDatabase = null; + LastOpenedEntry = null; QuickLocked = false; } } @@ -143,28 +147,24 @@ namespace keepass2android public void BroadcastDatabaseAction(Context ctx, string action) { - Intent i = new Intent(action); + foreach (Database db in OpenDatabases) + { + Intent i = new Intent(action); - //seems like this can happen. This code is for debugging. - if (App.Kp2a.GetDb().Ioc == null) - { - Kp2aLog.LogUnexpectedError(new Exception("App.Kp2a.GetDb().Ioc is null")); - return; - } - - i.PutExtra(Strings.ExtraDatabaseFileDisplayname, App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc).GetDisplayName(App.Kp2a.GetDb().Ioc)); - i.PutExtra(Strings.ExtraDatabaseFilepath, App.Kp2a.GetDb().Ioc.Path); - foreach (var plugin in new PluginDatabase(ctx).GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions)) - { - i.SetPackage(plugin); - ctx.SendBroadcast(i); - } + i.PutExtra(Strings.ExtraDatabaseFileDisplayname, GetFileStorage(db.Ioc).GetDisplayName(db.Ioc)); + i.PutExtra(Strings.ExtraDatabaseFilepath, db.Ioc.Path); + foreach (var plugin in new PluginDatabase(ctx).GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions)) + { + i.SetPackage(plugin); + ctx.SendBroadcast(i); + } + } + } - public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compositeKey, - ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat) + public Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compositeKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat) { var prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context); var createBackup = prefs.GetBoolean(Application.Context.GetString(Resource.String.CreateBackups_key), true) @@ -180,10 +180,27 @@ namespace keepass2android memoryStream.Seek(0, SeekOrigin.Begin); } - _db = CreateNewDatabase(); - _db.LoadData(this, ioConnectionInfo, memoryStream, compositeKey, statusLogger, databaseFormat); + foreach (Database openDb in _openDatabases) + { + if (openDb.Ioc.IsSameFileAs(ioConnectionInfo)) + { + //TODO check this earlier and simply open the database's root group + throw new Exception("Database already loaded!"); + } + + } - if (createBackup) + var newDb = new Database(new DrawableFactory(), this); + newDb.LoadData(this, ioConnectionInfo, memoryStream, compositeKey, statusLogger, databaseFormat); + + + + _currentDatabase = newDb; + _openDatabases.Add(newDb); + + + + if (createBackup) { statusLogger.UpdateMessage(Application.Context.GetString(Resource.String.UpdatingBackup)); Java.IO.File internalDirectory = IoUtil.GetInternalDirectory(Application.Context); @@ -201,9 +218,12 @@ namespace keepass2android using (var transaction = new LocalFileStorage(App.Kp2a).OpenWriteTransaction(targetIoc, false)) { - var file = transaction.OpenFile(); - backupCopy.CopyTo(file); - transaction.CommitWrite(); + using (var file = transaction.OpenFile()) + { + backupCopy.CopyTo(file); + transaction.CommitWrite(); + } + } Java.Lang.Object baseIocDisplayName = baseDisplayName; @@ -226,9 +246,53 @@ namespace keepass2android } UpdateOngoingNotification(); - } - internal void UnlockDatabase() + return newDb; + } + + public Database FindDatabaseForEntryId(PwUuid entryKey) + { + foreach (Database db in OpenDatabases) + { + if (db.Entries.ContainsKey(entryKey)) + return db; + } + return null; + } + + + public void CloseDatabase(Database db) + { + if (!_openDatabases.Contains(db)) + throw new Exception("Cannot close database which is not open!"); + if (_openDatabases.Count == 1) + { + Lock(false); + return; + } + if (LastOpenedEntry != null && db.Entries.ContainsKey(LastOpenedEntry.Uuid)) + { + LastOpenedEntry = null; + } + + _openDatabases.Remove(db); + if (_currentDatabase == db) + _currentDatabase = _openDatabases.First(); + UpdateOngoingNotification(); + //TODO broadcast event so affected activities can close/update? + } + + public Database FindDatabaseForGroupId(PwUuid groupKey) + { + foreach (Database db in OpenDatabases) + { + if (db.Groups.ContainsKey(groupKey)) + return db; + } + return null; + } + + internal void UnlockDatabase() { QuickLocked = false; @@ -258,7 +322,7 @@ namespace keepass2android public bool DatabaseIsUnlocked { - get { return GetDb() != null && !QuickLocked; } + get { return OpenDatabases.Any() && !QuickLocked; } } #region QuickUnlock @@ -287,8 +351,6 @@ namespace keepass2android #endregion - private Database _db; - /// /// See comments to EntryEditActivityState. /// @@ -297,10 +359,50 @@ namespace keepass2android public FileDbHelper FileDbHelper; private List _fileStorages; - public Database GetDb() - { - return _db; - } + private readonly List _openDatabases = new List(); + private Database _currentDatabase; + + public IEnumerable OpenDatabases + { + get { return _openDatabases; } + } + + public readonly HashSet dirty = new HashSet(new PwGroupEqualityFromIdComparer()); + public HashSet DirtyGroups { get { return dirty; } } + + + public void MarkAllGroupsAsDirty() + { + foreach (var db in OpenDatabases) + foreach (PwGroup group in db.Groups.Values) + { + DirtyGroups.Add(group); + } + + + } + + + /// + /// Information about the last opened entry. Includes the entry but also transformed fields. + /// + public PwEntryOutput LastOpenedEntry { get; set; } + + public Database CurrentDb + { + get { return _currentDatabase; } + set + { + if (!OpenDatabases.Contains(value)) + throw new Exception("Cannot set database as current. Not in list of opened databases!"); + _currentDatabase = value; + } + } + + public Database GetDbForQuickUnlock() + { + return OpenDatabases.FirstOrDefault(); + } @@ -325,19 +427,28 @@ namespace keepass2android public void CheckForOpenFileChanged(Activity activity) { - if (GetDb()?.DidOpenFileChange() == true) + if (CurrentDb?.DidOpenFileChange() == true) { - if (GetDb().ReloadRequested) + if (CurrentDb.ReloadRequested) { - LockDatabase(false); activity.SetResult(KeePass.ExitReloadDb); activity.Finish(); } - AskForReload(activity); + else + { + AskForReload(activity); + } + } } - private void AskForReload(Activity activity) + public void LockSingleDatabase(Database databaseToLock, bool allowQuickUnlock) + { + //TODO implement + throw new Exception("lock single is not implemented"); + } + + private void AskForReload(Activity activity) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.SetTitle(activity.GetString(Resource.String.AskReloadFile_title)); @@ -347,7 +458,7 @@ namespace keepass2android builder.SetPositiveButton(activity.GetString(Android.Resource.String.Yes), (dlgSender, dlgEvt) => { - GetDb().ReloadRequested = true; + CurrentDb.ReloadRequested = true; activity.SetResult(KeePass.ExitReloadDb); activity.Finish(); @@ -723,8 +834,9 @@ namespace keepass2android internal void OnTerminate() { - - _db = null; + + _openDatabases.Clear(); + _currentDatabase = null; if (FileDbHelper != null && FileDbHelper.IsOpen()) { @@ -752,8 +864,9 @@ namespace keepass2android public Database CreateNewDatabase() { - _db = new Database(new DrawableFactory(), this); - return _db; + _currentDatabase = new Database(new DrawableFactory(), this); + _openDatabases.Add(_currentDatabase); + return _currentDatabase; } internal void ShowToast(string message) @@ -893,10 +1006,45 @@ namespace keepass2android Application.Context.GetString(Resource.String.LockWhenScreenOff_key), false)) { - App.Kp2a.LockDatabase(); + App.Kp2a.Lock(); } } - } + + public Database GetDatabase(IOConnectionInfo dbIoc) + { + foreach (Database db in OpenDatabases) + { + if (db.Ioc.IsSameFileAs(dbIoc)) + return db; + } + throw new Exception("Database not found for dbIoc!"); + } + + public PwGroup FindGroup(PwUuid uuid) + { + PwGroup result; + foreach (Database db in OpenDatabases) + { + if (db.Groups.TryGetValue(uuid, out result)) + return result; + } + return null; + } + public IStructureItem FindStructureItem(PwUuid uuid) + { + + foreach (Database db in OpenDatabases) + { + PwGroup resultGroup; + if (db.Groups.TryGetValue(uuid, out resultGroup)) + return resultGroup; + PwEntry resultEntry; + if (db.Entries.TryGetValue(uuid, out resultEntry)) + return resultEntry; + } + return null; + } + } ///Application class for Keepass2Android: Contains static Database variable to be used by all components. diff --git a/src/keepass2android/app/AppTask.cs b/src/keepass2android/app/AppTask.cs index 1a804c46..6abd0b33 100644 --- a/src/keepass2android/app/AppTask.cs +++ b/src/keepass2android/app/AppTask.cs @@ -344,7 +344,7 @@ namespace keepass2android activity.StartNotificationsService(false); } - virtual public void PopulatePasswordAccessServiceIntent(Intent intent) + public virtual void PopulatePasswordAccessServiceIntent(Intent intent) { } @@ -434,7 +434,8 @@ namespace keepass2android public override void CompleteOnCreateEntryActivity(EntryActivity activity) { - App.Kp2a.GetDb().LastOpenedEntry.SearchUrl = UrlToSearchFor; + if (App.Kp2a.LastOpenedEntry != null) + App.Kp2a.LastOpenedEntry.SearchUrl = UrlToSearchFor; base.CompleteOnCreateEntryActivity(activity); } } @@ -554,10 +555,11 @@ namespace keepass2android public override void CompleteOnCreateEntryActivity(EntryActivity activity) { - App.Kp2a.GetDb().LastOpenedEntry.SearchUrl = UrlToSearchFor; + if (App.Kp2a.LastOpenedEntry != null) + App.Kp2a.LastOpenedEntry.SearchUrl = UrlToSearchFor; //if the database is readonly (or no URL exists), don't offer to modify the URL - if ((App.Kp2a.GetDb().CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor))) + if ((App.Kp2a.CurrentDb.CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor))) { base.CompleteOnCreateEntryActivity(activity); return; @@ -790,7 +792,8 @@ namespace keepass2android #endif private LinkedList _groupUuid; - protected AppTask TaskToBeLaunchedAfterNavigation; + private readonly Database _db; + protected AppTask TaskToBeLaunchedAfterNavigation; protected String FullGroupName { get ; @@ -814,8 +817,9 @@ namespace keepass2android /// Groups. /// Task to be launched after navigation. /// If set to true, toast will be displayed after navigation. - protected NavigateAndLaunchTask(PwGroup groups, AppTask taskToBeLaunchedAfterNavigation, bool toastEnable = false) { - TaskToBeLaunchedAfterNavigation = taskToBeLaunchedAfterNavigation; + protected NavigateAndLaunchTask(Database db, PwGroup groups, AppTask taskToBeLaunchedAfterNavigation, bool toastEnable = false) { + _db = db; + TaskToBeLaunchedAfterNavigation = taskToBeLaunchedAfterNavigation; PopulateGroups (groups); ToastEnable = toastEnable; } @@ -943,7 +947,7 @@ namespace keepass2android PwUuid nextGroupPwUuid = new PwUuid (MemUtil.HexStringToByteArray (nextGroupUuid)); // Create Group Activity - PwGroup nextGroup = App.Kp2a.GetDb ().Groups[nextGroupPwUuid]; + PwGroup nextGroup = _db.Groups[nextGroupPwUuid]; GroupActivity.Launch (groupBaseActivity, nextGroup, this); } return; @@ -968,12 +972,9 @@ namespace keepass2android public class NavigateToFolder: NavigateAndLaunchTask { - public NavigateToFolder() - { - } - public NavigateToFolder(PwGroup groups, bool toastEnable = false) - : base(groups, new NullTask(), toastEnable) + public NavigateToFolder(Database db, PwGroup groups, bool toastEnable = false) + : base(db, groups, new NullTask(), toastEnable) { } @@ -981,14 +982,9 @@ namespace keepass2android public class NavigateToFolderAndLaunchMoveElementTask: NavigateAndLaunchTask { - public NavigateToFolderAndLaunchMoveElementTask() - { - } - - - public NavigateToFolderAndLaunchMoveElementTask(PwGroup groups, List uuids, bool toastEnable = false) - :base(groups, new MoveElementsTask() { Uuids = uuids }, toastEnable) { + public NavigateToFolderAndLaunchMoveElementTask(Database db, PwGroup groups, List uuids, bool toastEnable = false) + :base(db, groups, new MoveElementsTask() { Uuids = uuids }, toastEnable) { } public override void Setup(Bundle b) { diff --git a/src/keepass2android/app/ApplicationBroadcastReceiver.cs b/src/keepass2android/app/ApplicationBroadcastReceiver.cs index 51ba4303..031d4867 100644 --- a/src/keepass2android/app/ApplicationBroadcastReceiver.cs +++ b/src/keepass2android/app/ApplicationBroadcastReceiver.cs @@ -17,10 +17,10 @@ namespace keepass2android switch (intent.Action) { case Intents.LockDatabase: - App.Kp2a.LockDatabase(); + App.Kp2a.Lock(); break; case Intents.CloseDatabase: - App.Kp2a.LockDatabase(false /*no quick unlock*/); + App.Kp2a.Lock(false /*no quick unlock*/); break; } } diff --git a/src/keepass2android/fileselect/FileSelectActivity.cs b/src/keepass2android/fileselect/FileSelectActivity.cs index a44ec446..899baea2 100644 --- a/src/keepass2android/fileselect/FileSelectActivity.cs +++ b/src/keepass2android/fileselect/FileSelectActivity.cs @@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file */ using System; +using System.Linq; using Android.App; using Android.Content; using Android.Database; @@ -139,8 +140,7 @@ namespace keepass2android intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false); intent.PutExtra(SelectStorageLocationActivity.ExtraKeyWritableRequirements, (int) SelectStorageLocationActivity.WritableRequirements.WriteDesired); intent.PutExtra(FileStorageSetupDefs.ExtraIsForSave, false); - StartActivityForResult(intent, RequestCodeSelectIoc); - + StartActivityForResult(intent, RequestCodeSelectIoc); }; openFileButton.Click += openFileButtonClick; @@ -151,7 +151,9 @@ namespace keepass2android //ShowFilenameDialog(false, true, true, Android.OS.Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path), "", Intents.RequestCodeFileBrowseForCreate) Intent i = new Intent(this, typeof (CreateDatabaseActivity)); this.AppTask.ToIntent(i); - StartActivityForResult(i, 0); + i.SetFlags(ActivityFlags.ForwardResult); + StartActivity(i); + Finish(); }; createNewButton.Click += createNewButtonClick; @@ -460,26 +462,20 @@ namespace keepass2android base.OnStart(); Kp2aLog.Log("FileSelect.OnStart"); - var db = App.Kp2a.GetDb(); - if (db != null) + + //if no database is loaded: load the most recent database + if ( (Intent.GetBooleanExtra(NoForwardToPasswordActivity, false)==false) && _dbHelper.HasRecentFiles() && !App.Kp2a.OpenDatabases.Any()) { - LaunchPasswordActivityForIoc(db.Ioc); - } - else - { - //if no database is loaded: load the most recent database - if ( (Intent.GetBooleanExtra(NoForwardToPasswordActivity, false)==false) && _dbHelper.HasRecentFiles()) + ICursor filesCursor = _dbHelper.FetchAllFiles(); + StartManagingCursor(filesCursor); + filesCursor.MoveToFirst(); + IOConnectionInfo ioc = _dbHelper.CursorToIoc(filesCursor); + if (App.Kp2a.GetFileStorage(ioc).RequiresSetup(ioc) == false) { - ICursor filesCursor = _dbHelper.FetchAllFiles(); - StartManagingCursor(filesCursor); - filesCursor.MoveToFirst(); - IOConnectionInfo ioc = _dbHelper.CursorToIoc(filesCursor); - if (App.Kp2a.GetFileStorage(ioc).RequiresSetup(ioc) == false) - { - LaunchPasswordActivityForIoc(ioc); - } + LaunchPasswordActivityForIoc(ioc); } } + } diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index 9f427057..fe554de0 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -1885,6 +1885,9 @@ + + + diff --git a/src/keepass2android/search/SearchProvider.cs b/src/keepass2android/search/SearchProvider.cs index 07e78118..49af01eb 100644 --- a/src/keepass2android/search/SearchProvider.cs +++ b/src/keepass2android/search/SearchProvider.cs @@ -47,8 +47,9 @@ namespace keepass2android.search private const string GetIconPathQuery = "get_icon"; private const string IconIdParameter = "IconId"; private const string CustomIconUuidParameter = "CustomIconUuid"; + private const string DatabaseIndexParameter = "DatabaseIndex"; - private static UriMatcher UriMatcher = BuildUriMatcher(); + private static UriMatcher UriMatcher = BuildUriMatcher(); static UriMatcher BuildUriMatcher() { @@ -78,9 +79,24 @@ namespace keepass2android.search { try { - var resultsContexts = new Dictionary>(); - var result = App.Kp2a.GetDb().Search(new SearchParameters { SearchString = searchString }, resultsContexts ); - return new GroupCursor(result, resultsContexts); + List entriesWithContext = new List(); + int dbIndex = 0; + foreach (var db in App.Kp2a.OpenDatabases) + { + var resultsContexts = new Dictionary>(); + PwGroup group = db.Search(new SearchParameters { SearchString = searchString }, resultsContexts); + + foreach (var entry in group.Entries) + { + KeyValuePair context; + resultsContexts.TryGetValue(entry.Uuid, out context); + entriesWithContext.Add(new EntryListCursor.EntryWithContext(entry, context, dbIndex)); + } + dbIndex++; + + } + + return new EntryListCursor(entriesWithContext); } catch (Exception e) { @@ -116,8 +132,12 @@ namespace keepass2android.search case UriMatches.GetIcon: var iconId = (PwIcon)Enum.Parse(typeof(PwIcon), uri.GetQueryParameter(IconIdParameter)); var customIconUuid = new PwUuid(MemUtil.HexStringToByteArray(uri.GetQueryParameter(CustomIconUuidParameter))); + int databaseIndex = int.Parse(uri.GetQueryParameter(DatabaseIndexParameter)); + List databases = App.Kp2a.OpenDatabases.ToList(); + Database database = databases[databaseIndex]; - var iconDrawable = App.Kp2a.GetDb().DrawableFactory.GetIconDrawable(App.Context, App.Kp2a.GetDb().KpDatabase, iconId, customIconUuid, false) as BitmapDrawable; + + var iconDrawable = database.DrawableFactory.GetIconDrawable(App.Context, database.KpDatabase, iconId, customIconUuid, false) as BitmapDrawable; if (iconDrawable?.Bitmap != null) { @@ -186,29 +206,41 @@ namespace keepass2android.search #endregion - private class GroupCursor : AbstractCursor + private class EntryListCursor : AbstractCursor { - private static readonly string[] ColumnNames = new[] { Android.Provider.BaseColumns.Id, + private readonly List _entriesWithContexts; + + private static readonly string[] ColumnNames = new[] { Android.Provider.BaseColumns.Id, SearchManager.SuggestColumnText1, SearchManager.SuggestColumnText2, SearchManager.SuggestColumnIcon1, SearchManager.SuggestColumnIntentDataId, }; - private readonly PwGroup mGroup; - private readonly IDictionary> mResultContexts; + - public GroupCursor(PwGroup group, IDictionary> resultContexts) + public struct EntryWithContext + { + public readonly PwEntry entry; + public readonly KeyValuePair resultContext; + public readonly int DatabaseIndex; + + public EntryWithContext(PwEntry entry, KeyValuePair mResultContext, int databaseIndex) + { + this.entry = entry; + this.resultContext = mResultContext; + DatabaseIndex = databaseIndex; + } + } + + public EntryListCursor(List entriesWithContexts) { - System.Diagnostics.Debug.Assert(!group.Groups.Any(), "Expecting a flat list of groups"); - - mGroup = group; - mResultContexts = resultContexts; + _entriesWithContexts = entriesWithContexts; } public override int Count { - get { return (int)Math.Min(mGroup.GetEntriesCount(false), int.MaxValue); } + get { return _entriesWithContexts.Count; } } public override string[] GetColumnNames() @@ -231,7 +263,10 @@ namespace keepass2android.search { get { - return mGroup.Entries.GetAt((uint)MPos); + if (MPos < _entriesWithContexts.Count) + return _entriesWithContexts[MPos].entry; + return null; + } } @@ -255,12 +290,9 @@ namespace keepass2android.search case 1: // SuggestColumnText1 return CurrentEntry.Strings.ReadSafe(PwDefs.TitleField); case 2: // SuggestColumnText2 - KeyValuePair context; - if (mResultContexts.TryGetValue(CurrentEntry.Uuid, out context)) - { - return Internationalise(context); - } - return null; + if (MPos < _entriesWithContexts.Count) + return Internationalise(_entriesWithContexts[MPos].resultContext); + return ""; case 3: // SuggestColumnIcon1 var builder = new Android.Net.Uri.Builder(); builder.Scheme(ContentResolver.SchemeContent); @@ -268,7 +300,8 @@ namespace keepass2android.search builder.Path(GetIconPathQuery); builder.AppendQueryParameter(IconIdParameter, CurrentEntry.IconId.ToString()); builder.AppendQueryParameter(CustomIconUuidParameter, CurrentEntry.CustomIconUuid.ToHexString()); - return builder.Build().ToString(); + builder.AppendQueryParameter(DatabaseIndexParameter, _entriesWithContexts[MPos].DatabaseIndex.ToString()); + return builder.Build().ToString(); case 4: // SuggestColumnIntentDataId return CurrentEntry.Uuid.ToHexString(); default: diff --git a/src/keepass2android/search/SearchResults.cs b/src/keepass2android/search/SearchResults.cs index 48042ad9..d837e076 100644 --- a/src/keepass2android/search/SearchResults.cs +++ b/src/keepass2android/search/SearchResults.cs @@ -56,17 +56,12 @@ namespace keepass2android.search ProcessIntent(Intent); } - protected override bool AddEntryEnabled + public override bool EntriesBelongToCurrentDatabaseOnly { get { return false; } } - protected override bool AddGroupEnabled - { - get { return false; } - } - - + protected override void OnNewIntent(Intent intent) { ProcessIntent(intent); @@ -84,7 +79,7 @@ namespace keepass2android.search { var entryIntent = new Intent(this, typeof(EntryActivity)); entryIntent.PutExtra(EntryActivity.KeyEntry, intent.Data.LastPathSegment); - entryIntent.AddFlags(ActivityFlags.ForwardResult); + entryIntent.AddFlags(ActivityFlags.ForwardResult); Finish(); // Close this activity so that the entry activity is navigated to from the main activity, not this one. StartActivity(entryIntent); } @@ -97,9 +92,22 @@ namespace keepass2android.search private void Query (SearchParameters searchParams) { - try { - Group = App.Kp2a.GetDb().Search (searchParams, null); - } catch (Exception e) { + Group = null; + try { + foreach (var db in App.Kp2a.OpenDatabases) + { + PwGroup resultsForThisDb = db.Search(searchParams, null); + if (Group == null) + Group = resultsForThisDb; + else + { + foreach (var entry in resultsForThisDb.Entries) + { + Group.AddEntry(entry, false); + } + } + } + } catch (Exception e) { Kp2aLog.LogUnexpectedError(e); Toast.MakeText(this,e.Message, ToastLength.Long).Show(); Finish(); diff --git a/src/keepass2android/services/CopyToClipboardService.cs b/src/keepass2android/services/CopyToClipboardService.cs index 205cbd21..c84e9b9d 100644 --- a/src/keepass2android/services/CopyToClipboardService.cs +++ b/src/keepass2android/services/CopyToClipboardService.cs @@ -35,6 +35,7 @@ using KeePassLib; using KeePassLib.Utility; using Android.Views.InputMethods; using KeePass.Util.Spr; +using KeePassLib.Serialization; namespace keepass2android { @@ -283,7 +284,7 @@ namespace keepass2android if ((intent.Action == Intents.ShowNotification) || (intent.Action == Intents.UpdateKeyboard)) { String uuidBytes = intent.GetStringExtra(EntryActivity.KeyEntry); - String searchUrl = intent.GetStringExtra(SearchUrlTask.UrlToSearchKey); + String searchUrl = intent.GetStringExtra(SearchUrlTask.UrlToSearchKey); PwUuid entryId = PwUuid.Zero; if (uuidBytes != null) @@ -292,19 +293,21 @@ namespace keepass2android PwEntryOutput entry; try { - if ((App.Kp2a.GetDb().LastOpenedEntry != null) - && (entryId.Equals(App.Kp2a.GetDb().LastOpenedEntry.Uuid))) + if ((App.Kp2a.LastOpenedEntry != null) + && (entryId.Equals(App.Kp2a.LastOpenedEntry.Uuid))) { - entry = App.Kp2a.GetDb().LastOpenedEntry; + entry = App.Kp2a.LastOpenedEntry; } else { - entry = new PwEntryOutput(App.Kp2a.GetDb().Entries[entryId], App.Kp2a.GetDb().KpDatabase); + Database entryDb = App.Kp2a.FindDatabaseForEntryId(entryId); + entry = new PwEntryOutput(entryDb.Entries[entryId], entryDb); } } - catch (Exception) + catch (Exception e) { + Kp2aLog.LogUnexpectedError(e); //seems like restarting the service happened after closing the DB StopSelf(); return StartCommandResult.NotSticky; @@ -400,9 +403,10 @@ namespace keepass2android var hadKeyboardData = ClearNotifications(); String entryName = entry.OutputStrings.ReadSafe(PwDefs.TitleField); + Database db = App.Kp2a.FindDatabaseForEntryId(entry.Uuid); - var bmp = Util.DrawableToBitmap(App.Kp2a.GetDb().DrawableFactory.GetIconDrawable(this, - App.Kp2a.GetDb().KpDatabase, entry.Entry.IconId, entry.Entry.CustomIconUuid, false)); + var bmp = Util.DrawableToBitmap(db.DrawableFactory.GetIconDrawable(this, + db.KpDatabase, entry.Entry.IconId, entry.Entry.CustomIconUuid, false)); if (!(((entry.Entry.CustomIconUuid != null) && (!entry.Entry.CustomIconUuid.Equals(PwUuid.Zero)))) @@ -807,7 +811,7 @@ namespace keepass2android //check if we have a last opened entry //this should always be non-null, but if the OS has killed the app, it might occur. - if (App.Kp2a.GetDb().LastOpenedEntry == null) + if (App.Kp2a.LastOpenedEntry == null) { Intent i = new Intent(context, typeof(AppKilledInfo)); i.SetFlags(ActivityFlags.ClearTask | ActivityFlags.NewTask); @@ -817,7 +821,7 @@ namespace keepass2android if (action.Equals(Intents.CopyUsername)) { - String username = App.Kp2a.GetDb().LastOpenedEntry.OutputStrings.ReadSafe(PwDefs.UserNameField); + String username = App.Kp2a.LastOpenedEntry.OutputStrings.ReadSafe(PwDefs.UserNameField); if (username.Length > 0) { CopyToClipboardService.CopyValueToClipboardWithTimeout(context, username); @@ -826,7 +830,7 @@ namespace keepass2android } else if (action.Equals(Intents.CopyPassword)) { - String password = App.Kp2a.GetDb().LastOpenedEntry.OutputStrings.ReadSafe(PwDefs.PasswordField); + String password = App.Kp2a.LastOpenedEntry.OutputStrings.ReadSafe(PwDefs.PasswordField); if (password.Length > 0) { CopyToClipboardService.CopyValueToClipboardWithTimeout(context, password); diff --git a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs index f257e548..9ac61d64 100644 --- a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs +++ b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs @@ -28,7 +28,7 @@ namespace keepass2android.services.Kp2aAutofill { //launch FileSelectActivity (which is root of the stack (exception: we're even below!)) with the appropriate task. //will return the results later - Intent i = new Intent(this, typeof(FileSelectActivity)); + Intent i = new Intent(this, typeof(SelectCurrentDbActivity)); //don't show user notifications when an entry is opened. var task = new SearchUrlTask() { UrlToSearchFor = requestedUrl, ShowUserNotifications = false, AutoReturnFromQuery = autoReturnFromQuery }; task.ToIntent(i); @@ -39,9 +39,9 @@ namespace keepass2android.services.Kp2aAutofill protected override FilledAutofillFieldCollection GetDataset(Intent data) { - if (App.Kp2a.GetDb()==null || (App.Kp2a.QuickLocked)) + if (App.Kp2a.CurrentDb==null || (App.Kp2a.QuickLocked)) return null; - var entryOutput = App.Kp2a.GetDb().LastOpenedEntry; + var entryOutput = App.Kp2a.LastOpenedEntry; return GetFilledAutofillFieldCollectionFromEntry(entryOutput, this); } diff --git a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs index 88cd6131..ec97ba64 100644 --- a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs +++ b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs @@ -33,9 +33,9 @@ namespace keepass2android.services protected override FilledAutofillFieldCollection GetSuggestedEntry(string query) { - if (App.Kp2a.GetDb()?.LastOpenedEntry?.SearchUrl == query) + if (App.Kp2a.LastOpenedEntry?.SearchUrl == query) return ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry( - App.Kp2a.GetDb()?.LastOpenedEntry, this); + App.Kp2a.LastOpenedEntry, this); return null; } @@ -43,7 +43,7 @@ namespace keepass2android.services { - var intent = new Intent(this, typeof(FileSelectActivity)); + var intent = new Intent(this, typeof(SelectCurrentDbActivity)); Dictionary outputFields = new Dictionary(); foreach (var p in parser.ClientFormData.HintMap) diff --git a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs index 12830a83..78a41289 100644 --- a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs +++ b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs @@ -26,7 +26,7 @@ namespace keepass2android.services public Intent GetRestartAppIntent(Context context) { - var intent = new Intent(context, typeof(FileSelectActivity)); + var intent = new Intent(context, typeof(SelectCurrentDbActivity)); intent.AddFlags(ActivityFlags.ForwardResult); return intent; } diff --git a/src/keepass2android/services/OngoingNotificationsService.cs b/src/keepass2android/services/OngoingNotificationsService.cs index dfda57e0..0b3f56b6 100644 --- a/src/keepass2android/services/OngoingNotificationsService.cs +++ b/src/keepass2android/services/OngoingNotificationsService.cs @@ -97,7 +97,7 @@ namespace keepass2android Kp2aLog.Log("OngoingNotificationsService.OnTaskRemoved: " + rootIntent.Action); // If the user has closed the task (probably by swiping it out of the recent apps list) then lock the database - App.Kp2a.LockDatabase(); + App.Kp2a.Lock(); } public override void OnDestroy() @@ -111,7 +111,7 @@ namespace keepass2android // If the service is killed, then lock the database immediately if (App.Kp2a.DatabaseIsUnlocked) { - App.Kp2a.LockDatabase(false); + App.Kp2a.Lock(false); } UnregisterReceiver(_screenOffReceiver); @@ -228,16 +228,22 @@ namespace keepass2android private static string GetDatabaseName() { - - var db = App.Kp2a.GetDb().KpDatabase; - var name = db.Name; - if (String.IsNullOrEmpty(name)) - { - //todo: if paranoid ("don't remember recent files") return "***" - name = App.Kp2a.GetFileStorage(db.IOConnectionInfo).GetFilenameWithoutPathAndExt(db.IOConnectionInfo); - } + string displayString = ""; + foreach (Database db in App.Kp2a.OpenDatabases) + { + var kpDatabase = db.KpDatabase; + var dbname = kpDatabase.Name; + if (String.IsNullOrEmpty(dbname)) + { + //todo: if paranoid ("don't remember recent files") return "***" + dbname = App.Kp2a.GetFileStorage(kpDatabase.IOConnectionInfo).GetFilenameWithoutPathAndExt(kpDatabase.IOConnectionInfo); + } + if (displayString != "") + displayString = displayString + ", "; + displayString += dbname; + } - return name; + return displayString; } #endregion diff --git a/src/keepass2android/settings/Argon2Preference.cs b/src/keepass2android/settings/Argon2Preference.cs index b8396f23..41efd76d 100644 --- a/src/keepass2android/settings/Argon2Preference.cs +++ b/src/keepass2android/settings/Argon2Preference.cs @@ -18,8 +18,8 @@ namespace keepass2android.settings { get { - var kdfparams = App.Kp2a.GetDb().KpDatabase.KdfParameters; - var kdf = KdfPool.Get(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid); + var kdfparams = App.Kp2a.CurrentDb.KpDatabase.KdfParameters; + var kdf = KdfPool.Get(App.Kp2a.CurrentDb.KpDatabase.KdfParameters.KdfUuid); if (!(kdf is Argon2Kdf)) { new Argon2Kdf().GetDefaultParameters(); @@ -29,7 +29,7 @@ namespace keepass2android.settings } set { - App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(Argon2Kdf.ParamIterations, value); + App.Kp2a.CurrentDb.KpDatabase.KdfParameters.SetUInt64(Argon2Kdf.ParamIterations, value); } } } @@ -50,8 +50,8 @@ namespace keepass2android.settings { get { - var kdfparams = App.Kp2a.GetDb().KpDatabase.KdfParameters; - var kdf = KdfPool.Get(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid); + var kdfparams = App.Kp2a.CurrentDb.KpDatabase.KdfParameters; + var kdf = KdfPool.Get(App.Kp2a.CurrentDb.KpDatabase.KdfParameters.KdfUuid); if (!(kdf is Argon2Kdf)) { new Argon2Kdf().GetDefaultParameters(); @@ -61,7 +61,7 @@ namespace keepass2android.settings } set { - App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt32(Argon2Kdf.ParamParallelism, (uint) value); + App.Kp2a.CurrentDb.KpDatabase.KdfParameters.SetUInt32(Argon2Kdf.ParamParallelism, (uint) value); } } } @@ -82,8 +82,8 @@ namespace keepass2android.settings { get { - var kdfparams = App.Kp2a.GetDb().KpDatabase.KdfParameters; - var kdf = KdfPool.Get(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid); + var kdfparams = App.Kp2a.CurrentDb.KpDatabase.KdfParameters; + var kdf = KdfPool.Get(App.Kp2a.CurrentDb.KpDatabase.KdfParameters.KdfUuid); if (!(kdf is Argon2Kdf)) { new Argon2Kdf().GetDefaultParameters(); @@ -93,7 +93,7 @@ namespace keepass2android.settings } set { - App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(Argon2Kdf.ParamMemory, value); + App.Kp2a.CurrentDb.KpDatabase.KdfParameters.SetUInt64(Argon2Kdf.ParamMemory, value); } } } diff --git a/src/keepass2android/settings/DatabaseSettingsActivity.cs b/src/keepass2android/settings/DatabaseSettingsActivity.cs index eed864b7..65935cdb 100644 --- a/src/keepass2android/settings/DatabaseSettingsActivity.cs +++ b/src/keepass2android/settings/DatabaseSettingsActivity.cs @@ -423,7 +423,7 @@ namespace keepass2android FindPreference(GetString(Resource.String.design_key)).PreferenceChange += (sender, args) => Activity.Recreate(); - Database db = App.Kp2a.GetDb(); + Database db = App.Kp2a.CurrentDb; if (db != null) { ListPreference kdfPref = (ListPreference) FindPreference(GetString(Resource.String.kdf_key)); @@ -457,7 +457,7 @@ namespace keepass2android algorithmPref.SetValueIndex(algoValues.Select((v, i) => new { kdf = v, index = i }).First(el => el.kdf == db.KpDatabase.DataCipherUuid.ToHexString()).index); algorithmPref.PreferenceChange += AlgorithmPrefChange; algorithmPref.Summary = - CipherPool.GlobalPool.GetCipher(App.Kp2a.GetDb().KpDatabase.DataCipherUuid).DisplayName; + CipherPool.GlobalPool.GetCipher(App.Kp2a.CurrentDb.KpDatabase.DataCipherUuid).DisplayName; UpdateImportDbPref(); UpdateImportKeyfilePref(); } @@ -504,8 +504,8 @@ namespace keepass2android FindPreference("IconSetKey").PreferenceChange += (sender, args) => { - if (App.Kp2a.GetDb() != null) - App.Kp2a.GetDb().DrawableFactory.Clear(); + if (App.Kp2a.CurrentDb!= null) + App.Kp2a.CurrentDb.DrawableFactory.Clear(); }; @@ -565,11 +565,11 @@ namespace keepass2android private void AlgorithmPrefChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs) { - var db = App.Kp2a.GetDb(); + var db = App.Kp2a.CurrentDb; var previousCipher = db.KpDatabase.DataCipherUuid; db.KpDatabase.DataCipherUuid = new PwUuid(MemUtil.HexStringToByteArray((string)preferenceChangeEventArgs.NewValue)); - SaveDb save = new SaveDb(Activity, App.Kp2a, new ActionOnFinish(Activity, (success, message, activity) => + SaveDb save = new SaveDb(Activity, App.Kp2a, App.Kp2a.CurrentDb, new ActionOnFinish(Activity, (success, message, activity) => { if (!success) { @@ -586,7 +586,7 @@ namespace keepass2android private void UpdateKdfScreen() { - var db = App.Kp2a.GetDb(); + var db = App.Kp2a.CurrentDb; var kdf = KdfPool.Get(db.KpDatabase.KdfParameters.KdfUuid); var kdfpref = FindPreference(GetString(Resource.String.kdf_key)); @@ -624,7 +624,7 @@ namespace keepass2android private void OnKdfChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs) { - var db = App.Kp2a.GetDb(); + var db = App.Kp2a.CurrentDb; var previousKdfParams = db.KpDatabase.KdfParameters; Kp2aLog.Log("previous kdf: " + KdfPool.Get(db.KpDatabase.KdfParameters.KdfUuid) + " " + db.KpDatabase.KdfParameters.KdfUuid.ToHexString() ); db.KpDatabase.KdfParameters = @@ -634,7 +634,7 @@ namespace keepass2android Kp2aLog.Log("--new kdf: " + KdfPool.Get(db.KpDatabase.KdfParameters.KdfUuid) + " " + db.KpDatabase.KdfParameters.KdfUuid.ToHexString()); - SaveDb save = new SaveDb(Activity, App.Kp2a, new ActionOnFinish(Activity, (success, message, activity) => + SaveDb save = new SaveDb(Activity, App.Kp2a, App.Kp2a.CurrentDb, new ActionOnFinish(Activity, (success, message, activity) => { if (!success) { @@ -669,7 +669,7 @@ namespace keepass2android private void PrepareTemplates(Database db) { Preference pref = FindPreference("AddTemplates_pref_key"); - if ((!db.DatabaseFormat.SupportsTemplates) || (AddTemplateEntries.ContainsAllTemplates(App.Kp2a))) + if ((!db.DatabaseFormat.SupportsTemplates) || (AddTemplateEntries.ContainsAllTemplates(App.Kp2a.CurrentDb))) { pref.Enabled = false; } @@ -692,7 +692,7 @@ namespace keepass2android private void PrepareMasterPassword() { Preference changeMaster = FindPreference(GetString(Resource.String.master_pwd_key)); - if (App.Kp2a.GetDb().CanWrite) + if (App.Kp2a.CurrentDb.CanWrite) { changeMaster.Enabled = true; changeMaster.PreferenceClick += delegate { new SetPasswordDialog(Activity).Show(); }; @@ -717,7 +717,7 @@ namespace keepass2android String previousName = db.KpDatabase.Name; db.KpDatabase.Name = e.NewValue.ToString(); - SaveDb save = new SaveDb(Activity, App.Kp2a, new ActionOnFinish(Activity, (success, message, activity) => + SaveDb save = new SaveDb(Activity, App.Kp2a, App.Kp2a.CurrentDb, new ActionOnFinish(Activity, (success, message, activity) => { if (!success) { @@ -755,7 +755,7 @@ namespace keepass2android String previousUsername = db.KpDatabase.DefaultUserName; db.KpDatabase.DefaultUserName = e.NewValue.ToString(); - SaveDb save = new SaveDb(Activity, App.Kp2a, new ActionOnFinish(Activity, (success, message, activity) => + SaveDb save = new SaveDb(Activity, App.Kp2a, App.Kp2a.CurrentDb, new ActionOnFinish(Activity, (success, message, activity) => { if (!success) { @@ -791,8 +791,8 @@ namespace keepass2android private void OnUseOfflineCacheChanged(object sender, Preference.PreferenceChangeEventArgs e) { //ensure the user gets a matching database - if (App.Kp2a.GetDb() != null && !App.Kp2a.GetDb().Ioc.IsLocalFile()) - App.Kp2a.LockDatabase(false); + if (App.Kp2a.CurrentDb!= null && !App.Kp2a.CurrentDb.Ioc.IsLocalFile()) + App.Kp2a.LockSingleDatabase(App.Kp2a.CurrentDb, false); if (!(bool)e.NewValue) { @@ -842,7 +842,7 @@ namespace keepass2android importDb.Enabled = false; return; } - CompositeKey masterKey = App.Kp2a.GetDb().KpDatabase.MasterKey; + CompositeKey masterKey = App.Kp2a.CurrentDb.KpDatabase.MasterKey; if (masterKey.ContainsType(typeof(KcpKeyFile))) { IOConnectionInfo iocKeyfile = ((KcpKeyFile)masterKey.GetUserKey(typeof(KcpKeyFile))).Ioc; @@ -871,12 +871,12 @@ namespace keepass2android { try { - CompositeKey masterKey = App.Kp2a.GetDb().KpDatabase.MasterKey; + CompositeKey masterKey = App.Kp2a.CurrentDb.KpDatabase.MasterKey; var sourceIoc = ((KcpKeyFile)masterKey.GetUserKey(typeof(KcpKeyFile))).Ioc; var newIoc = IoUtil.ImportFileToInternalDirectory(sourceIoc, Activity, App.Kp2a); ((KcpKeyFile)masterKey.GetUserKey(typeof(KcpKeyFile))).ResetIoc(newIoc); var keyfileString = IOConnectionInfo.SerializeToString(newIoc); - App.Kp2a.StoreOpenedFileAsRecent(App.Kp2a.GetDb().Ioc, keyfileString); + App.Kp2a.StoreOpenedFileAsRecent(App.Kp2a.CurrentDb.Ioc, keyfileString); return () => { UpdateImportKeyfilePref(); @@ -914,7 +914,7 @@ namespace keepass2android //Import db/key file preferences: Preference importDb = FindPreference("import_db_prefs"); bool isLocalOrContent = - App.Kp2a.GetDb().Ioc.IsLocalFile() || App.Kp2a.GetDb().Ioc.Path.StartsWith("content://"); + App.Kp2a.CurrentDb.Ioc.IsLocalFile() || App.Kp2a.CurrentDb.Ioc.Path.StartsWith("content://"); if (!isLocalOrContent) { importDb.Summary = GetString(Resource.String.OnlyAvailableForLocalFiles); @@ -922,7 +922,7 @@ namespace keepass2android } else { - if (IoUtil.IsInInternalDirectory(App.Kp2a.GetDb().Ioc.Path, Activity)) + if (IoUtil.IsInInternalDirectory(App.Kp2a.CurrentDb.Ioc.Path, Activity)) { importDb.Summary = GetString(Resource.String.FileIsInInternalDirectory); importDb.Enabled = false; @@ -941,7 +941,7 @@ namespace keepass2android { try { - var sourceIoc = App.Kp2a.GetDb().Ioc; + var sourceIoc = App.Kp2a.CurrentDb.Ioc; var newIoc = IoUtil.ImportFileToInternalDirectory(sourceIoc, Activity, App.Kp2a); return () => { @@ -949,7 +949,12 @@ namespace keepass2android builder .SetMessage(Resource.String.DatabaseFileMoved); builder.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => - PasswordActivity.Launch(Activity, newIoc, new NullTask(), App.Kp2a.GetDb().KpDatabase.MasterKey)); + { + var key = App.Kp2a.CurrentDb.KpDatabase.MasterKey; + App.Kp2a.CloseDatabase(App.Kp2a.CurrentDb); + PasswordActivity.Launch(Activity, newIoc, new NullTask(), key); + + }); builder.Show(); }; diff --git a/src/keepass2android/settings/RoundsPreference.cs b/src/keepass2android/settings/RoundsPreference.cs index 8056de8f..1769e1be 100644 --- a/src/keepass2android/settings/RoundsPreference.cs +++ b/src/keepass2android/settings/RoundsPreference.cs @@ -77,7 +77,7 @@ namespace keepass2android.settings paramValue = 1; } - Database db = App.Kp2a.GetDb(); + Database db = App.Kp2a.CurrentDb; ulong oldValue = ParamValue; @@ -89,7 +89,7 @@ namespace keepass2android.settings ParamValue = paramValue; Handler handler = new Handler(); - SaveDb save = new SaveDb((Activity)Context, App.Kp2a, new KdfNumberParamPreference.AfterSave((Activity)Context, handler, oldValue, this)); + SaveDb save = new SaveDb((Activity)Context, App.Kp2a, App.Kp2a.CurrentDb, new KdfNumberParamPreference.AfterSave((Activity)Context, handler, oldValue, this)); ProgressTask pt = new ProgressTask(App.Kp2a, (Activity)Context, save); pt.Run(); @@ -118,7 +118,7 @@ namespace keepass2android.settings } else { DisplayMessage(_ctx); - App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds, _oldRounds); + App.Kp2a.CurrentDb.KpDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds, _oldRounds); } base.Run(); @@ -140,18 +140,18 @@ namespace keepass2android.settings get { AesKdf kdf = new AesKdf(); - if (!kdf.Uuid.Equals(App.Kp2a.GetDb().KpDatabase.KdfParameters.KdfUuid)) + if (!kdf.Uuid.Equals(App.Kp2a.CurrentDb.KpDatabase.KdfParameters.KdfUuid)) return (uint) PwDefs.DefaultKeyEncryptionRounds; else { - ulong uRounds = App.Kp2a.GetDb().KpDatabase.KdfParameters.GetUInt64( + ulong uRounds = App.Kp2a.CurrentDb.KpDatabase.KdfParameters.GetUInt64( AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds); uRounds = Math.Min(uRounds, 0xFFFFFFFEUL); return (uint) uRounds; } } - set { App.Kp2a.GetDb().KpDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds, value); } + set { App.Kp2a.CurrentDb.KpDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds, value); } } public RoundsPreference(Context context, IAttributeSet attrs):base(context, attrs) diff --git a/src/keepass2android/timeout/TimeoutHelper.cs b/src/keepass2android/timeout/TimeoutHelper.cs index 4d34ec8a..834f6cde 100644 --- a/src/keepass2android/timeout/TimeoutHelper.cs +++ b/src/keepass2android/timeout/TimeoutHelper.cs @@ -83,7 +83,7 @@ namespace keepass2android public static void Resume(Activity act) { - if ( App.Kp2a.GetDb() != null) + if ( App.Kp2a.CurrentDb!= null) { Timeout.Cancel(act); } @@ -92,14 +92,13 @@ namespace keepass2android static bool IocChanged(IOConnectionInfo ioc, IOConnectionInfo other) { if ((ioc == null) || (other == null)) return false; - return ioc.GetDisplayName() != other.GetDisplayName(); + return !ioc.IsSameFileAs(other); } - public static bool CheckShutdown(Activity act, IOConnectionInfo ioc) { + public static bool CheckDbChanged(Activity act, IOConnectionInfo ioc) { if (( !App.Kp2a.DatabaseIsUnlocked ) - || (IocChanged(ioc, App.Kp2a.GetDb().Ioc))) //file was changed from ActionSend-Intent + || (IocChanged(ioc, App.Kp2a.CurrentDb.Ioc))) //file was changed from ActionSend-Intent { - App.Kp2a.LockDatabase(); return true; } return false; diff --git a/src/keepass2android/views/PwEntryView.cs b/src/keepass2android/views/PwEntryView.cs index 930b692e..f34ca265 100644 --- a/src/keepass2android/views/PwEntryView.cs +++ b/src/keepass2android/views/PwEntryView.cs @@ -71,8 +71,9 @@ namespace keepass2android.view _textView = (TextView)ev.FindViewById(Resource.Id.entry_text); _textView.TextSize = PrefsUtil.GetListTextSize(groupActivity); + Database db = App.Kp2a.FindDatabaseForEntryId(pw.Uuid); - ev.FindViewById(Resource.Id.entry_icon_bkg).Visibility = App.Kp2a.GetDb().DrawableFactory.IsWhiteIconSet ? ViewStates.Visible : ViewStates.Gone; + ev.FindViewById(Resource.Id.entry_icon_bkg).Visibility = db.DrawableFactory.IsWhiteIconSet ? ViewStates.Visible : ViewStates.Gone; _textviewDetails = (TextView)ev.FindViewById(Resource.Id.entry_text_detail); _textviewDetails.TextSize = PrefsUtil.GetListDetailTextSize(groupActivity); @@ -106,15 +107,16 @@ namespace keepass2android.view ev.FindViewById(Resource.Id.icon).Visibility = ViewStates.Visible; ev.FindViewById(Resource.Id.check_mark).Visibility = ViewStates.Invisible; + Database db = App.Kp2a.FindDatabaseForEntryId(_entry.Uuid); ImageView iv = (ImageView)ev.FindViewById(Resource.Id.icon); bool isExpired = pw.Expires && pw.ExpiryTime < DateTime.Now; if (isExpired) { - App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iv, Context, App.Kp2a.GetDb().KpDatabase, PwIcon.Expired, PwUuid.Zero, false); + db.DrawableFactory.AssignDrawableTo(iv, Context, db.KpDatabase, PwIcon.Expired, PwUuid.Zero, false); } else { - App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iv, Context, App.Kp2a.GetDb().KpDatabase, pw.IconId, pw.CustomIconUuid, false); + db.DrawableFactory.AssignDrawableTo(iv, Context, db.KpDatabase, pw.IconId, pw.CustomIconUuid, false); } String title = pw.Strings.ReadSafe(PwDefs.TitleField); @@ -138,7 +140,7 @@ namespace keepass2android.view _textView.SetTextColor(new Color((int)_defaultTextColor)); String detail = pw.Strings.ReadSafe(PwDefs.UserNameField); - detail = SprEngine.Compile(detail, new SprContext(_entry, App.Kp2a.GetDb().KpDatabase, SprCompileFlags.All)); + detail = SprEngine.Compile(detail, new SprContext(_entry, db.KpDatabase, SprCompileFlags.All)); if ((_showDetail == false) || (String.IsNullOrEmpty(detail))) { diff --git a/src/keepass2android/views/PwGroupView.cs b/src/keepass2android/views/PwGroupView.cs index f94157b0..819c3c4a 100644 --- a/src/keepass2android/views/PwGroupView.cs +++ b/src/keepass2android/views/PwGroupView.cs @@ -63,7 +63,9 @@ namespace keepass2android.view _label = (TextView) gv.FindViewById(Resource.Id.group_label); _label.TextSize = size-8; - gv.FindViewById(Resource.Id.group_icon_bkg).Visibility = App.Kp2a.GetDb().DrawableFactory.IsWhiteIconSet ? ViewStates.Visible : ViewStates.Gone; + Database db = App.Kp2a.FindDatabaseForGroupId(pw.Uuid); + + gv.FindViewById(Resource.Id.group_icon_bkg).Visibility = db.DrawableFactory.IsWhiteIconSet ? ViewStates.Visible : ViewStates.Gone; gv.FindViewById(Resource.Id.icon).Visibility = ViewStates.Visible; gv.FindViewById(Resource.Id.check_mark).Visibility = ViewStates.Invisible; @@ -79,7 +81,8 @@ namespace keepass2android.view _pwGroup = pw; ImageView iv = (ImageView) gv.FindViewById(Resource.Id.icon); - App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iv, _groupBaseActivity, App.Kp2a.GetDb().KpDatabase, pw.IconId, pw.CustomIconUuid, true); + Database db = App.Kp2a.FindDatabaseForGroupId(pw.Uuid); + db.DrawableFactory.AssignDrawableTo(iv, _groupBaseActivity, db.KpDatabase, pw.IconId, pw.CustomIconUuid, true); gv.FindViewById(Resource.Id.icon).Visibility = ViewStates.Visible; gv.FindViewById(Resource.Id.check_mark).Visibility = ViewStates.Invisible;