public override void Run()
        {
            StatusLogger.UpdateMessage(UiStringKey.SettingPassword);
            PwDatabase   pm     = _app.GetDb().KpDatabase;
            CompositeKey newKey = new CompositeKey();

            if (String.IsNullOrEmpty(_password) == false)
            {
                newKey.AddUserKey(new KcpPassword(_password));
            }
            if (String.IsNullOrEmpty(_keyfile) == false)
            {
                try {
                    newKey.AddUserKey(new KcpKeyFile(_keyfile));
                } catch (Exception) {
                    //TODO MessageService.ShowWarning (strKeyFile, KPRes.KeyFileError, exKF);
                    return;
                }
            }

            DateTime     previousMasterKeyChanged = pm.MasterKeyChanged;
            CompositeKey previousKey = pm.MasterKey;

            pm.MasterKeyChanged = DateTime.Now;
            pm.MasterKey        = newKey;

            // Save Database
            _onFinishToRun = new AfterSave(previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
            SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave);

            save.SetStatusLogger(StatusLogger);
            save.Run();
        }
Exemplo n.º 2
0
            public override void Run()
            {
                StatusLogger.UpdateMessage(UiStringKey.exporting_database);
                var          pd     = _app.GetDb().KpDatabase;
                PwExportInfo pwInfo = new PwExportInfo(pd.RootGroup, pd, true);

                try
                {
                    using (var writeTransaction = _app.GetFileStorage(_targetIoc).OpenWriteTransaction(_targetIoc, _app.GetDb().KpDatabase.UseFileTransactions))
                    {
                        Stream sOut = writeTransaction.OpenFile();
                        _fileFormat.Export(pwInfo, sOut, new NullStatusLogger());

                        if (sOut != null)
                        {
                            sOut.Close();
                        }

                        writeTransaction.CommitWrite();
                    }
                    Finish(true);
                }
                catch (Exception ex)
                {
                    Finish(false, ex.Message);
                }
            }
            public override void Run()
            {
                StatusLogger.UpdateMessage(UiStringKey.exporting_database);

                try
                {
                    var fileStorage = _app.GetFileStorage(_targetIoc);
                    if (fileStorage is IOfflineSwitchable)
                    {
                        ((IOfflineSwitchable)fileStorage).IsOffline = false;
                    }

                    CompositeKey masterKey = App.Kp2a.CurrentDb.KpDatabase.MasterKey;
                    var          sourceIoc = ((KcpKeyFile)masterKey.GetUserKey(typeof(KcpKeyFile))).Ioc;

                    IoUtil.Copy(_targetIoc, sourceIoc, App.Kp2a);

                    if (fileStorage is IOfflineSwitchable)
                    {
                        ((IOfflineSwitchable)fileStorage).IsOffline = App.Kp2a.OfflineMode;
                    }

                    Finish(true);
                }
                catch (Exception ex)
                {
                    Finish(false, ex.Message);
                }
            }
Exemplo n.º 4
0
 public Connection(DiscordSocketClient client, StatusLogger statusLogger, DiscordMessageHandler messageHandler, IDataStorage storage)
 {
     _client         = client;
     _statusLogger   = statusLogger;
     _messageHandler = messageHandler;
     _storage        = storage;
 }
Exemplo n.º 5
0
 private FileHashChange FileHashChanged(IOConnectionInfo ioc, byte[] hashOfFileOnDisk)
 {
     StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.CheckingTargetFileForChanges));
     byte[] fileHash = HashOriginalFile(ioc);
     if (fileHash == null)
     {
         return(FileHashChange.FileNotAvailable);
     }
     return(MemUtil.ArraysEqual(fileHash, hashOfFileOnDisk) ? FileHashChange.Equal : FileHashChange.Changed);
 }
Exemplo n.º 6
0
        public override void Run()
        {
            StatusLogger.UpdateMessage(UiStringKey.AddingGroup);
            // Generate new group
            Group = new PwGroup(true, true, _name, (PwIcon)_iconId);
            if (_groupCustomIconId != null)
            {
                Group.CustomIconUuid = _groupCustomIconId;
            }
            Parent.AddGroup(Group, true);

            // Commit to disk
            SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, DontSave);

            save.SetStatusLogger(StatusLogger);
            save.Run();
        }
