2017-11-29 19:13:38 +01:00
|
|
|
using System;
|
2020-03-24 13:01:14 +01:00
|
|
|
using System.Runtime.InteropServices.WindowsRuntime;
|
|
|
|
using System.Threading.Tasks;
|
2017-09-18 18:40:43 +02:00
|
|
|
using Windows.Storage;
|
2018-06-12 18:40:54 +02:00
|
|
|
using Microsoft.HockeyApp;
|
2020-03-24 17:31:34 +01:00
|
|
|
using ModernKeePass.Domain.Exceptions;
|
2017-11-22 18:54:03 +01:00
|
|
|
using ModernKeePass.Interfaces;
|
2017-09-18 18:40:43 +02:00
|
|
|
using ModernKeePass.ViewModels;
|
2017-09-26 15:38:58 +02:00
|
|
|
using ModernKeePassLib;
|
2017-11-02 18:45:08 +01:00
|
|
|
using ModernKeePassLib.Cryptography.KeyDerivation;
|
2017-09-26 15:38:58 +02:00
|
|
|
using ModernKeePassLib.Interfaces;
|
|
|
|
using ModernKeePassLib.Keys;
|
|
|
|
using ModernKeePassLib.Serialization;
|
2017-09-18 18:40:43 +02:00
|
|
|
|
2017-11-29 19:13:38 +01:00
|
|
|
namespace ModernKeePass.Services
|
2017-09-18 18:40:43 +02:00
|
|
|
{
|
2018-02-23 18:09:21 +01:00
|
|
|
public class DatabaseService: SingletonServiceBase<DatabaseService>, IDatabaseService
|
2017-09-18 18:40:43 +02:00
|
|
|
{
|
2017-10-30 18:34:38 +01:00
|
|
|
private readonly PwDatabase _pwDatabase = new PwDatabase();
|
2018-02-23 18:09:21 +01:00
|
|
|
private readonly ISettingsService _settings;
|
2017-10-11 18:43:27 +02:00
|
|
|
private StorageFile _databaseFile;
|
2017-10-30 18:34:38 +01:00
|
|
|
private GroupVm _recycleBin;
|
2018-06-21 16:40:04 +02:00
|
|
|
private CompositeKey _compositeKey;
|
2017-09-18 18:40:43 +02:00
|
|
|
|
|
|
|
public GroupVm RootGroup { get; set; }
|
2017-10-30 18:34:38 +01:00
|
|
|
|
|
|
|
public GroupVm RecycleBin
|
|
|
|
{
|
|
|
|
get { return _recycleBin; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
_recycleBin = value;
|
2018-03-12 17:30:03 +01:00
|
|
|
_pwDatabase.RecycleBinUuid = _recycleBin?.IdUuid;
|
2017-10-30 18:34:38 +01:00
|
|
|
}
|
|
|
|
}
|
2017-11-02 11:30:03 +01:00
|
|
|
|
2018-06-19 18:47:37 +02:00
|
|
|
public string Name => _databaseFile?.Name;
|
2017-10-30 18:34:38 +01:00
|
|
|
|
|
|
|
public bool RecycleBinEnabled
|
|
|
|
{
|
|
|
|
get { return _pwDatabase.RecycleBinEnabled; }
|
|
|
|
set { _pwDatabase.RecycleBinEnabled = value; }
|
|
|
|
}
|
2018-06-21 16:40:04 +02:00
|
|
|
|
2017-11-02 11:30:03 +01:00
|
|
|
public PwUuid DataCipher
|
|
|
|
{
|
|
|
|
get { return _pwDatabase.DataCipherUuid; }
|
2017-11-03 15:48:55 +01:00
|
|
|
set { _pwDatabase.DataCipherUuid = value; }
|
2017-11-02 11:30:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public PwCompressionAlgorithm CompressionAlgorithm
|
|
|
|
{
|
|
|
|
get { return _pwDatabase.Compression; }
|
|
|
|
set { _pwDatabase.Compression = value; }
|
|
|
|
}
|
|
|
|
|
2017-11-03 15:48:55 +01:00
|
|
|
public KdfParameters KeyDerivation
|
|
|
|
{
|
|
|
|
get { return _pwDatabase.KdfParameters; }
|
|
|
|
set { _pwDatabase.KdfParameters = value; }
|
|
|
|
}
|
2017-12-01 17:59:38 +01:00
|
|
|
|
2018-01-08 18:52:03 +01:00
|
|
|
public bool IsOpen => _pwDatabase.IsOpen;
|
|
|
|
public bool IsFileOpen => !_pwDatabase.IsOpen && _databaseFile != null;
|
|
|
|
public bool IsClosed => _databaseFile == null;
|
2018-02-23 18:09:21 +01:00
|
|
|
public bool HasChanged { get; set; }
|
|
|
|
|
|
|
|
public DatabaseService() : this(SettingsService.Instance)
|
|
|
|
{
|
|
|
|
}
|
2018-01-08 18:52:03 +01:00
|
|
|
|
2018-02-23 18:09:21 +01:00
|
|
|
public DatabaseService(ISettingsService settings)
|
2017-12-01 17:59:38 +01:00
|
|
|
{
|
|
|
|
_settings = settings;
|
|
|
|
}
|
2018-09-07 18:16:40 +02:00
|
|
|
|
2018-02-23 18:09:21 +01:00
|
|
|
|
2017-10-25 18:29:50 +02:00
|
|
|
/// <summary>
|
|
|
|
/// Open a KeePass database
|
|
|
|
/// </summary>
|
2018-06-19 18:47:37 +02:00
|
|
|
/// <param name="databaseFile">The database file</param>
|
2017-11-07 18:45:35 +01:00
|
|
|
/// <param name="key">The database composite key</param>
|
2017-10-25 18:29:50 +02:00
|
|
|
/// <param name="createNew">True to create a new database before opening it</param>
|
|
|
|
/// <returns>An error message, if any</returns>
|
2020-03-24 13:01:14 +01:00
|
|
|
public async Task Open(StorageFile databaseFile, CompositeKey key, bool createNew = false)
|
2017-09-18 18:40:43 +02:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2018-06-21 16:40:04 +02:00
|
|
|
if (databaseFile == null)
|
|
|
|
{
|
|
|
|
throw new ArgumentNullException(nameof(databaseFile));
|
|
|
|
}
|
2017-11-22 18:54:03 +01:00
|
|
|
if (key == null)
|
|
|
|
{
|
2018-01-08 18:52:03 +01:00
|
|
|
throw new ArgumentNullException(nameof(key));
|
2017-11-22 18:54:03 +01:00
|
|
|
}
|
2018-03-09 18:06:06 +01:00
|
|
|
|
2018-06-21 16:40:04 +02:00
|
|
|
_compositeKey = key;
|
2020-03-24 13:01:14 +01:00
|
|
|
var fileContents = await FileIO.ReadBufferAsync(databaseFile);
|
|
|
|
var ioConnection = IOConnectionInfo.FromByteArray(fileContents.ToArray());
|
2017-11-29 19:13:38 +01:00
|
|
|
if (createNew)
|
|
|
|
{
|
|
|
|
_pwDatabase.New(ioConnection, key);
|
2018-09-07 18:16:40 +02:00
|
|
|
|
2017-12-01 17:59:38 +01:00
|
|
|
var fileFormat = _settings.GetSetting<string>("DefaultFileFormat");
|
2017-11-29 19:13:38 +01:00
|
|
|
switch (fileFormat)
|
|
|
|
{
|
|
|
|
case "4":
|
|
|
|
KeyDerivation = KdfPool.Get("Argon2").GetDefaultParameters();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-10-12 11:07:34 +02:00
|
|
|
else _pwDatabase.Open(ioConnection, key, new NullStatusLogger());
|
2018-06-19 18:47:37 +02:00
|
|
|
|
|
|
|
_databaseFile = databaseFile;
|
2017-11-22 18:54:03 +01:00
|
|
|
RootGroup = new GroupVm(_pwDatabase.RootGroup, null, RecycleBinEnabled ? _pwDatabase.RecycleBinUuid : null);
|
2017-09-18 18:40:43 +02:00
|
|
|
}
|
2018-01-08 18:52:03 +01:00
|
|
|
catch (InvalidCompositeKeyException ex)
|
2017-09-18 18:40:43 +02:00
|
|
|
{
|
2018-06-12 18:40:54 +02:00
|
|
|
HockeyClient.Current.TrackException(ex);
|
2018-01-08 18:52:03 +01:00
|
|
|
throw new ArgumentException(ex.Message, ex);
|
2017-09-18 18:40:43 +02:00
|
|
|
}
|
2018-01-08 18:52:03 +01:00
|
|
|
}
|
2018-09-07 18:16:40 +02:00
|
|
|
|
2020-03-24 17:31:34 +01:00
|
|
|
public async Task ReOpen()
|
2018-03-09 18:06:06 +01:00
|
|
|
{
|
2020-03-24 17:31:34 +01:00
|
|
|
await Open(_databaseFile, _compositeKey);
|
2018-03-09 18:06:06 +01:00
|
|
|
}
|
|
|
|
|
2018-01-08 18:52:03 +01:00
|
|
|
/// <summary>
|
|
|
|
/// Commit the changes to the currently opened database to file
|
|
|
|
/// </summary>
|
|
|
|
public void Save()
|
|
|
|
{
|
2018-03-09 18:06:06 +01:00
|
|
|
if (!IsOpen) return;
|
2018-01-08 18:52:03 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
_pwDatabase.Save(new NullStatusLogger());
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
throw new SaveException(e);
|
2017-09-22 15:40:24 +02:00
|
|
|
}
|
2017-09-18 18:40:43 +02:00
|
|
|
}
|
2017-10-12 11:45:00 +02:00
|
|
|
|
2017-10-25 18:29:50 +02:00
|
|
|
/// <summary>
|
|
|
|
/// Save the current database to another file and open it
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="file">The new database file</param>
|
2020-03-24 13:01:14 +01:00
|
|
|
public async Task Save(StorageFile file)
|
2017-10-12 11:45:00 +02:00
|
|
|
{
|
2018-06-19 18:47:37 +02:00
|
|
|
var oldFile = _databaseFile;
|
|
|
|
_databaseFile = file;
|
2017-11-24 12:17:41 +01:00
|
|
|
try
|
|
|
|
{
|
2020-03-24 13:01:14 +01:00
|
|
|
var fileContents = await FileIO.ReadBufferAsync(file);
|
|
|
|
_pwDatabase.SaveAs(IOConnectionInfo.FromByteArray(fileContents.ToArray()), true, new NullStatusLogger());
|
2017-11-24 12:17:41 +01:00
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
2018-06-19 18:47:37 +02:00
|
|
|
_databaseFile = oldFile;
|
2017-11-24 12:17:41 +01:00
|
|
|
throw;
|
|
|
|
}
|
2017-10-12 11:45:00 +02:00
|
|
|
}
|
|
|
|
|
2017-10-25 18:29:50 +02:00
|
|
|
/// <summary>
|
2018-01-08 18:52:03 +01:00
|
|
|
/// Close the currently opened database
|
2017-10-25 18:29:50 +02:00
|
|
|
/// </summary>
|
2018-06-18 15:39:01 +02:00
|
|
|
public void Close(bool releaseFile = true)
|
2017-09-18 18:40:43 +02:00
|
|
|
{
|
2018-01-08 18:52:03 +01:00
|
|
|
_pwDatabase?.Close();
|
2018-06-19 18:47:37 +02:00
|
|
|
if (releaseFile) _databaseFile = null;
|
2017-09-18 18:40:43 +02:00
|
|
|
}
|
2017-10-31 12:14:26 +01:00
|
|
|
|
|
|
|
public void AddDeletedItem(PwUuid id)
|
|
|
|
{
|
|
|
|
_pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow));
|
|
|
|
}
|
2017-11-01 17:35:57 -04:00
|
|
|
|
2018-03-12 17:30:03 +01:00
|
|
|
public void CreateRecycleBin(string title)
|
2017-11-01 17:35:57 -04:00
|
|
|
{
|
2018-03-12 17:30:03 +01:00
|
|
|
RecycleBin = RootGroup.AddNewGroup(title);
|
2017-11-01 17:35:57 -04:00
|
|
|
RecycleBin.IsSelected = true;
|
2018-06-14 10:20:00 +02:00
|
|
|
RecycleBin.IconId = (int)PwIcon.TrashBin;
|
2017-11-01 17:35:57 -04:00
|
|
|
}
|
2018-06-21 16:40:04 +02:00
|
|
|
|
|
|
|
public void UpdateCompositeKey(CompositeKey newCompositeKey)
|
|
|
|
{
|
2018-07-12 12:30:58 +02:00
|
|
|
if (newCompositeKey == null) return;
|
|
|
|
_compositeKey = newCompositeKey;
|
|
|
|
_pwDatabase.MasterKey = newCompositeKey;
|
2018-06-21 16:40:04 +02:00
|
|
|
}
|
2017-09-18 18:40:43 +02:00
|
|
|
}
|
|
|
|
}
|