first version to have multiple databases open at the same time. needs testing and bug fixing.

This commit is contained in:
Philipp Crocoll
2018-10-16 06:33:00 +02:00
parent 8a993b7dcb
commit a2dab72b25
73 changed files with 1329 additions and 669 deletions

View File

@@ -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
{
/// <summary>
/// Locks the currently open database, quicklocking if available (unless false is passed for allowQuickUnlock)
/// </summary>
void LockDatabase(bool allowQuickUnlock = true);
/// <summary>
/// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
/// </summary>
void Lock(bool allowQuickUnlock);
/// <summary>
/// Loads the specified data as the currently open database, as unlocked.
/// </summary>
void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey,
ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat);
/// <summary>
/// Returns the current database
/// </summary>
Database GetDb();
/// <summary>
/// Loads the specified data as the currently open database, as unlocked.
/// </summary>
Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat);
/// <summary>
/// Tell the app that the file from ioc was opened with keyfile.
/// </summary>
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = "");
HashSet<PwGroup> DirtyGroups { get; }
void MarkAllGroupsAsDirty();
/// <summary>
/// Returns the current database
/// </summary>
Database CurrentDb { get; }
IEnumerable<Database> OpenDatabases { get; }
void CloseDatabase(Database db);
Database FindDatabaseForGroupId(PwUuid groupKey);
Database FindDatabaseForEntryId(PwUuid entryId);
/// <summary>
/// Tell the app that the file from ioc was opened with keyfile.
/// </summary>
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = "");
/// <summary>
/// Creates a new database and returns it
@@ -111,6 +123,10 @@ namespace keepass2android
bool CheckForDuplicateUuids { get; }
#if !NoNet
ICertificateErrorHandler CertificateErrorHandler { get; }
#endif
}
}
}

View File

@@ -90,8 +90,9 @@
<ItemGroup>
<Compile Include="database\CheckDatabaseForChanges.cs" />
<Compile Include="database\edit\AddTemplateEntries.cs" />
<Compile Include="database\edit\ChangeTemplateIds.cs" />
<Compile Include="database\edit\CopyEntry.cs" />
<Compile Include="database\edit\DeleteMultipleItems.cs" />
<Compile Include="database\edit\DeleteMultipleItemsFromOneDatabase.cs" />
<Compile Include="database\edit\EditGroup.cs" />
<Compile Include="database\edit\MoveElements.cs" />
<Compile Include="database\KdbDatabaseFormat.cs" />

View File

@@ -23,7 +23,7 @@ namespace keepass2android
/// <summary>
/// EqualityComparer implementation to compare PwGroups based on their Id
/// </summary>
class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
public class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
{
#region IEqualityComparer implementation
public bool Equals (PwGroup x, PwGroup y)

View File

@@ -86,6 +86,11 @@ namespace keepass2android
ReadOnlyReason_PreKitKat,
ReadOnlyReason_ReadOnlyFlag,
ReadOnlyReason_ReadOnlyKitKat,
ReadOnlyReason_LocalBackup
ReadOnlyReason_LocalBackup,
UpdatingTemplateIds,
ChangleLegacyTemplateIds_Message,
ChangleLegacyTemplateIds_Title,
Ok,
cancel
}
}

View File

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

View File

@@ -38,7 +38,6 @@ namespace keepass2android
public Dictionary<PwUuid, PwGroup> Groups = new Dictionary<PwUuid, PwGroup>(new PwUuidEqualityComparer());
public Dictionary<PwUuid, PwEntry> Entries = new Dictionary<PwUuid, PwEntry>(new PwUuidEqualityComparer());
public HashSet<PwGroup> Dirty = new HashSet<PwGroup>(new PwGroupEqualityFromIdComparer());
public PwGroup Root;
public PwDatabase KpDatabase;
public IOConnectionInfo Ioc
@@ -49,11 +48,6 @@ namespace keepass2android
}
}
/// <summary>
/// Information about the last opened entry. Includes the entry but also transformed fields.
/// </summary>
public PwEntryOutput LastOpenedEntry { get; set; }
/// <summary>
/// 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);
}
}
}

View File

@@ -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();
/// <summary>
/// Constructs the PwEntryOutput by replacing the placeholders
/// </summary>
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;
}

View File

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

View File

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

View File

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

View File

@@ -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<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
public static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
{
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<PwEntry> 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<PwEntry>();
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));
}
}
}

View File

@@ -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<string, string> uuidMap = new Dictionary<string, string>();
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();
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -7,12 +7,12 @@ using KeePassLib.Interfaces;
namespace keepass2android
{
public class DeleteMultipleItems : DeleteRunnable
public class DeleteMultipleItemsFromOneDatabase : DeleteRunnable
{
private readonly List<IStructureItem> _elementsToDelete;
private readonly bool _canRecycle;
public DeleteMultipleItems(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List<IStructureItem> 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;

View File

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

View File

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

View File

@@ -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
/// </summary>
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;
}

View File

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

View File

@@ -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;
/// <summary>
/// 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
/// <param name="finish"></param>
/// <param name="dontSave"></param>
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
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)

View File

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

View File

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