Exemplo n.º 7
0
            public override void Run()
            {
                StatusLogger.UpdateMessage(UiStringKey.SynchronizingOtpAuxFile);
                try
                {
                    //simply open the file. The file storage does a complete sync.
                    using (App.Kp2a.GetOtpAuxFileStorage(_ioc).OpenFileForRead(_ioc))
                    {
                    }

                    Finish(true);
                }
                catch (Exception e)
                {
                    Finish(false, e.Message);
                }
            }
Exemplo n.º 8
0
        public override void Run()
        {
            StatusLogger.UpdateMessage(UiStringKey.AddingEntry);

            List <PwEntry> addedEntries;
            var            templateGroup = AddTemplates(out addedEntries);

            if (addedEntries.Any())
            {
                _app.DirtyGroups.Add(templateGroup);

                // Commit to disk
                SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
                save.SetStatusLogger(StatusLogger);
                save.Run();
            }
        }
Exemplo n.º 9
0
        private void MergeIn(IFileStorage fileStorage, IOConnectionInfo ioc)
        {
            StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase));

            PwDatabase pwImp      = new PwDatabase();
            PwDatabase pwDatabase = _db.KpDatabase;

            pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey);
            pwImp.MemoryProtection = pwDatabase.MemoryProtection.CloneDeep();
            pwImp.MasterKey        = pwDatabase.MasterKey;
            var stream = GetStreamForBaseFile(fileStorage, ioc);

            _db.DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null);


            pwDatabase.MergeIn(pwImp, PwMergeMethod.Synchronize, null);
        }
Exemplo n.º 10
0
        public override void Run()
        {
            StatusLogger.UpdateMessage(UiStringKey.progress_create);
            Database db = _app.CreateNewDatabase(_makeCurrent);

            db.KpDatabase = new KeePassLib.PwDatabase();

            if (_key == null)
            {
                _key = new CompositeKey();                 //use a temporary key which should be changed after creation
            }

            db.KpDatabase.New(_ioc, _key, _app.GetFileStorage(_ioc).GetFilenameWithoutPathAndExt(_ioc));

            db.KpDatabase.KdfParameters = (new AesKdf()).GetDefaultParameters();
            db.KpDatabase.Name          = "Keepass2Android Password Database";
            //re-set the name of the root group because the PwDatabase uses UrlUtil which is not appropriate for all file storages:
            db.KpDatabase.RootGroup.Name = _app.GetFileStorage(_ioc).GetFilenameWithoutPathAndExt(_ioc);

            // Set Database state
            db.Root         = db.KpDatabase.RootGroup;
            db.SearchHelper = new SearchDbHelper(_app);

            // Add a couple default groups
            AddGroup internet = AddGroup.GetInstance(_ctx, _app, "Internet", 1, null, db.KpDatabase.RootGroup, null, true);

            internet.Run();
            AddGroup email = AddGroup.GetInstance(_ctx, _app, "eMail", 19, null, db.KpDatabase.RootGroup, null, true);

            email.Run();

            List <PwEntry>     addedEntries;
            AddTemplateEntries addTemplates = new AddTemplateEntries(_ctx, _app, null);

            addTemplates.AddTemplates(out addedEntries);

            // Commit changes
            SaveDb save = new SaveDb(_ctx, _app, db, OnFinishToRun, _dontSave);

            save.SetStatusLogger(StatusLogger);
            _onFinishToRun = null;
            save.Run();

            db.UpdateGlobals();
        }
Exemplo n.º 11
0
            public override void Run()
            {
                if (Success)
                {
                    // Mark parent group dirty
                    _addGroup.Db.Dirty.Add(_addGroup.Parent);

                    // Add group to global list
                    _addGroup.Db.Groups[_addGroup.Group.Uuid] = _addGroup.Group;
                }
                else
                {
                    StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
                    _addGroup.Parent.Groups.Remove(_addGroup.Group);
                }

                base.Run();
            }
Exemplo n.º 12
0
            public override void Run()
            {
                if (Success)
                {
                    PwGroup parent = _entry.ParentGroup;

                    // Mark parent group dirty
                    _app.DirtyGroups.Add(parent);
                }
                else
                {
                    StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
                    //TODO test fail
                    _entry.ParentGroup.Entries.Remove(_entry);
                }

                base.Run();
            }
Exemplo n.º 13
0
        public override void Run()
        {
            StatusLogger.UpdateMessage(UiStringKey.AddingEntry);

            //make sure we're not adding the entry if it was added before.
            //(this might occur in very rare cases where the user dismissis the save dialog
            //by rotating the screen while saving and then presses save again)
            if (_parentGroup.FindEntry(_entry.Uuid, false) == null)
            {
                _parentGroup.AddEntry(_entry, true);
            }


            // Commit to disk
            SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);

            save.SetStatusLogger(StatusLogger);
            save.Run();
        }
Exemplo n.º 14
0
        public override void Run()
        {
            StatusLogger.UpdateMessage(StatusMessage);

            List <PwGroup> touchedGroups            = new List <PwGroup>();
            List <PwGroup> permanentlyDeletedGroups = new List <PwGroup>();

            Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
            PerformDelete(touchedGroups, permanentlyDeletedGroups);

            _onFinishToRun = new ActionOnFinish(ActiveActivity, (success, message, activity) =>
            {
                if (success)
                {
                    foreach (var g in touchedGroups)
                    {
                        App.DirtyGroups.Add(g);
                    }
                    foreach (var g in permanentlyDeletedGroups)
                    {
                        //remove groups from global lists if present there
                        App.DirtyGroups.Remove(g);
                        Db.GroupsById.Remove(g.Uuid);
                        Db.Elements.Remove(g);
                    }
                }
                else
                {
                    // Let's not bother recovering from a failure to save.  It is too much work.
                    App.Lock(false);
                }
            }, OnFinishToRun);

            // Commit database
            SaveDb save = new SaveDb(Ctx, App, Db, OnFinishToRun, false);

            save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;

            save.SetStatusLogger(StatusLogger);
            save.Run();
        }
Exemplo n.º 15
0
        public override void Run()
        {
            try
            {
                IOConnectionInfo ioc         = _app.GetDb().Ioc;
                IFileStorage     fileStorage = _app.GetFileStorage(ioc);
                if (fileStorage is CachingFileStorage)
                {
                    throw new Exception("Cannot sync a cached database!");
                }
                StatusLogger.UpdateMessage(UiStringKey.CheckingDatabaseForChanges);

                //download file from remote location and calculate hash:
                StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile));


                MemoryStream remoteData = new MemoryStream();
                using (
                    HashingStreamEx hashingRemoteStream = new HashingStreamEx(fileStorage.OpenFileForRead(ioc), false,
                                                                              new SHA256Managed()))
                {
                    hashingRemoteStream.CopyTo(remoteData);
                    hashingRemoteStream.Close();

                    if (!MemUtil.ArraysEqual(_app.GetDb().KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
                    {
                        _app.TriggerReload(_context);
                        Finish(true);
                    }
                    else
                    {
                        Finish(true, _app.GetResourceString(UiStringKey.RemoteDatabaseUnchanged));
                    }
                }
            }
            catch (Exception e)
            {
                Finish(false, e.Message);
            }
        }
Exemplo n.º 16
0
        private void PrefectureQueryRoutine()
        {
            while (true)
            {
                foreach (Prefecture pPrefecture in Prefectures)
                {
                    if (pPrefecture.Enabled)
                    {
                        if (pPrefecture.SecondsUntilQuery == 0)
                        {
                            pPrefecture.Status = ePrefectureStatus.Querying;

                            StatusText = "Interrogation de " + pPrefecture.Name;

                            bool?bResult = pPrefecture.Requestor.Request();

                            pPrefecture.Status = bResult == null ? ePrefectureStatus.Error : (bResult == true ? ePrefectureStatus.AppointmentAvailable : ePrefectureStatus.AppointmentUnavailable);

                            if (pPrefecture.Status == ePrefectureStatus.AppointmentAvailable)
                            {
                                StatusLogger.WriteEvent(pPrefecture.Id);

                                SystemSounds.Asterisk.Play();
                            }

                            pPrefecture.SecondsUntilQuery = pPrefecture.QueryTime;
                        }
                        else
                        {
                            pPrefecture.SecondsUntilQuery--;
                        }
                    }

                    StatusText = "Inactif.";
                }

                Thread.Sleep(1000);
            }
        }
Exemplo n.º 17
0
            public override void Run()
            {
                if (Success)
                {
                    // Mark parent group dirty. Even only the last modification date changed, this might affect sort order
                    PwGroup parent = _updatedEntry.ParentGroup;
                    if (parent != null)
                    {
                        // Mark parent group dirty
                        _app.GetDb().Dirty.Add(parent);
                    }
                }
                else
                {
                    StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
                    // If we fail to save, back out changes to global structure
                    //TODO test fail
                    _updatedEntry.AssignProperties(_backup, false, true, false);
                }

                base.Run();
            }
Exemplo n.º 18
0
            public override void Run()
            {
                StatusLogger.UpdateMessage(UiStringKey.exporting_database);
                var          pd     = _app.CurrentDb.KpDatabase;
                PwExportInfo pwInfo = new PwExportInfo(pd.RootGroup, pd, true);

                try
                {
                    var fileStorage = _app.GetFileStorage(_targetIoc);
                    if (fileStorage is IOfflineSwitchable)
                    {
                        ((IOfflineSwitchable)fileStorage).IsOffline = false;
                    }
                    using (var writeTransaction = fileStorage.OpenWriteTransaction(_targetIoc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
                    {
                        Stream sOut = writeTransaction.OpenFile();
                        _fileFormat.Export(pwInfo, sOut, new NullStatusLogger());

                        if (sOut != null)
                        {
                            sOut.Close();
                        }

                        writeTransaction.CommitWrite();
                    }
                    if (fileStorage is IOfflineSwitchable)
                    {
                        ((IOfflineSwitchable)fileStorage).IsOffline = App.Kp2a.OfflineMode;
                    }

                    Finish(true);
                }
                catch (Exception ex)
                {
                    Finish(false, ex.Message);
                }
            }
Exemplo n.º 19
0
        public override void Run()
        {
            try
            {
                try
                {
                    //make sure the file data is stored in the recent files list even if loading fails
                    SaveFileData(_ioc, _keyfileOrProvider);


                    StatusLogger.UpdateMessage(UiStringKey.loading_database);
                    //get the stream data into a single stream variable (databaseStream) regardless whether its preloaded or not:
                    MemoryStream preloadedMemoryStream = _databaseData == null ? null : _databaseData.Result;
                    MemoryStream databaseStream;
                    if (preloadedMemoryStream != null)
                    {
                        databaseStream = preloadedMemoryStream;
                    }
                    else
                    {
                        using (Stream s = _app.GetFileStorage(_ioc).OpenFileForRead(_ioc))
                        {
                            databaseStream = new MemoryStream();
                            s.CopyTo(databaseStream);
                            databaseStream.Seek(0, SeekOrigin.Begin);
                        }
                    }

                    //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(_app.GetFileStorage(_ioc).GetFileExtension(_ioc)));
                    TryLoad(databaseStream);



                    success = true;
                }
                catch (Exception e)
                {
                    this.Exception = e;
                    throw;
                }
            }
            catch (KeyFileException)
            {
                Kp2aLog.Log("KeyFileException");
                Finish(false,                 /*TODO Localize: use Keepass error text KPRes.KeyFileError (including "or invalid format")*/
                       _app.GetResourceString(UiStringKey.keyfile_does_not_exist), false, Exception);
            }
            catch (AggregateException e)
            {
                string message = e.Message;
                foreach (var innerException in e.InnerExceptions)
                {
                    message = innerException.Message;
                    // Override the message shown with the last (hopefully most recent) inner exception
                    Kp2aLog.LogUnexpectedError(innerException);
                }
                Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + message, false, Exception);
                return;
            }
            catch (DuplicateUuidsException e)
            {
                Kp2aLog.Log(e.ToString());
                Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + e.Message + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), false, Exception);
                return;
            }
            catch (Exception e)
            {
                if (!(e is InvalidCompositeKeyException))
                {
                    Kp2aLog.LogUnexpectedError(e);
                }
                Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + (e.Message ?? (e is FileNotFoundException ? _app.GetResourceString(UiStringKey.FileNotFound) :  "")), false, Exception);
                return;
            }
        }
Exemplo n.º 20
0
        public override void Run()
        {
            if (!_dontSave)
            {
                try
                {
                    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!");
                        return;
                    }

                    string message = _app.GetResourceString(UiStringKey.saving_database);

                    if (ShowDatabaseIocInStatus)
                    {
                        message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
                    }

                    StatusLogger.UpdateMessage(message);

                    IOConnectionInfo ioc         = _db.Ioc;
                    IFileStorage     fileStorage = _app.GetFileStorage(ioc);

                    if (_streamForOrigFile == null)
                    {
                        if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave)) ||
                            (_db.KpDatabase.HashOfFileOnDisk == null))                                //first time saving
                        {
                            PerformSaveWithoutCheck(fileStorage, ioc);
                            Finish(true);
                            return;
                        }
                    }


                    if (
                        (_streamForOrigFile != null) ||
                        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:
                        )
                    {
                        //ask user...
                        _app.AskYesNoCancel(UiStringKey.TitleSyncQuestion, UiStringKey.MessageSyncQuestion,
                                            UiStringKey.YesSynchronize,
                                            UiStringKey.NoOverwrite,
                                            //yes = sync
                                            (sender, args) =>
                        {
                            Action runHandler = () =>
                            {
                                //note: when synced, the file might be downloaded once again from the server. Caching the data
                                //in the hashing function would solve this but increases complexity. I currently assume the files are
                                //small.
                                MergeIn(fileStorage, ioc);
                                PerformSaveWithoutCheck(fileStorage, ioc);
                                _db.UpdateGlobals();
                                Finish(true);
                            };
                            RunInWorkerThread(runHandler);
                        },
                                            //no = overwrite
                                            (sender, args) =>
                        {
                            RunInWorkerThread(() =>
                            {
                                PerformSaveWithoutCheck(fileStorage, ioc);
                                Finish(true);
                            });
                        },
                                            //cancel
                                            (sender, args) =>
                        {
                            RunInWorkerThread(() => Finish(false));
                        },
                                            _ctx
                                            );
                    }
                    else
                    {
                        PerformSaveWithoutCheck(fileStorage, ioc);
                        Finish(true);
                    }
                }
                catch (Exception e)
                {
                    /* TODO KPDesktop:
                     * catch(Exception exSave)
                     * {
                     * MessageService.ShowSaveWarning(pd.IOConnectionInfo, exSave, true);
                     * bSuccess = false;
                     * }
                     */
                    Kp2aLog.LogUnexpectedError(e);
                    Finish(false, e.Message);
                    return;
                }
            }
            else
            {
                Finish(true);
            }
        }
Exemplo n.º 21
0
 private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
 {
     StatusLogger.UpdateSubMessage("");
     _db.SaveData();
     _db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
 }
        public override void Run()
        {
            try
            {
                IOConnectionInfo ioc         = _app.GetDb().Ioc;
                IFileStorage     fileStorage = _app.GetFileStorage(ioc);
                if (!(fileStorage is CachingFileStorage))
                {
                    throw new Exception("Cannot sync a non-cached database!");
                }
                StatusLogger.UpdateMessage(UiStringKey.SynchronizingCachedDatabase);
                CachingFileStorage cachingFileStorage = (CachingFileStorage)fileStorage;

                //download file from remote location and calculate hash:
                StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile));
                string hash;

                MemoryStream remoteData;
                try
                {
                    remoteData = cachingFileStorage.GetRemoteDataAndHash(ioc, out hash);
                }
                catch (FileNotFoundException)
                {
                    StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.RestoringRemoteFile));
                    cachingFileStorage.UpdateRemoteFile(ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
                    Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
                    return;
                }

                //check if remote file was modified:
                if (cachingFileStorage.GetBaseVersionHash(ioc) != hash)
                {
                    //remote file is modified
                    if (cachingFileStorage.HasLocalChanges(ioc))
                    {
                        //conflict! need to merge
                        _saveDb = new SaveDb(_context, _app, new ActionOnFinish((success, result) =>
                        {
                            if (!success)
                            {
                                Finish(false, result);
                            }
                            else
                            {
                                Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
                            }
                            _saveDb = null;
                        }), false, remoteData);
                        _saveDb.Run();

                        _app.GetDb().MarkAllGroupsAsDirty();
                    }
                    else
                    {
                        //only the remote file was modified -> reload database.
                        //note: it's best to lock the database and do a complete reload here (also better for UI consistency in case something goes wrong etc.)
                        _app.TriggerReload(_context);
                        Finish(true);
                    }
                }
                else
                {
                    //remote file is unmodified
                    if (cachingFileStorage.HasLocalChanges(ioc))
                    {
                        //but we have local changes -> upload:
                        StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.UploadingFile));
                        cachingFileStorage.UpdateRemoteFile(ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
                        StatusLogger.UpdateSubMessage("");
                        Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
                    }
                    else
                    {
                        //files are in sync: just set the result
                        Finish(true, _app.GetResourceString(UiStringKey.FilesInSync));
                    }
                }
            }
            catch (Exception e)
            {
                Finish(false, e.Message);
            }
        }
Exemplo n.º 23
0
 public RegistrationHandler()
 {
     _botConfiguration = Unity.Resolve <BotConfiguration>();
     _statusLogger     = Unity.Resolve <StatusLogger>();
 }
Exemplo n.º 24
0
        public override void Run()
        {
            if (!_dontSave)
            {
                try
                {
                    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!");
                        return;
                    }

                    string message = _app.GetResourceString(UiStringKey.saving_database);

                    if (ShowDatabaseIocInStatus)
                    {
                        message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
                    }

                    StatusLogger.UpdateMessage(message);

                    IOConnectionInfo ioc         = _db.Ioc;
                    IFileStorage     fileStorage = _app.GetFileStorage(ioc);

                    if (_streamForOrigFile == null)
                    {
                        if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave)) ||
                            (_db.KpDatabase.HashOfFileOnDisk == null))                                //first time saving
                        {
                            PerformSaveWithoutCheck(fileStorage, ioc);
                            Finish(true);
                            return;
                        }
                    }


                    bool hasStreamForOrigFile = (_streamForOrigFile != null);
                    bool hasChangeFast        = hasStreamForOrigFile ||
                                                fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion); //first try to use the fast change detection;
                    bool hasHashChanged = hasChangeFast ||
                                          (FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) ==
                                           FileHashChange.Changed); //if that fails, hash the file and compare:

                    if (hasHashChanged)
                    {
                        Kp2aLog.Log("Conflict. " + hasStreamForOrigFile + " " + hasChangeFast + " " + hasHashChanged);

                        bool alwaysMerge = (PreferenceManager.GetDefaultSharedPreferences(Application.Context)
                                            .GetBoolean("AlwaysMergeOnConflict", false));

                        if (alwaysMerge)
                        {
                            MergeAndFinish(fileStorage, ioc);
                        }
                        else
                        {
                            //ask user...
                            _app.AskYesNoCancel(UiStringKey.TitleSyncQuestion, UiStringKey.MessageSyncQuestion,
                                                UiStringKey.YesSynchronize,
                                                UiStringKey.NoOverwrite,
                                                //yes = sync
                                                (sender, args) =>
                            {
                                Action runHandler = () => { MergeAndFinish(fileStorage, ioc); };
                                RunInWorkerThread(runHandler);
                            },
                                                //no = overwrite
                                                (sender, args) =>
                            {
                                RunInWorkerThread(() =>
                                {
                                    PerformSaveWithoutCheck(fileStorage, ioc);
                                    Finish(true);
                                });
                            },
                                                //cancel
                                                (sender, args) =>
                            {
                                RunInWorkerThread(() => Finish(false));
                            },
                                                _ctx
                                                );
                        }
                    }
                    else
                    {
                        PerformSaveWithoutCheck(fileStorage, ioc);
                        Finish(true);
                    }
                }
                catch (Exception e)
                {
                    /* TODO KPDesktop:
                     * catch(Exception exSave)
                     * {
                     * MessageService.ShowSaveWarning(pd.IOConnectionInfo, exSave, true);
                     * bSuccess = false;
                     * }
                     */
                    Kp2aLog.LogUnexpectedError(e);
                    Finish(false, e.Message);
                    return;
                }
            }
            else
            {
                Finish(true);
            }
        }