Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/autotype/AutoType.cpp4
-rw-r--r--src/autotype/AutoType.h2
-rw-r--r--src/browser/BrowserEntrySaveDialog.cpp9
-rw-r--r--src/browser/BrowserService.cpp95
-rw-r--r--src/browser/BrowserService.h15
-rw-r--r--src/cli/Add.cpp8
-rw-r--r--src/cli/Clip.cpp4
-rw-r--r--src/cli/Clip.h2
-rw-r--r--src/cli/Edit.cpp6
-rw-r--r--src/cli/Extract.cpp3
-rw-r--r--src/cli/List.cpp2
-rw-r--r--src/cli/Locate.cpp2
-rw-r--r--src/cli/Merge.cpp20
-rw-r--r--src/cli/Remove.cpp6
-rw-r--r--src/cli/Show.cpp2
-rw-r--r--src/core/Config.cpp1
-rw-r--r--src/core/CustomData.cpp10
-rw-r--r--src/core/CustomData.h2
-rw-r--r--src/core/Database.cpp630
-rw-r--r--src/core/Database.h144
-rw-r--r--src/core/Entry.cpp39
-rw-r--r--src/core/Entry.h5
-rw-r--r--src/core/EntryAttachments.cpp10
-rw-r--r--src/core/EntryAttachments.h2
-rw-r--r--src/core/EntryAttributes.cpp12
-rw-r--r--src/core/EntryAttributes.h2
-rw-r--r--src/core/Group.cpp131
-rw-r--r--src/core/Group.h18
-rw-r--r--src/core/Metadata.cpp14
-rw-r--r--src/core/Metadata.h3
-rw-r--r--src/crypto/SymmetricCipher.cpp4
-rw-r--r--src/format/CsvExporter.cpp4
-rw-r--r--src/format/CsvExporter.h5
-rw-r--r--src/format/Kdbx3Reader.cpp57
-rw-r--r--src/format/Kdbx3Reader.h10
-rw-r--r--src/format/Kdbx3Writer.cpp4
-rw-r--r--src/format/Kdbx4Reader.cpp61
-rw-r--r--src/format/Kdbx4Reader.h10
-rw-r--r--src/format/Kdbx4Writer.cpp4
-rw-r--r--src/format/KdbxReader.cpp20
-rw-r--r--src/format/KdbxReader.h24
-rw-r--r--src/format/KdbxXmlReader.cpp8
-rw-r--r--src/format/KdbxXmlReader.h4
-rw-r--r--src/format/KdbxXmlWriter.cpp2
-rw-r--r--src/format/KeePass1Reader.cpp68
-rw-r--r--src/format/KeePass1Reader.h9
-rw-r--r--src/format/KeePass2Reader.cpp28
-rw-r--r--src/format/KeePass2Reader.h4
-rw-r--r--src/format/KeePass2Writer.cpp4
-rw-r--r--src/gui/DatabaseOpenWidget.cpp35
-rw-r--r--src/gui/DatabaseOpenWidget.h6
-rw-r--r--src/gui/DatabaseTabWidget.cpp776
-rw-r--r--src/gui/DatabaseTabWidget.h91
-rw-r--r--src/gui/DatabaseWidget.cpp621
-rw-r--r--src/gui/DatabaseWidget.h150
-rw-r--r--src/gui/DatabaseWidgetStateSync.cpp4
-rw-r--r--src/gui/EditWidget.cpp2
-rw-r--r--src/gui/EditWidgetIcons.cpp39
-rw-r--r--src/gui/EditWidgetIcons.h7
-rw-r--r--src/gui/EntryPreviewWidget.cpp5
-rw-r--r--src/gui/KeePass1OpenWidget.cpp4
-rw-r--r--src/gui/MainWindow.cpp81
-rw-r--r--src/gui/MainWindow.h5
-rw-r--r--src/gui/SearchWidget.cpp2
-rw-r--r--src/gui/UnlockDatabaseDialog.cpp4
-rw-r--r--src/gui/UnlockDatabaseDialog.h2
-rw-r--r--src/gui/csvImport/CsvParserModel.cpp4
-rw-r--r--src/gui/csvImport/CsvParserModel.h2
-rw-r--r--src/gui/dbsettings/DatabaseSettingsDialog.cpp2
-rw-r--r--src/gui/dbsettings/DatabaseSettingsDialog.h7
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidget.cpp2
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidget.h6
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp6
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp2
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h2
-rw-r--r--src/gui/entry/AutoTypeAssociationsModel.cpp2
-rw-r--r--src/gui/entry/AutoTypeAssociationsModel.h2
-rw-r--r--src/gui/entry/EditEntryWidget.cpp36
-rw-r--r--src/gui/entry/EditEntryWidget.h13
-rw-r--r--src/gui/group/EditGroupWidget.cpp9
-rw-r--r--src/gui/group/EditGroupWidget.h4
-rw-r--r--src/gui/group/GroupModel.cpp8
-rw-r--r--src/gui/group/GroupView.cpp4
-rw-r--r--src/gui/group/GroupView.h2
-rw-r--r--src/gui/widgets/ElidedLabel.cpp1
-rw-r--r--src/gui/wizard/NewDatabaseWizard.cpp23
-rw-r--r--src/gui/wizard/NewDatabaseWizard.h6
-rw-r--r--src/gui/wizard/NewDatabaseWizardPage.cpp6
-rw-r--r--src/gui/wizard/NewDatabaseWizardPage.h4
-rw-r--r--src/keys/drivers/YubiKey.cpp6
-rw-r--r--src/sshagent/SSHAgent.cpp4
-rw-r--r--src/sshagent/SSHAgent.h2
-rw-r--r--tests/TestAutoType.cpp3
-rw-r--r--tests/TestAutoType.h5
-rw-r--r--tests/TestCli.cpp12
-rw-r--r--tests/TestCsvExporter.cpp20
-rw-r--r--tests/TestCsvExporter.h5
-rw-r--r--tests/TestDatabase.cpp37
-rw-r--r--tests/TestDeletedObjects.cpp27
-rw-r--r--tests/TestDeletedObjects.h2
-rw-r--r--tests/TestKdbx2.cpp17
-rw-r--r--tests/TestKdbx2.h2
-rw-r--r--tests/TestKdbx3.cpp34
-rw-r--r--tests/TestKdbx3.h8
-rw-r--r--tests/TestKdbx4.cpp28
-rw-r--r--tests/TestKdbx4.h8
-rw-r--r--tests/TestKeePass1Reader.cpp27
-rw-r--r--tests/TestKeePass1Reader.h5
-rw-r--r--tests/TestKeePass2Format.cpp17
-rw-r--r--tests/TestKeePass2Format.h16
-rw-r--r--tests/TestKeys.cpp25
-rw-r--r--tests/TestMerge.cpp8
-rw-r--r--tests/TestModified.cpp234
-rw-r--r--tests/gui/TestGui.cpp104
-rw-r--r--tests/gui/TestGui.h3
115 files changed, 2065 insertions, 2114 deletions
diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp
index 9d6b75557..c2ddc9752 100644
--- a/src/autotype/AutoType.cpp
+++ b/src/autotype/AutoType.cpp
@@ -268,7 +268,7 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
* Global Autotype entry-point function
* Perform global Auto-Type on the active window
*/
-void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
+void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbList)
{
if (!m_plugin) {
return;
@@ -287,7 +287,7 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
QList<AutoTypeMatch> matchList;
- for (Database* db : dbList) {
+ for (const auto& db : dbList) {
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
for (Entry* entry : dbEntries) {
const QSet<QString> sequences = autoTypeSequences(entry, windowTitle).toSet();
diff --git a/src/autotype/AutoType.h b/src/autotype/AutoType.h
index 28b36bd82..f58a1c0c1 100644
--- a/src/autotype/AutoType.h
+++ b/src/autotype/AutoType.h
@@ -58,7 +58,7 @@ public:
static void createTestInstance();
public slots:
- void performGlobalAutoType(const QList<Database*>& dbList);
+ void performGlobalAutoType(const QList<QSharedPointer<Database>>& dbList);
void raiseWindow();
signals:
diff --git a/src/browser/BrowserEntrySaveDialog.cpp b/src/browser/BrowserEntrySaveDialog.cpp
index 384482b84..305c46c6d 100644
--- a/src/browser/BrowserEntrySaveDialog.cpp
+++ b/src/browser/BrowserEntrySaveDialog.cpp
@@ -19,6 +19,9 @@
#include "BrowserEntrySaveDialog.h"
#include "ui_BrowserEntrySaveDialog.h"
+#include "core/Database.h"
+#include "gui/DatabaseWidget.h"
+
BrowserEntrySaveDialog::BrowserEntrySaveDialog(QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::BrowserEntrySaveDialog())
@@ -43,10 +46,10 @@ int BrowserEntrySaveDialog::setItems(QList<DatabaseWidget*>& databaseWidgets, Da
uint counter = 0;
int activeIndex = -1;
for (const auto dbWidget : databaseWidgets) {
- QString databaseName = dbWidget->getDatabaseName();
- QString databaseFileName = dbWidget->getDatabaseFileName();
+ QString databaseName = dbWidget->database()->metadata()->name();
+ QString databaseFileName = dbWidget->database()->filePath();
- QListWidgetItem* item = new QListWidgetItem();
+ auto* item = new QListWidgetItem();
item->setData(Qt::UserRole, counter);
// Show database name (and filename if the name has been set in metadata)
diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp
index 95b9008e6..1f616bd85 100644
--- a/src/browser/BrowserService.cpp
+++ b/src/browser/BrowserService.cpp
@@ -69,7 +69,7 @@ bool BrowserService::isDatabaseOpened() const
return false;
}
- return dbWidget->currentMode() == DatabaseWidget::ViewMode || dbWidget->currentMode() == DatabaseWidget::EditMode;
+ return dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode;
}
bool BrowserService::openDatabase(bool triggerUnlock)
@@ -83,7 +83,7 @@ bool BrowserService::openDatabase(bool triggerUnlock)
return false;
}
- if (dbWidget->currentMode() == DatabaseWidget::ViewMode || dbWidget->currentMode() == DatabaseWidget::EditMode) {
+ if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
return true;
}
@@ -106,14 +106,14 @@ void BrowserService::lockDatabase()
return;
}
- if (dbWidget->currentMode() == DatabaseWidget::ViewMode || dbWidget->currentMode() == DatabaseWidget::EditMode) {
+ if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
dbWidget->lock();
}
}
QString BrowserService::getDatabaseRootUuid()
{
- Database* db = getDatabase();
+ auto db = getDatabase();
if (!db) {
return {};
}
@@ -128,7 +128,7 @@ QString BrowserService::getDatabaseRootUuid()
QString BrowserService::getDatabaseRecycleBinUuid()
{
- Database* db = getDatabase();
+ auto db = getDatabase();
if (!db) {
return {};
}
@@ -150,7 +150,7 @@ QString BrowserService::storeKey(const QString& key)
return id;
}
- Database* db = getDatabase();
+ auto db = getDatabase();
if (!db) {
return {};
}
@@ -194,7 +194,7 @@ QString BrowserService::storeKey(const QString& key)
QString BrowserService::getKey(const QString& id)
{
- Database* db = getDatabase();
+ auto db = getDatabase();
if (!db) {
return {};
}
@@ -268,13 +268,10 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
return result;
}
-void BrowserService::addEntry(const QString& id,
- const QString& login,
- const QString& password,
- const QString& url,
- const QString& submitUrl,
- const QString& realm,
- Database* selectedDb)
+void BrowserService::addEntry(const QString& id, const QString& login,
+ const QString& password, const QString& url,
+ const QString& submitUrl, const QString& realm,
+ QSharedPointer<Database> selectedDb)
{
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this,
@@ -286,10 +283,10 @@ void BrowserService::addEntry(const QString& id,
Q_ARG(QString, url),
Q_ARG(QString, submitUrl),
Q_ARG(QString, realm),
- Q_ARG(Database*, selectedDb));
+ Q_ARG(QSharedPointer<Database>, selectedDb));
}
- Database* db = selectedDb ? selectedDb : selectedDatabase();
+ auto db = selectedDb ? selectedDb : selectedDatabase();
if (!db) {
return;
}
@@ -299,7 +296,7 @@ void BrowserService::addEntry(const QString& id,
return;
}
- Entry* entry = new Entry();
+ auto* entry = new Entry();
entry->setUuid(QUuid::createUuid());
entry->setTitle(QUrl(url).host());
entry->setUrl(url);
@@ -341,12 +338,12 @@ void BrowserService::updateEntry(const QString& id,
Q_ARG(QString, submitUrl));
}
- Database* db = selectedDatabase();
+ auto db = selectedDatabase();
if (!db) {
return;
}
- Entry* entry = db->resolveEntry(QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1())));
+ Entry* entry = db->rootGroup()->findEntryByUuid(QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1())));
if (!entry) {
// If entry is not found for update, add a new one to the selected database
addEntry(id, login, password, url, submitUrl, "", db);
@@ -382,10 +379,10 @@ void BrowserService::updateEntry(const QString& id,
}
}
-QList<Entry*> BrowserService::searchEntries(Database* db, const QString& hostname, const QString& url)
+QList<Entry*> BrowserService::searchEntries(QSharedPointer<Database> db, const QString& hostname, const QString& url)
{
QList<Entry*> entries;
- Group* rootGroup = db->rootGroup();
+ auto* rootGroup = db->rootGroup();
if (!rootGroup) {
return entries;
}
@@ -415,12 +412,12 @@ QList<Entry*> BrowserService::searchEntries(Database* db, const QString& hostnam
QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPairList& keyList)
{
// Get the list of databases to search
- QList<Database*> databases;
+ QList<QSharedPointer<Database>> databases;
if (browserSettings()->searchInAllDatabases()) {
const int count = m_dbTabWidget->count();
for (int i = 0; i < count; ++i) {
- if (DatabaseWidget* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
- if (Database* db = dbWidget->database()) {
+ if (auto* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
+ if (const auto& db = dbWidget->database()) {
// Check if database is connected with KeePassXC-Browser
for (const StringPair& keyPair : keyList) {
QString key = db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first);
@@ -431,7 +428,7 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPair
}
}
}
- } else if (Database* db = getDatabase()) {
+ } else if (const auto& db = getDatabase()) {
databases << db;
}
@@ -439,7 +436,7 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPair
QString hostname = QUrl(url).host();
QList<Entry*> entries;
do {
- for (Database* db : databases) {
+ for (const auto& db : databases) {
entries << searchEntries(db, hostname, url);
}
} while (entries.isEmpty() && removeFirstDomain(hostname));
@@ -447,9 +444,9 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPair
return entries;
}
-void BrowserService::convertAttributesToCustomData(Database *currentDb)
+void BrowserService::convertAttributesToCustomData(QSharedPointer<Database> currentDb)
{
- Database* db = currentDb ? currentDb : getDatabase();
+ auto db = currentDb ? currentDb : getDatabase();
if (!db) {
return;
}
@@ -651,9 +648,9 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri
return Unknown;
}
-Group* BrowserService::findCreateAddEntryGroup(Database* selectedDb)
+Group* BrowserService::findCreateAddEntryGroup(QSharedPointer<Database> selectedDb)
{
- Database* db = selectedDb ? selectedDb : getDatabase();
+ auto db = selectedDb ? selectedDb : getDatabase();
if (!db) {
return nullptr;
}
@@ -668,11 +665,11 @@ Group* BrowserService::findCreateAddEntryGroup(Database* selectedDb)
for (const Group* g : rootGroup->groupsRecursive(true)) {
if (g->name() == groupName) {
- return db->resolveGroup(g->uuid());
+ return db->rootGroup()->findGroupByUuid(g->uuid());
}
}
- Group* group = new Group();
+ auto* group = new Group();
group->setUuid(QUuid::createUuid());
group->setName(groupName);
group->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
@@ -775,26 +772,26 @@ QString BrowserService::baseDomain(const QString& url) const
return baseDomain;
}
-Database* BrowserService::getDatabase()
+QSharedPointer<Database> BrowserService::getDatabase()
{
if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) {
- if (Database* db = dbWidget->database()) {
+ if (const auto& db = dbWidget->database()) {
return db;
}
}
- return nullptr;
+ return {};
}
-Database* BrowserService::selectedDatabase()
+QSharedPointer<Database> BrowserService::selectedDatabase()
{
QList<DatabaseWidget*> databaseWidgets;
- for (int i = 0;; ++i) {
- const auto dbStruct = m_dbTabWidget->indexDatabaseManagerStruct(i);
+ for (int i = 0; ; ++i) {
+ auto* dbWidget = m_dbTabWidget->databaseWidgetFromIndex(i);
// Add only open databases
- if (dbStruct.dbWidget && dbStruct.dbWidget->dbHasKey() &&
- (dbStruct.dbWidget->currentMode() == DatabaseWidget::ViewMode ||
- dbStruct.dbWidget->currentMode() == DatabaseWidget::EditMode)) {
- databaseWidgets.push_back(dbStruct.dbWidget);
+ if (dbWidget && dbWidget->database()->hasKey() &&
+ (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode ||
+ dbWidget->currentMode() == DatabaseWidget::Mode::EditMode)) {
+ databaseWidgets.push_back(dbWidget);
continue;
}
@@ -813,7 +810,7 @@ Database* BrowserService::selectedDatabase()
return databaseWidgets[index]->database();
}
} else {
- return nullptr;
+ return {};
}
}
@@ -836,7 +833,7 @@ bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name)
return false;
}
-int BrowserService::moveKeysToCustomData(Entry* entry, Database* db) const
+int BrowserService::moveKeysToCustomData(Entry* entry, QSharedPointer<Database> db) const
{
int keyCounter = 0;
for (const auto& key : entry->attributes()->keys()) {
@@ -857,7 +854,7 @@ int BrowserService::moveKeysToCustomData(Entry* entry, Database* db) const
bool BrowserService::checkLegacySettings()
{
- Database* db = getDatabase();
+ auto db = getDatabase();
if (!db) {
return false;
}
@@ -882,12 +879,8 @@ bool BrowserService::checkLegacySettings()
"Do you want to upgrade the settings to the latest standard?\n"
"This is necessary to maintain compatibility with the browser plugin."),
QMessageBox::Yes | QMessageBox::No);
-
- if (dialogResult == QMessageBox::No) {
- return false;
- }
- return true;
+ return dialogResult == QMessageBox::Yes;
}
void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
@@ -916,7 +909,7 @@ void BrowserService::activateDatabaseChanged(DatabaseWidget* dbWidget)
{
if (dbWidget) {
auto currentMode = dbWidget->currentMode();
- if (currentMode == DatabaseWidget::ViewMode || currentMode == DatabaseWidget::EditMode) {
+ if (currentMode == DatabaseWidget::Mode::ViewMode || currentMode == DatabaseWidget::Mode::EditMode) {
emit databaseUnlocked();
} else {
emit databaseLocked();
diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h
index bf93edf72..612b55eec 100644
--- a/src/browser/BrowserService.h
+++ b/src/browser/BrowserService.h
@@ -44,7 +44,6 @@ public:
bool openDatabase(bool triggerUnlock);
QString getDatabaseRootUuid();
QString getDatabaseRecycleBinUuid();
- Entry* getConfigEntry(bool create = false);
QString getKey(const QString& id);
void addEntry(const QString& id,
const QString& login,
@@ -52,10 +51,10 @@ public:
const QString& url,
const QString& submitUrl,
const QString& realm,
- Database* selectedDb = nullptr);
- QList<Entry*> searchEntries(Database* db, const QString& hostname, const QString& url);
+ QSharedPointer<Database> selectedDb = {});
+ QList<Entry*> searchEntries(QSharedPointer<Database> db, const QString& hostname, const QString& url);
QList<Entry*> searchEntries(const QString& url, const StringPairList& keyList);
- void convertAttributesToCustomData(Database *currentDb = nullptr);
+ void convertAttributesToCustomData(QSharedPointer<Database> currentDb = {});
public:
static const char KEEPASSXCBROWSER_NAME[];
@@ -103,16 +102,16 @@ private:
const QString& realm);
QJsonObject prepareEntry(const Entry* entry);
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
- Group* findCreateAddEntryGroup(Database* selectedDb = nullptr);
+ Group* findCreateAddEntryGroup(QSharedPointer<Database> selectedDb = {});
int
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
bool matchUrlScheme(const QString& url);
bool removeFirstDomain(QString& hostname);
QString baseDomain(const QString& url) const;
- Database* getDatabase();
- Database* selectedDatabase();
+ QSharedPointer<Database> getDatabase();
+ QSharedPointer<Database> selectedDatabase();
bool moveSettingsToCustomData(Entry* entry, const QString& name) const;
- int moveKeysToCustomData(Entry* entry, Database* db) const;
+ int moveKeysToCustomData(Entry* entry, QSharedPointer<Database> db) const;
bool checkLegacySettings();
private:
diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp
index 09a161071..257ea7f6b 100644
--- a/src/cli/Add.cpp
+++ b/src/cli/Add.cpp
@@ -89,7 +89,7 @@ int Add::execute(const QStringList& arguments)
const QString& databasePath = args.at(0);
const QString& entryPath = args.at(1);
- Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
+ auto db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db) {
return EXIT_FAILURE;
}
@@ -126,7 +126,7 @@ int Add::execute(const QStringList& arguments)
if (passwordLength.isEmpty()) {
passwordGenerator.setLength(PasswordGenerator::DefaultLength);
} else {
- passwordGenerator.setLength(static_cast<size_t>(passwordLength.toInt()));
+ passwordGenerator.setLength(passwordLength.toInt());
}
passwordGenerator.setCharClasses(PasswordGenerator::DefaultCharset);
@@ -135,8 +135,8 @@ int Add::execute(const QStringList& arguments)
entry->setPassword(password);
}
- QString errorMessage = db->saveToFile(databasePath);
- if (!errorMessage.isEmpty()) {
+ QString errorMessage;
+ if (!db->save(databasePath, &errorMessage, true, false)) {
errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
return EXIT_FAILURE;
}
diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp
index 2cc15411b..6b466c5a7 100644
--- a/src/cli/Clip.cpp
+++ b/src/cli/Clip.cpp
@@ -66,7 +66,7 @@ int Clip::execute(const QStringList& arguments)
return EXIT_FAILURE;
}
- Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
+ auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db) {
return EXIT_FAILURE;
}
@@ -74,7 +74,7 @@ int Clip::execute(const QStringList& arguments)
return clipEntry(db, args.at(1), args.value(2), parser.isSet(totp));
}
-int Clip::clipEntry(Database* database, const QString& entryPath, const QString& timeout, bool clipTotp)
+int Clip::clipEntry(QSharedPointer<Database> database, const QString& entryPath, const QString& timeout, bool clipTotp)
{
TextStream err(Utils::STDERR);
diff --git a/src/cli/Clip.h b/src/cli/Clip.h
index 9f7151322..87f56bb0e 100644
--- a/src/cli/Clip.h
+++ b/src/cli/Clip.h
@@ -26,7 +26,7 @@ public:
Clip();
~Clip();
int execute(const QStringList& arguments) override;
- int clipEntry(Database* database, const QString& entryPath, const QString& timeout, bool clipTotp);
+ int clipEntry(QSharedPointer<Database> database, const QString& entryPath, const QString& timeout, bool clipTotp);
};
#endif // KEEPASSXC_CLIP_H
diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp
index 91a76b195..cb7c58baa 100644
--- a/src/cli/Edit.cpp
+++ b/src/cli/Edit.cpp
@@ -93,7 +93,7 @@ int Edit::execute(const QStringList& arguments)
const QString& databasePath = args.at(0);
const QString& entryPath = args.at(1);
- Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
+ auto db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db) {
return EXIT_FAILURE;
}
@@ -152,8 +152,8 @@ int Edit::execute(const QStringList& arguments)
entry->endUpdate();
- QString errorMessage = db->saveToFile(databasePath);
- if (!errorMessage.isEmpty()) {
+ QString errorMessage;
+ if (!db->save(databasePath, &errorMessage, true, false)) {
err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
return EXIT_FAILURE;
}
diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp
index c0b1b1119..01b3d1ebe 100644
--- a/src/cli/Extract.cpp
+++ b/src/cli/Extract.cpp
@@ -104,7 +104,8 @@ int Extract::execute(const QStringList& arguments)
KeePass2Reader reader;
reader.setSaveXml(true);
- QScopedPointer<Database> db(reader.readDatabase(&dbFile, compositeKey));
+ auto db = QSharedPointer<Database>::create();
+ reader.readDatabase(&dbFile, compositeKey, db.data());
QByteArray xmlData = reader.reader()->xmlData();
diff --git a/src/cli/List.cpp b/src/cli/List.cpp
index 98d08d3b2..3b8938189 100644
--- a/src/cli/List.cpp
+++ b/src/cli/List.cpp
@@ -64,7 +64,7 @@ int List::execute(const QStringList& arguments)
bool recursive = parser.isSet(recursiveOption);
- QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
+ auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db) {
return EXIT_FAILURE;
}
diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp
index 8ab8f4c61..8039f0693 100644
--- a/src/cli/Locate.cpp
+++ b/src/cli/Locate.cpp
@@ -61,7 +61,7 @@ int Locate::execute(const QStringList& arguments)
return EXIT_FAILURE;
}
- QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
+ auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db) {
return EXIT_FAILURE;
}
diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp
index 65a03f38b..e5b01665c 100644
--- a/src/cli/Merge.cpp
+++ b/src/cli/Merge.cpp
@@ -68,27 +68,29 @@ int Merge::execute(const QStringList& arguments)
return EXIT_FAILURE;
}
- QScopedPointer<Database> db1(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
+ auto db1 = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db1) {
return EXIT_FAILURE;
}
- QScopedPointer<Database> db2;
+ QSharedPointer<Database> db2;
if (!parser.isSet("same-credentials")) {
- db2.reset(Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom), Utils::STDOUT, Utils::STDERR));
+ db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom), Utils::STDOUT, Utils::STDERR);
} else {
- db2.reset(Database::openDatabaseFile(args.at(1), db1->key()));
- }
- if (!db2) {
- return EXIT_FAILURE;
+ db2 = QSharedPointer<Database>::create();
+ QString errorMessage;
+ if (!db2->open(args.at(1), db1->key(), &errorMessage, false)) {
+ err << QObject::tr("Error reading merge file:\n%1").arg(errorMessage);
+ return EXIT_FAILURE;
+ }
}
Merger merger(db2.data(), db1.data());
bool databaseChanged = merger.merge();
if (databaseChanged) {
- QString errorMessage = db1->saveToFile(args.at(0));
- if (!errorMessage.isEmpty()) {
+ QString errorMessage;
+ if (!db1->save(args.at(0), &errorMessage, true, false)) {
err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl;
return EXIT_FAILURE;
}
diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp
index 4800b5c94..9f4877e5b 100644
--- a/src/cli/Remove.cpp
+++ b/src/cli/Remove.cpp
@@ -63,7 +63,7 @@ int Remove::execute(const QStringList& arguments)
return EXIT_FAILURE;
}
- QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
+ auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db) {
return EXIT_FAILURE;
}
@@ -92,8 +92,8 @@ int Remove::removeEntry(Database* database, const QString& databasePath, const Q
database->recycleEntry(entry);
};
- QString errorMessage = database->saveToFile(databasePath);
- if (!errorMessage.isEmpty()) {
+ QString errorMessage;
+ if (!database->save(databasePath, &errorMessage, true, false)) {
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
return EXIT_FAILURE;
}
diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp
index 7b42de7ab..3678d0e3d 100644
--- a/src/cli/Show.cpp
+++ b/src/cli/Show.cpp
@@ -71,7 +71,7 @@ int Show::execute(const QStringList& arguments)
return EXIT_FAILURE;
}
- QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
+ auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
if (!db) {
return EXIT_FAILURE;
}
diff --git a/src/core/Config.cpp b/src/core/Config.cpp
index 13d375e7d..01497caf9 100644
--- a/src/core/Config.cpp
+++ b/src/core/Config.cpp
@@ -153,6 +153,7 @@ void Config::init(const QString& fileName)
m_defaults.insert("SingleInstance", true);
m_defaults.insert("RememberLastDatabases", true);
+ m_defaults.insert("NumberOfRememberedLastDatabases", 5);
m_defaults.insert("RememberLastKeyFiles", true);
m_defaults.insert("OpenPreviousDatabasesOnStartup", true);
m_defaults.insert("AutoSaveAfterEveryChange", true);
diff --git a/src/core/CustomData.cpp b/src/core/CustomData.cpp
index ea0a35804..835497215 100644
--- a/src/core/CustomData.cpp
+++ b/src/core/CustomData.cpp
@@ -58,7 +58,7 @@ void CustomData::set(const QString& key, const QString& value)
if (addAttribute || changeValue) {
m_data.insert(key, value);
- emit modified();
+ emit customDataModified();
}
if (addAttribute) {
@@ -73,7 +73,7 @@ void CustomData::remove(const QString& key)
m_data.remove(key);
emit removed(key);
- emit modified();
+ emit customDataModified();
}
void CustomData::rename(const QString& oldKey, const QString& newKey)
@@ -92,7 +92,7 @@ void CustomData::rename(const QString& oldKey, const QString& newKey)
m_data.remove(oldKey);
m_data.insert(newKey, data);
- emit modified();
+ emit customDataModified();
emit renamed(oldKey, newKey);
}
@@ -107,7 +107,7 @@ void CustomData::copyDataFrom(const CustomData* other)
m_data = other->m_data;
emit reset();
- emit modified();
+ emit customDataModified();
}
bool CustomData::operator==(const CustomData& other) const
{
@@ -126,7 +126,7 @@ void CustomData::clear()
m_data.clear();
emit reset();
- emit modified();
+ emit customDataModified();
}
bool CustomData::isEmpty() const
diff --git a/src/core/CustomData.h b/src/core/CustomData.h
index 297fd16fb..d085c9409 100644
--- a/src/core/CustomData.h
+++ b/src/core/CustomData.h
@@ -46,7 +46,7 @@ public:
bool operator!=(const CustomData& other) const;
signals:
- void modified();
+ void customDataModified();
void aboutToBeAdded(const QString& key);
void added(const QString& key);
void aboutToBeRemoved(const QString& key);
diff --git a/src/core/Database.cpp b/src/core/Database.cpp
index c44df7ffd..bbea11d54 100644
--- a/src/core/Database.cpp
+++ b/src/core/Database.cpp
@@ -1,6 +1,6 @@
/*
+ * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,201 +18,385 @@
#include "Database.h"
-#include <QDebug>
-#include <QFile>
-#include <QSaveFile>
-#include <QTemporaryFile>
-#include <QTextStream>
-#include <QTimer>
-#include <QXmlStreamReader>
-#include <utility>
-
#include "cli/Utils.h"
+#include "cli/TextStream.h"
#include "core/Clock.h"
#include "core/Group.h"
#include "core/Merger.h"
#include "core/Metadata.h"
-#include "crypto/kdf/AesKdf.h"
-#include "format/KeePass2.h"
#include "format/KeePass2Reader.h"
#include "format/KeePass2Writer.h"
#include "keys/FileKey.h"
#include "keys/PasswordKey.h"
-QHash<QUuid, Database*> Database::m_uuidMap;
+#include <QFile>
+#include <QSaveFile>
+#include <QTemporaryFile>
+#include <QTimer>
+#include <QXmlStreamReader>
+#include <QFileInfo>
+
+QHash<QUuid, QPointer<Database>> Database::s_uuidMap;
+QHash<QString, QPointer<Database>> Database::s_filePathMap;
Database::Database()
: m_metadata(new Metadata(this))
+ , m_data()
, m_rootGroup(nullptr)
, m_timer(new QTimer(this))
, m_emitModified(false)
, m_uuid(QUuid::createUuid())
{
- m_data.cipher = KeePass2::CIPHER_AES256;
- m_data.compressionAlgo = CompressionGZip;
-
- // instantiate default AES-KDF with legacy KDBX3 flag set
- // KDBX4+ will re-initialize the KDF using parameters read from the KDBX file
- m_data.kdf = QSharedPointer<AesKdf>::create(true);
- m_data.kdf->randomizeSeed();
- m_data.hasKey = false;
-
setRootGroup(new Group());
rootGroup()->setUuid(QUuid::createUuid());
+ rootGroup()->setName(tr("Root", "Root group name"));
m_timer->setSingleShot(true);
- m_uuidMap.insert(m_uuid, this);
+ s_uuidMap.insert(m_uuid, this);
- connect(m_metadata, SIGNAL(modified()), this, SIGNAL(modifiedImmediate()));
- connect(m_metadata, SIGNAL(nameTextChanged()), this, SIGNAL(nameTextChanged()));
- connect(this, SIGNAL(modifiedImmediate()), this, SLOT(startModifiedTimer()));
- connect(m_timer, SIGNAL(timeout()), SIGNAL(modified()));
+ connect(m_metadata, SIGNAL(metadataModified()), this, SLOT(markAsModified()));
+ connect(m_timer, SIGNAL(timeout()), SIGNAL(databaseModified()));
+
+ m_modified = false;
+ m_emitModified = true;
+}
+
+Database::Database(const QString& filePath)
+ : Database()
+{
+ setFilePath(filePath);
}
Database::~Database()
{
- m_uuidMap.remove(m_uuid);
+ s_uuidMap.remove(m_uuid);
+
+ if (m_modified) {
+ emit databaseDiscarded();
+ }
}
-Group* Database::rootGroup()
+QUuid Database::uuid() const
{
- return m_rootGroup;
+ return m_uuid;
}
-const Group* Database::rootGroup() const
+/**
+ * Open the database from a previously specified file.
+ * Unless `readOnly` is set to false, the database will be opened in
+ * read-write mode and fall back to read-only if that is not possible.
+ *
+ * @param key composite key for unlocking the database
+ * @param readOnly open in read-only mode
+ * @param error error message in case of failure
+ * @return true on success
+ */
+bool Database::open(QSharedPointer<const CompositeKey> key, QString* error, bool readOnly)
{
- return m_rootGroup;
+ Q_ASSERT(!m_data.filePath.isEmpty());
+ if (m_data.filePath.isEmpty()) {
+ return false;
+ }
+ return open(m_data.filePath, std::move(key), error, readOnly);
}
-void Database::setRootGroup(Group* group)
+/**
+ * Open the database from a file.
+ * Unless `readOnly` is set to false, the database will be opened in
+ * read-write mode and fall back to read-only if that is not possible.
+ *
+ * @param filePath path to the file
+ * @param key composite key for unlocking the database
+ * @param readOnly open in read-only mode
+ * @param error error message in case of failure
+ * @return true on success
+ */
+bool Database::open(const QString& filePath, QSharedPointer<const CompositeKey> key, QString* error, bool readOnly)
{
- Q_ASSERT(group);
+ if (isInitialized() && m_modified) {
+ emit databaseDiscarded();
+ }
- m_rootGroup = group;
- m_rootGroup->setParent(this);
+ setEmitModified(false);
+
+ QFile dbFile(filePath);
+ if (!dbFile.exists()) {
+ if (error) {
+ *error = tr("File %1 does not exist.").arg(filePath);
+ }
+ return false;
+ }
+
+ if (!readOnly && !dbFile.open(QIODevice::ReadWrite)) {
+ readOnly = true;
+ }
+
+ if (!dbFile.isOpen() && !dbFile.open(QIODevice::ReadOnly)) {
+ if (error) {
+ *error = tr("Unable to open file %1.").arg(filePath);
+ }
+ return false;
+ }
+
+ KeePass2Reader reader;
+ bool ok = reader.readDatabase(&dbFile, std::move(key), this);
+ if (reader.hasError()) {
+ if (error) {
+ *error = tr("Error while reading the database: %1").arg(reader.errorString());
+ }
+ return false;
+ }
+
+ setReadOnly(readOnly);
+ setFilePath(filePath);
+ dbFile.close();
+
+ setInitialized(ok);
+ markAsClean();
+
+ setEmitModified(true);
+ return ok;
}
-Metadata* Database::metadata()
+/**
+ * Save the database back to the file is has been opened from.
+ * This method behaves the same as its overloads.
+ *
+ * @see Database::save(const QString&, bool, bool, QString*)
+ *
+ * @param atomic Use atomic file transactions
+ * @param backup Backup the existing database file, if exists
+ * @param error error message in case of failure
+ * @return true on success
+ */
+bool Database::save(QString* error, bool atomic, bool backup)
{
- return m_metadata;
+ Q_ASSERT(!m_data.filePath.isEmpty());
+ if (m_data.filePath.isEmpty()) {
+ if (error) {
+ *error = tr("Could not save, database has no file name.");
+ }
+ return false;
+ }
+
+ return save(m_data.filePath, error, atomic, backup);
}
-const Metadata* Database::metadata() const
+/**
+ * Save the database to a file.
+ *
+ * This function uses QTemporaryFile instead of QSaveFile due to a bug
+ * in Qt (https://bugreports.qt.io/browse/QTBUG-57299) that may prevent
+ * the QSaveFile from renaming itself when using Dropbox, Drive, or OneDrive.
+ *
+ * The risk in using QTemporaryFile is that the rename function is not atomic
+ * and may result in loss of data if there is a crash or power loss at the
+ * wrong moment.
+ *
+ * @param filePath Absolute path of the file to save
+ * @param atomic Use atomic file transactions
+ * @param backup Backup the existing database file, if exists
+ * @param error error message in case of failure
+ * @return true on success
+ */
+bool Database::save(const QString& filePath, QString* error, bool atomic, bool backup)
{
- return m_metadata;
+ Q_ASSERT(!m_data.isReadOnly);
+ if (m_data.isReadOnly) {
+ return false;
+ }
+
+ if (atomic) {
+ QSaveFile saveFile(filePath);
+ if (saveFile.open(QIODevice::WriteOnly)) {
+ // write the database to the file
+ if (!writeDatabase(&saveFile, error)) {
+ return false;
+ }
+
+ if (backup) {
+ backupDatabase(filePath);
+ }
+
+ if (saveFile.commit()) {
+ // successfully saved database file
+ setFilePath(filePath);
+ return true;
+ }
+ }
+ if (error) {
+ *error = saveFile.errorString();
+ }
+ } else {
+ QTemporaryFile tempFile;
+ if (tempFile.open()) {
+ // write the database to the file
+ if (!writeDatabase(&tempFile, error)) {
+ return false;
+ }
+
+ tempFile.close(); // flush to disk
+
+ if (backup) {
+ backupDatabase(filePath);
+ }
+
+ // Delete the original db and move the temp file in place
+ QFile::remove(filePath);
+#ifdef Q_OS_LINUX
+ // workaround to make this workaround work, see: https://bugreports.qt.io/browse/QTBUG-64008
+ if (tempFile.copy(filePath)) {
+ // successfully saved database file
+ return true;
+ }
+#else
+ if (tempFile.rename(filePath)) {
+ // successfully saved database file
+ tempFile.setAutoRemove(false);
+ setFilePath(filePath);
+ return true;
+ }
+#endif
+ }
+ if (error) {
+ *error = tempFile.errorString();
+ }
+ }
+
+ // Saving failed
+ return false;
}
-QString Database::filePath() const
+bool Database::writeDatabase(QIODevice* device, QString* error)
{
- return m_filePath;
+ Q_ASSERT(!m_data.isReadOnly);
+ if (m_data.isReadOnly) {
+ if (error) {
+ *error = tr("File cannot be written as it is opened in read-only mode.");
+ }
+ return false;
+ }
+
+ KeePass2Writer writer;
+ setEmitModified(false);
+ writer.writeDatabase(device, this);
+ setEmitModified(true);
+
+ if (writer.hasError()) {
+ // the writer failed
+ if (error) {
+ *error = writer.errorString();
+ }
+ return false;
+ }
+
+ markAsClean();
+ return true;
}
-void Database::setFilePath(const QString& filePath)
+/**
+ * Remove the old backup and replace it with a new one
+ * backups are named <filename>.old.kdbx
+ *
+ * @param filePath Path to the file to backup
+ * @return true on success
+ */
+bool Database::backupDatabase(const QString& filePath)
+{
+ QString backupFilePath = filePath;
+ auto re = QRegularExpression("\\.kdbx$|(?<!\\.kdbx)$", QRegularExpression::CaseInsensitiveOption);
+ backupFilePath.replace(re, ".old.kdbx");
+ QFile::remove(backupFilePath);
+ return QFile::copy(filePath, backupFilePath);
+}
+
+bool Database::isReadOnly() const
{
- m_filePath = filePath;
+ return m_data.isReadOnly;
}
-Entry* Database::resolveEntry(const QUuid& uuid)
+void Database::setReadOnly(bool readOnly)
{
- return findEntryRecursive(uuid, m_rootGroup);
+ m_data.isReadOnly = readOnly;
}
-Entry* Database::resolveEntry(const QString& text, EntryReferenceType referenceType)
+/**
+ * Returns true if database has been fully decrypted and populated, i.e. if
+ * it's not just an empty default instance.
+ *
+ * @return true if database has been fully initialized
+ */
+bool Database::isInitialized() const
{
- return findEntryRecursive(text, referenceType, m_rootGroup);
+ return m_initialized;
}
-Entry* Database::findEntryRecursive(const QUuid& uuid, Group* group)
+/**
+ * @param initialized true to mark database as initialized
+ */
+void Database::setInitialized(bool initialized)
{
- const QList<Entry*> entryList = group->entries();
- for (Entry* entry : entryList) {
- if (entry->uuid() == uuid) {
- return entry;
- }
- }
+ m_initialized = initialized;
+}
- const QList<Group*> children = group->children();
- for (Group* child : children) {
- Entry* result = findEntryRecursive(uuid, child);
- if (result) {
- return result;
- }
- }
+Group* Database::rootGroup()
+{
+ return m_rootGroup;
+}
- return nullptr;
-}
-
-Entry* Database::findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group)
-{
- Q_ASSERT_X(referenceType != EntryReferenceType::Unknown,
- "Database::findEntryRecursive",
- "Can't search entry with \"referenceType\" parameter equal to \"Unknown\"");
-
- bool found = false;
- const QList<Entry*> entryList = group->entries();
- for (Entry* entry : entryList) {
- switch (referenceType) {
- case EntryReferenceType::Unknown:
- return nullptr;
- case EntryReferenceType::Title:
- found = entry->title() == text;
- break;
- case EntryReferenceType::UserName:
- found = entry->username() == text;
- break;
- case EntryReferenceType::Password:
- found = entry->password() == text;
- break;
- case EntryReferenceType::Url:
- found = entry->url() == text;
- break;
- case EntryReferenceType::Notes:
- found = entry->notes() == text;
- break;
- case EntryReferenceType::QUuid:
- found = entry->uuid() == QUuid::fromRfc4122(QByteArray::fromHex(text.toLatin1()));
- break;
- case EntryReferenceType::CustomAttributes:
- found = entry->attributes()->containsValue(text);
- break;
- }
+const Group* Database::rootGroup() const
+{
+ return m_rootGroup;
+}
- if (found) {
- return entry;
- }
- }
+/**
+ * Sets group as the root group and takes ownership of it.
+ * Warning: Be careful when calling this method as it doesn't
+ * emit any notifications so e.g. models aren't updated.
+ * The caller is responsible for cleaning up the previous
+ root group.
+ */
+void Database::setRootGroup(Group* group)
+{
+ Q_ASSERT(group);
- const QList<Group*> children = group->children();
- for (Group* child : children) {
- Entry* result = findEntryRecursive(text, referenceType, child);
- if (result) {
- return result;
- }
+ if (isInitialized() && m_modified) {
+ emit databaseDiscarded();
}
- return nullptr;
+ m_rootGroup = group;
+ m_rootGroup->setParent(this);
}
-Group* Database::resolveGroup(const QUuid& uuid)
+Metadata* Database::metadata()
{
- return findGroupRecursive(uuid, m_rootGroup);
+ return m_metadata;
}
-Group* Database::findGroupRecursive(const QUuid& uuid, Group* group)
+const Metadata* Database::metadata() const
{
- if (group->uuid() == uuid) {
- return group;
+ return m_metadata;
+}
+
+QString Database::filePath() const
+{
+ return m_data.filePath;
+}
+
+void Database::setFilePath(const QString& filePath)
+{
+ if (filePath == m_data.filePath) {
+ return;
}
- const QList<Group*> children = group->children();
- for (Group* child : children) {
- Group* result = findGroupRecursive(uuid, child);
- if (result) {
- return result;
- }
+ if (s_filePathMap.contains(m_data.filePath)) {
+ s_filePathMap.remove(m_data.filePath);
}
+ QString oldPath = m_data.filePath;
+ m_data.filePath = filePath;
+ s_filePathMap.insert(m_data.filePath, this);
- return nullptr;
+ emit filePathChanged(oldPath, filePath);
}
QList<DeletedObject> Database::deletedObjects()
@@ -273,9 +457,9 @@ const QUuid& Database::cipher() const
return m_data.cipher;
}
-Database::CompressionAlgorithm Database::compressionAlgo() const
+Database::CompressionAlgorithm Database::compressionAlgorithm() const
{
- return m_data.compressionAlgo;
+ return m_data.compressionAlgorithm;
}
QByteArray Database::transformedMasterKey() const
@@ -301,11 +485,11 @@ void Database::setCipher(const QUuid& cipher)
m_data.cipher = cipher;
}
-void Database::setCompressionAlgo(Database::CompressionAlgorithm algo)
+void Database::setCompressionAlgorithm(Database::CompressionAlgorithm algo)
{
Q_ASSERT(static_cast<quint32>(algo) <= CompressionAlgorithmMax);
- m_data.compressionAlgo = algo;
+ m_data.compressionAlgorithm = algo;
}
/**
@@ -318,6 +502,8 @@ void Database::setCompressionAlgo(Database::CompressionAlgorithm algo)
*/
bool Database::setKey(const QSharedPointer<const CompositeKey>& key, bool updateChangedTime, bool updateTransformSalt)
{
+ Q_ASSERT(!m_data.isReadOnly);
+
if (!key) {
m_data.key.reset();
m_data.transformedMasterKey = {};
@@ -344,7 +530,7 @@ bool Database::setKey(const QSharedPointer<const CompositeKey>& key, bool update
}
if (oldTransformedMasterKey != m_data.transformedMasterKey) {
- emit modifiedImmediate();
+ markAsModified();
}
return true;
@@ -388,11 +574,13 @@ const QVariantMap& Database::publicCustomData() const
void Database::setPublicCustomData(const QVariantMap& customData)
{
+ Q_ASSERT(!m_data.isReadOnly);
m_data.publicCustomData = customData;
}
void Database::createRecycleBin()
{
+ Q_ASSERT(!m_data.isReadOnly);
Group* recycleBin = Group::createRecycleBin();
recycleBin->setParent(rootGroup());
m_metadata->setRecycleBin(recycleBin);
@@ -400,6 +588,7 @@ void Database::createRecycleBin()
void Database::recycleEntry(Entry* entry)
{
+ Q_ASSERT(!m_data.isReadOnly);
if (m_metadata->recycleBinEnabled()) {
if (!m_metadata->recycleBin()) {
createRecycleBin();
@@ -412,6 +601,7 @@ void Database::recycleEntry(Entry* entry)
void Database::recycleGroup(Group* group)
{
+ Q_ASSERT(!m_data.isReadOnly);
if (m_metadata->recycleBinEnabled()) {
if (!m_metadata->recycleBin()) {
createRecycleBin();
@@ -424,6 +614,7 @@ void Database::recycleGroup(Group* group)
void Database::emptyRecycleBin()
{
+ Q_ASSERT(!m_data.isReadOnly);
if (m_metadata->recycleBinEnabled() && m_metadata->recycleBin()) {
// destroying direct entries of the recycle bin
QList<Entry*> subEntries = m_metadata->recycleBin()->entries();
@@ -447,23 +638,54 @@ void Database::setEmitModified(bool value)
m_emitModified = value;
}
+bool Database::isModified() const
+{
+ return m_modified;
+}
+
void Database::markAsModified()
{
- emit modified();
+ if (isReadOnly()) {
+ return;
+ }
+
+ m_modified = true;
+ if (m_emitModified) {
+ startModifiedTimer();
+ }
}
-const QUuid& Database::uuid()
+void Database::markAsClean()
{
- return m_uuid;
+ bool emitSignal = m_modified;
+ m_modified = false;
+ if (emitSignal) {
+ emit databaseSaved();
+ }
}
+/**
+ * @param uuid UUID of the database
+ * @return pointer to the database or nullptr if no such database exists
+ */
Database* Database::databaseByUuid(const QUuid& uuid)
{
- return m_uuidMap.value(uuid, 0);
+ return s_uuidMap.value(uuid, nullptr);
+}
+
+/**
+ * @param filePath file path of the database
+ * @return pointer to the database or nullptr if the database has not been opened
+ */
+Database* Database::databaseByFilePath(const QString& filePath)
+{
+ return s_filePathMap.value(filePath, nullptr);
}
void Database::startModifiedTimer()
{
+ Q_ASSERT(!m_data.isReadOnly);
+
if (!m_emitModified) {
return;
}
@@ -479,34 +701,12 @@ QSharedPointer<const CompositeKey> Database::key() const
return m_data.key;
}
-Database* Database::openDatabaseFile(const QString& fileName, QSharedPointer<const CompositeKey> key)
-{
-
- QFile dbFile(fileName);
- if (!dbFile.exists()) {
- qCritical("Database file %s does not exist.", qPrintable(fileName));
- return nullptr;
- }
- if (!dbFile.open(QIODevice::ReadOnly)) {
- qCritical("Unable to open database file %s.", qPrintable(fileName));
- return nullptr;
- }
-
- KeePass2Reader reader;
- Database* db = reader.readDatabase(&dbFile, std::move(key));
- if (reader.hasError()) {
- qCritical("Error while parsing the database: %s", qPrintable(reader.errorString()));
- return nullptr;
- }
-
- return db;
-}
-
-Database* Database::unlockFromStdin(const QString& databaseFilename, const QString& keyFilename, FILE* outputDescriptor, FILE* errorDescriptor)
+QSharedPointer<Database> Database::unlockFromStdin(const QString& databaseFilename, const QString& keyFilename,
+ FILE* outputDescriptor, FILE* errorDescriptor)
{
auto compositeKey = QSharedPointer<CompositeKey>::create();
- QTextStream out(outputDescriptor);
- QTextStream err(errorDescriptor);
+ TextStream out(outputDescriptor);
+ TextStream err(errorDescriptor);
out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename);
out.flush();
@@ -522,7 +722,7 @@ Database* Database::unlockFromStdin(const QString& databaseFilename, const QStri
// LCOV_EXCL_START
if (!fileKey->load(keyFilename, &errorMessage)) {
err << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage)<< endl;
- return nullptr;
+ return {};
}
if (fileKey->type() != FileKey::Hashed) {
@@ -535,112 +735,9 @@ Database* Database::unlockFromStdin(const QString& databaseFilename, const QStri
compositeKey->addKey(fileKey);
}
- return Database::openDatabaseFile(databaseFilename, compositeKey);
-}
-
-/**
- * Save the database to a file.
- *
- * This function uses QTemporaryFile instead of QSaveFile due to a bug
- * in Qt (https://bugreports.qt.io/browse/QTBUG-57299) that may prevent
- * the QSaveFile from renaming itself when using Dropbox, Drive, or OneDrive.
- *
- * The risk in using QTemporaryFile is that the rename function is not atomic
- * and may result in loss of data if there is a crash or power loss at the
- * wrong moment.
- *
- * @param filePath Absolute path of the file to save
- * @param atomic Use atomic file transactions
- * @param backup Backup the existing database file, if exists
- * @return error string, if any
- */
-QString Database::saveToFile(const QString& filePath, bool atomic, bool backup)
-{
- QString error;
- if (atomic) {
- QSaveFile saveFile(filePath);
- if (saveFile.open(QIODevice::WriteOnly)) {
- // write the database to the file
- error = writeDatabase(&saveFile);
- if (!error.isEmpty()) {
- return error;
- }
-
- if (backup) {
- backupDatabase(filePath);
- }
-
- if (saveFile.commit()) {
- // successfully saved database file
- return {};
- }
- }
- error = saveFile.errorString();
- } else {
- QTemporaryFile tempFile;
- if (tempFile.open()) {
- // write the database to the file
- error = writeDatabase(&tempFile);
- if (!error.isEmpty()) {
- return error;
- }
-
- tempFile.close(); // flush to disk
-
- if (backup) {
- backupDatabase(filePath);
- }
-
- // Delete the original db and move the temp file in place
- QFile::remove(filePath);
-#ifdef Q_OS_LINUX
- // workaround to make this workaround work, see: https://bugreports.qt.io/browse/QTBUG-64008
- if (tempFile.copy(filePath)) {
- // successfully saved database file
- return {};
- }
-#else
- if (tempFile.rename(filePath)) {
- // successfully saved database file
- tempFile.setAutoRemove(false);
- return {};
- }
-#endif
- }
- error = tempFile.errorString();
- }
- // Saving failed
- return error;
-}
-
-QString Database::writeDatabase(QIODevice* device)
-{
- KeePass2Writer writer;
- setEmitModified(false);
- writer.writeDatabase(device, this);
- setEmitModified(true);
-
- if (writer.hasError()) {
- // the writer failed
- return writer.errorString();
- }
- return {};
-}
-
-/**
- * Remove the old backup and replace it with a new one
- * backups are named <filename>.old.kdbx
- *
- * @param filePath Path to the file to backup
- * @return
- */
-bool Database::backupDatabase(const QString& filePath)
-{
- QString backupFilePath = filePath;
- auto re = QRegularExpression("\\.kdbx$|(?<!\\.kdbx)$", QRegularExpression::CaseInsensitiveOption);
- backupFilePath.replace(re, ".old.kdbx");
- QFile::remove(backupFilePath);
- return QFile::copy(filePath, backupFilePath);
+ auto db = QSharedPointer<Database>::create();
+ db->open(databaseFilename, compositeKey, nullptr, false);
+ return db;
}
QSharedPointer<Kdf> Database::kdf() const
@@ -650,11 +747,14 @@ QSharedPointer<Kdf> Database::kdf() const
void Database::setKdf(QSharedPointer<Kdf> kdf)
{
+ Q_ASSERT(!m_data.isReadOnly);
m_data.kdf = std::move(kdf);
}
bool Database::changeKdf(const QSharedPointer<Kdf>& kdf)
{
+ Q_ASSERT(!m_data.isReadOnly);
+
kdf->randomizeSeed();
QByteArray transformedMasterKey;
if (!m_data.key) {
@@ -666,7 +766,7 @@ bool Database::changeKdf(const QSharedPointer<Kdf>& kdf)
setKdf(kdf);
m_data.transformedMasterKey = transformedMasterKey;
- emit modifiedImmediate();
+ markAsModified();
return true;
}
diff --git a/src/core/Database.h b/src/core/Database.h
index 692444f4d..6d660b66d 100644
--- a/src/core/Database.h
+++ b/src/core/Database.h
@@ -1,6 +1,6 @@
/*
+ * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,9 +22,12 @@
#include <QDateTime>
#include <QHash>
#include <QObject>
+#include <QPointer>
#include "crypto/kdf/Kdf.h"
+#include "format/KeePass2.h"
#include "keys/CompositeKey.h"
+#include "crypto/kdf/AesKdf.h"
class Entry;
enum class EntryReferenceType;
@@ -57,40 +60,38 @@ public:
};
static const quint32 CompressionAlgorithmMax = CompressionGZip;
- struct DatabaseData
- {
- QUuid cipher;
- CompressionAlgorithm compressionAlgo;
- QByteArray transformedMasterKey;
- QSharedPointer<Kdf> kdf;
- QSharedPointer<const CompositeKey> key;
- bool hasKey;
- QByteArray masterSeed;
- QByteArray challengeResponseKey;
- QVariantMap publicCustomData;
- };
-
Database();
+ explicit Database(const QString& filePath);
~Database() override;
- Group* rootGroup();
- const Group* rootGroup() const;
- /**
- * Sets group as the root group and takes ownership of it.
- * Warning: Be careful when calling this method as it doesn't
- * emit any notifications so e.g. models aren't updated.
- * The caller is responsible for cleaning up the previous
- root group.
- */
- void setRootGroup(Group* group);
+ bool open(QSharedPointer<const CompositeKey> key, QString* error = nullptr, bool readOnly = false);
+ bool open(const QString& filePath, QSharedPointer<const CompositeKey> key, QString* error = nullptr, bool readOnly = false);
+ bool save(QString* error = nullptr, bool atomic = true, bool backup = false);
+ bool save(const QString& filePath, QString* error = nullptr, bool atomic = true, bool backup = false);
- Metadata* metadata();
- const Metadata* metadata() const;
+ bool isInitialized() const;
+ void setInitialized(bool initialized);
+ bool isModified() const;
+ void setEmitModified(bool value);
+ bool isReadOnly() const;
+ void setReadOnly(bool readOnly);
+
+ QUuid uuid() const;
QString filePath() const;
void setFilePath(const QString& filePath);
- Entry* resolveEntry(const QUuid& uuid);
- Entry* resolveEntry(const QString& text, EntryReferenceType referenceType);
- Group* resolveGroup(const QUuid& uuid);
+
+ Metadata* metadata();
+ const Metadata* metadata() const;
+ Group* rootGroup();
+ const Group* rootGroup() const;
+ void setRootGroup(Group* group);
+ QVariantMap& publicCustomData();
+ const QVariantMap& publicCustomData() const;
+ void setPublicCustomData(const QVariantMap& customData);
+
+ void recycleGroup(Group* group);
+ void recycleEntry(Entry* entry);
+ void emptyRecycleBin();
QList<DeletedObject> deletedObjects();
const QList<DeletedObject>& deletedObjects() const;
void addDeletedObject(const DeletedObject& delObj);
@@ -99,42 +100,33 @@ public:
bool containsDeletedObject(const DeletedObject& uuid) const;
void setDeletedObjects(const QList<DeletedObject>& delObjs);
- const QUuid& cipher() const;
- Database::CompressionAlgorithm compressionAlgo() const;
- QSharedPointer<Kdf> kdf() const;
- QByteArray transformedMasterKey() const;
+ bool hasKey() const;
QSharedPointer<const CompositeKey> key() const;
+ bool setKey(const QSharedPointer<const CompositeKey>& key, bool updateChangedTime = true, bool updateTransformSalt = false);
QByteArray challengeResponseKey() const;
bool challengeMasterSeed(const QByteArray& masterSeed);
-
- void setCipher(const QUuid& cipher);
- void setCompressionAlgo(Database::CompressionAlgorithm algo);
- void setKdf(QSharedPointer<Kdf> kdf);
- bool setKey(const QSharedPointer<const CompositeKey>& key, bool updateChangedTime = true, bool updateTransformSalt = false);
- bool hasKey() const;
bool verifyKey(const QSharedPointer<CompositeKey>& key) const;
- QVariantMap& publicCustomData();
- const QVariantMap& publicCustomData() const;
- void setPublicCustomData(const QVariantMap& customData);
- void recycleEntry(Entry* entry);
- void recycleGroup(Group* group);
- void emptyRecycleBin();
- void setEmitModified(bool value);
- void markAsModified();
- QString saveToFile(const QString& filePath, bool atomic = true, bool backup = false);
+ const QUuid& cipher() const;
+ void setCipher(const QUuid& cipher);
+ Database::CompressionAlgorithm compressionAlgorithm() const;
+ void setCompressionAlgorithm(Database::CompressionAlgorithm algo);
- /**
- * Returns a unique id that is only valid as long as the Database exists.
- */
- const QUuid& uuid();
+ QSharedPointer<Kdf> kdf() const;
+ void setKdf(QSharedPointer<Kdf> kdf);
bool changeKdf(const QSharedPointer<Kdf>& kdf);
+ QByteArray transformedMasterKey() const;
static Database* databaseByUuid(const QUuid& uuid);
- static Database* openDatabaseFile(const QString& fileName, QSharedPointer<const CompositeKey> key);
- static Database* unlockFromStdin(const QString& databaseFilename, const QString& keyFilename = {},
- FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr);
+ static Database* databaseByFilePath(const QString& filePath);
+ static QSharedPointer<Database> unlockFromStdin(const QString& databaseFilename, const QString& keyFilename = {},
+ FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr);
+
+public slots:
+ void markAsModified();
+ void markAsClean();
signals:
+ void filePathChanged(const QString& oldPath, const QString& newPath);
void groupDataChanged(Group* group);
void groupAboutToAdd(Group* group, int index);
void groupAdded();
@@ -142,33 +134,51 @@ signals:
void groupRemoved();
void groupAboutToMove(Group* group, Group* toGroup, int index);
void groupMoved();
- void nameTextChanged();
- void modified();
- void modifiedImmediate();
+ void databaseModified();
+ void databaseSaved();
+ void databaseDiscarded();
private slots:
void startModifiedTimer();
private:
- Entry* findEntryRecursive(const QUuid& uuid, Group* group);
- Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group);
- Group* findGroupRecursive(const QUuid& uuid, Group* group);
+ struct DatabaseData
+ {
+ QString filePath;
+ bool isReadOnly = false;
+ QUuid cipher = KeePass2::CIPHER_AES256;
+ CompressionAlgorithm compressionAlgorithm = CompressionGZip;
+ QByteArray transformedMasterKey;
+ QSharedPointer<Kdf> kdf = QSharedPointer<AesKdf>::create(true);
+ QSharedPointer<const CompositeKey> key;
+ bool hasKey = false;
+ QByteArray masterSeed;
+ QByteArray challengeResponseKey;
+ QVariantMap publicCustomData;
+
+ DatabaseData()
+ {
+ kdf->randomizeSeed();
+ }
+ };
void createRecycleBin();
- QString writeDatabase(QIODevice* device);
+
+ bool writeDatabase(QIODevice* device, QString* error = nullptr);
bool backupDatabase(const QString& filePath);
Metadata* const m_metadata;
+ DatabaseData m_data;
Group* m_rootGroup;
QList<DeletedObject> m_deletedObjects;
- QTimer* m_timer;
- DatabaseData m_data;
+ QPointer<QTimer> m_timer;
+ bool m_initialized = false;
+ bool m_modified = false;
bool m_emitModified;
- QString m_filePath;
-
QUuid m_uuid;
- static QHash<QUuid, Database*> m_uuidMap;
+ static QHash<QUuid, QPointer<Database>> s_uuidMap;
+ static QHash<QString, QPointer<Database>> s_filePathMap;
};
#endif // KEEPASSX_DATABASE_H
diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp
index f6790da4b..793a373bb 100644
--- a/src/core/Entry.cpp
+++ b/src/core/Entry.cpp
@@ -26,7 +26,6 @@
#include "core/Metadata.h"
#include "totp/totp.h"
-#include <QDebug>
#include <QDir>
#include <QRegularExpression>
#include <utility>
@@ -49,15 +48,15 @@ Entry::Entry()
m_data.autoTypeEnabled = true;
m_data.autoTypeObfuscation = 0;
- connect(m_attributes, SIGNAL(modified()), SLOT(updateTotp()));
- connect(m_attributes, SIGNAL(modified()), this, SIGNAL(modified()));
+ connect(m_attributes, SIGNAL(entryAttributesModified()), SLOT(updateTotp()));
+ connect(m_attributes, SIGNAL(entryAttributesModified()), this, SIGNAL(entryModified()));
connect(m_attributes, SIGNAL(defaultKeyModified()), SLOT(emitDataChanged()));
- connect(m_attachments, SIGNAL(modified()), this, SIGNAL(modified()));
- connect(m_autoTypeAssociations, SIGNAL(modified()), SIGNAL(modified()));
- connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
+ connect(m_attachments, SIGNAL(entryAttachmentsModified()), this, SIGNAL(entryModified()));
+ connect(m_autoTypeAssociations, SIGNAL(modified()), SIGNAL(entryModified()));
+ connect(m_customData, SIGNAL(customDataModified()), this, SIGNAL(entryModified()));
- connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
- connect(this, SIGNAL(modified()), SLOT(updateModifiedSinceBegin()));
+ connect(this, SIGNAL(entryModified()), SLOT(updateTimeinfo()));
+ connect(this, SIGNAL(entryModified()), SLOT(updateModifiedSinceBegin()));
}
Entry::~Entry()
@@ -78,7 +77,7 @@ template <class T> inline bool Entry::set(T& property, const T& value)
{
if (property != value) {
property = value;
- emit modified();
+ emit entryModified();
return true;
}
return false;
@@ -409,7 +408,7 @@ void Entry::setIcon(int iconNumber)
m_data.iconNumber = iconNumber;
m_data.customIcon = QUuid();
- emit modified();
+ emit entryModified();
emitDataChanged();
}
}
@@ -422,7 +421,7 @@ void Entry::setIcon(const QUuid& uuid)
m_data.customIcon = uuid;
m_data.iconNumber = 0;
- emit modified();
+ emit entryModified();
emitDataChanged();
}
}
@@ -502,7 +501,7 @@ void Entry::setExpires(const bool& value)
{
if (m_data.timeInfo.expires() != value) {
m_data.timeInfo.setExpires(value);
- emit modified();
+ emit entryModified();
}
}
@@ -510,7 +509,7 @@ void Entry::setExpiryTime(const QDateTime& dateTime)
{
if (m_data.timeInfo.expiryTime() != dateTime) {
m_data.timeInfo.setExpiryTime(dateTime);
- emit modified();
+ emit entryModified();
}
}
@@ -529,7 +528,7 @@ void Entry::addHistoryItem(Entry* entry)
Q_ASSERT(!entry->parent());
m_history.append(entry);
- emit modified();
+ emit entryModified();
}
void Entry::removeHistoryItems(const QList<Entry*>& historyEntries)
@@ -547,7 +546,7 @@ void Entry::removeHistoryItems(const QList<Entry*>& historyEntries)
delete entry;
}
- emit modified();
+ emit entryModified();
}
void Entry::truncateHistory()
@@ -742,7 +741,7 @@ void Entry::updateModifiedSinceBegin()
QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const
{
if (maxDepth <= 0) {
- qWarning() << QString("Maximum depth of replacement has been reached. Entry uuid: %1").arg(uuid().toString());
+ qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", uuid().toString().toLatin1().data());
return str;
}
@@ -766,7 +765,7 @@ QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxD
QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const
{
if (maxDepth <= 0) {
- qWarning() << QString("Maximum depth of replacement has been reached. Entry uuid: %1").arg(uuid().toString());
+ qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", uuid().toString().toLatin1().data());
return placeholder;
}
@@ -830,7 +829,7 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const
{
if (maxDepth <= 0) {
- qWarning() << QString("Maximum depth of replacement has been reached. Entry uuid: %1").arg(uuid().toString());
+ qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", uuid().toString().toLatin1().data());
return placeholder;
}
@@ -850,7 +849,7 @@ QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder,
Q_ASSERT(m_group);
Q_ASSERT(m_group->database());
- const Entry* refEntry = m_group->database()->resolveEntry(searchText, searchInType);
+ const Entry* refEntry = m_group->findEntryBySearchTerm(searchText, searchInType);
if (refEntry) {
const QString wantedField = match.captured(EntryAttributes::WantedFieldGroupName);
@@ -930,7 +929,7 @@ void Entry::setGroup(Group* group)
void Entry::emitDataChanged()
{
- emit dataChanged(this);
+ emit entryDataChanged(this);
}
const Database* Entry::database() const
diff --git a/src/core/Entry.h b/src/core/Entry.h
index 05ed30bc0..f6518bdd3 100644
--- a/src/core/Entry.h
+++ b/src/core/Entry.h
@@ -221,9 +221,8 @@ signals:
/**
* Emitted when a default attribute has been changed.
*/
- void dataChanged(Entry* entry);
-
- void modified();
+ void entryDataChanged(Entry* entry);
+ void entryModified();
private slots:
void emitDataChanged();
diff --git a/src/core/EntryAttachments.cpp b/src/core/EntryAttachments.cpp
index d6d459636..dfa59ec8c 100644
--- a/src/core/EntryAttachments.cpp
+++ b/src/core/EntryAttachments.cpp
@@ -66,7 +66,7 @@ void EntryAttachments::set(const QString& key, const QByteArray& value)
}
if (emitModified) {
- emit modified();
+ emit entryAttachmentsModified();
}
}
@@ -82,7 +82,7 @@ void EntryAttachments::remove(const QString& key)
m_attachments.remove(key);
emit removed(key);
- emit modified();
+ emit entryAttachmentsModified();
}
void EntryAttachments::remove(const QStringList& keys)
@@ -106,7 +106,7 @@ void EntryAttachments::remove(const QStringList& keys)
}
if (isModified) {
- emit modified();
+ emit entryAttachmentsModified();
}
}
@@ -126,7 +126,7 @@ void EntryAttachments::clear()
m_attachments.clear();
emit reset();
- emit modified();
+ emit entryAttachmentsModified();
}
void EntryAttachments::copyDataFrom(const EntryAttachments* other)
@@ -137,7 +137,7 @@ void EntryAttachments::copyDataFrom(const EntryAttachments* other)
m_attachments = other->m_attachments;
emit reset();
- emit modified();
+ emit entryAttachmentsModified();
}
}
diff --git a/src/core/EntryAttachments.h b/src/core/EntryAttachments.h
index c2fa55b44..2cb884e77 100644
--- a/src/core/EntryAttachments.h
+++ b/src/core/EntryAttachments.h
@@ -44,7 +44,7 @@ public:
int attachmentsSize() const;
signals:
- void modified();
+ void entryAttachmentsModified();
void keyModified(const QString& key);
void aboutToBeAdded(const QString& key);
void added(const QString& key);
diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp
index 423e9d49e..b9fcd9ac2 100644
--- a/src/core/EntryAttributes.cpp
+++ b/src/core/EntryAttributes.cpp
@@ -118,7 +118,7 @@ void EntryAttributes::set(const QString& key, const QString& value, bool protect
}
if (emitModified) {
- emit modified();
+ emit entryAttributesModified();
}
if (defaultAttribute && changeValue) {
@@ -145,7 +145,7 @@ void EntryAttributes::remove(const QString& key)
m_protectedAttributes.remove(key);
emit removed(key);
- emit modified();
+ emit entryAttributesModified();
}
void EntryAttributes::rename(const QString& oldKey, const QString& newKey)
@@ -175,7 +175,7 @@ void EntryAttributes::rename(const QString& oldKey, const QString& newKey)
m_protectedAttributes.insert(newKey);
}
- emit modified();
+ emit entryAttributesModified();
emit renamed(oldKey, newKey);
}
@@ -207,7 +207,7 @@ void EntryAttributes::copyCustomKeysFrom(const EntryAttributes* other)
}
emit reset();
- emit modified();
+ emit entryAttributesModified();
}
bool EntryAttributes::areCustomKeysDifferent(const EntryAttributes* other)
@@ -240,7 +240,7 @@ void EntryAttributes::copyDataFrom(const EntryAttributes* other)
m_protectedAttributes = other->m_protectedAttributes;
emit reset();
- emit modified();
+ emit entryAttributesModified();
}
}
@@ -275,7 +275,7 @@ void EntryAttributes::clear()
}
emit reset();
- emit modified();
+ emit entryAttributesModified();
}
int EntryAttributes::attributesSize() const
diff --git a/src/core/EntryAttributes.h b/src/core/EntryAttributes.h
index 91cc50879..15c84bdcd 100644
--- a/src/core/EntryAttributes.h
+++ b/src/core/EntryAttributes.h
@@ -66,7 +66,7 @@ public:
static const QString SearchTextGroupName;
signals:
- void modified();
+ void entryAttributesModified();
void defaultKeyModified();
void customKeyModified(const QString& key);
void aboutToBeAdded(const QString& key);
diff --git a/src/core/Group.cpp b/src/core/Group.cpp
index fcb8fe8a8..68c2bcf47 100644
--- a/src/core/Group.cpp
+++ b/src/core/Group.cpp
@@ -43,8 +43,8 @@ Group::Group()
m_data.searchingEnabled = Inherit;
m_data.mergeMode = Default;
- connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
- connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
+ connect(m_customData, SIGNAL(customDataModified()), this, SIGNAL(groupModified()));
+ connect(this, SIGNAL(groupModified()), SLOT(updateTimeinfo()));
}
Group::~Group()
@@ -87,7 +87,7 @@ template <class P, class V> inline bool Group::set(P& property, const V& value)
{
if (property != value) {
property = value;
- emit modified();
+ emit groupModified();
return true;
} else {
return false;
@@ -310,7 +310,7 @@ void Group::setUuid(const QUuid& uuid)
void Group::setName(const QString& name)
{
if (set(m_data.name, name)) {
- emit dataChanged(this);
+ emit groupDataChanged(this);
}
}
@@ -324,8 +324,8 @@ void Group::setIcon(int iconNumber)
if (iconNumber >= 0 && (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull())) {
m_data.iconNumber = iconNumber;
m_data.customIcon = QUuid();
- emit modified();
- emit dataChanged(this);
+ emit groupModified();
+ emit groupDataChanged(this);
}
}
@@ -334,8 +334,8 @@ void Group::setIcon(const QUuid& uuid)
if (!uuid.isNull() && m_data.customIcon != uuid) {
m_data.customIcon = uuid;
m_data.iconNumber = 0;
- emit modified();
- emit dataChanged(this);
+ emit groupModified();
+ emit groupDataChanged(this);
}
}
@@ -352,7 +352,7 @@ void Group::setExpanded(bool expanded)
updateTimeinfo();
return;
}
- emit modified();
+ emit groupModified();
}
}
@@ -380,7 +380,7 @@ void Group::setExpires(bool value)
{
if (m_data.timeInfo.expires() != value) {
m_data.timeInfo.setExpires(value);
- emit modified();
+ emit groupModified();
}
}
@@ -388,7 +388,7 @@ void Group::setExpiryTime(const QDateTime& dateTime)
{
if (m_data.timeInfo.expiryTime() != dateTime) {
m_data.timeInfo.setExpiryTime(dateTime);
- emit modified();
+ emit groupModified();
}
}
@@ -441,10 +441,10 @@ void Group::setParent(Group* parent, int index)
}
}
if (m_db != parent->m_db) {
- recSetDatabase(parent->m_db);
+ connectDatabaseSignalsRecursive(parent->m_db);
}
QObject::setParent(parent);
- emit aboutToAdd(this, index);
+ emit groupAboutToAdd(this, index);
Q_ASSERT(index <= parent->m_children.size());
parent->m_children.insert(index, this);
} else {
@@ -460,12 +460,12 @@ void Group::setParent(Group* parent, int index)
m_data.timeInfo.setLocationChanged(Clock::currentDateTimeUtc());
}
- emit modified();
+ emit groupModified();
if (!moveWithinDatabase) {
- emit added();
+ emit groupAdded();
} else {
- emit moved();
+ emit groupMoved();
}
}
@@ -477,7 +477,7 @@ void Group::setParent(Database* db)
cleanupParent();
m_parent = nullptr;
- recSetDatabase(db);
+ connectDatabaseSignalsRecursive(db);
QObject::setParent(db);
}
@@ -578,6 +578,53 @@ Entry* Group::findEntryByPath(const QString& entryPath)
return findEntryByPathRecursive(normalizedEntryPath, "/");
}
+Entry* Group::findEntryBySearchTerm(const QString& term, EntryReferenceType referenceType)
+{
+ Q_ASSERT_X(referenceType != EntryReferenceType::Unknown,
+ "Database::findEntryRecursive",
+ "Can't search entry with \"referenceType\" parameter equal to \"Unknown\"");
+
+ const QList<Group*> groups = groupsRecursive(true);
+
+ for (const Group* group : groups) {
+ bool found = false;
+ const QList<Entry*>& entryList = group->entries();
+ for (Entry* entry : entryList) {
+ switch (referenceType) {
+ case EntryReferenceType::Unknown:
+ return nullptr;
+ case EntryReferenceType::Title:
+ found = entry->title() == term;
+ break;
+ case EntryReferenceType::UserName:
+ found = entry->username() == term;
+ break;
+ case EntryReferenceType::Password:
+ found = entry->password() == term;
+ break;
+ case EntryReferenceType::Url:
+ found = entry->url() == term;
+ break;
+ case EntryReferenceType::Notes:
+ found = entry->notes() == term;
+ break;
+ case EntryReferenceType::QUuid:
+ found = entry->uuid() == QUuid::fromRfc4122(QByteArray::fromHex(term.toLatin1()));
+ break;
+ case EntryReferenceType::CustomAttributes:
+ found = entry->attributes()->containsValue(term);
+ break;
+ }
+
+ if (found) {
+ return entry;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
Entry* Group::findEntryByPathRecursive(const QString& entryPath, const QString& basePath)
{
// Return the first entry that matches the full path OR if there is no leading
@@ -798,12 +845,12 @@ void Group::addEntry(Entry* entry)
emit entryAboutToAdd(entry);
m_entries << entry;
- connect(entry, SIGNAL(dataChanged(Entry*)), SIGNAL(entryDataChanged(Entry*)));
+ connect(entry, SIGNAL(entryDataChanged(Entry*)), SIGNAL(entryDataChanged(Entry*)));
if (m_db) {
- connect(entry, SIGNAL(modified()), m_db, SIGNAL(modifiedImmediate()));
+ connect(entry, SIGNAL(entryModified()), m_db, SLOT(markAsModified()));
}
- emit modified();
+ emit groupModified();
emit entryAdded(entry);
}
@@ -820,21 +867,21 @@ void Group::removeEntry(Entry* entry)
entry->disconnect(m_db);
}
m_entries.removeAll(entry);
- emit modified();
+ emit groupModified();
emit entryRemoved(entry);
}
-void Group::recSetDatabase(Database* db)
+void Group::connectDatabaseSignalsRecursive(Database* db)
{
if (m_db) {
- disconnect(SIGNAL(dataChanged(Group*)), m_db);
- disconnect(SIGNAL(aboutToRemove(Group*)), m_db);
- disconnect(SIGNAL(removed()), m_db);
- disconnect(SIGNAL(aboutToAdd(Group*, int)), m_db);
- disconnect(SIGNAL(added()), m_db);
+ disconnect(SIGNAL(groupDataChanged(Group*)), m_db);
+ disconnect(SIGNAL(groupAboutToRemove(Group*)), m_db);
+ disconnect(SIGNAL(groupRemoved()), m_db);
+ disconnect(SIGNAL(groupAboutToAdd(Group*, int)), m_db);
+ disconnect(SIGNAL(groupAdded()), m_db);
disconnect(SIGNAL(aboutToMove(Group*, Group*, int)), m_db);
- disconnect(SIGNAL(moved()), m_db);
- disconnect(SIGNAL(modified()), m_db);
+ disconnect(SIGNAL(groupMoved()), m_db);
+ disconnect(SIGNAL(groupModified()), m_db);
}
for (Entry* entry : asConst(m_entries)) {
@@ -842,35 +889,35 @@ void Group::recSetDatabase(Database* db)
entry->disconnect(m_db);
}
if (db) {
- connect(entry, SIGNAL(modified()), db, SIGNAL(modifiedImmediate()));
+ connect(entry, SIGNAL(entryModified()), db, SLOT(markAsModified()));
}
}
if (db) {
- connect(this, SIGNAL(dataChanged(Group*)), db, SIGNAL(groupDataChanged(Group*)));
- connect(this, SIGNAL(aboutToRemove(Group*)), db, SIGNAL(groupAboutToRemove(Group*)));
- connect(this, SIGNAL(removed()), db, SIGNAL(groupRemoved()));
- connect(this, SIGNAL(aboutToAdd(Group*,int)), db, SIGNAL(groupAboutToAdd(Group*,int)));
- connect(this, SIGNAL(added()), db, SIGNAL(groupAdded()));
+ connect(this, SIGNAL(groupDataChanged(Group*)), db, SIGNAL(groupDataChanged(Group*)));
+ connect(this, SIGNAL(groupAboutToRemove(Group*)), db, SIGNAL(groupAboutToRemove(Group*)));
+ connect(this, SIGNAL(groupRemoved()), db, SIGNAL(groupRemoved()));
+ connect(this, SIGNAL(groupAboutToAdd(Group*, int)), db, SIGNAL(groupAboutToAdd(Group*,int)));
+ connect(this, SIGNAL(groupAdded()), db, SIGNAL(groupAdded()));
connect(this, SIGNAL(aboutToMove(Group*,Group*,int)), db, SIGNAL(groupAboutToMove(Group*,Group*,int)));
- connect(this, SIGNAL(moved()), db, SIGNAL(groupMoved()));
- connect(this, SIGNAL(modified()), db, SIGNAL(modifiedImmediate()));
+ connect(this, SIGNAL(groupMoved()), db, SIGNAL(groupMoved()));
+ connect(this, SIGNAL(groupModified()), db, SLOT(markAsModified()));
}
m_db = db;
for (Group* group : asConst(m_children)) {
- group->recSetDatabase(db);
+ group->connectDatabaseSignalsRecursive(db);
}
}
void Group::cleanupParent()
{
if (m_parent) {
- emit aboutToRemove(this);
+ emit groupAboutToRemove(this);
m_parent->m_children.removeAll(this);
- emit modified();
- emit removed();
+ emit groupModified();
+ emit groupRemoved();
}
}
@@ -965,7 +1012,7 @@ Entry* Group::addEntryWithPath(const QString& entryPath)
return nullptr;
}
- Entry* entry = new Entry();
+ auto* entry = new Entry();
entry->setTitle(entryTitle);
entry->setUuid(QUuid::createUuid());
entry->setGroup(group);
diff --git a/src/core/Group.h b/src/core/Group.h
index da6994d2a..e3ef9a596 100644
--- a/src/core/Group.h
+++ b/src/core/Group.h
@@ -116,6 +116,7 @@ public:
Group* findChildByName(const QString& name);
Entry* findEntryByUuid(const QUuid& uuid) const;
Entry* findEntryByPath(const QString& entryPath);
+ Entry* findEntryBySearchTerm(const QString& term, EntryReferenceType referenceType);
Group* findGroupByUuid(const QUuid& uuid);
Group* findGroupByPath(const QString& groupPath);
QStringList locate(const QString& locateTerm, const QString& currentPath = {"/"}) const;
@@ -149,6 +150,7 @@ public:
const QList<Group*>& children() const;
QList<Entry*> entries();
const QList<Entry*>& entries() const;
+ Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group = nullptr);
QList<Entry*> entriesRecursive(bool includeHistoryItems = false) const;
QList<const Group*> groupsRecursive(bool includeSelf) const;
QList<Group*> groupsRecursive(bool includeSelf);
@@ -164,14 +166,14 @@ public:
void removeEntry(Entry* entry);
signals:
- void dataChanged(Group* group);
- void aboutToAdd(Group* group, int index);
- void added();
- void aboutToRemove(Group* group);
- void removed();
+ void groupDataChanged(Group* group);
+ void groupAboutToAdd(Group* group, int index);
+ void groupAdded();
+ void groupAboutToRemove(Group* group);
+ void groupRemoved();
void aboutToMove(Group* group, Group* toGroup, int index);
- void moved();
- void modified();
+ void groupMoved();
+ void groupModified();
void entryAboutToAdd(Entry* entry);
void entryAdded(Entry* entry);
void entryAboutToRemove(Entry* entry);
@@ -186,7 +188,7 @@ private:
void setParent(Database* db);
- void recSetDatabase(Database* db);
+ void connectDatabaseSignalsRecursive(Database* db);
void cleanupParent();
void recCreateDelObjects();
diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp
index fb247b891..40bf52775 100644
--- a/src/core/Metadata.cpp
+++ b/src/core/Metadata.cpp
@@ -53,14 +53,14 @@ Metadata::Metadata(QObject* parent)
m_masterKeyChanged = now;
m_settingsChanged = now;
- connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
+ connect(m_customData, SIGNAL(customDataModified()), this, SIGNAL(metadataModified()));
}
template <class P, class V> bool Metadata::set(P& property, const V& value)
{
if (property != value) {
property = value;
- emit modified();
+ emit metadataModified();
return true;
} else {
return false;
@@ -74,7 +74,7 @@ template <class P, class V> bool Metadata::set(P& property, const V& value, QDat
if (m_updateDatetime) {
dateTime = Clock::currentDateTimeUtc();
}
- emit modified();
+ emit metadataModified();
return true;
} else {
return false;
@@ -311,9 +311,7 @@ void Metadata::setGenerator(const QString& value)
void Metadata::setName(const QString& value)
{
- if (set(m_data.name, value, m_data.nameChanged)) {
- emit nameTextChanged();
- }
+ set(m_data.name, value, m_data.nameChanged);
}
void Metadata::setNameChanged(const QDateTime& value)
@@ -393,7 +391,7 @@ void Metadata::addCustomIcon(const QUuid& uuid, const QImage& icon)
QByteArray hash = hashImage(icon);
m_customIconsHashes[hash] = uuid;
Q_ASSERT(m_customIcons.count() == m_customIconsOrder.count());
- emit modified();
+ emit metadataModified();
}
void Metadata::addCustomIconScaled(const QUuid& uuid, const QImage& icon)
@@ -428,7 +426,7 @@ void Metadata::removeCustomIcon(const QUuid& uuid)
m_customIconScaledCacheKeys.remove(uuid);
m_customIconsOrder.removeAll(uuid);
Q_ASSERT(m_customIcons.count() == m_customIconsOrder.count());
- emit modified();
+ emit metadataModified();
}
QUuid Metadata::findCustomIcon(const QImage &candidate)
diff --git a/src/core/Metadata.h b/src/core/Metadata.h
index f6fdd56e5..01abcb809 100644
--- a/src/core/Metadata.h
+++ b/src/core/Metadata.h
@@ -148,8 +148,7 @@ public:
void copyAttributesFrom(const Metadata* other);
signals:
- void nameTextChanged();
- void modified();
+ void metadataModified();
private:
template <class P, class V> bool set(P& property, const V& value);
diff --git a/src/crypto/SymmetricCipher.cpp b/src/crypto/SymmetricCipher.cpp
index 828d3a998..10077e7e5 100644
--- a/src/crypto/SymmetricCipher.cpp
+++ b/src/crypto/SymmetricCipher.cpp
@@ -20,8 +20,6 @@
#include "config-keepassx.h"
#include "crypto/SymmetricCipherGcrypt.h"
-#include <QDebug>
-
SymmetricCipher::SymmetricCipher(Algorithm algo, Mode mode, Direction direction)
: m_backend(createBackend(algo, mode, direction))
, m_initialized(false)
@@ -102,7 +100,7 @@ SymmetricCipher::Algorithm SymmetricCipher::cipherToAlgorithm(const QUuid& ciphe
return Twofish;
}
- qWarning() << "SymmetricCipher::cipherToAlgorithm: invalid UUID " << cipher;
+ qWarning("SymmetricCipher::cipherToAlgorithm: invalid UUID %s", cipher.toString().toLatin1().data());
return InvalidAlgorithm;
}
diff --git a/src/format/CsvExporter.cpp b/src/format/CsvExporter.cpp
index 67e6a44fc..0d9a4e0a7 100644
--- a/src/format/CsvExporter.cpp
+++ b/src/format/CsvExporter.cpp
@@ -23,7 +23,7 @@
#include "core/Database.h"
#include "core/Group.h"
-bool CsvExporter::exportDatabase(const QString& filename, const Database* db)
+bool CsvExporter::exportDatabase(const QString& filename, QSharedPointer<const Database> db)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
@@ -33,7 +33,7 @@ bool CsvExporter::exportDatabase(const QString& filename, const Database* db)
return exportDatabase(&file, db);
}
-bool CsvExporter::exportDatabase(QIODevice* device, const Database* db)
+bool CsvExporter::exportDatabase(QIODevice* device, QSharedPointer<const Database> db)
{
QString header;
addColumn(header, "Group");
diff --git a/src/format/CsvExporter.h b/src/format/CsvExporter.h
index 4040a3505..3cb41c0af 100644
--- a/src/format/CsvExporter.h
+++ b/src/format/CsvExporter.h
@@ -20,6 +20,7 @@
#define KEEPASSX_CSVEXPORTER_H
#include <QString>
+#include <QtCore/QSharedPointer>
class Database;
class Group;
@@ -28,8 +29,8 @@ class QIODevice;
class CsvExporter
{
public:
- bool exportDatabase(const QString& filename, const Database* db);
- bool exportDatabase(QIODevice* device, const Database* db);
+ bool exportDatabase(const QString& filename, QSharedPointer<const Database> db);
+ bool exportDatabase(QIODevice* device, QSharedPointer<const Database> db);
QString errorString() const;
private:
diff --git a/src/format/Kdbx3Reader.cpp b/src/format/Kdbx3Reader.cpp
index 0114a0b76..2ef6820ca 100644
--- a/src/format/Kdbx3Reader.cpp
+++ b/src/format/Kdbx3Reader.cpp
@@ -29,77 +29,77 @@
#include <QBuffer>
-Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device,
- const QByteArray& headerData,
- QSharedPointer<const CompositeKey> key,
- bool keepDatabase)
+bool Kdbx3Reader::readDatabaseImpl(QIODevice* device,
+ const QByteArray& headerData,
+ QSharedPointer<const CompositeKey> key,
+ Database* db)
{
Q_ASSERT(m_kdbxVersion <= KeePass2::FILE_VERSION_3_1);
if (hasError()) {
- return nullptr;
+ return false;
}
// check if all required headers were present
if (m_masterSeed.isEmpty() || m_encryptionIV.isEmpty() || m_streamStartBytes.isEmpty()
|| m_protectedStreamKey.isEmpty()
- || m_db->cipher().isNull()) {
+ || db->cipher().isNull()) {
raiseError(tr("missing database headers"));
- return nullptr;
+ return false;
}
- if (!m_db->setKey(key, false)) {
+ if (!db->setKey(key, false)) {
raiseError(tr("Unable to calculate master key"));
- return nullptr;
+ return false;
}
- if (!m_db->challengeMasterSeed(m_masterSeed)) {
+ if (!db->challengeMasterSeed(m_masterSeed)) {
raiseError(tr("Unable to issue challenge-response."));
- return nullptr;
+ return false;
}
CryptoHash hash(CryptoHash::Sha256);
hash.addData(m_masterSeed);
- hash.addData(m_db->challengeResponseKey());
- hash.addData(m_db->transformedMasterKey());
+ hash.addData(db->challengeResponseKey());
+ hash.addData(db->transformedMasterKey());
QByteArray finalKey = hash.result();
- SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(m_db->cipher());
+ SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(db->cipher());
SymmetricCipherStream cipherStream(
device, cipher, SymmetricCipher::algorithmMode(cipher), SymmetricCipher::Decrypt);
if (!cipherStream.init(finalKey, m_encryptionIV)) {
raiseError(cipherStream.errorString());
- return nullptr;
+ return false;
}
if (!cipherStream.open(QIODevice::ReadOnly)) {
raiseError(cipherStream.errorString());
- return nullptr;
+ return false;
}
QByteArray realStart = cipherStream.read(32);
if (realStart != m_streamStartBytes) {
raiseError(tr("Wrong key or database file is corrupt."));
- return nullptr;
+ return false;
}
HashedBlockStream hashedStream(&cipherStream);
if (!hashedStream.open(QIODevice::ReadOnly)) {
raiseError(hashedStream.errorString());
- return nullptr;
+ return false;
}
QIODevice* xmlDevice = nullptr;
QScopedPointer<QtIOCompressor> ioCompressor;
- if (m_db->compressionAlgo() == Database::CompressionNone) {
+ if (db->compressionAlgorithm() == Database::CompressionNone) {
xmlDevice = &hashedStream;
} else {
ioCompressor.reset(new QtIOCompressor(&hashedStream));
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
if (!ioCompressor->open(QIODevice::ReadOnly)) {
raiseError(ioCompressor->errorString());
- return nullptr;
+ return false;
}
xmlDevice = ioCompressor.data();
}
@@ -107,20 +107,17 @@ Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device,
KeePass2RandomStream randomStream(KeePass2::ProtectedStreamAlgo::Salsa20);
if (!randomStream.init(m_protectedStreamKey)) {
raiseError(randomStream.errorString());
- return nullptr;
+ return false;
}
Q_ASSERT(xmlDevice);
KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_3_1);
- xmlReader.readDatabase(xmlDevice, m_db.data(), &randomStream);
+ xmlReader.readDatabase(xmlDevice, db, &randomStream);
if (xmlReader.hasError()) {
raiseError(xmlReader.errorString());
- if (keepDatabase) {
- return m_db.take();
- }
- return nullptr;
+ return false;
}
Q_ASSERT(!xmlReader.headerHash().isEmpty() || m_kdbxVersion < KeePass2::FILE_VERSION_3_1);
@@ -129,15 +126,17 @@ Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device,
QByteArray headerHash = CryptoHash::hash(headerData, CryptoHash::Sha256);
if (headerHash != xmlReader.headerHash()) {
raiseError(tr("Header doesn't match hash"));
- return nullptr;
+ return false;
}
}
- return m_db.take();
+ return true;
}
-bool Kdbx3Reader::readHeaderField(StoreDataStream& headerStream)
+bool Kdbx3Reader::readHeaderField(StoreDataStream& headerStream, Database* db)
{
+ Q_UNUSED(db);
+
QByteArray fieldIDArray = headerStream.read(1);
if (fieldIDArray.size() != 1) {
raiseError(tr("Invalid header id size"));
diff --git a/src/format/Kdbx3Reader.h b/src/format/Kdbx3Reader.h
index d0dd4c1b7..ded7c52dc 100644
--- a/src/format/Kdbx3Reader.h
+++ b/src/format/Kdbx3Reader.h
@@ -29,13 +29,13 @@ class Kdbx3Reader : public KdbxReader
Q_DECLARE_TR_FUNCTIONS(Kdbx3Reader)
public:
- Database* readDatabaseImpl(QIODevice* device,
- const QByteArray& headerData,
- QSharedPointer<const CompositeKey> key,
- bool keepDatabase) override;
+ bool readDatabaseImpl(QIODevice* device,
+ const QByteArray& headerData,
+ QSharedPointer<const CompositeKey> key,
+ Database* db) override;
protected:
- bool readHeaderField(StoreDataStream& headerStream) override;
+ bool readHeaderField(StoreDataStream& headerStream, Database* db) override;
};
#endif // KEEPASSX_KDBX3READER_H
diff --git a/src/format/Kdbx3Writer.cpp b/src/format/Kdbx3Writer.cpp
index f17da25a1..20af01406 100644
--- a/src/format/Kdbx3Writer.cpp
+++ b/src/format/Kdbx3Writer.cpp
@@ -67,7 +67,7 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db)
CHECK_RETURN_FALSE(writeHeaderField<quint16>(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toRfc4122()));
CHECK_RETURN_FALSE(writeHeaderField<quint16>(&header, KeePass2::HeaderFieldID::CompressionFlags,
- Endian::sizedIntToBytes<qint32>(db->compressionAlgo(),
+ Endian::sizedIntToBytes<qint32>(db->compressionAlgorithm(),
KeePass2::BYTEORDER)));
auto kdf = db->kdf();
CHECK_RETURN_FALSE(writeHeaderField<quint16>(&header, KeePass2::HeaderFieldID::MasterSeed, masterSeed));
@@ -112,7 +112,7 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db)
QIODevice* outputDevice = nullptr;
QScopedPointer<QtIOCompressor> ioCompressor;
- if (db->compressionAlgo() == Database::CompressionNone) {
+ if (db->compressionAlgorithm() == Database::CompressionNone) {
outputDevice = &hashedStream;
} else {
ioCompressor.reset(new QtIOCompressor(&hashedStream));
diff --git a/src/format/Kdbx4Reader.cpp b/src/format/Kdbx4Reader.cpp
index 5a024a254..dd9283062 100644
--- a/src/format/Kdbx4Reader.cpp
+++ b/src/format/Kdbx4Reader.cpp
@@ -28,85 +28,85 @@
#include "streams/QtIOCompressor"
#include "streams/SymmetricCipherStream.h"
-Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device,
- const QByteArray& headerData,
- QSharedPointer<const CompositeKey> key,
- bool keepDatabase)
+bool Kdbx4Reader::readDatabaseImpl(QIODevice* device,
+ const QByteArray& headerData,
+ QSharedPointer<const CompositeKey> key,
+ Database* db)
{
Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4);
m_binaryPoolInverse.clear();
if (hasError()) {
- return nullptr;
+ return false;
}
// check if all required headers were present
- if (m_masterSeed.isEmpty() || m_encryptionIV.isEmpty() || m_db->cipher().isNull()) {
+ if (m_masterSeed.isEmpty() || m_encryptionIV.isEmpty() || db->cipher().isNull()) {
raiseError(tr("missing database headers"));
- return nullptr;
+ return false;
}
- if (!m_db->setKey(key, false, false)) {
+ if (!db->setKey(key, false, false)) {
raiseError(tr("Unable to calculate master key"));
- return nullptr;
+ return false;
}
CryptoHash hash(CryptoHash::Sha256);
hash.addData(m_masterSeed);
- hash.addData(m_db->transformedMasterKey());
+ hash.addData(db->transformedMasterKey());
QByteArray finalKey = hash.result();
QByteArray headerSha256 = device->read(32);
QByteArray headerHmac = device->read(32);
if (headerSha256.size() != 32 || headerHmac.size() != 32) {
raiseError(tr("Invalid header checksum size"));
- return nullptr;
+ return false;
}
if (headerSha256 != CryptoHash::hash(headerData, CryptoHash::Sha256)) {
raiseError(tr("Header SHA256 mismatch"));
- return nullptr;
+ return false;
}
- QByteArray hmacKey = KeePass2::hmacKey(m_masterSeed, m_db->transformedMasterKey());
+ QByteArray hmacKey = KeePass2::hmacKey(m_masterSeed, db->transformedMasterKey());
if (headerHmac
!= CryptoHash::hmac(headerData, HmacBlockStream::getHmacKey(UINT64_MAX, hmacKey), CryptoHash::Sha256)) {
raiseError(tr("Wrong key or database file is corrupt. (HMAC mismatch)"));
- return nullptr;
+ return false;
}
HmacBlockStream hmacStream(device, hmacKey);
if (!hmacStream.open(QIODevice::ReadOnly)) {
raiseError(hmacStream.errorString());
- return nullptr;
+ return false;
}
- SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(m_db->cipher());
+ SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(db->cipher());
if (cipher == SymmetricCipher::InvalidAlgorithm) {
raiseError(tr("Unknown cipher"));
- return nullptr;
+ return false;
}
SymmetricCipherStream cipherStream(
&hmacStream, cipher, SymmetricCipher::algorithmMode(cipher), SymmetricCipher::Decrypt);
if (!cipherStream.init(finalKey, m_encryptionIV)) {
raiseError(cipherStream.errorString());
- return nullptr;
+ return false;
}
if (!cipherStream.open(QIODevice::ReadOnly)) {
raiseError(cipherStream.errorString());
- return nullptr;
+ return false;
}
QIODevice* xmlDevice = nullptr;
QScopedPointer<QtIOCompressor> ioCompressor;
- if (m_db->compressionAlgo() == Database::CompressionNone) {
+ if (db->compressionAlgorithm() == Database::CompressionNone) {
xmlDevice = &cipherStream;
} else {
ioCompressor.reset(new QtIOCompressor(&cipherStream));
ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
if (!ioCompressor->open(QIODevice::ReadOnly)) {
raiseError(ioCompressor->errorString());
- return nullptr;
+ return false;
}
xmlDevice = ioCompressor.data();
}
@@ -115,32 +115,29 @@ Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device,
}
if (hasError()) {
- return nullptr;
+ return false;
}
KeePass2RandomStream randomStream(m_irsAlgo);
if (!randomStream.init(m_protectedStreamKey)) {
raiseError(randomStream.errorString());
- return nullptr;
+ return false;
}
Q_ASSERT(xmlDevice);
KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_4, binaryPool());
- xmlReader.readDatabase(xmlDevice, m_db.data(), &randomStream);
+ xmlReader.readDatabase(xmlDevice, db, &randomStream);
if (xmlReader.hasError()) {
raiseError(xmlReader.errorString());
- if (keepDatabase) {
- return m_db.take();
- }
- return nullptr;
+ return false;
}
- return m_db.take();
+ return true;
}
-bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
+bool Kdbx4Reader::readHeaderField(StoreDataStream& device, Database* db)
{
QByteArray fieldIDArray = device.read(1);
if (fieldIDArray.size() != 1) {
@@ -197,7 +194,7 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
raiseError(tr("Unsupported key derivation function (KDF) or invalid parameters"));
return false;
}
- m_db->setKdf(kdf);
+ db->setKdf(kdf);
break;
}
@@ -206,7 +203,7 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
variantBuffer.setBuffer(&fieldData);
variantBuffer.open(QBuffer::ReadOnly);
QVariantMap data = readVariantMap(&variantBuffer);
- m_db->setPublicCustomData(data);
+ db->setPublicCustomData(data);
break;
}
diff --git a/src/format/Kdbx4Reader.h b/src/format/Kdbx4Reader.h
index f415543e7..3afb5bf69 100644
--- a/src/format/Kdbx4Reader.h
+++ b/src/format/Kdbx4Reader.h
@@ -30,15 +30,15 @@ class Kdbx4Reader : public KdbxReader
Q_DECLARE_TR_FUNCTIONS(Kdbx4Reader)
public:
- Database* readDatabaseImpl(QIODevice* device,
- const QByteArray& headerData,
- QSharedPointer<const CompositeKey> key,
- bool keepDatabase) override;
+ bool readDatabaseImpl(QIODevice* device,
+ const QByteArray& headerData,
+ QSharedPointer<const CompositeKey> key,
+ Database* db) override;
QHash<QByteArray, QString> binaryPoolInverse() const;
QHash<QString, QByteArray> binaryPool() const;
protected:
- bool readHeaderField(StoreDataStream& headerStream) override;
+ bool readHeaderField(StoreDataStream& headerStream, Database* db) override;
private:
bool readInnerHeaderField(QIODevice* device);
diff --git a/src/format/Kdbx4Writer.cpp b/src/format/Kdbx4Writer.cpp
index db90c8592..8439fb1db 100644
--- a/src/format/Kdbx4Writer.cpp
+++ b/src/format/Kdbx4Writer.cpp
@@ -75,7 +75,7 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db)
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toRfc4122()));
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::CompressionFlags,
- Endian::sizedIntToBytes(static_cast<int>(db->compressionAlgo()),
+ Endian::sizedIntToBytes(static_cast<int>(db->compressionAlgorithm()),
KeePass2::BYTEORDER)));
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::MasterSeed, masterSeed));
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::EncryptionIV, encryptionIV));
@@ -138,7 +138,7 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db)
QIODevice* outputDevice = nullptr;
QScopedPointer<QtIOCompressor> ioCompressor;
- if (db->compressionAlgo() == Database::CompressionNone) {
+ if (db->compressionAlgorithm() == Database::CompressionNone) {
outputDevice = cipherStream.data();
} else {
ioCompressor.reset(new QtIOCompressor(cipherStream.data()));
diff --git a/src/format/KdbxReader.cpp b/src/format/KdbxReader.cpp
index 13a792fd5..50b7a7245 100644
--- a/src/format/KdbxReader.cpp
+++ b/src/format/KdbxReader.cpp
@@ -59,14 +59,14 @@ bool KdbxReader::readMagicNumbers(QIODevice* device, quint32& sig1, quint32& sig
*
* @param device input device
* @param key database encryption composite key
- * @param keepDatabase keep database in case of read failure
- * @return pointer to the read database, nullptr on failure
+ * @param db database to read into
+ * @return true on success
*/
-Database* KdbxReader::readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase)
+bool KdbxReader::readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, Database* db)
{
device->seek(0);
- m_db.reset(new Database());
+ m_db = db;
m_xmlData.clear();
m_masterSeed.clear();
m_encryptionIV.clear();
@@ -79,7 +79,7 @@ Database* KdbxReader::readDatabase(QIODevice* device, QSharedPointer<const Compo
// read KDBX magic numbers
quint32 sig1, sig2;
if (!readMagicNumbers(&headerStream, sig1, sig2, m_kdbxVersion)) {
- return nullptr;
+ return false;
}
m_kdbxSignature = qMakePair(sig1, sig2);
@@ -87,24 +87,24 @@ Database* KdbxReader::readDatabase(QIODevice* device, QSharedPointer<const Compo
m_kdbxVersion &= KeePass2::FILE_VERSION_CRITICAL_MASK;
// read header fields
- while (readHeaderField(headerStream) && !hasError()) {
+ while (readHeaderField(headerStream, m_db) && !hasError()) {
}
headerStream.close();
if (hasError()) {
- return nullptr;
+ return false;
}
// read payload
- auto* db = readDatabaseImpl(device, headerStream.storedData(), std::move(key), keepDatabase);
+ bool ok = readDatabaseImpl(device, headerStream.storedData(), std::move(key), db);
if (saveXml()) {
m_xmlData.clear();
decryptXmlInnerStream(m_xmlData, db);
}
- return db;
+ return ok;
}
bool KdbxReader::hasError() const
@@ -175,7 +175,7 @@ void KdbxReader::setCompressionFlags(const QByteArray& data)
raiseError(tr("Unsupported compression algorithm"));
return;
}
- m_db->setCompressionAlgo(static_cast<Database::CompressionAlgorithm>(id));
+ m_db->setCompressionAlgorithm(static_cast<Database::CompressionAlgorithm>(id));
}
/**
diff --git a/src/format/KdbxReader.h b/src/format/KdbxReader.h
index e7bddfbeb..9a500c3f6 100644
--- a/src/format/KdbxReader.h
+++ b/src/format/KdbxReader.h
@@ -40,7 +40,7 @@ public:
virtual ~KdbxReader() = default;
static bool readMagicNumbers(QIODevice* device, quint32& sig1, quint32& sig2, quint32& version);
- Database* readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase = false);
+ bool readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, Database* db);
bool hasError() const;
QString errorString() const;
@@ -57,22 +57,22 @@ protected:
* @param device input device at the payload starting position
* @param KDBX header data as bytes
* @param key database encryption composite key
- * @param keepDatabase keep database in case of read failure
- * @return pointer to the read database, nullptr on failure
+ * @param db database to read into
+ * @return true on success
*/
- virtual Database*
- readDatabaseImpl(QIODevice* device,
- const QByteArray& headerData,
- QSharedPointer<const CompositeKey> key,
- bool keepDatabase) = 0;
+ virtual bool readDatabaseImpl(QIODevice* device,
+ const QByteArray& headerData,
+ QSharedPointer<const CompositeKey> key,
+ Database* db) = 0;
/**
* Read next header field from stream.
*
* @param headerStream input header stream
+ * @param database to read header field for
* @return true if there are more header fields
*/
- virtual bool readHeaderField(StoreDataStream& headerStream) = 0;
+ virtual bool readHeaderField(StoreDataStream& headerStream, Database* db) = 0;
virtual void setCipher(const QByteArray& data);
virtual void setCompressionFlags(const QByteArray& data);
@@ -88,9 +88,6 @@ protected:
void decryptXmlInnerStream(QByteArray& xmlOutput, Database* db) const;
- QScopedPointer<Database> m_db;
-
- QPair<quint32, quint32> m_kdbxSignature;
quint32 m_kdbxVersion = 0;
QByteArray m_masterSeed;
@@ -102,6 +99,9 @@ protected:
QByteArray m_xmlData;
private:
+ QPair<quint32, quint32> m_kdbxSignature;
+ QPointer<Database> m_db;
+
bool m_saveXml = false;
bool m_error = false;
QString m_errorStr = "";
diff --git a/src/format/KdbxXmlReader.cpp b/src/format/KdbxXmlReader.cpp
index ec556dd85..b64c28a22 100644
--- a/src/format/KdbxXmlReader.cpp
+++ b/src/format/KdbxXmlReader.cpp
@@ -56,7 +56,7 @@ KdbxXmlReader::KdbxXmlReader(quint32 version, QHash<QString, QByteArray> binary
* @param device input file
* @return pointer to the new database
*/
-Database* KdbxXmlReader::readDatabase(const QString& filename)
+QSharedPointer<Database> KdbxXmlReader::readDatabase(const QString& filename)
{
QFile file(filename);
file.open(QIODevice::ReadOnly);
@@ -69,10 +69,10 @@ Database* KdbxXmlReader::readDatabase(const QString& filename)
* @param device input device
* @return pointer to the new database
*/
-Database* KdbxXmlReader::readDatabase(QIODevice* device)
+QSharedPointer<Database> KdbxXmlReader::readDatabase(QIODevice* device)
{
- auto db = new Database();
- readDatabase(device, db);
+ auto db = QSharedPointer<Database>::create();
+ readDatabase(device, db.data());
return db;
}
diff --git a/src/format/KdbxXmlReader.h b/src/format/KdbxXmlReader.h
index 4ab82cc0e..dab6fc639 100644
--- a/src/format/KdbxXmlReader.h
+++ b/src/format/KdbxXmlReader.h
@@ -45,8 +45,8 @@ public:
explicit KdbxXmlReader(quint32 version, QHash<QString, QByteArray> binaryPool);
virtual ~KdbxXmlReader() = default;
- virtual Database* readDatabase(const QString& filename);
- virtual Database* readDatabase(QIODevice* device);
+ virtual QSharedPointer<Database> readDatabase(const QString& filename);
+ virtual QSharedPointer<Database> readDatabase(QIODevice* device);
virtual void readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream = nullptr);
bool hasError() const;
diff --git a/src/format/KdbxXmlWriter.cpp b/src/format/KdbxXmlWriter.cpp
index c26d316dc..6bc4be51e 100644
--- a/src/format/KdbxXmlWriter.cpp
+++ b/src/format/KdbxXmlWriter.cpp
@@ -190,7 +190,7 @@ void KdbxXmlWriter::writeBinaries()
m_xml.writeAttribute("ID", QString::number(i.value()));
QByteArray data;
- if (m_db->compressionAlgo() == Database::CompressionGZip) {
+ if (m_db->compressionAlgorithm() == Database::CompressionGZip) {
m_xml.writeAttribute("Compressed", "True");
QBuffer buffer;
diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp
index a991b572f..dc80627d0 100644
--- a/src/format/KeePass1Reader.cpp
+++ b/src/format/KeePass1Reader.cpp
@@ -48,8 +48,7 @@ private:
};
KeePass1Reader::KeePass1Reader()
- : m_db(nullptr)
- , m_tmpParent(nullptr)
+ : m_tmpParent(nullptr)
, m_device(nullptr)
, m_encryptionFlags(0)
, m_transformRounds(0)
@@ -57,7 +56,7 @@ KeePass1Reader::KeePass1Reader()
{
}
-Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& password, QIODevice* keyfileDevice)
+QSharedPointer<Database> KeePass1Reader::readDatabase(QIODevice* device, const QString& password, QIODevice* keyfileDevice)
{
m_error = false;
m_errorStr.clear();
@@ -70,22 +69,22 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
if (keyfileData.isEmpty()) {
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
- return nullptr;
+ return {};
}
if (!keyfileDevice->seek(0)) {
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
- return nullptr;
+ return {};
}
if (!newFileKey->load(keyfileDevice)) {
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
- return nullptr;
+ return {};
}
}
- QScopedPointer<Database> db(new Database());
+ auto db = QSharedPointer<Database>::create();
QScopedPointer<Group> tmpParent(new Group());
- m_db = db.data();
+ m_db = db;
m_tmpParent = tmpParent.data();
m_device = device;
@@ -94,19 +93,19 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
auto signature1 = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
if (!ok || signature1 != KeePass1::SIGNATURE_1) {
raiseError(tr("Not a KeePass database."));
- return nullptr;
+ return {};
}
auto signature2 = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
if (!ok || signature2 != KeePass1::SIGNATURE_2) {
raiseError(tr("Not a KeePass database."));
- return nullptr;
+ return {};
}
m_encryptionFlags = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
if (!ok || !(m_encryptionFlags & KeePass1::Rijndael || m_encryptionFlags & KeePass1::Twofish)) {
raiseError(tr("Unsupported encryption algorithm."));
- return nullptr;
+ return {};
}
auto version = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
@@ -114,49 +113,49 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|| (version & KeePass1::FILE_VERSION_CRITICAL_MASK)
!= (KeePass1::FILE_VERSION & KeePass1::FILE_VERSION_CRITICAL_MASK)) {
raiseError(tr("Unsupported KeePass database version."));
- return nullptr;
+ return {};
}
m_masterSeed = m_device->read(16);
if (m_masterSeed.size() != 16) {
raiseError("Unable to read master seed");
- return nullptr;
+ return {};
}
m_encryptionIV = m_device->read(16);
if (m_encryptionIV.size() != 16) {
raiseError(tr("Unable to read encryption IV", "IV = Initialization Vector for symmetric cipher"));
- return nullptr;
+ return {};
}
auto numGroups = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
if (!ok) {
raiseError(tr("Invalid number of groups"));
- return nullptr;
+ return {};
}
auto numEntries = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
if (!ok) {
raiseError(tr("Invalid number of entries"));
- return nullptr;
+ return {};
}
m_contentHashHeader = m_device->read(32);
if (m_contentHashHeader.size() != 32) {
raiseError(tr("Invalid content hash size"));
- return nullptr;
+ return {};
}
m_transformSeed = m_device->read(32);
if (m_transformSeed.size() != 32) {
raiseError(tr("Invalid transform seed size"));
- return nullptr;
+ return {};
}
m_transformRounds = Endian::readSizedInt<quint32>(m_device, KeePass1::BYTEORDER, &ok);
if (!ok) {
raiseError(tr("Invalid number of transform rounds"));
- return nullptr;
+ return {};
}
auto kdf = QSharedPointer<AesKdf>::create(true);
kdf->setRounds(m_transformRounds);
@@ -168,14 +167,14 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
QScopedPointer<SymmetricCipherStream> cipherStream(testKeys(password, keyfileData, contentPos));
if (!cipherStream) {
- return nullptr;
+ return {};
}
QList<Group*> groups;
for (quint32 i = 0; i < numGroups; i++) {
Group* group = readGroup(cipherStream.data());
if (!group) {
- return nullptr;
+ return {};
}
groups.append(group);
}
@@ -184,14 +183,14 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
for (quint32 i = 0; i < numEntries; i++) {
Entry* entry = readEntry(cipherStream.data());
if (!entry) {
- return nullptr;
+ return {};
}
entries.append(entry);
}
if (!constructGroupTree(groups)) {
raiseError(tr("Unable to construct group tree"));
- return nullptr;
+ return {};
}
for (Entry* entry : asConst(entries)) {
@@ -243,41 +242,39 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
if (!db->setKey(key)) {
raiseError(tr("Unable to calculate master key"));
- return nullptr;
+ return {};
}
- return db.take();
+ return db;
}
-Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& password, const QString& keyfileName)
+QSharedPointer<Database> KeePass1Reader::readDatabase(QIODevice* device, const QString& password, const QString& keyfileName)
{
QScopedPointer<QFile> keyFile;
if (!keyfileName.isEmpty()) {
keyFile.reset(new QFile(keyfileName));
if (!keyFile->open(QFile::ReadOnly)) {
raiseError(keyFile->errorString());
- return nullptr;
+ return {};
}
}
- QScopedPointer<Database> db(readDatabase(device, password, keyFile.data()));
-
- return db.take();
+ return QSharedPointer<Database>(readDatabase(device, password, keyFile.data()));
}
-Database* KeePass1Reader::readDatabase(const QString& filename, const QString& password, const QString& keyfileName)
+QSharedPointer<Database> KeePass1Reader::readDatabase(const QString& filename, const QString& password, const QString& keyfileName)
{
QFile dbFile(filename);
if (!dbFile.open(QFile::ReadOnly)) {
raiseError(dbFile.errorString());
- return nullptr;
+ return {};
}
- Database* db = readDatabase(&dbFile, password, keyfileName);
+ auto db = readDatabase(&dbFile, password, keyfileName);
if (dbFile.error() != QFile::NoError) {
raiseError(dbFile.errorString());
- return nullptr;
+ return {};
}
return db;
@@ -293,8 +290,7 @@ QString KeePass1Reader::errorString()
return m_errorStr;
}
-SymmetricCipherStream*
-KeePass1Reader::testKeys(const QString& password, const QByteArray& keyfileData, qint64 contentPos)
+SymmetricCipherStream* KeePass1Reader::testKeys(const QString& password, const QByteArray& keyfileData, qint64 contentPos)
{
const QList<PasswordEncoding> encodings = {Windows1252, Latin1, UTF8};
diff --git a/src/format/KeePass1Reader.h b/src/format/KeePass1Reader.h
index 8302746c8..b9ad6ee66 100644
--- a/src/format/KeePass1Reader.h
+++ b/src/format/KeePass1Reader.h
@@ -21,6 +21,7 @@
#include <QCoreApplication>
#include <QDateTime>
#include <QHash>
+#include <QSharedPointer>
class Database;
class Entry;
@@ -34,9 +35,9 @@ class KeePass1Reader
public:
KeePass1Reader();
- Database* readDatabase(QIODevice* device, const QString& password, QIODevice* keyfileDevice);
- Database* readDatabase(QIODevice* device, const QString& password, const QString& keyfileName);
- Database* readDatabase(const QString& filename, const QString& password, const QString& keyfileName);
+ QSharedPointer<Database> readDatabase(QIODevice* device, const QString& password, QIODevice* keyfileDevice);
+ QSharedPointer<Database> readDatabase(QIODevice* device, const QString& password, const QString& keyfileName);
+ QSharedPointer<Database> readDatabase(const QString& filename, const QString& password, const QString& keyfileName);
bool hasError();
QString errorString();
@@ -63,7 +64,7 @@ private:
static QDateTime dateFromPackedStruct(const QByteArray& data);
static bool isMetaStream(const Entry* entry);
- Database* m_db;
+ QSharedPointer<Database> m_db;
Group* m_tmpParent;
QIODevice* m_device;
quint32 m_encryptionFlags;
diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp
index f727f274d..b62e398d9 100644
--- a/src/format/KeePass2Reader.cpp
+++ b/src/format/KeePass2Reader.cpp
@@ -21,31 +21,31 @@
#include "format/KeePass1.h"
#include <QFile>
-#include <utility>
/**
* Read database from file and detect correct file format.
*
* @param filename input file
* @param key database encryption composite key
- * @return pointer to the read database, nullptr on failure
+ * @param db Database to read into
+ * @return true on success
*/
-Database* KeePass2Reader::readDatabase(const QString& filename, QSharedPointer<const CompositeKey> key)
+bool KeePass2Reader::readDatabase(const QString& filename, QSharedPointer<const CompositeKey> key, Database* db)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
raiseError(file.errorString());
- return nullptr;
+ return false;
}
- QScopedPointer<Database> db(readDatabase(&file, std::move(key)));
+ bool ok = readDatabase(&file, std::move(key), db);
if (file.error() != QFile::NoError) {
raiseError(file.errorString());
- return nullptr;
+ return false;
}
- return db.take();
+ return ok;
}
/**
@@ -53,10 +53,10 @@ Database* KeePass2Reader::readDatabase(const QString& filename, QSharedPointer<c
*
* @param device input device
* @param key database encryption composite key
- * @param keepDatabase keep database in case of read failure
- * @return pointer to the read database, nullptr on failure
+ * @param db Database to read into
+ * @return true on success
*/
-Database* KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase)
+bool KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, Database* db)
{
m_error = false;
m_errorStr.clear();
@@ -69,7 +69,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer<const C
if (!ok || signature1 != KeePass2::SIGNATURE_1 || signature2 != KeePass2::SIGNATURE_2) {
raiseError(tr("Not a KeePass database."));
- return nullptr;
+ return false;
}
if (signature2 == KeePass1::SIGNATURE_2) {
@@ -77,13 +77,13 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer<const C
"You can import it by clicking on Database > 'Import KeePass 1 database...'.\n"
"This is a one-way migration. You won't be able to open the imported "
"database with the old KeePassX 0.4 version."));
- return nullptr;
+ return false;
}
quint32 maxVersion = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
if (m_version < KeePass2::FILE_VERSION_MIN || m_version > maxVersion) {
raiseError(tr("Unsupported KeePass 2 database version."));
- return nullptr;
+ return false;
}
// determine file format (KDBX 2/3 or 4)
@@ -94,7 +94,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer<const C
}
m_reader->setSaveXml(m_saveXml);
- return m_reader->readDatabase(device, std::move(key), keepDatabase);
+ return m_reader->readDatabase(device, std::move(key), db);
}
bool KeePass2Reader::hasError() const
diff --git a/src/format/KeePass2Reader.h b/src/format/KeePass2Reader.h
index 5ed720d4a..c6c3b0f2b 100644
--- a/src/format/KeePass2Reader.h
+++ b/src/format/KeePass2Reader.h
@@ -35,8 +35,8 @@ class KeePass2Reader
Q_DECLARE_TR_FUNCTIONS(KdbxReader)
public:
- Database* readDatabase(const QString& filename, QSharedPointer<const CompositeKey> key);
- Database* readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase = false);
+ bool readDatabase(const QString& filename, QSharedPointer<const CompositeKey> key, Database* db);
+ bool readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, Database* db);
bool hasError() const;
QString errorString() const;
diff --git a/src/format/KeePass2Writer.cpp b/src/format/KeePass2Writer.cpp
index 262bebc24..46bb8ba22 100644
--- a/src/format/KeePass2Writer.cpp
+++ b/src/format/KeePass2Writer.cpp
@@ -48,6 +48,10 @@ bool KeePass2Writer::writeDatabase(const QString& filename, Database* db)
*/
bool KeePass2Writer::implicitUpgradeNeeded(Database const* db) const
{
+ if (db->kdf()->uuid() != KeePass2::KDF_AES_KDBX3 ) {
+ return false;
+ }
+
if (!db->publicCustomData().isEmpty()) {
return true;
}
diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp
index d872ca68d..73c41ae0c 100644
--- a/src/gui/DatabaseOpenWidget.cpp
+++ b/src/gui/DatabaseOpenWidget.cpp
@@ -105,8 +105,7 @@ void DatabaseOpenWidget::showEvent(QShowEvent* event)
#ifdef WITH_XC_YUBIKEY
// showEvent() may be called twice, so make sure we are only polling once
if (!m_yubiKeyBeingPolled) {
- connect(
- YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection);
+ connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(detectComplete()), SLOT(yubikeyDetectComplete()), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
@@ -156,10 +155,10 @@ void DatabaseOpenWidget::clearForms()
m_ui->checkChallengeResponse->setChecked(false);
m_ui->checkTouchID->setChecked(false);
m_ui->buttonTogglePassword->setChecked(false);
- m_db = nullptr;
+ m_db.reset();
}
-Database* DatabaseOpenWidget::database()
+QSharedPointer<Database> DatabaseOpenWidget::database()
{
return m_db;
}
@@ -178,9 +177,8 @@ void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile)
void DatabaseOpenWidget::openDatabase()
{
- KeePass2Reader reader;
QSharedPointer<CompositeKey> masterKey = databaseKey();
- if (masterKey.isNull()) {
+ if (!masterKey) {
return;
}
@@ -189,18 +187,17 @@ void DatabaseOpenWidget::openDatabase()
}
QCoreApplication::processEvents();
- QFile file(m_filename);
- if (!file.open(QIODevice::ReadOnly)) {
- m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n").append(file.errorString()),
- MessageWidget::Error);
- return;
- }
-
- delete m_db;
-
+ m_db.reset(new Database());
+ QString error;
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- m_db = reader.readDatabase(&file, masterKey);
+ bool ok = m_db->open(m_filename, masterKey, &error, false);
QApplication::restoreOverrideCursor();
+ if (!ok) {
+ m_ui->messageWidget->showMessage(
+ tr("Unable to open the database:\n%1").arg(error),
+ MessageWidget::MessageType::Error);
+ return;
+ }
if (m_db) {
#ifdef WITH_XC_TOUCHID
@@ -224,9 +221,9 @@ void DatabaseOpenWidget::openDatabase()
if (m_ui->messageWidget->isVisible()) {
m_ui->messageWidget->animatedHide();
}
- emit editFinished(true);
+ emit dialogFinished(true);
} else {
- m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n").append(reader.errorString()),
+ m_ui->messageWidget->showMessage(tr("Unable to open the database:\n%1").arg(error),
MessageWidget::Error);
m_ui->editPassword->clear();
@@ -326,7 +323,7 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::databaseKey()
void DatabaseOpenWidget::reject()
{
- emit editFinished(false);
+ emit dialogFinished(false);
}
void DatabaseOpenWidget::activatePassword()
diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h
index 0c4c938b8..72f07cfa8 100644
--- a/src/gui/DatabaseOpenWidget.h
+++ b/src/gui/DatabaseOpenWidget.h
@@ -42,13 +42,13 @@ public:
void load(const QString& filename);
void clearForms();
void enterKey(const QString& pw, const QString& keyFile);
- Database* database();
+ QSharedPointer<Database> database();
public slots:
void pollYubikey();
signals:
- void editFinished(bool accepted);
+ void dialogFinished(bool accepted);
protected:
void showEvent(QShowEvent* event) override;
@@ -70,7 +70,7 @@ private slots:
protected:
const QScopedPointer<Ui::DatabaseOpenWidget> m_ui;
- Database* m_db;
+ QSharedPointer<Database> m_db;
QString m_filename;
private:
diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp
index fcd4f954b..49c5a1545 100644
--- a/src/gui/DatabaseTabWidget.cpp
+++ b/src/gui/DatabaseTabWidget.cpp
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,40 +40,24 @@
#include "gui/group/GroupView.h"
#include "gui/wizard/NewDatabaseWizard.h"
-DatabaseManagerStruct::DatabaseManagerStruct()
- : dbWidget(nullptr)
- , modified(false)
- , readOnly(false)
- , saveAttempts(0)
-{
-}
-
-const int DatabaseTabWidget::LastDatabasesCount = 5;
-
DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
: QTabWidget(parent)
, m_dbWidgetStateSync(new DatabaseWidgetStateSync(this))
, m_dbPendingLock(nullptr)
{
- DragTabBar* tabBar = new DragTabBar(this);
+ auto* tabBar = new DragTabBar(this);
setTabBar(tabBar);
setDocumentMode(true);
- connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabase(int)));
+ connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabaseTab(int)));
connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged()));
- connect(
- this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
+ connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase()));
}
DatabaseTabWidget::~DatabaseTabWidget()
{
- QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
- while (i.hasNext()) {
- i.next();
- deleteDatabase(i.key());
- }
}
void DatabaseTabWidget::toggleTabbar()
@@ -92,18 +75,18 @@ void DatabaseTabWidget::toggleTabbar()
*
* @return pointer to the configured new database, nullptr on failure
*/
-Database* DatabaseTabWidget::execNewDatabaseWizard()
+QSharedPointer<Database> DatabaseTabWidget::execNewDatabaseWizard()
{
// use QScopedPointer to ensure deletion after scope ends, but still parent
// it to this to make it modal and allow easier access in unit tests
QScopedPointer<NewDatabaseWizard> wizard(new NewDatabaseWizard(this));
if (!wizard->exec()) {
- return nullptr;
+ return {};
}
- auto* db = wizard->takeDatabase();
+ auto db = wizard->takeDatabase();
if (!db) {
- return nullptr;
+ return {};
}
Q_ASSERT(db->key());
Q_ASSERT(db->kdf());
@@ -112,7 +95,7 @@ Database* DatabaseTabWidget::execNewDatabaseWizard()
tr("The created database has no key or KDF, refusing to save it.\n"
"This is definitely a bug, please report it to the developers."),
QMessageBox::Ok, QMessageBox::Ok);
- return nullptr;
+ return {};
}
return db;
@@ -120,118 +103,103 @@ Database* DatabaseTabWidget::execNewDatabaseWizard()
void DatabaseTabWidget::newDatabase()
{
- auto* db = execNewDatabaseWizard();
+ auto db = execNewDatabaseWizard();
if (!db) {
return;
}
- DatabaseManagerStruct dbStruct;
- dbStruct.dbWidget = new DatabaseWidget(db, this);
- insertDatabase(db, dbStruct);
-
- if (!saveDatabaseAs(db)) {
- // mark database as dirty if user canceled save dialog
- emit db->modifiedImmediate();
- }
+ addDatabaseTab(new DatabaseWidget(db, this));
+ db->markAsModified();
}
void DatabaseTabWidget::openDatabase()
{
QString filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
- QString fileName = fileDialog()->getOpenFileName(this, tr("Open database"), QDir::homePath(), filter);
+ QString fileName = fileDialog()->getOpenFileName(this, tr("Open database"), "", filter);
if (!fileName.isEmpty()) {
- openDatabase(fileName);
+ addDatabaseTab(fileName);
}
}
-void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, const QString& keyFile)
+/**
+ * Add a new database tab or switch to an existing one if the
+ * database has been opened already.
+ *
+ * @param filePath database file path
+ */
+void DatabaseTabWidget::addDatabaseTab(const QString& filePath)
{
- QFileInfo fileInfo(fileName);
+ QFileInfo fileInfo(filePath);
QString canonicalFilePath = fileInfo.canonicalFilePath();
if (canonicalFilePath.isEmpty()) {
- emit messageGlobal(tr("File not found!"), MessageWidget::Error);
+ emit messageGlobal(tr("The database file does not exist or is not accessible."), MessageWidget::Error);
return;
}
- QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
- while (i.hasNext()) {
- i.next();
- if (i.value().fileInfo.canonicalFilePath() == canonicalFilePath) {
- if (!i.value().dbWidget->dbHasKey() && !(pw.isNull() && keyFile.isEmpty())) {
- // If the database is locked and a pw or keyfile is provided, unlock it
- i.value().dbWidget->switchToOpenDatabase(i.value().fileInfo.absoluteFilePath(), pw, keyFile);
- } else {
- setCurrentIndex(databaseIndex(i.key()));
- }
+ for (int i = 0, c = count(); i < c; ++i) {
+ auto* dbWidget = databaseWidgetFromIndex(i);
+ Q_ASSERT(dbWidget);
+ if (dbWidget && dbWidget->database()->filePath() == canonicalFilePath) {
+ // switch to existing tab if file is already open
+ setCurrentIndex(indexOf(dbWidget));
return;
}
}
- DatabaseManagerStruct dbStruct;
-
- // test if we can read/write or read the file
- QFile file(fileName);
- if (!file.open(QIODevice::ReadWrite)) {
- if (!file.open(QIODevice::ReadOnly)) {
- // can't open
- emit messageGlobal(tr("Unable to open the database.").append("\n").append(file.errorString()),
- MessageWidget::Error);
- return;
- } else {
- // can only open read-only
- dbStruct.readOnly = true;
- }
- }
- file.close();
-
- Database* db = new Database();
- dbStruct.dbWidget = new DatabaseWidget(db, this);
- dbStruct.fileInfo = fileInfo;
-
- insertDatabase(db, dbStruct);
-
- if (dbStruct.readOnly) {
- emit messageTab(tr("File opened in read only mode."), MessageWidget::Warning);
- }
+ auto* dbWidget = new DatabaseWidget(QSharedPointer<Database>::create(filePath), this);
+ addDatabaseTab(dbWidget);
+ updateLastDatabases(filePath);
+}
- updateLastDatabases(dbStruct.fileInfo.absoluteFilePath());
+/**
+ * Add a new database tab containing the given DatabaseWidget
+ * @param filePath
+ */
+void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget)
+{
+ auto db = dbWidget->database();
+ Q_ASSERT(db);
- if (!pw.isNull() || !keyFile.isEmpty()) {
- dbStruct.dbWidget->switchToOpenDatabase(dbStruct.fileInfo.absoluteFilePath(), pw, keyFile);
- } else {
- dbStruct.dbWidget->switchToOpenDatabase(dbStruct.fileInfo.absoluteFilePath());
- }
+ int index = addTab(dbWidget, "");
+ updateTabName(index);
+ setCurrentIndex(index);
+ toggleTabbar();
- emit messageDismissTab();
+ connect(dbWidget, SIGNAL(databaseFilePathChanged(QString,QString)), SLOT(updateTabName()));
+ connect(dbWidget, SIGNAL(closeRequest()), SLOT(closeDatabaseTabFromSender()));
+ connect(dbWidget, SIGNAL(databaseModified()), SLOT(updateTabName()));
+ connect(dbWidget, SIGNAL(databaseSaved()), SLOT(updateTabName()));
+ connect(dbWidget, SIGNAL(databaseUnlocked()), SLOT(updateTabName()));
+ connect(dbWidget, SIGNAL(databaseUnlocked()), SLOT(emitDatabaseLockChanged()));
+ connect(dbWidget, SIGNAL(databaseLocked()), SLOT(updateTabName()));
+ connect(dbWidget, SIGNAL(databaseLocked()), SLOT(emitDatabaseLockChanged()));
}
void DatabaseTabWidget::importCsv()
{
QString filter = QString("%1 (*.csv);;%2 (*)").arg(tr("CSV file"), tr("All files"));
- QString fileName = fileDialog()->getOpenFileName(this, tr("Select CSV file"), {}, filter);
+ QString fileName = fileDialog()->getOpenFileName(this, tr("Select CSV file"), "", filter);
if (fileName.isEmpty()) {
return;
}
- auto* db = execNewDatabaseWizard();
+ auto db = execNewDatabaseWizard();
if (!db) {
return;
}
- DatabaseManagerStruct dbStruct;
- dbStruct.dbWidget = new DatabaseWidget(db, this);
- insertDatabase(db, dbStruct);
- dbStruct.dbWidget->switchToCsvImport(fileName);
+ auto* dbWidget = new DatabaseWidget(db, this);
+ addDatabaseTab(dbWidget);
+ dbWidget->switchToCsvImport(fileName);
}
void DatabaseTabWidget::mergeDatabase()
{
auto dbWidget = currentDatabaseWidget();
- if (dbWidget && dbWidget->currentMode() != DatabaseWidget::LockedMode) {
+ if (dbWidget && !dbWidget->isLocked()) {
QString filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
- const QString fileName = fileDialog()->getOpenFileName(this, tr("Merge database"), QString(),
- filter);
+ const QString fileName = fileDialog()->getOpenFileName(this, tr("Merge database"), "", filter);
if (!fileName.isEmpty()) {
mergeDatabase(fileName);
}
@@ -252,213 +220,85 @@ void DatabaseTabWidget::importKeePass1Database()
return;
}
- Database* db = new Database();
- DatabaseManagerStruct dbStruct;
- dbStruct.dbWidget = new DatabaseWidget(db, this);
- dbStruct.dbWidget->databaseModified();
- dbStruct.modified = true;
-
- insertDatabase(db, dbStruct);
-
- dbStruct.dbWidget->switchToImportKeepass1(fileName);
+ auto db = QSharedPointer<Database>::create();
+ auto* dbWidget = new DatabaseWidget(db, this);
+ addDatabaseTab(dbWidget);
+ dbWidget->switchToImportKeepass1(fileName);
}
-bool DatabaseTabWidget::closeDatabase(Database* db)
+/**
+ * Attempt to close the current database and remove its tab afterwards.
+ *
+ * @param index index of the database tab to close
+ * @return true if database was closed successully
+ */
+bool DatabaseTabWidget::closeCurrentDatabaseTab()
{
- Q_ASSERT(db);
-
- const DatabaseManagerStruct& dbStruct = m_dbList.value(db);
- int index = databaseIndex(db);
- Q_ASSERT(index != -1);
-
- dbStruct.dbWidget->closeUnlockDialog();
- QString dbName = tabText(index);
- if (dbName.right(1) == "*") {
- dbName.chop(1);
- }
- if (dbStruct.dbWidget->isInEditMode() && db->hasKey() && dbStruct.dbWidget->isEditWidgetModified()) {
- QMessageBox::StandardButton result = MessageBox::question(
- this,
- tr("Close?"),
- tr("\"%1\" is in edit mode.\nDiscard changes and close anyway?").arg(dbName.toHtmlEscaped()),
- QMessageBox::Discard | QMessageBox::Cancel,
- QMessageBox::Cancel);
- if (result == QMessageBox::Cancel) {
- return false;
- }
- }
- if (dbStruct.modified) {
- if (config()->get("AutoSaveOnExit").toBool()) {
- if (!saveDatabase(db)) {
- return false;
- }
- } else if (dbStruct.dbWidget->currentMode() != DatabaseWidget::LockedMode) {
- QMessageBox::StandardButton result =
- MessageBox::question(this,
- tr("Save changes?"),
- tr("\"%1\" was modified.\nSave changes?").arg(dbName.toHtmlEscaped()),
- QMessageBox::Yes | QMessageBox::Discard | QMessageBox::Cancel,
- QMessageBox::Yes);
- if (result == QMessageBox::Yes) {
- if (!saveDatabase(db)) {
- return false;
- }
- } else if (result == QMessageBox::Cancel) {
- return false;
- }
- }
- }
-
- deleteDatabase(db);
-
- return true;
+ return closeDatabaseTab(currentIndex());
}
-void DatabaseTabWidget::deleteDatabase(Database* db)
+/**
+ * Attempt to close the database tab that sent the close request.
+ *
+ * @param index index of the database tab to close
+ * @return true if database was closed successully
+ */
+bool DatabaseTabWidget::closeDatabaseTabFromSender()
{
- const DatabaseManagerStruct dbStruct = m_dbList.value(db);
- bool emitDatabaseWithFileClosed = dbStruct.fileInfo.exists() && !dbStruct.readOnly;
- QString filePath = dbStruct.fileInfo.absoluteFilePath();
-
- int index = databaseIndex(db);
-
- removeTab(index);
- toggleTabbar();
- m_dbList.remove(db);
- delete dbStruct.dbWidget;
- delete db;
-
- if (emitDatabaseWithFileClosed) {
- emit databaseWithFileClosed(filePath);
- }
+ return closeDatabaseTab(qobject_cast<DatabaseWidget*>(sender()));
}
-bool DatabaseTabWidget::closeAllDatabases()
+/**
+ * Attempt to close a database and remove its tab afterwards.
+ *
+ * @param index index of the database tab to close
+ * @return true if database was closed successully
+ */
+bool DatabaseTabWidget::closeDatabaseTab(int index)
{
- while (!m_dbList.isEmpty()) {
- if (!closeDatabase()) {
- return false;
- }
- }
- return true;
+ return closeDatabaseTab(qobject_cast<DatabaseWidget*>(widget(index)));
}
-bool DatabaseTabWidget::saveDatabase(Database* db, QString filePath)
+/**
+ * Attempt to close a database and remove its tab afterwards.
+ *
+ * @param dbWidget \link DatabaseWidget to close
+ * @return true if database was closed successully
+ */
+bool DatabaseTabWidget::closeDatabaseTab(DatabaseWidget* dbWidget)
{
- DatabaseManagerStruct& dbStruct = m_dbList[db];
-
- // Never allow saving a locked database; it causes corruption
- Q_ASSERT(dbStruct.dbWidget->currentMode() != DatabaseWidget::LockedMode);
- // Release build interlock
- if (dbStruct.dbWidget->currentMode() == DatabaseWidget::LockedMode) {
- // We return true since a save is not required
- return true;
- }
-
- if (filePath.isEmpty()) {
- filePath = dbStruct.fileInfo.canonicalFilePath();
- }
-
- if (dbStruct.readOnly || filePath.isEmpty()) {
- return saveDatabaseAs(db);
- }
-
- dbStruct.dbWidget->blockAutoReload(true);
- // TODO: Make this async, but lock out the database widget to prevent re-entrance
- bool useAtomicSaves = config()->get("UseAtomicSaves", true).toBool();
- QString errorMessage = db->saveToFile(filePath, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
- dbStruct.dbWidget->blockAutoReload(false);
-
- if (errorMessage.isEmpty()) {
- // successfully saved database file
- dbStruct.modified = false;
- dbStruct.saveAttempts = 0;
- dbStruct.fileInfo = QFileInfo(filePath);
- dbStruct.dbWidget->databaseSaved();
- updateTabName(db);
- emit messageDismissTab();
- return true;
- } else {
- dbStruct.modified = true;
- updateTabName(db);
-
- if (++dbStruct.saveAttempts > 2 && useAtomicSaves) {
- // Saving failed 3 times, issue a warning and attempt to resolve
- auto choice = MessageBox::question(this,
- tr("Disable safe saves?"),
- tr("KeePassXC has failed to save the database multiple times. "
- "This is likely caused by file sync services holding a lock on "
- "the save file.\nDisable safe saves and try again?"),
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes);
- if (choice == QMessageBox::Yes) {
- config()->set("UseAtomicSaves", false);
- return saveDatabase(db, filePath);
- }
- // Reset save attempts without changing anything
- dbStruct.saveAttempts = 0;
- }
-
- emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage), MessageWidget::Error);
+ int tabIndex = indexOf(dbWidget);
+ if (!dbWidget || tabIndex < 0) {
return false;
}
-}
-bool DatabaseTabWidget::saveDatabaseAs(Database* db)
-{
- while (true) {
- DatabaseManagerStruct& dbStruct = m_dbList[db];
- QString oldFilePath;
- if (dbStruct.fileInfo.exists()) {
- oldFilePath = dbStruct.fileInfo.absoluteFilePath();
- } else {
- oldFilePath = QDir::toNativeSeparators(QDir::homePath() + "/" + tr("Passwords").append(".kdbx"));
- }
- QString newFilePath = fileDialog()->getSaveFileName(this,
- tr("Save database as"),
- oldFilePath,
- tr("KeePass 2 Database").append(" (*.kdbx)"),
- nullptr,
- nullptr,
- "kdbx");
- if (!newFilePath.isEmpty()) {
- // Ensure we don't recurse back into this function
- dbStruct.readOnly = false;
-
- if (!saveDatabase(db, newFilePath)) {
- // Failed to save, try again
- continue;
- }
-
- dbStruct.dbWidget->updateFilePath(dbStruct.fileInfo.absoluteFilePath());
- updateLastDatabases(dbStruct.fileInfo.absoluteFilePath());
- return true;
- }
-
- // Canceled file selection
+ QString filePath = dbWidget->database()->filePath();
+ if (!dbWidget->close()) {
return false;
}
+
+ removeTab(tabIndex);
+ dbWidget->deleteLater();
+ toggleTabbar();
+ emit databaseClosed(filePath);
+ return true;
}
-bool DatabaseTabWidget::closeDatabase(int index)
+/**
+ * Attempt to close all opened databases.
+ * The attempt will be aborted with the first database that cannot be closed.
+ *
+ * @return true if all databases could be closed.
+ */
+bool DatabaseTabWidget::closeAllDatabaseTabs()
{
- if (index == -1) {
- index = currentIndex();
+ while (count() > 0) {
+ if (!closeDatabaseTab(0)) {
+ return false;
+ }
}
- setCurrentIndex(index);
-
- return closeDatabase(indexDatabase(index));
-}
-
-void DatabaseTabWidget::closeDatabaseFromSender()
-{
- Q_ASSERT(sender());
- DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(sender());
- Database* db = databaseFromDatabaseWidget(dbWidget);
- int index = databaseIndex(db);
- setCurrentIndex(index);
- closeDatabase(db);
+ return true;
}
bool DatabaseTabWidget::saveDatabase(int index)
@@ -467,7 +307,7 @@ bool DatabaseTabWidget::saveDatabase(int index)
index = currentIndex();
}
- return saveDatabase(indexDatabase(index));
+ return databaseWidgetFromIndex(index)->save();
}
bool DatabaseTabWidget::saveDatabaseAs(int index)
@@ -476,12 +316,24 @@ bool DatabaseTabWidget::saveDatabaseAs(int index)
index = currentIndex();
}
- return saveDatabaseAs(indexDatabase(index));
+ auto* dbWidget = databaseWidgetFromIndex(index);
+ bool ok = dbWidget->saveAs();
+ if (ok) {
+ updateLastDatabases(dbWidget->database()->filePath());
+ }
+ return ok;
+}
+
+void DatabaseTabWidget::closeDatabaseFromSender()
+{
+ auto* dbWidget = qobject_cast<DatabaseWidget*>(sender());
+ Q_ASSERT(dbWidget);
+ closeDatabaseTab(dbWidget);
}
void DatabaseTabWidget::exportToCsv()
{
- Database* db = indexDatabase(currentIndex());
+ auto db = databaseWidgetFromIndex(currentIndex())->database();
if (!db) {
Q_ASSERT(false);
return;
@@ -510,272 +362,140 @@ void DatabaseTabWidget::changeDatabaseSettings()
currentDatabaseWidget()->switchToDatabaseSettings();
}
-bool DatabaseTabWidget::readOnly(int index)
+bool DatabaseTabWidget::isReadOnly(int index) const
{
+ if (count() == 0) {
+ return false;
+ }
+
if (index == -1) {
index = currentIndex();
}
- return indexDatabaseManagerStruct(index).readOnly;
+ auto db = databaseWidgetFromIndex(index)->database();
+ return db && db->isReadOnly();
}
-bool DatabaseTabWidget::canSave(int index)
+bool DatabaseTabWidget::isModified(int index) const
{
+ if (count() == 0) {
+ return false;
+ }
+
if (index == -1) {
index = currentIndex();
}
- const DatabaseManagerStruct& dbStruct = indexDatabaseManagerStruct(index);
- return dbStruct.modified && !dbStruct.readOnly;
+ auto db = databaseWidgetFromIndex(index)->database();
+ return db && db->isModified();
}
-bool DatabaseTabWidget::isModified(int index)
+bool DatabaseTabWidget::canSave(int index) const
{
- if (index == -1) {
- index = currentIndex();
- }
-
- return indexDatabaseManagerStruct(index).modified;
+ return !isReadOnly(index) && isModified(index);
}
-QString DatabaseTabWidget::databasePath(int index)
+bool DatabaseTabWidget::hasLockableDatabases() const
{
- if (index == -1) {
- index = currentIndex();
+ for (int i = 0, c = count(); i < c; ++i) {
+ if (!databaseWidgetFromIndex(i)->isLocked()) {
+ return true;
+ }
}
-
- return indexDatabaseManagerStruct(index).fileInfo.absoluteFilePath();
+ return false;
}
-void DatabaseTabWidget::updateTabName(Database* db)
+/**
+ * Get the tab's (original) display name without platform-specific
+ * mangling that may occur when reading back the actual widget's \link tabText()
+ *
+ * @param index tab index
+ * @return tab name
+ */
+QString DatabaseTabWidget::tabName(int index)
{
- int index = databaseIndex(db);
- Q_ASSERT(index != -1);
+ if (index == -1 || index > count()) {
+ return "";
+ }
- const DatabaseManagerStruct& dbStruct = m_dbList.value(db);
+ auto* dbWidget = databaseWidgetFromIndex(index);
+
+ auto db = dbWidget->database();
+ Q_ASSERT(db);
+ if (!db) {
+ return "";
+ }
QString tabName;
- QString fileName;
- if (dbStruct.fileInfo.exists()) {
+ if (!db->filePath().isEmpty()) {
+ QFileInfo fileInfo(db->filePath());
+
if (db->metadata()->name().isEmpty()) {
- tabName = dbStruct.fileInfo.fileName();
+ tabName = fileInfo.fileName();
} else {
tabName = db->metadata()->name();
}
- fileName = dbStruct.fileInfo.fileName();
- setTabToolTip(index, dbStruct.fileInfo.absoluteFilePath());
+ setTabToolTip(index, fileInfo.absoluteFilePath());
} else {
if (db->metadata()->name().isEmpty()) {
- tabName = tr("New database");
+ tabName = tr("New Database");
} else {
- tabName = tr("%1 [New database]", "tab modifier").arg(db->metadata()->name());
+ tabName = tr("%1 [New Database]", "Database tab name modifier").arg(db->metadata()->name());
}
}
- if (dbStruct.dbWidget->currentMode() == DatabaseWidget::LockedMode) {
- tabName = tr("%1 [locked]", "tab modifier").arg(tabName);
+ if (dbWidget->isLocked()) {
+ tabName = tr("%1 [Locked]", "Database tab name modifier").arg(tabName);
}
- if (dbStruct.modified) {
- tabName.append("*");
+ if (db->isReadOnly()) {
+ tabName = tr("%1 [Read-only]", "Database tab name modifier").arg(tabName);
}
- dbStruct.dbWidget->setDatabaseName(tabName);
- dbStruct.dbWidget->setDatabaseFileName(fileName);
-
- setTabText(index, tabName);
- emit tabNameChanged();
-}
-
-void DatabaseTabWidget::updateTabNameFromDbSender()
-{
- Q_ASSERT(qobject_cast<Database*>(sender()));
-
- updateTabName(static_cast<Database*>(sender()));
-}
-
-void DatabaseTabWidget::updateTabNameFromDbWidgetSender()
-{
- Q_ASSERT(qobject_cast<DatabaseWidget*>(sender()));
- Q_ASSERT(databaseFromDatabaseWidget(qobject_cast<DatabaseWidget*>(sender())));
-
- DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(sender());
- updateTabName(databaseFromDatabaseWidget(dbWidget));
-
- Database* db = dbWidget->database();
- Group* autoload = db->rootGroup()->findChildByName("AutoOpen");
- if (autoload) {
- const DatabaseManagerStruct& dbStruct = m_dbList.value(db);
- QDir dbFolder(dbStruct.fileInfo.canonicalPath());
- for (auto entry : autoload->entries()) {
- if (entry->url().isEmpty() || entry->password().isEmpty()) {
- continue;
- }
- QFileInfo filepath;
- if (entry->url().startsWith("file://")) {
- QUrl url(entry->url());
- filepath.setFile(url.toLocalFile());
- } else {
- filepath.setFile(entry->url());
- if (filepath.isRelative()) {
- filepath.setFile(dbFolder, entry->url());
- }
- }
-
- if (!filepath.isFile()) {
- continue;
- }
-
- openDatabase(filepath.canonicalFilePath(), entry->password(), "");
- }
- }
-}
-
-int DatabaseTabWidget::databaseIndex(Database* db)
-{
- QWidget* dbWidget = m_dbList.value(db).dbWidget;
- return indexOf(dbWidget);
-}
-
-Database* DatabaseTabWidget::indexDatabase(int index)
-{
- QWidget* dbWidget = widget(index);
-
- QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
- while (i.hasNext()) {
- i.next();
- if (i.value().dbWidget == dbWidget) {
- return i.key();
- }
+ if (db->isModified()) {
+ tabName.append("*");
}
- return nullptr;
+ return tabName;
}
-DatabaseManagerStruct DatabaseTabWidget::indexDatabaseManagerStruct(int index)
+/**
+ * Update of the given tab index or of the sending
+ * DatabaseWidget if `index` == -1.
+ */
+void DatabaseTabWidget::updateTabName(int index)
{
- QWidget* dbWidget = widget(index);
-
- QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
- while (i.hasNext()) {
- i.next();
- if (i.value().dbWidget == dbWidget) {
- return i.value();
- }
+ auto* dbWidget = databaseWidgetFromIndex(index);
+ if (!dbWidget) {
+ dbWidget = qobject_cast<DatabaseWidget*>(sender());
}
-
- return DatabaseManagerStruct();
-}
-
-Database* DatabaseTabWidget::databaseFromDatabaseWidget(DatabaseWidget* dbWidget)
-{
- QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
- while (i.hasNext()) {
- i.next();
- if (i.value().dbWidget == dbWidget) {
- return i.key();
- }
+ Q_ASSERT(dbWidget);
+ if (!dbWidget) {
+ return;
}
-
- return nullptr;
+ index = indexOf(dbWidget);
+ setTabText(index, tabName(index));
+ emit tabNameChanged();
}
-void DatabaseTabWidget::insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct)
+DatabaseWidget* DatabaseTabWidget::databaseWidgetFromIndex(int index) const
{
- m_dbList.insert(db, dbStruct);
-
- addTab(dbStruct.dbWidget, "");
- toggleTabbar();
- updateTabName(db);
- int index = databaseIndex(db);
- setCurrentIndex(index);
- connectDatabase(db);
- connect(dbStruct.dbWidget, SIGNAL(closeRequest()), SLOT(closeDatabaseFromSender()));
- connect(dbStruct.dbWidget, SIGNAL(databaseChanged(Database*,bool)), SLOT(changeDatabase(Database*,bool)));
- connect(dbStruct.dbWidget, SIGNAL(unlockedDatabase()), SLOT(updateTabNameFromDbWidgetSender()));
- connect(dbStruct.dbWidget, SIGNAL(unlockedDatabase()), SLOT(emitDatabaseUnlockedFromDbWidgetSender()));
+ return qobject_cast<DatabaseWidget*>(widget(index));
}
DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget()
{
- Database* db = indexDatabase(currentIndex());
- if (db) {
- return m_dbList[db].dbWidget;
- } else {
- return nullptr;
- }
-}
-
-bool DatabaseTabWidget::hasLockableDatabases() const
-{
- QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
- while (i.hasNext()) {
- i.next();
- DatabaseWidget::Mode mode = i.value().dbWidget->currentMode();
-
- if ((mode == DatabaseWidget::ViewMode || mode == DatabaseWidget::EditMode) && i.value().dbWidget->dbHasKey()) {
- return true;
- }
- }
-
- return false;
+ return qobject_cast<DatabaseWidget*>(currentWidget());
}
void DatabaseTabWidget::lockDatabases()
{
- clipboard()->clearCopiedText();
-
- for (int i = 0; i < count(); i++) {
- DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(widget(i));
- Database* db = databaseFromDatabaseWidget(dbWidget);
-
- if (dbWidget->currentMode() == DatabaseWidget::LockedMode || !dbWidget->dbHasKey()) {
- continue;
- }
-
- // show the correct tab widget before we are asking questions about it
- setCurrentWidget(dbWidget);
-
- if (dbWidget->currentMode() == DatabaseWidget::EditMode && dbWidget->isEditWidgetModified()) {
- QMessageBox::StandardButton result =
- MessageBox::question(this,
- tr("Lock database"),
- tr("Can't lock the database as you are currently editing it.\nPlease press cancel "
- "to finish your changes or discard them."),
- QMessageBox::Discard | QMessageBox::Cancel,
- QMessageBox::Cancel);
- if (result == QMessageBox::Cancel) {
- continue;
- }
- }
-
- if (m_dbList[db].modified) {
- QMessageBox::StandardButton result =
- MessageBox::question(this,
- tr("Lock database"),
- tr("This database has been modified.\nDo you want to save the database before "
- "locking it?\nOtherwise your changes are lost."),
- QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
- QMessageBox::Cancel);
- if (result == QMessageBox::Save) {
- if (!saveDatabase(db)) {
- continue;
- }
- } else if (result == QMessageBox::Discard) {
- m_dbList[db].modified = false;
- m_dbList[db].dbWidget->databaseSaved();
- } else if (result == QMessageBox::Cancel) {
- continue;
- }
+ for (int i = 0, c = count(); i < c; ++i) {
+ if (!databaseWidgetFromIndex(i)->lock()) {
+ return;
}
-
- dbWidget->lock();
- // database has changed so we can't use the db variable anymore
- updateTabName(dbWidget->database());
-
- emit databaseLocked(dbWidget);
}
}
@@ -789,36 +509,15 @@ void DatabaseTabWidget::relockPendingDatabase()
return;
}
- if (m_dbPendingLock->currentMode() == DatabaseWidget::LockedMode || !m_dbPendingLock->dbHasKey()) {
+ if (m_dbPendingLock->isLocked() || !m_dbPendingLock->database()->hasKey()) {
m_dbPendingLock = nullptr;
return;
}
m_dbPendingLock->lock();
-
- emit databaseLocked(m_dbPendingLock);
m_dbPendingLock = nullptr;
}
-void DatabaseTabWidget::modified()
-{
- Q_ASSERT(qobject_cast<Database*>(sender()));
-
- Database* db = static_cast<Database*>(sender());
- DatabaseManagerStruct& dbStruct = m_dbList[db];
-
- if (config()->get("AutoSaveAfterEveryChange").toBool() && !dbStruct.readOnly) {
- saveDatabase(db);
- return;
- }
-
- if (!dbStruct.modified) {
- dbStruct.modified = true;
- dbStruct.dbWidget->databaseModified();
- updateTabName(db);
- }
-}
-
void DatabaseTabWidget::updateLastDatabases(const QString& filename)
{
if (!config()->get("RememberLastDatabases").toBool()) {
@@ -828,68 +527,49 @@ void DatabaseTabWidget::updateLastDatabases(const QString& filename)
lastDatabases.prepend(filename);
lastDatabases.removeDuplicates();
- while (lastDatabases.count() > LastDatabasesCount) {
+ while (lastDatabases.count() > config()->get("NumberOfRememberedLastDatabases").toInt()) {
lastDatabases.removeLast();
}
config()->set("LastDatabases", lastDatabases);
}
}
-void DatabaseTabWidget::changeDatabase(Database* newDb, bool unsavedChanges)
-{
- Q_ASSERT(sender());
- Q_ASSERT(!m_dbList.contains(newDb));
-
- DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(sender());
- Database* oldDb = databaseFromDatabaseWidget(dbWidget);
- DatabaseManagerStruct dbStruct = m_dbList[oldDb];
- dbStruct.modified = unsavedChanges;
- m_dbList.remove(oldDb);
- m_dbList.insert(newDb, dbStruct);
-
- updateTabName(newDb);
- connectDatabase(newDb, oldDb);
-}
-
void DatabaseTabWidget::emitActivateDatabaseChanged()
{
emit activateDatabaseChanged(currentDatabaseWidget());
}
-void DatabaseTabWidget::emitDatabaseUnlockedFromDbWidgetSender()
+void DatabaseTabWidget::emitDatabaseLockChanged()
{
- emit databaseUnlocked(static_cast<DatabaseWidget*>(sender()));
-}
-
-void DatabaseTabWidget::connectDatabase(Database* newDb, Database* oldDb)
-{
- if (oldDb) {
- oldDb->disconnect(this);
+ auto* dbWidget = qobject_cast<DatabaseWidget*>(sender());
+ Q_ASSERT(dbWidget);
+ if (!dbWidget) {
+ return;
}
- connect(newDb, SIGNAL(nameTextChanged()), SLOT(updateTabNameFromDbSender()));
- connect(newDb, SIGNAL(modified()), SLOT(modified()));
- newDb->setEmitModified(true);
+ if (dbWidget->isLocked()) {
+ emit databaseLocked(dbWidget);
+ } else {
+ emit databaseUnlocked(dbWidget);
+ }
}
void DatabaseTabWidget::performGlobalAutoType()
{
- QList<Database*> unlockedDatabases;
-
- QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
- while (i.hasNext()) {
- i.next();
- DatabaseWidget::Mode mode = i.value().dbWidget->currentMode();
+ QList<QSharedPointer<Database>> unlockedDatabases;
- if (mode != DatabaseWidget::LockedMode) {
- unlockedDatabases.append(i.key());
+ for (int i = 0, c = count(); i < c; ++i) {
+ auto* dbWidget = databaseWidgetFromIndex(i);
+ if (!dbWidget->isLocked()) {
+ unlockedDatabases.append(dbWidget->database());
}
}
- if (unlockedDatabases.size() > 0) {
+ if (!unlockedDatabases.isEmpty()) {
autoType()->performGlobalAutoType(unlockedDatabases);
- } else if (m_dbList.size() > 0) {
- m_dbPendingLock = indexDatabaseManagerStruct(0).dbWidget;
- m_dbPendingLock->showUnlockDialog();
+ } else if (count() > 0) {
+ // TODO: allow for database selection during Auto-Type instead of using the first tab
+ m_dbPendingLock = databaseWidgetFromIndex(0);
+ m_dbPendingLock->prepareUnlock();
}
}
diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h
index 87e171ab0..d24b45af8 100644
--- a/src/gui/DatabaseTabWidget.h
+++ b/src/gui/DatabaseTabWidget.h
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- * Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
+ * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,31 +18,16 @@
#ifndef KEEPASSX_DATABASETABWIDGET_H
#define KEEPASSX_DATABASETABWIDGET_H
-#include <QFileInfo>
-#include <QHash>
-#include <QTabWidget>
-
-#include "gui/DatabaseWidget.h"
#include "gui/MessageWidget.h"
+#include <QTabWidget>
+#include <QPointer>
+
+class Database;
class DatabaseWidget;
class DatabaseWidgetStateSync;
class DatabaseOpenWidget;
class QFile;
-class MessageWidget;
-
-struct DatabaseManagerStruct
-{
- DatabaseManagerStruct();
-
- DatabaseWidget* dbWidget;
- QFileInfo fileInfo;
- bool modified;
- bool readOnly;
- int saveAttempts;
-};
-
-Q_DECLARE_TYPEINFO(DatabaseManagerStruct, Q_MOVABLE_TYPE);
class DatabaseTabWidget : public QTabWidget
{
@@ -52,71 +36,62 @@ class DatabaseTabWidget : public QTabWidget
public:
explicit DatabaseTabWidget(QWidget* parent = nullptr);
~DatabaseTabWidget() override;
- void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString());
- void mergeDatabase(const QString& fileName);
+ void mergeDatabase(const QString& filePath);
+
+ QString tabName(int index);
DatabaseWidget* currentDatabaseWidget();
- bool hasLockableDatabases() const;
- DatabaseManagerStruct indexDatabaseManagerStruct(int index);
+ DatabaseWidget* databaseWidgetFromIndex(int index) const;
- static const int LastDatabasesCount;
+ bool isReadOnly(int index = -1) const;
+ bool canSave(int index = -1) const;
+ bool isModified(int index = -1) const;
+ bool hasLockableDatabases() const;
public slots:
+ void addDatabaseTab(const QString& filePath);
+ void addDatabaseTab(DatabaseWidget* dbWidget);
+ bool closeDatabaseTab(int index);
+ bool closeDatabaseTab(DatabaseWidget* dbWidget);
+ bool closeAllDatabaseTabs();
+ bool closeCurrentDatabaseTab();
+ bool closeDatabaseTabFromSender();
+ void updateTabName(int index = -1);
+
void newDatabase();
void openDatabase();
- void importCsv();
void mergeDatabase();
+ void importCsv();
void importKeePass1Database();
bool saveDatabase(int index = -1);
bool saveDatabaseAs(int index = -1);
void exportToCsv();
- bool closeDatabase(int index = -1);
+
+ void lockDatabases();
void closeDatabaseFromSender();
- bool closeAllDatabases();
+ void relockPendingDatabase();
+
void changeMasterKey();
void changeDatabaseSettings();
- bool readOnly(int index = -1);
- bool canSave(int index = -1);
- bool isModified(int index = -1);
void performGlobalAutoType();
- void lockDatabases();
- void relockPendingDatabase();
- QString databasePath(int index = -1);
signals:
- void tabNameChanged();
- void databaseWithFileClosed(QString filePath);
- void activateDatabaseChanged(DatabaseWidget* dbWidget);
- void databaseLocked(DatabaseWidget* dbWidget);
+ void databaseClosed(const QString& filePath);
void databaseUnlocked(DatabaseWidget* dbWidget);
+ void databaseLocked(DatabaseWidget* dbWidget);
+ void activateDatabaseChanged(DatabaseWidget* dbWidget);
+ void tabNameChanged();
void messageGlobal(const QString&, MessageWidget::MessageType type);
- void messageTab(const QString&, MessageWidget::MessageType type);
void messageDismissGlobal();
- void messageDismissTab();
private slots:
- void updateTabName(Database* db);
- void updateTabNameFromDbSender();
- void updateTabNameFromDbWidgetSender();
- void modified();
void toggleTabbar();
- void changeDatabase(Database* newDb, bool unsavedChanges);
void emitActivateDatabaseChanged();
- void emitDatabaseUnlockedFromDbWidgetSender();
+ void emitDatabaseLockChanged();
private:
- Database* execNewDatabaseWizard();
- bool saveDatabase(Database* db, QString filePath = "");
- bool saveDatabaseAs(Database* db);
- bool closeDatabase(Database* db);
- void deleteDatabase(Database* db);
- int databaseIndex(Database* db);
- Database* indexDatabase(int index);
- Database* databaseFromDatabaseWidget(DatabaseWidget* dbWidget);
- void insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct);
+ QSharedPointer<Database> execNewDatabaseWizard();
void updateLastDatabases(const QString& filename);
- void connectDatabase(Database* newDb, Database* oldDb = nullptr);
- QHash<Database*, DatabaseManagerStruct> m_dbList;
QPointer<DatabaseWidgetStateSync> m_dbWidgetStateSync;
QPointer<DatabaseWidget> m_dbPendingLock;
};
diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp
index 5c8d7bc9d..71db6258e 100644
--- a/src/gui/DatabaseWidget.cpp
+++ b/src/gui/DatabaseWidget.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
@@ -32,6 +32,7 @@
#include <QSplitter>
#include "autotype/AutoType.h"
+#include "core/Database.h"
#include "core/Config.h"
#include "core/EntrySearcher.h"
#include "core/FilePath.h"
@@ -40,6 +41,7 @@
#include "core/Metadata.h"
#include "core/Tools.h"
#include "format/KeePass2Reader.h"
+#include "gui/FileDialog.h"
#include "gui/Clipboard.h"
#include "gui/CloneDialog.h"
#include "gui/DatabaseOpenWidget.h"
@@ -68,131 +70,127 @@
#include "sshagent/SSHAgent.h"
#endif
-DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
+DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
: QStackedWidget(parent)
- , m_db(db)
- , m_newGroup(nullptr)
- , m_newEntry(nullptr)
- , m_newParent(nullptr)
+ , m_db(std::move(db))
+
+ , m_mainWidget(new QWidget(this))
+ , m_mainSplitter(new QSplitter(m_mainWidget))
+ , m_messageWidget(new MessageWidget(this))
+ , m_previewView(new EntryPreviewWidget(this))
+ , m_previewSplitter(new QSplitter(m_mainWidget))
+ , m_searchingLabel(new QLabel(this))
+ , m_csvImportWizard(new CsvImportWizard(this))
+ , m_editEntryWidget(new EditEntryWidget(this))
+ , m_editGroupWidget(new EditGroupWidget(this))
+ , m_historyEditEntryWidget(new EditEntryWidget(this))
+ , m_databaseSettingDialog(new DatabaseSettingsDialog(this))
+ , m_databaseOpenWidget(new DatabaseOpenWidget(this))
+ , m_databaseOpenMergeWidget(new DatabaseOpenWidget(this))
+ , m_keepass1OpenWidget(new KeePass1OpenWidget(this))
+ , m_unlockDatabaseWidget(new UnlockDatabaseWidget(this))
+ , m_unlockDatabaseDialog(new UnlockDatabaseDialog(this))
+ , m_groupView(new GroupView(m_db.data(), m_mainSplitter))
+ , m_entryView(nullptr)
+
+ , m_newGroup()
+ , m_newEntry()
+ , m_newParent()
{
- m_mainWidget = new QWidget(this);
-
- m_messageWidget = new MessageWidget(this);
m_messageWidget->setHidden(true);
auto* mainLayout = new QVBoxLayout();
- QLayout* layout = new QHBoxLayout();
mainLayout->addWidget(m_messageWidget);
- mainLayout->addLayout(layout);
- m_mainSplitter = new QSplitter(m_mainWidget);
+ auto* hbox = new QHBoxLayout();
+ mainLayout->addLayout(hbox);
+ hbox->addWidget(m_mainSplitter);
+ m_mainWidget->setLayout(mainLayout);
+
+ auto* rightHandSideWidget = new QWidget(m_mainSplitter);
+ auto* vbox = new QVBoxLayout();
+ vbox->setMargin(0);
+ vbox->addWidget(m_searchingLabel);
+ vbox->addWidget(m_previewSplitter);
+ rightHandSideWidget->setLayout(vbox);
+ m_entryView = new EntryView(rightHandSideWidget);
+
m_mainSplitter->setChildrenCollapsible(false);
- m_previewSplitter = new QSplitter(m_mainWidget);
+ m_mainSplitter->addWidget(m_groupView);
+ m_mainSplitter->addWidget(rightHandSideWidget);
+ m_mainSplitter->setStretchFactor(0, 30);
+ m_mainSplitter->setStretchFactor(1, 70);
+
m_previewSplitter->setOrientation(Qt::Vertical);
m_previewSplitter->setChildrenCollapsible(true);
- QWidget* rightHandSideWidget = new QWidget(m_mainSplitter);
-
- m_groupView = new GroupView(db, m_mainSplitter);
m_groupView->setObjectName("groupView");
m_groupView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_groupView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(emitGroupContextMenuRequested(QPoint)));
- m_entryView = new EntryView(rightHandSideWidget);
m_entryView->setObjectName("entryView");
m_entryView->setContextMenuPolicy(Qt::CustomContextMenu);
- m_entryView->displayGroup(db->rootGroup());
+ m_entryView->displayGroup(m_db->rootGroup());
connect(m_entryView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(emitEntryContextMenuRequested(QPoint)));
// Add a notification for when we are searching
- m_searchingLabel = new QLabel();
m_searchingLabel->setText(tr("Searching..."));
m_searchingLabel->setAlignment(Qt::AlignCenter);
m_searchingLabel->setStyleSheet("color: rgb(0, 0, 0);"
"background-color: rgb(255, 253, 160);"
"border: 2px solid rgb(190, 190, 190);"
- "border-radius: 5px;");
+ "border-radius: 2px;");
+ m_searchingLabel->setVisible(false);
- m_previewView = new EntryPreviewWidget(this);
m_previewView->hide();
- connect(this, SIGNAL(pressedGroup(Group*)), m_previewView, SLOT(setGroup(Group*)));
- connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)),
- m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode)));
- connect(m_previewView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString)));
-
- auto* vLayout = new QVBoxLayout(rightHandSideWidget);
- vLayout->setMargin(0);
- vLayout->addWidget(m_searchingLabel);
- vLayout->addWidget(m_previewSplitter);
-
m_previewSplitter->addWidget(m_entryView);
m_previewSplitter->addWidget(m_previewView);
-
m_previewSplitter->setStretchFactor(0, 100);
m_previewSplitter->setStretchFactor(1, 0);
m_previewSplitter->setSizes({1, 1});
- m_searchingLabel->setVisible(false);
-
- rightHandSideWidget->setLayout(vLayout);
-
- m_mainSplitter->addWidget(m_groupView);
- m_mainSplitter->addWidget(rightHandSideWidget);
-
- m_mainSplitter->setStretchFactor(0, 30);
- m_mainSplitter->setStretchFactor(1, 70);
-
- layout->addWidget(m_mainSplitter);
- m_mainWidget->setLayout(mainLayout);
-
- m_editEntryWidget = new EditEntryWidget();
m_editEntryWidget->setObjectName("editEntryWidget");
- m_historyEditEntryWidget = new EditEntryWidget();
- m_editGroupWidget = new EditGroupWidget();
m_editGroupWidget->setObjectName("editGroupWidget");
- m_csvImportWizard = new CsvImportWizard();
m_csvImportWizard->setObjectName("csvImportWizard");
- m_databaseSettingDialog = new DatabaseSettingsDialog();
m_databaseSettingDialog->setObjectName("databaseSettingsDialog");
- m_databaseOpenWidget = new DatabaseOpenWidget();
m_databaseOpenWidget->setObjectName("databaseOpenWidget");
- m_databaseOpenMergeWidget = new DatabaseOpenWidget();
m_databaseOpenMergeWidget->setObjectName("databaseOpenMergeWidget");
- m_keepass1OpenWidget = new KeePass1OpenWidget();
m_keepass1OpenWidget->setObjectName("keepass1OpenWidget");
- m_unlockDatabaseWidget = new UnlockDatabaseWidget();
m_unlockDatabaseWidget->setObjectName("unlockDatabaseWidget");
- m_unlockDatabaseDialog = new UnlockDatabaseDialog();
m_unlockDatabaseDialog->setObjectName("unlockDatabaseDialog");
- addWidget(m_mainWidget);
- addWidget(m_editEntryWidget);
- addWidget(m_editGroupWidget);
- addWidget(m_databaseSettingDialog);
- addWidget(m_historyEditEntryWidget);
- addWidget(m_databaseOpenWidget);
- addWidget(m_csvImportWizard);
- addWidget(m_databaseOpenMergeWidget);
- addWidget(m_keepass1OpenWidget);
- addWidget(m_unlockDatabaseWidget);
+
+ addChildWidget(m_mainWidget);
+ addChildWidget(m_editEntryWidget);
+ addChildWidget(m_editGroupWidget);
+ addChildWidget(m_databaseSettingDialog);
+ addChildWidget(m_historyEditEntryWidget);
+ addChildWidget(m_databaseOpenWidget);
+ addChildWidget(m_csvImportWizard);
+ addChildWidget(m_databaseOpenMergeWidget);
+ addChildWidget(m_keepass1OpenWidget);
+ addChildWidget(m_unlockDatabaseWidget);
connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged()));
connect(m_previewSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(previewSplitterSizesChanged()));
+ connect(this, SIGNAL(pressedEntry(Entry*)), m_previewView, SLOT(setEntry(Entry*)));
+ connect(this, SIGNAL(pressedGroup(Group*)), m_previewView, SLOT(setGroup(Group*)));
+ connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode)));
+ connect(m_previewView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString)));
connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged()));
connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(onGroupChanged(Group*)));
connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged()));
- connect(m_entryView,
- SIGNAL(entryActivated(Entry*,EntryModel::ModelColumn)),
- SLOT(entryActivationSignalReceived(Entry*,EntryModel::ModelColumn)));
- connect(m_entryView, SIGNAL(entrySelectionChanged()), SLOT(emitEntrySelectionChanged()));
+ connect(m_entryView, SIGNAL(entryActivated(Entry*,EntryModel::ModelColumn)),
+ SLOT(entryActivationSignalReceived(Entry*,EntryModel::ModelColumn)));
+ connect(m_entryView, SIGNAL(entrySelectionChanged()), SIGNAL(entrySelectionChanged()));
connect(m_editEntryWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
connect(m_editEntryWidget, SIGNAL(historyEntryActivated(Entry*)), SLOT(switchToHistoryView(Entry*)));
connect(m_historyEditEntryWidget, SIGNAL(editFinished(bool)), SLOT(switchBackToEntryEdit()));
connect(m_editGroupWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
connect(m_databaseSettingDialog, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
- connect(m_databaseOpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
- connect(m_databaseOpenMergeWidget, SIGNAL(editFinished(bool)), SLOT(mergeDatabase(bool)));
- connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
+ connect(m_databaseOpenWidget, SIGNAL(dialogFinished(bool)), SLOT(loadDatabase(bool)));
+ connect(m_databaseOpenMergeWidget, SIGNAL(dialogFinished(bool)), SLOT(mergeDatabase(bool)));
+ connect(m_keepass1OpenWidget, SIGNAL(dialogFinished(bool)), SLOT(loadDatabase(bool)));
connect(m_csvImportWizard, SIGNAL(importFinished(bool)), SLOT(csvImportFinished(bool)));
- connect(m_unlockDatabaseWidget, SIGNAL(editFinished(bool)), SLOT(unlockDatabase(bool)));
+ connect(m_unlockDatabaseWidget, SIGNAL(dialogFinished(bool)), SLOT(unlockDatabase(bool)));
connect(m_unlockDatabaseDialog, SIGNAL(unlockDone(bool)), SLOT(unlockDatabase(bool)));
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onWatchedFileChanged()));
connect(&m_fileWatchTimer, SIGNAL(timeout()), this, SLOT(reloadDatabaseFile()));
@@ -203,7 +201,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
connect(m_groupView, SIGNAL(groupChanged(Group*)), SLOT(emitPressedGroup(Group*)));
connect(m_editEntryWidget, SIGNAL(editFinished(bool)), SLOT(emitEntrySelectionChanged()));
- m_databaseModified = false;
+ connectDatabaseSignals();
m_fileWatchTimer.setSingleShot(true);
m_fileWatchUnblockTimer.setSingleShot(true);
@@ -225,29 +223,44 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
setCurrentWidget(m_mainWidget);
}
+DatabaseWidget::DatabaseWidget(const QString& filePath, QWidget* parent)
+ : DatabaseWidget(QSharedPointer<Database>::create(filePath), parent)
+{
+}
+
DatabaseWidget::~DatabaseWidget()
{
delete m_EntrySearcher;
}
+QSharedPointer<Database> DatabaseWidget::database() const
+{
+ return m_db;
+}
+
DatabaseWidget::Mode DatabaseWidget::currentMode() const
{
if (currentWidget() == nullptr) {
- return DatabaseWidget::None;
+ return DatabaseWidget::Mode::None;
} else if (currentWidget() == m_csvImportWizard) {
- return DatabaseWidget::ImportMode;
+ return DatabaseWidget::Mode::ImportMode;
} else if (currentWidget() == m_mainWidget) {
- return DatabaseWidget::ViewMode;
+ return DatabaseWidget::Mode::ViewMode;
} else if (currentWidget() == m_unlockDatabaseWidget || currentWidget() == m_databaseOpenWidget) {
- return DatabaseWidget::LockedMode;
+ return DatabaseWidget::Mode::LockedMode;
} else {
- return DatabaseWidget::EditMode;
+ return DatabaseWidget::Mode::EditMode;
}
}
-bool DatabaseWidget::isInEditMode() const
+bool DatabaseWidget::isLocked() const
{
- return currentMode() == DatabaseWidget::EditMode;
+ return currentMode() == Mode::LockedMode;
+}
+
+bool DatabaseWidget::isSearchActive() const
+{
+ return m_entryView->inSearchMode();
}
bool DatabaseWidget::isEditWidgetModified() const
@@ -341,11 +354,6 @@ void DatabaseWidget::emitCurrentModeChanged()
emit currentModeChanged(currentMode());
}
-Database* DatabaseWidget::database()
-{
- return m_db;
-}
-
void DatabaseWidget::createEntry()
{
Q_ASSERT(m_groupView->currentGroup());
@@ -355,7 +363,7 @@ void DatabaseWidget::createEntry()
m_newEntry = new Entry();
- if (isInSearchMode()) {
+ if (isSearchActive()) {
m_newEntry->setTitle(getCurrentSearch());
endSearch();
}
@@ -383,13 +391,15 @@ void DatabaseWidget::setIconFromParent()
}
}
-void DatabaseWidget::replaceDatabase(Database* db)
+void DatabaseWidget::replaceDatabase(QSharedPointer<Database> db)
{
- Database* oldDb = m_db;
- m_db = db;
+ // TODO: instead of increasing the ref count temporarily, there should be a clean
+ // break from the old database. Without this crashes occur due to the change
+ // signals triggering dangling pointers.
+ auto oldDb = m_db;
+ m_db = std::move(db);
+ connectDatabaseSignals();
m_groupView->changeDatabase(m_db);
- emit databaseChanged(m_db, m_databaseModified);
- delete oldDb;
}
void DatabaseWidget::cloneEntry()
@@ -400,7 +410,7 @@ void DatabaseWidget::cloneEntry()
return;
}
- auto cloneDialog = new CloneDialog(this, m_db, currentEntry);
+ auto cloneDialog = new CloneDialog(this, m_db.data(), currentEntry);
cloneDialog->show();
}
@@ -660,8 +670,8 @@ void DatabaseWidget::deleteGroup()
auto* recycleBin = m_db->metadata()->recycleBin();
bool inRecycleBin = recycleBin && recycleBin->findGroupByUuid(currentGroup->uuid());
- bool isRecycleBin = (currentGroup == m_db->metadata()->recycleBin());
- bool isRecycleBinSubgroup = currentGroup->findGroupByUuid(m_db->metadata()->recycleBin()->uuid());
+ bool isRecycleBin = recycleBin && (currentGroup == recycleBin);
+ bool isRecycleBinSubgroup = recycleBin && currentGroup->findGroupByUuid(recycleBin->uuid());
if (inRecycleBin || isRecycleBin || isRecycleBinSubgroup || !m_db->metadata()->recycleBinEnabled()) {
QMessageBox::StandardButton result = MessageBox::question(
this,
@@ -676,40 +686,14 @@ void DatabaseWidget::deleteGroup()
}
}
-int DatabaseWidget::addWidget(QWidget* w)
+int DatabaseWidget::addChildWidget(QWidget* w)
{
w->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
-
int index = QStackedWidget::addWidget(w);
-
adjustSize();
-
return index;
}
-void DatabaseWidget::setCurrentIndex(int index)
-{
- // use setCurrentWidget() instead
- // index is not reliable
- Q_UNUSED(index);
- Q_ASSERT(false);
-}
-
-void DatabaseWidget::setCurrentWidget(QWidget* widget)
-{
- if (currentWidget()) {
- currentWidget()->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
- }
-
- QStackedWidget::setCurrentWidget(widget);
-
- if (currentWidget()) {
- currentWidget()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
- }
-
- adjustSize();
-}
-
void DatabaseWidget::csvImportFinished(bool accepted)
{
if (!accepted) {
@@ -788,24 +772,32 @@ void DatabaseWidget::switchToGroupEdit(Group* group, bool create)
setCurrentWidget(m_editGroupWidget);
}
-void DatabaseWidget::openDatabase(bool accepted)
+void DatabaseWidget::connectDatabaseSignals()
{
+ // relayed Database events
+ connect(m_db.data(), SIGNAL(filePathChanged(QString,QString)),
+ this, SIGNAL(databaseFilePathChanged(QString,QString)));
+ connect(m_db.data(), SIGNAL(databaseModified()), this, SIGNAL(databaseModified()));
+ connect(m_db.data(), SIGNAL(databaseSaved()), this, SIGNAL(databaseSaved()));
+}
+
+void DatabaseWidget::loadDatabase(bool accepted)
+{
+ auto* openWidget = qobject_cast<DatabaseOpenWidget*>(sender());
+ Q_ASSERT(openWidget);
+ if (!openWidget) {
+ return;
+ }
+
if (accepted) {
- replaceDatabase(static_cast<DatabaseOpenWidget*>(sender())->database());
+ replaceDatabase(openWidget->database());
setCurrentWidget(m_mainWidget);
- emit unlockedDatabase();
-
- // We won't need those anymore and KeePass1OpenWidget closes
- // the file in its dtor.
- delete m_databaseOpenWidget;
- m_databaseOpenWidget = nullptr;
- delete m_keepass1OpenWidget;
- m_keepass1OpenWidget = nullptr;
- m_fileWatcher.addPath(m_filePath);
+ m_fileWatcher.addPath(m_db->filePath());
+ emit databaseUnlocked();
} else {
- m_fileWatcher.removePath(m_filePath);
+ m_fileWatcher.removePath(m_db->filePath());
if (m_databaseOpenWidget->database()) {
- delete m_databaseOpenWidget->database();
+ m_databaseOpenWidget->database().reset();
}
emit closeRequest();
}
@@ -815,18 +807,18 @@ void DatabaseWidget::mergeDatabase(bool accepted)
{
if (accepted) {
if (!m_db) {
- m_messageWidget->showMessage(tr("No current database."), MessageWidget::Error);
+ showMessage(tr("No current database."), MessageWidget::Error);
return;
}
- Database* srcDb = static_cast<DatabaseOpenWidget*>(sender())->database();
+ auto srcDb = qobject_cast<DatabaseOpenWidget*>(sender())->database();
if (!srcDb) {
- m_messageWidget->showMessage(tr("No source database, nothing to do."), MessageWidget::Error);
+ showMessage(tr("No source database, nothing to do."), MessageWidget::Error);
return;
}
- Merger merger(srcDb, m_db);
+ Merger merger(srcDb.data(), m_db.data());
merger.merge();
}
@@ -842,7 +834,7 @@ void DatabaseWidget::unlockDatabase(bool accepted)
return;
}
- Database* db = nullptr;
+ auto db = QSharedPointer<Database>::create();
if (sender() == m_unlockDatabaseDialog) {
db = m_unlockDatabaseDialog->database();
} else if (sender() == m_unlockDatabaseWidget) {
@@ -850,6 +842,9 @@ void DatabaseWidget::unlockDatabase(bool accepted)
}
replaceDatabase(db);
+ if (db->isReadOnly()) {
+ showMessage(tr("File opened in read only mode."), MessageWidget::Warning, false, -1);
+ }
restoreGroupEntryFocus(m_groupBeforeLock, m_entryBeforeLock);
m_groupBeforeLock = QUuid();
@@ -857,10 +852,10 @@ void DatabaseWidget::unlockDatabase(bool accepted)
setCurrentWidget(m_mainWidget);
m_unlockDatabaseWidget->clearForms();
- emit unlockedDatabase();
+ emit databaseUnlocked();
if (sender() == m_unlockDatabaseDialog) {
- QList<Database*> dbList;
+ QList<QSharedPointer<Database>> dbList;
dbList.append(m_db);
autoType()->performGlobalAutoType(dbList);
}
@@ -946,6 +941,11 @@ void DatabaseWidget::switchToDatabaseSettings()
setCurrentWidget(m_databaseSettingDialog);
}
+void DatabaseWidget::switchToOpenDatabase()
+{
+ switchToOpenDatabase(m_db->filePath());
+}
+
void DatabaseWidget::switchToOpenDatabase(const QString& filePath)
{
updateFilePath(filePath);
@@ -957,22 +957,10 @@ void DatabaseWidget::switchToOpenDatabase(const QString& filePath)
setCurrentWidget(m_unlockDatabaseWidget);
}
}
-
-void DatabaseWidget::switchToOpenDatabase(const QString& filePath, const QString& password, const QString& keyFile)
-{
- updateFilePath(filePath);
- switchToOpenDatabase(filePath);
- if (m_databaseOpenWidget) {
- m_databaseOpenWidget->enterKey(password, keyFile);
- } else if (m_unlockDatabaseWidget) {
- m_unlockDatabaseWidget->enterKey(password, keyFile);
- }
-}
-
void DatabaseWidget::switchToCsvImport(const QString& filePath)
{
setCurrentWidget(m_csvImportWizard);
- m_csvImportWizard->load(filePath, m_db);
+ m_csvImportWizard->load(filePath, m_db.data());
}
void DatabaseWidget::switchToOpenMergeDatabase(const QString& filePath)
@@ -995,19 +983,9 @@ void DatabaseWidget::switchToImportKeepass1(const QString& filePath)
setCurrentWidget(m_keepass1OpenWidget);
}
-void DatabaseWidget::databaseModified()
-{
- m_databaseModified = true;
-}
-
-void DatabaseWidget::databaseSaved()
-{
- m_databaseModified = false;
-}
-
void DatabaseWidget::refreshSearch()
{
- if (isInSearchMode()) {
+ if (isSearchActive()) {
search(m_lastSearchText);
}
}
@@ -1054,10 +1032,10 @@ void DatabaseWidget::setSearchLimitGroup(bool state)
void DatabaseWidget::onGroupChanged(Group* group)
{
- if (isInSearchMode() && m_searchLimitGroup) {
- // Perform new search if we are limiting search to the current group
+ // Intercept group changes if in search mode
+ if (isSearchActive()) {
search(m_lastSearchText);
- } else if (isInSearchMode()) {
+ } else if (isSearchActive()) {
// Otherwise cancel search
emit clearSearch();
} else {
@@ -1072,7 +1050,7 @@ QString DatabaseWidget::getCurrentSearch()
void DatabaseWidget::endSearch()
{
- if (isInSearchMode()) {
+ if (isSearchActive()) {
emit listModeAboutToActivate();
// Show the normal entry view of the current group
@@ -1117,30 +1095,74 @@ void DatabaseWidget::emitPressedGroup(Group* currentGroup)
emit pressedGroup(currentGroup);
}
-bool DatabaseWidget::dbHasKey() const
-{
- return m_db->hasKey();
-}
-
bool DatabaseWidget::canDeleteCurrentGroup() const
{
bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup();
return !isRootGroup;
}
-bool DatabaseWidget::isInSearchMode() const
+Group* DatabaseWidget::currentGroup() const
{
- return m_entryView->inSearchMode();
+ return m_groupView->currentGroup();
}
-Group* DatabaseWidget::currentGroup() const
+void DatabaseWidget::closeEvent(QCloseEvent* event)
{
- return m_groupView->currentGroup();
+ if (!isLocked() && !lock()) {
+ event->ignore();
+ return;
+ }
+
+ event->accept();
+}
+
+void DatabaseWidget::showEvent(QShowEvent* event)
+{
+ if (!m_db->isInitialized() || isLocked()) {
+ switchToOpenDatabase();
+ }
+
+ event->accept();
}
-void DatabaseWidget::lock()
+bool DatabaseWidget::lock()
{
- Q_ASSERT(currentMode() != DatabaseWidget::LockedMode);
+ if (isLocked()) {
+ return true;
+ }
+
+ clipboard()->clearCopiedText();
+
+ if (currentMode() == DatabaseWidget::Mode::EditMode) {
+ auto result = MessageBox::question(this, tr("Lock Database?"),
+ tr("You are editing an entry. Discard changes and lock anyway?"),
+ QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
+ if (result == QMessageBox::Cancel) {
+ return false;
+ }
+ }
+
+ if (m_db->isModified()) {
+ if (config()->get("AutoSaveOnExit").toBool()) {
+ if (!m_db->save(nullptr, false, false)) {
+ return false;
+ }
+ } else if (isLocked()) {
+ QString msg;
+ if (!m_db->metadata()->name().toHtmlEscaped().isEmpty()) {
+ msg = tr("\"%1\" was modified.\nSave changes?").arg(m_db->metadata()->name().toHtmlEscaped());
+ } else {
+ msg = tr("Database was modified.\nSave changes?");
+ }
+ auto result = MessageBox::question(this, tr("Save changes?"), msg,
+ QMessageBox::Yes | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Yes);
+ if (result == QMessageBox::Yes && !m_db->save(nullptr, false, false)) {
+ return false;
+ } else if (result == QMessageBox::Cancel) {
+ return false;
+ }
+ }
+ }
if (m_groupView->currentGroup()) {
m_groupBeforeLock = m_groupView->currentGroup()->uuid();
@@ -1154,21 +1176,24 @@ void DatabaseWidget::lock()
endSearch();
clearAllWidgets();
- m_unlockDatabaseWidget->load(m_filePath);
+ m_unlockDatabaseWidget->load(m_db->filePath());
setCurrentWidget(m_unlockDatabaseWidget);
- Database* newDb = new Database();
- newDb->metadata()->setName(m_db->metadata()->name());
+
+ auto newDb = QSharedPointer<Database>::create(m_db->filePath());
replaceDatabase(newDb);
- emit lockedDatabase();
+
+ emit databaseLocked();
+
+ return true;
}
void DatabaseWidget::updateFilePath(const QString& filePath)
{
- if (!m_filePath.isEmpty()) {
- m_fileWatcher.removePath(m_filePath);
+ if (!m_db->filePath().isEmpty()) {
+ m_fileWatcher.removePath(m_db->filePath());
}
-#if defined(Q_OS_LINUX)
+#ifdef Q_OS_LINUX
struct statfs statfsBuf;
bool forcePolling = false;
const auto NFS_SUPER_MAGIC = 0x6969;
@@ -1184,7 +1209,6 @@ void DatabaseWidget::updateFilePath(const QString& filePath)
#endif
m_fileWatcher.addPath(filePath);
- m_filePath = filePath;
m_db->setFilePath(filePath);
}
@@ -1201,7 +1225,7 @@ void DatabaseWidget::blockAutoReload(bool block)
void DatabaseWidget::unblockAutoReload()
{
m_ignoreAutoReload = false;
- updateFilePath(m_filePath);
+ updateFilePath(m_db->filePath());
}
void DatabaseWidget::onWatchedFileChanged()
@@ -1217,86 +1241,69 @@ void DatabaseWidget::onWatchedFileChanged()
void DatabaseWidget::reloadDatabaseFile()
{
- if (!m_db || currentMode() == DatabaseWidget::LockedMode) {
- return;
- }
-
- if (currentMode() == DatabaseWidget::LockedMode) {
+ if (!m_db || isLocked()) {
return;
}
if (!config()->get("AutoReloadOnChange").toBool()) {
// Ask if we want to reload the db
- QMessageBox::StandardButton mb =
- MessageBox::question(this,
- tr("File has changed"),
- tr("The database file has changed. Do you want to load the changes?"),
- QMessageBox::Yes | QMessageBox::No);
+ auto result = MessageBox::question(this,
+ tr("File has changed"),
+ tr("The database file has changed. Do you want to load the changes?"),
+ QMessageBox::Yes | QMessageBox::No);
- if (mb == QMessageBox::No) {
+ if (result == QMessageBox::No) {
// Notify everyone the database does not match the file
m_db->markAsModified();
- m_databaseModified = true;
// Rewatch the database file
- m_fileWatcher.addPath(m_filePath);
+ m_fileWatcher.addPath(m_db->filePath());
return;
}
}
- KeePass2Reader reader;
- QFile file(m_filePath);
- if (file.open(QIODevice::ReadOnly)) {
- Database* db = reader.readDatabase(&file, database()->key());
- if (db != nullptr) {
- if (m_databaseModified) {
- // Ask if we want to merge changes into new database
- QMessageBox::StandardButton mb =
- MessageBox::question(this,
- tr("Merge Request"),
- tr("The database file has changed and you have unsaved changes.\n"
- "Do you want to merge your changes?"),
- QMessageBox::Yes | QMessageBox::No);
-
- if (mb == QMessageBox::Yes) {
- // Merge the old database into the new one
- m_db->setEmitModified(false);
- Merger merger(m_db, db);
- merger.merge();
- } else {
- // Since we are accepting the new file as-is, internally mark as unmodified
- // TODO: when saving is moved out of DatabaseTabWidget, this should be replaced
- m_databaseModified = false;
- }
- }
+ QString error;
+ auto db = QSharedPointer<Database>::create(m_db->filePath());
+ if (db->open(database()->key(), &error, true)) {
+ if (m_db->isModified()) {
+ // Ask if we want to merge changes into new database
+ auto result = MessageBox::question(this,
+ tr("Merge Request"),
+ tr("The database file has changed and you have unsaved changes.\nDo you want to merge your changes?"),
+ QMessageBox::Yes | QMessageBox::No);
- QUuid groupBeforeReload;
- if (m_groupView && m_groupView->currentGroup()) {
- groupBeforeReload = m_groupView->currentGroup()->uuid();
- } else {
- groupBeforeReload = m_db->rootGroup()->uuid();
+ if (result == QMessageBox::Yes) {
+ // Merge the old database into the new one
+ Merger merger(m_db.data(), db.data());
+ merger.merge();
}
+ }
- QUuid entryBeforeReload;
- if (m_entryView && m_entryView->currentEntry()) {
- entryBeforeReload = m_entryView->currentEntry()->uuid();
- }
+ QUuid groupBeforeReload;
+ if (m_groupView && m_groupView->currentGroup()) {
+ groupBeforeReload = m_groupView->currentGroup()->uuid();
+ } else {
+ groupBeforeReload = m_db->rootGroup()->uuid();
+ }
- replaceDatabase(db);
- restoreGroupEntryFocus(groupBeforeReload, entryBeforeReload);
+ QUuid entryBeforeReload;
+ if (m_entryView && m_entryView->currentEntry()) {
+ entryBeforeReload = m_entryView->currentEntry()->uuid();
}
+
+ bool isReadOnly = m_db->isReadOnly();
+ replaceDatabase(db);
+ m_db->setReadOnly(isReadOnly);
+ restoreGroupEntryFocus(groupBeforeReload, entryBeforeReload);
} else {
- m_messageWidget->showMessage(
- tr("Could not open the new database file while attempting to autoreload this database.")
- .append("\n")
- .append(file.errorString()),
+ showMessage(
+ tr("Could not open the new database file while attempting to autoreload.\nError: %1").arg(error),
MessageWidget::Error);
- // HACK: Directly calling the database's signal
// Mark db as modified since existing data may differ from file or file was deleted
m_db->markAsModified();
}
// Rewatch the database file
- m_fileWatcher.addPath(m_filePath);
+ m_fileWatcher.addPath(m_db->filePath());
}
int DatabaseWidget::numberOfSelectedEntries() const
@@ -1319,7 +1326,7 @@ QStringList DatabaseWidget::customEntryAttributes() const
*/
void DatabaseWidget::restoreGroupEntryFocus(const QUuid& groupUuid, const QUuid& entryUuid)
{
- auto group = m_db->resolveGroup(groupUuid);
+ auto group = m_db->rootGroup()->findGroupByUuid(groupUuid);
if (group) {
m_groupView->setCurrentGroup(group);
auto entry = group->findEntryByUuid(entryUuid);
@@ -1409,10 +1416,10 @@ EntryView* DatabaseWidget::entryView()
return m_entryView;
}
-void DatabaseWidget::showUnlockDialog()
+void DatabaseWidget::prepareUnlock()
{
m_unlockDatabaseDialog->clearForms();
- m_unlockDatabaseDialog->setFilePath(m_filePath);
+ m_unlockDatabaseDialog->setFilePath(m_db->filePath());
#if defined(Q_OS_MACOS)
autoType()->raiseWindow();
@@ -1423,15 +1430,101 @@ void DatabaseWidget::showUnlockDialog()
m_unlockDatabaseDialog->activateWindow();
}
-void DatabaseWidget::closeUnlockDialog()
+/**
+ * Save the database to disk.
+ *
+ * This method will try to save several times in case of failure and
+ * ask to disable safe saves if it is unable to save after the third attempt.
+ * Set `attempt` to -1 to disable this behavior.
+ *
+ * @param attempt current save attempt or -1 to disable attempts
+ * @return true on success
+ */
+bool DatabaseWidget::save(int attempt)
+{
+ // Never allow saving a locked database; it causes corruption
+ Q_ASSERT(!isLocked());
+ // Release build interlock
+ if (isLocked()) {
+ // We return true since a save is not required
+ return true;
+ }
+
+ if (m_db->isReadOnly() || m_db->filePath().isEmpty()) {
+ return saveAs();
+ }
+
+ blockAutoReload(true);
+ // TODO: Make this async, but lock out the database widget to prevent re-entrance
+ bool useAtomicSaves = config()->get("UseAtomicSaves", true).toBool();
+ QString errorMessage;
+ bool ok = m_db->save(&errorMessage, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
+ blockAutoReload(false);
+
+ if (ok) {
+ return true;
+ }
+
+ if (attempt >= 0 && attempt <= 2) {
+ return save(attempt + 1);
+ }
+
+ if (attempt > 2 && useAtomicSaves) {
+ // Saving failed 3 times, issue a warning and attempt to resolve
+ auto choice = MessageBox::question(this,
+ tr("Disable safe saves?"),
+ tr("KeePassXC has failed to save the database multiple times. "
+ "This is likely caused by file sync services holding a lock on "
+ "the save file.\nDisable safe saves and try again?"),
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::Yes);
+ if (choice == QMessageBox::Yes) {
+ config()->set("UseAtomicSaves", false);
+ return save(attempt + 1);
+ }
+ }
+
+ showMessage(tr("Writing the database failed.\n%1").arg(errorMessage), MessageWidget::Error);
+ return false;
+}
+
+/**
+ * Save database under a new user-selected filename.
+ *
+ * @return true on success
+ */
+bool DatabaseWidget::saveAs()
{
- m_unlockDatabaseDialog->close();
+ while (true) {
+ QString oldFilePath = m_db->filePath();
+ if (!QFileInfo(oldFilePath).exists()) {
+ oldFilePath = QDir::toNativeSeparators(config()->get("LastDir", QDir::homePath()).toString()
+ + "/" + tr("Passwords").append(".kdbx"));
+ }
+ QString newFilePath = fileDialog()->getSaveFileName(
+ this, tr("Save database as"), oldFilePath,
+ tr("KeePass 2 Database").append(" (*.kdbx)"), nullptr, nullptr, "kdbx");
+
+ if (!newFilePath.isEmpty()) {
+ // Ensure we don't recurse back into this function
+ m_db->setReadOnly(false);
+ m_db->setFilePath(newFilePath);
+
+ if (!save(-1)) {
+ // Failed to save, try again
+ continue;
+ }
+
+ return true;
+ }
+
+ // Canceled file selection
+ return false;
+ }
}
-void DatabaseWidget::showMessage(const QString& text,
- MessageWidget::MessageType type,
- bool showClosebutton,
- int autoHideTimeout)
+void DatabaseWidget::showMessage(const QString& text, MessageWidget::MessageType type,
+ bool showClosebutton, int autoHideTimeout)
{
m_messageWidget->setCloseButtonVisible(showClosebutton);
m_messageWidget->showMessage(text, type, autoHideTimeout);
@@ -1454,26 +1547,6 @@ bool DatabaseWidget::isRecycleBinSelected() const
return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin();
}
-QString DatabaseWidget::getDatabaseName() const
-{
- return m_databaseName;
-}
-
-void DatabaseWidget::setDatabaseName(const QString& databaseName)
-{
- m_databaseName = databaseName;
-}
-
-QString DatabaseWidget::getDatabaseFileName() const
-{
- return m_databaseFileName;
-}
-
-void DatabaseWidget::setDatabaseFileName(const QString& databaseFileName)
-{
- m_databaseFileName = databaseFileName;
-}
-
void DatabaseWidget::emptyRecycleBin()
{
if (!isRecycleBinSelected()) {
diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h
index d0c4e2042..1a908cf19 100644
--- a/src/gui/DatabaseWidget.h
+++ b/src/gui/DatabaseWidget.h
@@ -1,6 +1,6 @@
/*
+ * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,7 +29,6 @@
#include "gui/csvImport/CsvImportWizard.h"
#include "gui/entry/EntryModel.h"
-class ChangeMasterKeyWidget;
class DatabaseOpenWidget;
class DatabaseSettingsDialog;
class Database;
@@ -61,7 +60,7 @@ class DatabaseWidget : public QStackedWidget
Q_OBJECT
public:
- enum Mode
+ enum class Mode
{
None,
ImportMode,
@@ -70,35 +69,39 @@ public:
LockedMode
};
- explicit DatabaseWidget(Database* db, QWidget* parent = nullptr);
+ explicit DatabaseWidget(QSharedPointer<Database> db, QWidget* parent = nullptr);
+ explicit DatabaseWidget(const QString& filePath, QWidget* parent = nullptr);
~DatabaseWidget();
- Database* database();
- bool dbHasKey() const;
- bool canDeleteCurrentGroup() const;
- bool isInSearchMode() const;
+
+ QSharedPointer<Database> database() const;
+
+ bool lock();
+ void prepareUnlock();
+ bool save(int attempt = 0);
+ bool saveAs();
+
+ DatabaseWidget::Mode currentMode() const;
+ bool isLocked() const;
+ bool isSearchActive() const;
+
QString getCurrentSearch();
+ void refreshSearch();
+
+ GroupView* groupView();
+ EntryView* entryView();
+
Group* currentGroup() const;
- int addWidget(QWidget* w);
- void setCurrentIndex(int index);
- void setCurrentWidget(QWidget* widget);
- DatabaseWidget::Mode currentMode() const;
- void lock();
- void updateFilePath(const QString& filePath);
+ bool canDeleteCurrentGroup() const;
+ bool isGroupSelected() const;
+ bool isRecycleBinSelected() const;
int numberOfSelectedEntries() const;
+
QStringList customEntryAttributes() const;
- bool isGroupSelected() const;
- bool isInEditMode() const;
bool isEditWidgetModified() const;
- QList<int> mainSplitterSizes() const;
- void setMainSplitterSizes(const QList<int>& sizes);
- QList<int> previewSplitterSizes() const;
- void setPreviewSplitterSizes(const QList<int>& sizes);
bool isUsernamesHidden() const;
void setUsernamesHidden(bool hide);
bool isPasswordsHidden() const;
void setPasswordsHidden(bool hide);
- QByteArray entryViewState() const;
- bool setEntryViewState(const QByteArray& state) const;
void clearAllWidgets();
bool currentEntryHasFocus();
bool currentEntryHasTitle();
@@ -107,31 +110,33 @@ public:
bool currentEntryHasUrl();
bool currentEntryHasNotes();
bool currentEntryHasTotp();
- GroupView* groupView();
- EntryView* entryView();
- void showUnlockDialog();
- void closeUnlockDialog();
+
void blockAutoReload(bool block = true);
- void refreshSearch();
- bool isRecycleBinSelected() const;
- QString getDatabaseName() const;
- void setDatabaseName(const QString& databaseName);
- QString getDatabaseFileName() const;
- void setDatabaseFileName(const QString& databaseFileName);
+
+ QByteArray entryViewState() const;
+ bool setEntryViewState(const QByteArray& state) const;
+ QList<int> mainSplitterSizes() const;
+ void setMainSplitterSizes(const QList<int>& sizes);
+ QList<int> previewSplitterSizes() const;
+ void setPreviewSplitterSizes(const QList<int>& sizes);
signals:
+ // relayed Database signals
+ void databaseFilePathChanged(const QString& oldPath, const QString& newPath);
+ void databaseModified();
+ void databaseSaved();
+ void databaseUnlocked();
+ void databaseLocked();
+
void closeRequest();
void currentModeChanged(DatabaseWidget::Mode mode);
void groupChanged();
void entrySelectionChanged();
- void databaseChanged(Database* newDb, bool unsavedChanges);
- void databaseMerged(Database* mergedDb);
+ void databaseMerged(QSharedPointer<Database> mergedDb);
void groupContextMenuRequested(const QPoint& globalPos);
void entryContextMenuRequested(const QPoint& globalPos);
void pressedEntry(Entry* selectedEntry);
void pressedGroup(Group* selectedGroup);
- void unlockedDatabase();
- void lockedDatabase();
void listModeAboutToActivate();
void listModeActivated();
void searchModeAboutToActivate();
@@ -142,6 +147,7 @@ signals:
void clearSearch();
public slots:
+ void replaceDatabase(QSharedPointer<Database> db);
void createEntry();
void cloneEntry();
void deleteEntries();
@@ -167,15 +173,13 @@ public slots:
void switchToGroupEdit();
void switchToMasterKeyChange();
void switchToDatabaseSettings();
+ void switchToOpenDatabase();
void switchToOpenDatabase(const QString& filePath);
- void switchToOpenDatabase(const QString& filePath, const QString& password, const QString& keyFile);
void switchToCsvImport(const QString& filePath);
void csvImportFinished(bool accepted);
void switchToOpenMergeDatabase(const QString& filePath);
void switchToOpenMergeDatabase(const QString& filePath, const QString& password, const QString& keyFile);
void switchToImportKeepass1(const QString& filePath);
- void databaseModified();
- void databaseSaved();
void emptyRecycleBin();
// Search related slots
@@ -191,18 +195,24 @@ public slots:
void showErrorMessage(const QString& errorMessage);
void hideMessage();
+protected:
+ void closeEvent(QCloseEvent* event) override;
+ void showEvent(QShowEvent* event) override;
+
private slots:
+ void updateFilePath(const QString& filePath);
void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column);
void switchBackToEntryEdit();
void switchToHistoryView(Entry* entry);
- void switchToEntryEdit(Entry* entry);
+ void switchToEntryEdit(Entry*);
void switchToEntryEdit(Entry* entry, bool create);
void switchToGroupEdit(Group* entry, bool create);
void emitGroupContextMenuRequested(const QPoint& pos);
void emitEntryContextMenuRequested(const QPoint& pos);
void emitPressedGroup(Group* currentGroup);
void emitEntrySelectionChanged();
- void openDatabase(bool accepted);
+ void connectDatabaseSignals();
+ void loadDatabase(bool accepted);
void mergeDatabase(bool accepted);
void unlockDatabase(bool accepted);
void emitCurrentModeChanged();
@@ -213,36 +223,38 @@ private slots:
void unblockAutoReload();
private:
+ int addChildWidget(QWidget* w);
void setClipboardTextAndMinimize(const QString& text);
void setIconFromParent();
- void replaceDatabase(Database* db);
-
- QPointer<Database> m_db;
- QWidget* m_mainWidget;
- EditEntryWidget* m_editEntryWidget;
- EditEntryWidget* m_historyEditEntryWidget;
- EditGroupWidget* m_editGroupWidget;
- ChangeMasterKeyWidget* m_changeMasterKeyWidget;
- CsvImportWizard* m_csvImportWizard;
- DatabaseSettingsDialog* m_databaseSettingDialog;
- DatabaseOpenWidget* m_databaseOpenWidget;
- DatabaseOpenWidget* m_databaseOpenMergeWidget;
- KeePass1OpenWidget* m_keepass1OpenWidget;
- UnlockDatabaseWidget* m_unlockDatabaseWidget;
- UnlockDatabaseDialog* m_unlockDatabaseDialog;
- QSplitter* m_mainSplitter;
- QSplitter* m_previewSplitter;
- GroupView* m_groupView;
- EntryView* m_entryView;
- QLabel* m_searchingLabel;
- Group* m_newGroup;
- Entry* m_newEntry;
- Group* m_newParent;
- QString m_filePath;
+
+ QSharedPointer<Database> m_db;
+
+ QPointer<QWidget> m_mainWidget;
+ QPointer<QSplitter> m_mainSplitter;
+ QPointer<MessageWidget> m_messageWidget;
+ QPointer<EntryPreviewWidget> m_previewView;
+ QPointer<QSplitter> m_previewSplitter;
+ QPointer<QLabel> m_searchingLabel;
+ QPointer<CsvImportWizard> m_csvImportWizard;
+ QPointer<EditEntryWidget> m_editEntryWidget;
+ QPointer<EditGroupWidget> m_editGroupWidget;
+ QPointer<EditEntryWidget> m_historyEditEntryWidget;
+ QPointer<DatabaseSettingsDialog> m_databaseSettingDialog;
+ QPointer<DatabaseOpenWidget> m_databaseOpenWidget;
+ QPointer<DatabaseOpenWidget> m_databaseOpenMergeWidget;
+ QPointer<KeePass1OpenWidget> m_keepass1OpenWidget;
+ QPointer<UnlockDatabaseWidget> m_unlockDatabaseWidget;
+ QPointer<UnlockDatabaseDialog> m_unlockDatabaseDialog;
+ QPointer<GroupView> m_groupView;
+ QPointer<EntryView> m_entryView;
+
+ QPointer<Group> m_newGroup;
+ QPointer<Entry> m_newEntry;
+ QPointer<Group> m_newParent;
+
QUuid m_groupBeforeLock;
QUuid m_entryBeforeLock;
- MessageWidget* m_messageWidget;
- EntryPreviewWidget* m_previewView;
+
QString m_databaseName;
QString m_databaseFileName;
@@ -251,15 +263,11 @@ private:
QString m_lastSearchText;
bool m_searchLimitGroup;
- // CSV import state
- bool m_importingCsv;
-
// Autoreload
QFileSystemWatcher m_fileWatcher;
QTimer m_fileWatchTimer;
QTimer m_fileWatchUnblockTimer;
bool m_ignoreAutoReload;
- bool m_databaseModified;
};
#endif // KEEPASSX_DATABASEWIDGET_H
diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp
index 1172569f5..5579b30cd 100644
--- a/src/gui/DatabaseWidgetStateSync.cpp
+++ b/src/gui/DatabaseWidgetStateSync.cpp
@@ -74,7 +74,7 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
m_activeDbWidget->setPreviewSplitterSizes(m_previewSplitterSizes);
}
- if (m_activeDbWidget->isInSearchMode()) {
+ if (m_activeDbWidget->isSearchActive()) {
restoreSearchView();
} else {
restoreListView();
@@ -177,7 +177,7 @@ void DatabaseWidgetStateSync::updateViewState()
m_hideUsernames = m_activeDbWidget->isUsernamesHidden();
m_hidePasswords = m_activeDbWidget->isPasswordsHidden();
- if (m_activeDbWidget->isInSearchMode()) {
+ if (m_activeDbWidget->isSearchActive()) {
m_searchViewState = m_activeDbWidget->entryViewState();
} else {
m_listViewState = m_activeDbWidget->entryViewState();
diff --git a/src/gui/EditWidget.cpp b/src/gui/EditWidget.cpp
index a58cc6d82..66038282d 100644
--- a/src/gui/EditWidget.cpp
+++ b/src/gui/EditWidget.cpp
@@ -56,7 +56,7 @@ void EditWidget::addPage(const QString& labelText, const QIcon& icon, QWidget* w
* from automatic resizing and it now should be able to fit into a user's monitor even if the monitor is only 768
* pixels high.
*/
- QScrollArea* scrollArea = new QScrollArea(m_ui->stackedWidget);
+ auto* scrollArea = new QScrollArea(m_ui->stackedWidget);
scrollArea->setFrameShape(QFrame::NoFrame);
scrollArea->setWidget(widget);
scrollArea->setWidgetResizable(true);
diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp
index e26966df6..61bffe701 100644
--- a/src/gui/EditWidgetIcons.cpp
+++ b/src/gui/EditWidgetIcons.cpp
@@ -61,7 +61,7 @@ void UrlFetchProgressDialog::networkReplyProgress(qint64 bytesRead, qint64 total
EditWidgetIcons::EditWidgetIcons(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::EditWidgetIcons())
- , m_database(nullptr)
+ , m_db(nullptr)
#ifdef WITH_XC_NETWORKING
, m_reply(nullptr)
#endif
@@ -102,7 +102,7 @@ EditWidgetIcons::~EditWidgetIcons()
IconStruct EditWidgetIcons::state()
{
- Q_ASSERT(m_database);
+ Q_ASSERT(m_db);
Q_ASSERT(!m_currentUuid.isNull());
IconStruct iconStruct;
@@ -127,16 +127,19 @@ IconStruct EditWidgetIcons::state()
void EditWidgetIcons::reset()
{
- m_database = nullptr;
+ m_db.reset();
m_currentUuid = QUuid();
}
-void EditWidgetIcons::load(const QUuid& currentUuid, Database* database, const IconStruct& iconStruct, const QString& url)
+void EditWidgetIcons::load(const QUuid& currentUuid,
+ QSharedPointer<Database> database,
+ const IconStruct& iconStruct,
+ const QString& url)
{
Q_ASSERT(database);
Q_ASSERT(!currentUuid.isNull());
- m_database = database;
+ m_db = database;
m_currentUuid = currentUuid;
setUrl(url);
@@ -329,7 +332,7 @@ void EditWidgetIcons::startFetchFavicon(const QUrl& url)
void EditWidgetIcons::addCustomIconFromFile()
{
- if (m_database) {
+ if (m_db) {
QString filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), Tools::imageReaderFilter(), tr("All files"));
auto filenames = QFileDialog::getOpenFileNames(this, tr("Select Image(s)"), "", filter);
@@ -378,19 +381,19 @@ void EditWidgetIcons::addCustomIconFromFile()
bool EditWidgetIcons::addCustomIcon(const QImage& icon)
{
bool added = false;
- if (m_database) {
+ if (m_db) {
// Don't add an icon larger than 128x128, but retain original size if smaller
auto scaledicon = icon;
if (icon.width() > 128 || icon.height() > 128) {
scaledicon = icon.scaled(128, 128);
}
- QUuid uuid = m_database->metadata()->findCustomIcon(scaledicon);
+ QUuid uuid = m_db->metadata()->findCustomIcon(scaledicon);
if (uuid.isNull()) {
uuid = QUuid::createUuid();
- m_database->metadata()->addCustomIcon(uuid, scaledicon);
- m_customIconModel->setIcons(m_database->metadata()->customIconsScaledPixmaps(),
- m_database->metadata()->customIconsOrder());
+ m_db->metadata()->addCustomIcon(uuid, scaledicon);
+ m_customIconModel->setIcons(m_db->metadata()->customIconsScaledPixmaps(),
+ m_db->metadata()->customIconsOrder());
added = true;
}
@@ -407,12 +410,12 @@ bool EditWidgetIcons::addCustomIcon(const QImage& icon)
void EditWidgetIcons::removeCustomIcon()
{
- if (m_database) {
+ if (m_db) {
QModelIndex index = m_ui->customIconsView->currentIndex();
if (index.isValid()) {
QUuid iconUuid = m_customIconModel->uuidFromIndex(index);
- const QList<Entry*> allEntries = m_database->rootGroup()->entriesRecursive(true);
+ const QList<Entry*> allEntries = m_db->rootGroup()->entriesRecursive(true);
QList<Entry*> entriesWithSameIcon;
QList<Entry*> historyEntriesWithSameIcon;
@@ -427,7 +430,7 @@ void EditWidgetIcons::removeCustomIcon()
}
}
- const QList<Group*> allGroups = m_database->rootGroup()->groupsRecursive(true);
+ const QList<Group*> allGroups = m_db->rootGroup()->groupsRecursive(true);
QList<Group*> groupsWithSameIcon;
for (Group* group : allGroups) {
@@ -471,14 +474,14 @@ void EditWidgetIcons::removeCustomIcon()
}
// Remove the icon from the database
- m_database->metadata()->removeCustomIcon(iconUuid);
- m_customIconModel->setIcons(m_database->metadata()->customIconsScaledPixmaps(),
- m_database->metadata()->customIconsOrder());
+ m_db->metadata()->removeCustomIcon(iconUuid);
+ m_customIconModel->setIcons(m_db->metadata()->customIconsScaledPixmaps(),
+ m_db->metadata()->customIconsOrder());
// Reset the current icon view
updateRadioButtonDefaultIcons();
- if (m_database->resolveEntry(m_currentUuid) != nullptr) {
+ if (m_db->rootGroup()->findEntryByUuid(m_currentUuid) != nullptr) {
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(Entry::DefaultIconNumber));
} else {
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(Group::DefaultIconNumber));
diff --git a/src/gui/EditWidgetIcons.h b/src/gui/EditWidgetIcons.h
index 7a14123b5..d00e064af 100644
--- a/src/gui/EditWidgetIcons.h
+++ b/src/gui/EditWidgetIcons.h
@@ -71,7 +71,10 @@ public:
IconStruct state();
void reset();
- void load(const QUuid& currentUuid, Database* database, const IconStruct& iconStruct, const QString& url = "");
+ void load(const QUuid& currentUuid,
+ QSharedPointer<Database> database,
+ const IconStruct& iconStruct,
+ const QString& url = "");
public slots:
void setUrl(const QString& url);
@@ -97,7 +100,7 @@ private slots:
private:
const QScopedPointer<Ui::EditWidgetIcons> m_ui;
- Database* m_database;
+ QSharedPointer<Database> m_db;
QUuid m_currentUuid;
#ifdef WITH_XC_NETWORKING
QUrl m_url;
diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp
index b9fa26383..7c88d1fa5 100644
--- a/src/gui/EntryPreviewWidget.cpp
+++ b/src/gui/EntryPreviewWidget.cpp
@@ -19,7 +19,6 @@
#include "EntryPreviewWidget.h"
#include "ui_EntryPreviewWidget.h"
-#include <QDebug>
#include <QDesktopServices>
#include <QDir>
@@ -115,12 +114,12 @@ void EntryPreviewWidget::setGroup(Group* selectedGroup)
void EntryPreviewWidget::setDatabaseMode(DatabaseWidget::Mode mode)
{
- m_locked = mode == DatabaseWidget::LockedMode;
+ m_locked = mode == DatabaseWidget::Mode::LockedMode;
if (m_locked) {
return;
}
- if (mode == DatabaseWidget::ViewMode) {
+ if (mode == DatabaseWidget::Mode::ViewMode) {
if (m_ui->stackedWidget->currentWidget() == m_ui->pageGroup) {
setGroup(m_currentGroup);
} else {
diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp
index 8123d239f..6b4c04a66 100644
--- a/src/gui/KeePass1OpenWidget.cpp
+++ b/src/gui/KeePass1OpenWidget.cpp
@@ -54,15 +54,13 @@ void KeePass1OpenWidget::openDatabase()
return;
}
- delete m_db;
-
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
m_db = reader.readDatabase(&file, password, keyFileName);
QApplication::restoreOverrideCursor();
if (m_db) {
m_db->metadata()->setName(QFileInfo(m_filename).completeBaseName());
- emit editFinished(true);
+ emit dialogFinished(true);
} else {
m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n").append(reader.errorString()),
MessageWidget::Error);
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index 67e453392..649990416 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -32,6 +32,9 @@
#include "core/FilePath.h"
#include "core/InactivityTimer.h"
#include "core/Metadata.h"
+#include "keys/CompositeKey.h"
+#include "keys/PasswordKey.h"
+#include "keys/FileKey.h"
#include "gui/AboutDialog.h"
#include "gui/DatabaseWidget.h"
#include "gui/SearchWidget.h"
@@ -132,7 +135,7 @@ MainWindow::MainWindow()
setAcceptDrops(true);
// Setup the search widget in the toolbar
- SearchWidget* search = new SearchWidget();
+ auto* search = new SearchWidget();
search->connectSignals(m_actionMultiplexer);
m_searchWidgetAction = m_ui->toolBar->addWidget(search);
m_searchWidgetAction->setEnabled(false);
@@ -293,7 +296,7 @@ MainWindow::MainWindow()
connect(m_ui->actionDatabaseOpen, SIGNAL(triggered()), m_ui->tabWidget, SLOT(openDatabase()));
connect(m_ui->actionDatabaseSave, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabase()));
connect(m_ui->actionDatabaseSaveAs, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabaseAs()));
- connect(m_ui->actionDatabaseClose, SIGNAL(triggered()), m_ui->tabWidget, SLOT(closeDatabase()));
+ connect(m_ui->actionDatabaseClose, SIGNAL(triggered()), m_ui->tabWidget, SLOT(closeCurrentDatabaseTab()));
connect(m_ui->actionDatabaseMerge, SIGNAL(triggered()), m_ui->tabWidget, SLOT(mergeDatabase()));
connect(m_ui->actionChangeMasterKey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(changeMasterKey()));
connect(m_ui->actionChangeDatabaseSettings, SIGNAL(triggered()), m_ui->tabWidget, SLOT(changeDatabaseSettings()));
@@ -349,11 +352,6 @@ MainWindow::MainWindow()
this,
SLOT(displayGlobalMessage(QString,MessageWidget::MessageType)));
connect(m_ui->tabWidget, SIGNAL(messageDismissGlobal()), this, SLOT(hideGlobalMessage()));
- connect(m_ui->tabWidget,
- SIGNAL(messageTab(QString,MessageWidget::MessageType)),
- this,
- SLOT(displayTabMessage(QString,MessageWidget::MessageType)));
- connect(m_ui->tabWidget, SIGNAL(messageDismissTab()), this, SLOT(hideTabMessage()));
m_screenLockListener = new ScreenLockListener(this);
connect(m_screenLockListener, SIGNAL(screenLocked()), SLOT(handleScreenLock()));
@@ -450,9 +448,28 @@ void MainWindow::clearLastDatabases()
}
}
-void MainWindow::openDatabase(const QString& fileName, const QString& pw, const QString& keyFile)
+void MainWindow::openDatabase(const QString& filePath, const QString& pw, const QString& keyFile)
{
- m_ui->tabWidget->openDatabase(fileName, pw, keyFile);
+ if (pw.isEmpty() && keyFile.isEmpty()) {
+ m_ui->tabWidget->addDatabaseTab(filePath);
+ return;
+ }
+
+ auto db = QSharedPointer<Database>::create();
+ auto key = QSharedPointer<CompositeKey>::create();
+ if (!pw.isEmpty()) {
+ key->addKey(QSharedPointer<PasswordKey>::create(pw));
+ }
+ if (!keyFile.isEmpty()) {
+ auto fileKey = QSharedPointer<FileKey>::create();
+ fileKey->load(keyFile);
+ key->addKey(fileKey);
+ }
+ if (db->open(filePath, key, nullptr, false)) {
+ auto* dbWidget = new DatabaseWidget(db, this);
+ m_ui->tabWidget->addDatabaseTab(dbWidget);
+ dbWidget->switchToView(true);
+ }
}
void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
@@ -476,12 +493,12 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget();
Q_ASSERT(dbWidget);
- if (mode == DatabaseWidget::None) {
+ if (mode == DatabaseWidget::Mode::None) {
mode = dbWidget->currentMode();
}
switch (mode) {
- case DatabaseWidget::ViewMode: {
+ case DatabaseWidget::Mode::ViewMode: {
// bool inSearch = dbWidget->isInSearchMode();
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1 && dbWidget->currentEntryHasFocus();
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0 && dbWidget->currentEntryHasFocus();
@@ -521,9 +538,9 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
break;
}
- case DatabaseWidget::EditMode:
- case DatabaseWidget::ImportMode:
- case DatabaseWidget::LockedMode: {
+ case DatabaseWidget::Mode::EditMode:
+ case DatabaseWidget::Mode::ImportMode:
+ case DatabaseWidget::Mode::LockedMode: {
const QList<QAction*> entryActions = m_ui->menuEntries->actions();
for (QAction* action : entryActions) {
action->setEnabled(false);
@@ -589,14 +606,11 @@ void MainWindow::updateWindowTitle()
bool isModified = m_ui->tabWidget->isModified(tabWidgetIndex);
if (stackedWidgetIndex == DatabaseTabScreen && tabWidgetIndex != -1) {
- customWindowTitlePart = m_ui->tabWidget->tabText(tabWidgetIndex);
+ customWindowTitlePart = m_ui->tabWidget->tabName(tabWidgetIndex);
if (isModified) {
// remove asterisk '*' from title
customWindowTitlePart.remove(customWindowTitlePart.size() - 1, 1);
}
- if (m_ui->tabWidget->readOnly(tabWidgetIndex)) {
- customWindowTitlePart = tr("%1 [read-only]", "window title modifier").arg(customWindowTitlePart);
- }
m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave(tabWidgetIndex));
} else if (stackedWidgetIndex == 1) {
customWindowTitlePart = tr("Settings");
@@ -612,17 +626,16 @@ void MainWindow::updateWindowTitle()
if (customWindowTitlePart.isEmpty() || stackedWidgetIndex == 1) {
setWindowFilePath("");
} else {
- setWindowFilePath(m_ui->tabWidget->databasePath(tabWidgetIndex));
+ setWindowFilePath(m_ui->tabWidget->databaseWidgetFromIndex(tabWidgetIndex)->database()->filePath());
}
- setWindowModified(isModified);
-
setWindowTitle(windowTitle);
+ setWindowModified(isModified);
}
void MainWindow::showAboutDialog()
{
- AboutDialog* aboutDialog = new AboutDialog(this);
+ auto* aboutDialog = new AboutDialog(this);
aboutDialog->open();
}
@@ -687,7 +700,7 @@ void MainWindow::switchToOpenDatabase()
void MainWindow::switchToDatabaseFile(const QString& file)
{
- m_ui->tabWidget->openDatabase(file);
+ m_ui->tabWidget->addDatabaseTab(file);
switchToDatabases();
}
@@ -703,8 +716,9 @@ void MainWindow::switchToCsvImport()
switchToDatabases();
}
-void MainWindow::databaseStatusChanged(DatabaseWidget*)
+void MainWindow::databaseStatusChanged(DatabaseWidget* dbWidget)
{
+ Q_UNUSED(dbWidget);
updateTrayIcon();
}
@@ -817,18 +831,14 @@ bool MainWindow::saveLastDatabases()
bool openPreviousDatabasesOnStartup = config()->get("OpenPreviousDatabasesOnStartup").toBool();
if (openPreviousDatabasesOnStartup) {
- connect(m_ui->tabWidget, SIGNAL(databaseWithFileClosed(QString)), this, SLOT(rememberOpenDatabases(QString)));
+ connect(m_ui->tabWidget, SIGNAL(databaseClosed(const QString&)), this, SLOT(rememberOpenDatabases(const QString&)));
}
- if (!m_ui->tabWidget->closeAllDatabases()) {
- accept = false;
- } else {
- accept = true;
- }
+ accept = m_ui->tabWidget->closeAllDatabaseTabs();
if (openPreviousDatabasesOnStartup) {
disconnect(
- m_ui->tabWidget, SIGNAL(databaseWithFileClosed(QString)), this, SLOT(rememberOpenDatabases(QString)));
+ m_ui->tabWidget, SIGNAL(databaseClosed(const QString&)), this, SLOT(rememberOpenDatabases(const QString&)));
config()->set("LastOpenedDatabases", m_openDatabases);
}
@@ -1036,13 +1046,6 @@ void MainWindow::hideGlobalMessage()
m_ui->globalMessageWidget->hideMessage();
}
-void MainWindow::hideTabMessage()
-{
- if (m_ui->stackedWidget->currentIndex() == DatabaseTabScreen) {
- m_ui->tabWidget->currentDatabaseWidget()->hideMessage();
- }
-}
-
void MainWindow::showYubiKeyPopup()
{
displayGlobalMessage(tr("Please touch the button on your YubiKey!"),
@@ -1121,7 +1124,7 @@ void MainWindow::dropEvent(QDropEvent* event)
void MainWindow::closeAllDatabases()
{
- m_ui->tabWidget->closeAllDatabases();
+ m_ui->tabWidget->closeAllDatabaseTabs();
}
void MainWindow::lockAllDatabases()
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index 38b1c9308..a9a47d3d8 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -56,7 +56,7 @@ public:
};
public slots:
- void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString());
+ void openDatabase(const QString& filePath, const QString& pw = {}, const QString& keyFile = {});
void appExit();
void displayGlobalMessage(const QString& text,
MessageWidget::MessageType type,
@@ -80,7 +80,7 @@ protected:
void changeEvent(QEvent* event) override;
private slots:
- void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::None);
+ void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::Mode::None);
void updateWindowTitle();
void showAboutDialog();
void openDonateUrl();
@@ -107,7 +107,6 @@ private slots:
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
void lockDatabasesAfterInactivity();
void forgetTouchIDAfterInactivity();
- void hideTabMessage();
void handleScreenLock();
void showErrorMessage(const QString& message);
void selectNextDatabaseTab();
diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp
index cde899576..86e18dca3 100644
--- a/src/gui/SearchWidget.cpp
+++ b/src/gui/SearchWidget.cpp
@@ -135,7 +135,7 @@ void SearchWidget::databaseChanged(DatabaseWidget* dbWidget)
// Set current search text from this database
m_ui->searchEdit->setText(dbWidget->getCurrentSearch());
// Keyboard focus on search widget at database unlocking
- connect(dbWidget, SIGNAL(unlockedDatabase()), this, SLOT(searchFocus()));
+ connect(dbWidget, SIGNAL(databaseUnlocked()), this, SLOT(searchFocus()));
// Enforce search policy
emit caseSensitiveChanged(m_actionCaseSensitive->isChecked());
emit limitGroupChanged(m_actionLimitGroup->isChecked());
diff --git a/src/gui/UnlockDatabaseDialog.cpp b/src/gui/UnlockDatabaseDialog.cpp
index 3d900f523..8e2066ef4 100644
--- a/src/gui/UnlockDatabaseDialog.cpp
+++ b/src/gui/UnlockDatabaseDialog.cpp
@@ -27,7 +27,7 @@ UnlockDatabaseDialog::UnlockDatabaseDialog(QWidget* parent)
, m_view(new UnlockDatabaseWidget(this))
{
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
- connect(m_view, SIGNAL(editFinished(bool)), this, SLOT(complete(bool)));
+ connect(m_view, SIGNAL(dialogFinished(bool)), this, SLOT(complete(bool)));
}
void UnlockDatabaseDialog::setFilePath(const QString& filePath)
@@ -40,7 +40,7 @@ void UnlockDatabaseDialog::clearForms()
m_view->clearForms();
}
-Database* UnlockDatabaseDialog::database()
+QSharedPointer<Database> UnlockDatabaseDialog::database()
{
return m_view->database();
}
diff --git a/src/gui/UnlockDatabaseDialog.h b/src/gui/UnlockDatabaseDialog.h
index 95d6ce238..7f06029c8 100644
--- a/src/gui/UnlockDatabaseDialog.h
+++ b/src/gui/UnlockDatabaseDialog.h
@@ -34,7 +34,7 @@ public:
explicit UnlockDatabaseDialog(QWidget* parent = nullptr);
void setFilePath(const QString& filePath);
void clearForms();
- Database* database();
+ QSharedPointer<Database> database();
signals:
void unlockDone(bool);
diff --git a/src/gui/csvImport/CsvParserModel.cpp b/src/gui/csvImport/CsvParserModel.cpp
index 269fbdd62..a6c24667d 100644
--- a/src/gui/csvImport/CsvParserModel.cpp
+++ b/src/gui/csvImport/CsvParserModel.cpp
@@ -92,9 +92,9 @@ void CsvParserModel::setSkippedRows(int skipped)
emit layoutChanged();
}
-void CsvParserModel::setHeaderLabels(QStringList l)
+void CsvParserModel::setHeaderLabels(const QStringList& labels)
{
- m_columnHeader = std::move(l);
+ m_columnHeader = labels;
}
int CsvParserModel::rowCount(const QModelIndex& parent) const
diff --git a/src/gui/csvImport/CsvParserModel.h b/src/gui/csvImport/CsvParserModel.h
index 7a13b0d1b..c1d5939d6 100644
--- a/src/gui/csvImport/CsvParserModel.h
+++ b/src/gui/csvImport/CsvParserModel.h
@@ -36,7 +36,7 @@ public:
QString getFileInfo();
bool parse();
- void setHeaderLabels(QStringList l);
+ void setHeaderLabels(const QStringList& labels);
void mapColumns(int csvColumn, int dbColumn);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
index 7dd96030c..75a3fb5ef 100644
--- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
@@ -73,7 +73,7 @@ DatabaseSettingsDialog::~DatabaseSettingsDialog()
{
}
-void DatabaseSettingsDialog::load(Database* db)
+void DatabaseSettingsDialog::load(QSharedPointer<Database> db)
{
m_ui->categoryList->setCurrentCategory(0);
m_generalWidget->load(db);
diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.h b/src/gui/dbsettings/DatabaseSettingsDialog.h
index 2dd457cd0..41fc508a9 100644
--- a/src/gui/dbsettings/DatabaseSettingsDialog.h
+++ b/src/gui/dbsettings/DatabaseSettingsDialog.h
@@ -21,8 +21,9 @@
#include "gui/DialogyWidget.h"
#include "config-keepassx.h"
-#include <QScopedPointer>
#include <QPointer>
+#include <QScopedPointer>
+#include <QSharedPointer>
class Database;
class DatabaseSettingsWidgetGeneral;
@@ -47,7 +48,7 @@ public:
~DatabaseSettingsDialog() override;
Q_DISABLE_COPY(DatabaseSettingsDialog);
- void load(Database* db);
+ void load(QSharedPointer<Database> db);
void showMasterKeySettings();
signals:
@@ -66,7 +67,7 @@ private:
Security = 1
};
- QPointer<Database> m_db;
+ QSharedPointer<Database> m_db;
const QScopedPointer<Ui::DatabaseSettingsDialog> m_ui;
QPointer<DatabaseSettingsWidgetGeneral> m_generalWidget;
QPointer<QTabWidget> m_securityTabWidget;
diff --git a/src/gui/dbsettings/DatabaseSettingsWidget.cpp b/src/gui/dbsettings/DatabaseSettingsWidget.cpp
index 992101fd4..67b3ef375 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidget.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsWidget.cpp
@@ -36,7 +36,7 @@ DatabaseSettingsWidget::~DatabaseSettingsWidget()
*
* @param db database object to be configured
*/
-void DatabaseSettingsWidget::load(Database* db)
+void DatabaseSettingsWidget::load(QSharedPointer<Database> db)
{
m_db = db;
initialize();
diff --git a/src/gui/dbsettings/DatabaseSettingsWidget.h b/src/gui/dbsettings/DatabaseSettingsWidget.h
index 61082d5c7..6d58ddeb7 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidget.h
+++ b/src/gui/dbsettings/DatabaseSettingsWidget.h
@@ -20,7 +20,7 @@
#include "gui/settings/SettingsWidget.h"
-#include <QPointer>
+#include <QSharedPointer>
class Database;
@@ -36,7 +36,7 @@ public:
Q_DISABLE_COPY(DatabaseSettingsWidget);
~DatabaseSettingsWidget() override;
- virtual void load(Database* db);
+ virtual void load(QSharedPointer<Database> db);
signals:
/**
@@ -45,7 +45,7 @@ signals:
void sizeChanged();
protected:
- QPointer<Database> m_db;
+ QSharedPointer<Database> m_db;
};
#endif //KEEPASSXC_DATABASESETTINGSWIDGET_H
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp
index 709e8c102..c8d71762d 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp
@@ -44,7 +44,7 @@ void DatabaseSettingsWidgetGeneral::initialize()
m_ui->dbDescriptionEdit->setText(meta->description());
m_ui->recycleBinEnabledCheckBox->setChecked(meta->recycleBinEnabled());
m_ui->defaultUsernameEdit->setText(meta->defaultUserName());
- m_ui->compressionCheckbox->setChecked(m_db->compressionAlgo() != Database::CompressionNone);
+ m_ui->compressionCheckbox->setChecked(m_db->compressionAlgorithm() != Database::CompressionNone);
if (meta->historyMaxItems() > -1) {
m_ui->historyMaxItemsSpinBox->setValue(meta->historyMaxItems());
@@ -75,8 +75,8 @@ void DatabaseSettingsWidgetGeneral::showEvent(QShowEvent* event)
bool DatabaseSettingsWidgetGeneral::save()
{
- m_db->setCompressionAlgo(m_ui->compressionCheckbox->isChecked() ? Database::CompressionGZip
- : Database::CompressionNone);
+ m_db->setCompressionAlgorithm(m_ui->compressionCheckbox->isChecked() ? Database::CompressionGZip
+ : Database::CompressionNone);
Metadata* meta = m_db->metadata();
meta->setName(m_ui->dbNameEdit->text());
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
index d64a89787..3cc37accb 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
@@ -68,7 +68,7 @@ DatabaseSettingsWidgetMasterKey::~DatabaseSettingsWidgetMasterKey()
{
}
-void DatabaseSettingsWidgetMasterKey::load(Database* db)
+void DatabaseSettingsWidgetMasterKey::load(QSharedPointer<Database> db)
{
DatabaseSettingsWidget::load(db);
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h
index 89e077895..7ab0b085f 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h
@@ -41,7 +41,7 @@ public:
Q_DISABLE_COPY(DatabaseSettingsWidgetMasterKey);
~DatabaseSettingsWidgetMasterKey() override;
- void load(Database* db) override;
+ void load(QSharedPointer<Database> db) override;
inline bool hasAdvancedMode() const override { return false; }
diff --git a/src/gui/entry/AutoTypeAssociationsModel.cpp b/src/gui/entry/AutoTypeAssociationsModel.cpp
index 59d2c84ff..fadd4fa1a 100644
--- a/src/gui/entry/AutoTypeAssociationsModel.cpp
+++ b/src/gui/entry/AutoTypeAssociationsModel.cpp
@@ -49,7 +49,7 @@ void AutoTypeAssociationsModel::setAutoTypeAssociations(AutoTypeAssociations* au
endResetModel();
}
-void AutoTypeAssociationsModel::setEntry(const Entry* entry)
+void AutoTypeAssociationsModel::setEntry(Entry* entry)
{
m_entry = entry;
}
diff --git a/src/gui/entry/AutoTypeAssociationsModel.h b/src/gui/entry/AutoTypeAssociationsModel.h
index 1daa4a9c7..63340cdca 100644
--- a/src/gui/entry/AutoTypeAssociationsModel.h
+++ b/src/gui/entry/AutoTypeAssociationsModel.h
@@ -32,7 +32,7 @@ class AutoTypeAssociationsModel : public QAbstractListModel
public:
explicit AutoTypeAssociationsModel(QObject* parent = nullptr);
void setAutoTypeAssociations(AutoTypeAssociations* autoTypeAssociations);
- void setEntry(const Entry* entry);
+ void setEntry(Entry* entry);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp
index 8a54b23e3..657c352b8 100644
--- a/src/gui/entry/EditEntryWidget.cpp
+++ b/src/gui/entry/EditEntryWidget.cpp
@@ -1,3 +1,5 @@
+#include <utility>
+
/*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
@@ -340,7 +342,7 @@ void EditEntryWidget::setupSSHAgent()
connect(m_sshAgentUi->decryptButton, SIGNAL(clicked()), SLOT(decryptPrivateKey()));
connect(m_sshAgentUi->copyToClipboardButton, SIGNAL(clicked()), SLOT(copyPublicKey()));
- connect(m_advancedUi->attachmentsWidget->entryAttachments(), SIGNAL(modified()), SLOT(updateSSHAgentAttachments()));
+ connect(m_advancedUi->attachmentsWidget->entryAttachments(), SIGNAL(entryAttachmentsModified()), SLOT(updateSSHAgentAttachments()));
addPage(tr("SSH Agent"), FilePath::instance()->icon("apps", "utilities-terminal"), m_sshAgentWidget);
}
@@ -640,10 +642,10 @@ QString EditEntryWidget::entryTitle() const
}
}
-void EditEntryWidget::loadEntry(Entry* entry, bool create, bool history, const QString& parentName, Database* database)
+void EditEntryWidget::loadEntry(Entry* entry, bool create, bool history, const QString& parentName, QSharedPointer<Database> database)
{
m_entry = entry;
- m_database = database;
+ m_db = std::move(database);
m_create = create;
m_history = history;
@@ -667,7 +669,7 @@ void EditEntryWidget::loadEntry(Entry* entry, bool create, bool history, const Q
setUnsavedChanges(m_create);
}
-void EditEntryWidget::setForms(const Entry* entry, bool restore)
+void EditEntryWidget::setForms(Entry* entry, bool restore)
{
m_mainUi->titleEdit->setReadOnly(m_history);
m_mainUi->usernameEdit->setReadOnly(m_history);
@@ -734,7 +736,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
IconStruct iconStruct;
iconStruct.uuid = entry->iconUuid();
iconStruct.number = entry->iconNumber();
- m_iconsWidget->load(entry->uuid(), m_database, iconStruct, entry->webUrl());
+ m_iconsWidget->load(entry->uuid(), m_db, iconStruct, entry->webUrl());
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), m_iconsWidget, SLOT(setUrl(QString)));
m_autoTypeUi->enableButton->setChecked(entry->autoTypeEnabled());
@@ -924,7 +926,7 @@ void EditEntryWidget::cancel()
return;
}
- if (!m_entry->iconUuid().isNull() && !m_database->metadata()->containsCustomIcon(m_entry->iconUuid())) {
+ if (!m_entry->iconUuid().isNull() && !m_db->metadata()->containsCustomIcon(m_entry->iconUuid())) {
m_entry->setIcon(Entry::DefaultIconNumber);
}
@@ -952,7 +954,7 @@ void EditEntryWidget::cancel()
void EditEntryWidget::clear()
{
m_entry = nullptr;
- m_database = nullptr;
+ m_db.reset();
m_entryAttributes->clear();
m_advancedUi->attachmentsWidget->clearAttachments();
m_autoTypeAssoc->clear();
@@ -969,11 +971,11 @@ bool EditEntryWidget::hasBeenModified() const
}
// check if updating the entry would modify it
- QScopedPointer<Entry> entry(new Entry());
- entry->copyDataFrom(m_entry);
+ auto* entry = new Entry();
+ entry->copyDataFrom(m_entry.data());
entry->beginUpdate();
- updateEntryData(entry.data());
+ updateEntryData(entry);
return entry->endUpdate();
}
@@ -1256,17 +1258,13 @@ void EditEntryWidget::deleteHistoryEntry()
void EditEntryWidget::deleteAllHistoryEntries()
{
m_historyModel->deleteAll();
- if (m_historyModel->rowCount() > 0) {
- m_historyUi->deleteAllButton->setEnabled(true);
- } else {
- m_historyUi->deleteAllButton->setEnabled(false);
- }
+ m_historyUi->deleteAllButton->setEnabled(m_historyModel->rowCount() > 0);
setUnsavedChanges(true);
}
QMenu* EditEntryWidget::createPresetsMenu()
{
- QMenu* expirePresetsMenu = new QMenu(this);
+ auto* expirePresetsMenu = new QMenu(this);
expirePresetsMenu->addAction(tr("Tomorrow"))->setData(QVariant::fromValue(TimeDelta::fromDays(1)));
expirePresetsMenu->addSeparator();
expirePresetsMenu->addAction(tr("%n week(s)", nullptr, 1))->setData(QVariant::fromValue(TimeDelta::fromDays(7)));
@@ -1277,9 +1275,9 @@ QMenu* EditEntryWidget::createPresetsMenu()
expirePresetsMenu->addAction(tr("%n month(s)", nullptr, 3))->setData(QVariant::fromValue(TimeDelta::fromMonths(3)));
expirePresetsMenu->addAction(tr("%n month(s)", nullptr, 6))->setData(QVariant::fromValue(TimeDelta::fromMonths(6)));
expirePresetsMenu->addSeparator();
- expirePresetsMenu->addAction(tr("%n year(s)", 0, 1))->setData(QVariant::fromValue(TimeDelta::fromYears(1)));
- expirePresetsMenu->addAction(tr("%n year(s)", 0, 2))->setData(QVariant::fromValue(TimeDelta::fromYears(2)));
- expirePresetsMenu->addAction(tr("%n year(s)", 0, 3))->setData(QVariant::fromValue(TimeDelta::fromYears(3)));
+ expirePresetsMenu->addAction(tr("%n year(s)", nullptr, 1))->setData(QVariant::fromValue(TimeDelta::fromYears(1)));
+ expirePresetsMenu->addAction(tr("%n year(s)", nullptr, 2))->setData(QVariant::fromValue(TimeDelta::fromYears(2)));
+ expirePresetsMenu->addAction(tr("%n year(s)", nullptr, 3))->setData(QVariant::fromValue(TimeDelta::fromYears(3)));
return expirePresetsMenu;
}
diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h
index b3c313b19..a594d2072 100644
--- a/src/gui/entry/EditEntryWidget.h
+++ b/src/gui/entry/EditEntryWidget.h
@@ -38,7 +38,6 @@ class EntryHistoryModel;
class QButtonGroup;
class QMenu;
class QSortFilterProxyModel;
-class QStackedLayout;
#ifdef WITH_XC_SSHAGENT
#include "sshagent/KeeAgentSettings.h"
class OpenSSHKey;
@@ -60,11 +59,11 @@ class EditEntryWidget : public EditWidget
public:
explicit EditEntryWidget(QWidget* parent = nullptr);
- ~EditEntryWidget();
+ ~EditEntryWidget() override;
- void loadEntry(Entry* entry, bool create, bool history, const QString& parentName, Database* database);
+ void loadEntry(Entry* entry, bool create, bool history, const QString& parentName,
+ QSharedPointer<Database> database);
- void createPresetsMenu(QMenu* expirePresetsMenu);
QString entryTitle() const;
void clear();
bool hasBeenModified() const;
@@ -128,7 +127,7 @@ private:
void setupColorButton(bool foreground, const QColor& color);
bool passwordsEqual();
- void setForms(const Entry* entry, bool restore = false);
+ void setForms(Entry* entry, bool restore = false);
QMenu* createPresetsMenu();
void updateEntryData(Entry* entry) const;
#ifdef WITH_XC_SSHAGENT
@@ -138,8 +137,8 @@ private:
void displayAttribute(QModelIndex index, bool showProtected);
- Entry* m_entry;
- Database* m_database;
+ QPointer<Entry> m_entry;
+ QSharedPointer<Database> m_db;
bool m_create;
bool m_history;
diff --git a/src/gui/group/EditGroupWidget.cpp b/src/gui/group/EditGroupWidget.cpp
index 44276a9f3..5143f4fc1 100644
--- a/src/gui/group/EditGroupWidget.cpp
+++ b/src/gui/group/EditGroupWidget.cpp
@@ -30,7 +30,6 @@ EditGroupWidget::EditGroupWidget(QWidget* parent)
, m_editGroupWidgetIcons(new EditWidgetIcons())
, m_editWidgetProperties(new EditWidgetProperties())
, m_group(nullptr)
- , m_database(nullptr)
{
m_mainUi->setupUi(m_editGroupWidgetMain);
@@ -58,10 +57,10 @@ EditGroupWidget::~EditGroupWidget()
{
}
-void EditGroupWidget::loadGroup(Group* group, bool create, Database* database)
+void EditGroupWidget::loadGroup(Group* group, bool create, QSharedPointer<Database> database)
{
m_group = group;
- m_database = database;
+ m_db = database;
if (create) {
setHeadline(tr("Add group"));
@@ -141,7 +140,7 @@ void EditGroupWidget::apply()
void EditGroupWidget::cancel()
{
- if (!m_group->iconUuid().isNull() && !m_database->metadata()->containsCustomIcon(m_group->iconUuid())) {
+ if (!m_group->iconUuid().isNull() && !m_db->metadata()->containsCustomIcon(m_group->iconUuid())) {
m_group->setIcon(Entry::DefaultIconNumber);
}
@@ -152,7 +151,7 @@ void EditGroupWidget::cancel()
void EditGroupWidget::clear()
{
m_group = nullptr;
- m_database = nullptr;
+ m_db.reset();
m_editGroupWidgetIcons->reset();
}
diff --git a/src/gui/group/EditGroupWidget.h b/src/gui/group/EditGroupWidget.h
index 87271871d..992af0072 100644
--- a/src/gui/group/EditGroupWidget.h
+++ b/src/gui/group/EditGroupWidget.h
@@ -41,7 +41,7 @@ public:
explicit EditGroupWidget(QWidget* parent = nullptr);
~EditGroupWidget();
- void loadGroup(Group* group, bool create, Database* database);
+ void loadGroup(Group* group, bool create, QSharedPointer<Database> database);
void clear();
signals:
@@ -65,7 +65,7 @@ private:
QPointer<EditWidgetProperties> m_editWidgetProperties;
QPointer<Group> m_group;
- QPointer<Database> m_database;
+ QSharedPointer<Database> m_db;
Q_DISABLE_COPY(EditGroupWidget)
};
diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp
index 791f6ce33..bb579be03 100644
--- a/src/gui/group/GroupModel.cpp
+++ b/src/gui/group/GroupModel.cpp
@@ -37,10 +37,6 @@ void GroupModel::changeDatabase(Database* newDb)
{
beginResetModel();
- if (m_db) {
- m_db->disconnect(this);
- }
-
m_db = newDb;
connect(m_db, SIGNAL(groupDataChanged(Group*)), SLOT(groupDataChanged(Group*)));
@@ -233,7 +229,7 @@ bool GroupModel::dropMimeData(const QMimeData* data,
return false;
}
- Group* dragGroup = db->resolveGroup(groupUuid);
+ Group* dragGroup = db->rootGroup()->findGroupByUuid(groupUuid);
if (!dragGroup || !db->rootGroup()->findGroupByUuid(dragGroup->uuid()) || dragGroup == db->rootGroup()) {
return false;
}
@@ -277,7 +273,7 @@ bool GroupModel::dropMimeData(const QMimeData* data,
continue;
}
- Entry* dragEntry = db->resolveEntry(entryUuid);
+ Entry* dragEntry = db->rootGroup()->findEntryByUuid(entryUuid);
if (!dragEntry || !db->rootGroup()->findEntryByUuid(dragEntry->uuid())) {
continue;
}
diff --git a/src/gui/group/GroupView.cpp b/src/gui/group/GroupView.cpp
index ee3913948..e98a7385e 100644
--- a/src/gui/group/GroupView.cpp
+++ b/src/gui/group/GroupView.cpp
@@ -51,9 +51,9 @@ GroupView::GroupView(Database* db, QWidget* parent)
setDefaultDropAction(Qt::MoveAction);
}
-void GroupView::changeDatabase(Database* newDb)
+void GroupView::changeDatabase(QSharedPointer<Database> newDb)
{
- m_model->changeDatabase(newDb);
+ m_model->changeDatabase(newDb.data());
}
void GroupView::dragMoveEvent(QDragMoveEvent* event)
diff --git a/src/gui/group/GroupView.h b/src/gui/group/GroupView.h
index 0a084425d..0c27b1a12 100644
--- a/src/gui/group/GroupView.h
+++ b/src/gui/group/GroupView.h
@@ -30,7 +30,7 @@ class GroupView : public QTreeView
public:
explicit GroupView(Database* db, QWidget* parent = nullptr);
- void changeDatabase(Database* newDb);
+ void changeDatabase(QSharedPointer<Database> newDb);
void setModel(QAbstractItemModel* model) override;
Group* currentGroup();
void setCurrentGroup(Group* group);
diff --git a/src/gui/widgets/ElidedLabel.cpp b/src/gui/widgets/ElidedLabel.cpp
index 03d2b5565..bc2771764 100644
--- a/src/gui/widgets/ElidedLabel.cpp
+++ b/src/gui/widgets/ElidedLabel.cpp
@@ -17,7 +17,6 @@
#include "ElidedLabel.h"
-#include <QDebug>
#include <QResizeEvent>
namespace
diff --git a/src/gui/wizard/NewDatabaseWizard.cpp b/src/gui/wizard/NewDatabaseWizard.cpp
index 96aa06629..004d92f31 100644
--- a/src/gui/wizard/NewDatabaseWizard.cpp
+++ b/src/gui/wizard/NewDatabaseWizard.cpp
@@ -39,7 +39,7 @@ NewDatabaseWizard::NewDatabaseWizard(QWidget* parent)
<< new NewDatabaseWizardPageEncryption()
<< new NewDatabaseWizardPageMasterKey();
- for (auto const& page: asConst(m_pages)) {
+ for (const auto& page: asConst(m_pages)) {
addPage(page);
}
@@ -54,23 +54,34 @@ NewDatabaseWizard::~NewDatabaseWizard()
bool NewDatabaseWizard::validateCurrentPage()
{
- return m_pages[currentId()]->validatePage();
+ bool ok = m_pages[currentId()]->validatePage();
+ if (ok && currentId() == m_pages.size() - 1) {
+ m_db->setInitialized(true);
+ }
+ return ok;
}
-Database* NewDatabaseWizard::takeDatabase()
+/**
+ * Take configured database and reset internal pointer.
+ *
+ * @return the configured database
+ */
+QSharedPointer<Database> NewDatabaseWizard::takeDatabase()
{
- return m_db.take();
+ auto tmpPointer = m_db;
+ m_db.reset();
+ return tmpPointer;
}
void NewDatabaseWizard::initializePage(int id)
{
if (id == startId()) {
- m_db.reset(new Database());
+ m_db = QSharedPointer<Database>::create();
m_db->rootGroup()->setName(tr("Root", "Root group"));
m_db->setKdf({});
m_db->setKey({});
}
- m_pages[id]->setDatabase(m_db.data());
+ m_pages[id]->setDatabase(m_db);
m_pages[id]->initializePage();
}
diff --git a/src/gui/wizard/NewDatabaseWizard.h b/src/gui/wizard/NewDatabaseWizard.h
index 5c3b49c01..802dc061e 100644
--- a/src/gui/wizard/NewDatabaseWizard.h
+++ b/src/gui/wizard/NewDatabaseWizard.h
@@ -19,7 +19,7 @@
#define KEEPASSXC_NEWDATABASEWIZARD_H
#include <QPointer>
-#include <QScopedPointer>
+#include <QSharedPointer>
#include <QWizard>
class Database;
@@ -36,14 +36,14 @@ public:
explicit NewDatabaseWizard(QWidget* parent = nullptr);
~NewDatabaseWizard() override;
- Database* takeDatabase();
+ QSharedPointer<Database> takeDatabase();
bool validateCurrentPage() override;
protected:
void initializePage(int id) override;
private:
- QScopedPointer<Database> m_db;
+ QSharedPointer<Database> m_db;
QList<QPointer<NewDatabaseWizardPage>> m_pages;
};
diff --git a/src/gui/wizard/NewDatabaseWizardPage.cpp b/src/gui/wizard/NewDatabaseWizardPage.cpp
index e38cb8641..b49b7b384 100644
--- a/src/gui/wizard/NewDatabaseWizardPage.cpp
+++ b/src/gui/wizard/NewDatabaseWizardPage.cpp
@@ -1,3 +1,5 @@
+#include <utility>
+
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
@@ -66,9 +68,9 @@ DatabaseSettingsWidget* NewDatabaseWizardPage::pageWidget()
*
* @param db database object to be configured
*/
-void NewDatabaseWizardPage::setDatabase(Database* db)
+void NewDatabaseWizardPage::setDatabase(QSharedPointer<Database> db)
{
- m_db = db;
+ m_db = std::move(db);
}
void NewDatabaseWizardPage::initializePage()
diff --git a/src/gui/wizard/NewDatabaseWizardPage.h b/src/gui/wizard/NewDatabaseWizardPage.h
index 39b47940e..785527e23 100644
--- a/src/gui/wizard/NewDatabaseWizardPage.h
+++ b/src/gui/wizard/NewDatabaseWizardPage.h
@@ -43,7 +43,7 @@ public:
void setPageWidget(DatabaseSettingsWidget* page);
DatabaseSettingsWidget* pageWidget();
- void setDatabase(Database* db);
+ void setDatabase(QSharedPointer<Database> db);
void initializePage() override;
bool validatePage() override;
@@ -53,7 +53,7 @@ public slots:
protected:
QPointer<DatabaseSettingsWidget> m_pageWidget;
- QPointer<Database> m_db;
+ QSharedPointer<Database> m_db;
const QScopedPointer<Ui::NewDatabaseWizardPage> m_ui;
};
diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp
index b6905fc38..38ea818e5 100644
--- a/src/keys/drivers/YubiKey.cpp
+++ b/src/keys/drivers/YubiKey.cpp
@@ -18,8 +18,6 @@
#include <stdio.h>
-#include <QDebug>
-
#include <ykcore.h>
#include <ykdef.h>
#include <ykstatus.h>
@@ -215,9 +213,9 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte
*/
if (yk_errno == YK_EUSBERR) {
- qWarning() << "USB error:" << yk_usb_strerror();
+ qWarning("USB error: %s", yk_usb_strerror());
} else {
- qWarning() << "YubiKey core error:" << yk_strerror(yk_errno);
+ qWarning("YubiKey core error: %s", yk_strerror(yk_errno));
}
return ERROR;
diff --git a/src/sshagent/SSHAgent.cpp b/src/sshagent/SSHAgent.cpp
index 487398238..05299091a 100644
--- a/src/sshagent/SSHAgent.cpp
+++ b/src/sshagent/SSHAgent.cpp
@@ -251,7 +251,7 @@ void SSHAgent::databaseModeChanged(DatabaseWidget::Mode mode)
const QUuid& uuid = widget->database()->uuid();
- if (mode == DatabaseWidget::LockedMode && m_keys.contains(uuid)) {
+ if (mode == DatabaseWidget::Mode::LockedMode && m_keys.contains(uuid)) {
QSet<OpenSSHKey> keys = m_keys.take(uuid);
for (OpenSSHKey key : keys) {
@@ -259,7 +259,7 @@ void SSHAgent::databaseModeChanged(DatabaseWidget::Mode mode)
emit error(m_error);
}
}
- } else if (mode == DatabaseWidget::ViewMode && !m_keys.contains(uuid)) {
+ } else if (mode == DatabaseWidget::Mode::ViewMode && !m_keys.contains(uuid)) {
for (Entry* e : widget->database()->rootGroup()->entriesRecursive()) {
if (widget->database()->metadata()->recycleBinEnabled()
diff --git a/src/sshagent/SSHAgent.h b/src/sshagent/SSHAgent.h
index acef6d62e..7cd8a1f1f 100644
--- a/src/sshagent/SSHAgent.h
+++ b/src/sshagent/SSHAgent.h
@@ -43,7 +43,7 @@ signals:
void error(const QString& message);
public slots:
- void databaseModeChanged(DatabaseWidget::Mode mode = DatabaseWidget::LockedMode);
+ void databaseModeChanged(DatabaseWidget::Mode mode = DatabaseWidget::Mode::LockedMode);
private:
const quint8 SSH_AGENT_FAILURE = 5;
diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp
index f59625d89..0f15032a1 100644
--- a/tests/TestAutoType.cpp
+++ b/tests/TestAutoType.cpp
@@ -57,7 +57,7 @@ void TestAutoType::init()
config()->set("AutoTypeEntryTitleMatch", false);
m_test->clearActions();
- m_db = new Database();
+ m_db = QSharedPointer<Database>::create();
m_dbList.clear();
m_dbList.append(m_db);
m_group = new Group();
@@ -126,7 +126,6 @@ void TestAutoType::init()
void TestAutoType::cleanup()
{
- delete m_db;
}
void TestAutoType::testInternal()
diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h
index 93a7d682c..03086a68e 100644
--- a/tests/TestAutoType.h
+++ b/tests/TestAutoType.h
@@ -20,6 +20,7 @@
#define KEEPASSX_TESTAUTOTYPE_H
#include <QObject>
+#include <QSharedPointer>
class AutoType;
class AutoTypePlatformInterface;
@@ -53,8 +54,8 @@ private:
AutoTypePlatformInterface* m_platform;
AutoTypeTestInterface* m_test;
AutoType* m_autoType;
- Database* m_db;
- QList<Database*> m_dbList;
+ QSharedPointer<Database> m_db;
+ QList<QSharedPointer<Database>> m_dbList;
Group* m_group;
Entry* m_entry1;
Entry* m_entry2;
diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp
index 36ca479d5..3c5afc347 100644
--- a/tests/TestCli.cpp
+++ b/tests/TestCli.cpp
@@ -671,7 +671,8 @@ void TestCli::testMerge()
QFile readBack(targetFile1.fileName());
readBack.open(QIODevice::ReadOnly);
- QScopedPointer<Database> mergedDb(reader.readDatabase(&readBack, oldKey));
+ auto mergedDb = QSharedPointer<Database>::create();
+ reader.readDatabase(&readBack, oldKey, mergedDb.data());
readBack.close();
QVERIFY(mergedDb);
auto* entry1 = mergedDb->rootGroup()->findEntryByPath("/Internet/Some Website");
@@ -691,7 +692,8 @@ void TestCli::testMerge()
readBack.setFileName(targetFile2.fileName());
readBack.open(QIODevice::ReadOnly);
- mergedDb.reset(reader.readDatabase(&readBack, key));
+ mergedDb = QSharedPointer<Database>::create();
+ reader.readDatabase(&readBack, key, mergedDb.data());
readBack.close();
QVERIFY(mergedDb);
entry1 = mergedDb->rootGroup()->findEntryByPath("/Internet/Some Website");
@@ -740,7 +742,8 @@ void TestCli::testRemove()
key->addKey(QSharedPointer<PasswordKey>::create("a"));
QFile readBack(m_dbFile->fileName());
readBack.open(QIODevice::ReadOnly);
- QScopedPointer<Database> readBackDb(reader.readDatabase(&readBack, key));
+ auto readBackDb = QSharedPointer<Database>::create();
+ reader.readDatabase(&readBack, key, readBackDb.data());
readBack.close();
QVERIFY(readBackDb);
QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry"));
@@ -757,7 +760,8 @@ void TestCli::testRemove()
readBack.setFileName(fileCopy.fileName());
readBack.open(QIODevice::ReadOnly);
- readBackDb.reset(reader.readDatabase(&readBack, key));
+ readBackDb = QSharedPointer<Database>::create();
+ reader.readDatabase(&readBack, key, readBackDb.data());
readBack.close();
QVERIFY(readBackDb);
QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry"));
diff --git a/tests/TestCsvExporter.cpp b/tests/TestCsvExporter.cpp
index 732e2636d..b71cf9ca7 100644
--- a/tests/TestCsvExporter.cpp
+++ b/tests/TestCsvExporter.cpp
@@ -31,8 +31,8 @@ const QString TestCsvExporter::ExpectedHeaderLine =
void TestCsvExporter::init()
{
- m_db = new Database();
- m_csvExporter = new CsvExporter();
+ m_db = QSharedPointer<Database>::create();
+ m_csvExporter = QSharedPointer<CsvExporter>::create();
}
void TestCsvExporter::initTestCase()
@@ -42,17 +42,15 @@ void TestCsvExporter::initTestCase()
void TestCsvExporter::cleanup()
{
- delete m_db;
- delete m_csvExporter;
}
void TestCsvExporter::testExport()
{
Group* groupRoot = m_db->rootGroup();
- Group* group = new Group();
+ auto* group = new Group();
group->setName("Test Group Name");
group->setParent(groupRoot);
- Entry* entry = new Entry();
+ auto* entry = new Entry();
entry->setGroup(group);
entry->setTitle("Test Entry Title");
entry->setUsername("Test Username");
@@ -65,7 +63,7 @@ void TestCsvExporter::testExport()
m_csvExporter->exportDatabase(&buffer, m_db);
QString expectedResult =
- QString().append(ExpectedHeaderLine).append("\"Test Group Name\",\"Test Entry Title\",\"Test Username\",\"Test "
+ QString().append(ExpectedHeaderLine).append("\"Root/Test Group Name\",\"Test Entry Title\",\"Test Username\",\"Test "
"Password\",\"http://test.url\",\"Test Notes\"\n");
QCOMPARE(QString::fromUtf8(buffer.buffer().constData()), expectedResult);
@@ -83,13 +81,13 @@ void TestCsvExporter::testEmptyDatabase()
void TestCsvExporter::testNestedGroups()
{
Group* groupRoot = m_db->rootGroup();
- Group* group = new Group();
+ auto* group = new Group();
group->setName("Test Group Name");
group->setParent(groupRoot);
- Group* childGroup = new Group();
+ auto* childGroup = new Group();
childGroup->setName("Test Sub Group Name");
childGroup->setParent(group);
- Entry* entry = new Entry();
+ auto* entry = new Entry();
entry->setGroup(childGroup);
entry->setTitle("Test Entry Title");
@@ -100,5 +98,5 @@ void TestCsvExporter::testNestedGroups()
QCOMPARE(QString::fromUtf8(buffer.buffer().constData()),
QString()
.append(ExpectedHeaderLine)
- .append("\"Test Group Name/Test Sub Group Name\",\"Test Entry Title\",\"\",\"\",\"\",\"\"\n"));
+ .append("\"Root/Test Group Name/Test Sub Group Name\",\"Test Entry Title\",\"\",\"\",\"\",\"\"\n"));
}
diff --git a/tests/TestCsvExporter.h b/tests/TestCsvExporter.h
index 39597f752..378ac6c0d 100644
--- a/tests/TestCsvExporter.h
+++ b/tests/TestCsvExporter.h
@@ -20,6 +20,7 @@
#define KEEPASSX_TESTCSVEXPORTER_H
#include <QObject>
+#include <QSharedPointer>
class Database;
class CsvExporter;
@@ -40,8 +41,8 @@ private slots:
void testNestedGroups();
private:
- Database* m_db;
- CsvExporter* m_csvExporter;
+ QSharedPointer<Database> m_db;
+ QSharedPointer<CsvExporter> m_csvExporter;
};
#endif // KEEPASSX_TESTCSVEXPORTER_H
diff --git a/tests/TestDatabase.cpp b/tests/TestDatabase.cpp
index 78e1b10a4..94e3c8ba7 100644
--- a/tests/TestDatabase.cpp
+++ b/tests/TestDatabase.cpp
@@ -40,14 +40,18 @@ void TestDatabase::testEmptyRecycleBinOnDisabled()
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinDisabled.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("123"));
- QScopedPointer<Database> db(Database::openDatabaseFile(filename, key));
- QVERIFY(db);
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(filename, key, nullptr, false));
- QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
+ // Explicitly mark DB as read-write in case it was opened from a read-only drive.
+ // Prevents assertion failures on CI systems when the data dir is not writable
+ db->setReadOnly(false);
+
+ QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
db->emptyRecycleBin();
// The database must be unmodified in this test after emptying the recycle bin.
- QCOMPARE(spyModified.count(), 0);
+ QTRY_COMPARE(spyModified.count(), 0);
}
void TestDatabase::testEmptyRecycleBinOnNotCreated()
@@ -55,14 +59,15 @@ void TestDatabase::testEmptyRecycleBinOnNotCreated()
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinNotYetCreated.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("123"));
- QScopedPointer<Database> db(Database::openDatabaseFile(filename, key));
- QVERIFY(db);
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(filename, key, nullptr, false));
+ db->setReadOnly(false);
- QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
db->emptyRecycleBin();
// The database must be unmodified in this test after emptying the recycle bin.
- QCOMPARE(spyModified.count(), 0);
+ QTRY_COMPARE(spyModified.count(), 0);
}
void TestDatabase::testEmptyRecycleBinOnEmpty()
@@ -70,14 +75,15 @@ void TestDatabase::testEmptyRecycleBinOnEmpty()
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinEmpty.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("123"));
- QScopedPointer<Database> db(Database::openDatabaseFile(filename, key));
- QVERIFY(db);
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(filename, key, nullptr, false));
+ db->setReadOnly(false);
- QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
db->emptyRecycleBin();
// The database must be unmodified in this test after emptying the recycle bin.
- QCOMPARE(spyModified.count(), 0);
+ QTRY_COMPARE(spyModified.count(), 0);
}
void TestDatabase::testEmptyRecycleBinWithHierarchicalData()
@@ -85,8 +91,9 @@ void TestDatabase::testEmptyRecycleBinWithHierarchicalData()
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinWithData.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("123"));
- QScopedPointer<Database> db(Database::openDatabaseFile(filename, key));
- QVERIFY(db);
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(filename, key, nullptr, false));
+ db->setReadOnly(false);
QFile originalFile(filename);
qint64 initialSize = originalFile.size();
@@ -97,6 +104,8 @@ void TestDatabase::testEmptyRecycleBinWithHierarchicalData()
QVERIFY(db->metadata()->recycleBin()->children().empty());
QTemporaryFile afterCleanup;
+ afterCleanup.open();
+
KeePass2Writer writer;
writer.writeDatabase(&afterCleanup, db.data());
QVERIFY(afterCleanup.size() < initialSize);
diff --git a/tests/TestDeletedObjects.cpp b/tests/TestDeletedObjects.cpp
index 00b9cd52c..150249879 100644
--- a/tests/TestDeletedObjects.cpp
+++ b/tests/TestDeletedObjects.cpp
@@ -30,7 +30,7 @@ void TestDeletedObjects::initTestCase()
QVERIFY(Crypto::init());
}
-void TestDeletedObjects::createAndDelete(Database* db, int delObjectsSize)
+void TestDeletedObjects::createAndDelete(QSharedPointer<Database> db, int delObjectsSize)
{
QCOMPARE(db->deletedObjects().size(), delObjectsSize);
Group* root = db->rootGroup();
@@ -89,32 +89,27 @@ void TestDeletedObjects::testDeletedObjectsFromFile()
KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
reader.setStrictMode(true);
QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml");
- Database* db = reader.readDatabase(xmlFile);
+ auto db = reader.readDatabase(xmlFile);
createAndDelete(db, 2);
-
- delete db;
}
void TestDeletedObjects::testDeletedObjectsFromNewDb()
{
- Database* db = new Database();
-
+ auto db = QSharedPointer<Database>::create();
createAndDelete(db, 0);
-
- delete db;
}
void TestDeletedObjects::testDatabaseChange()
{
- Database* db = new Database();
+ auto db = QSharedPointer<Database>::create();
Group* root = db->rootGroup();
int delObjectsSize = 0;
- Database* db2 = new Database();
+ auto db2 = QSharedPointer<Database>::create();
Group* root2 = db2->rootGroup();
int delObjectsSize2 = 0;
- Entry* e = new Entry();
+ auto* e = new Entry();
e->setGroup(root);
QCOMPARE(db->deletedObjects().size(), delObjectsSize);
@@ -130,11 +125,11 @@ void TestDeletedObjects::testDatabaseChange()
QCOMPARE(db->deletedObjects().size(), delObjectsSize);
QCOMPARE(db2->deletedObjects().size(), ++delObjectsSize2);
- Group* g1 = new Group();
+ auto* g1 = new Group();
g1->setParent(root);
QUuid g1Uuid = QUuid::createUuid();
g1->setUuid(g1Uuid);
- Entry* e1 = new Entry();
+ auto* e1 = new Entry();
e1->setGroup(g1);
QUuid e1Uuid = QUuid::createUuid();
e1->setUuid(e1Uuid);
@@ -146,8 +141,8 @@ void TestDeletedObjects::testDatabaseChange()
QCOMPARE(db->deletedObjects().at(delObjectsSize - 2).uuid, e1Uuid);
QCOMPARE(db->deletedObjects().at(delObjectsSize - 1).uuid, g1Uuid);
- Group* group = new Group();
- Entry* entry = new Entry();
+ auto* group = new Group();
+ auto* entry = new Entry();
entry->setGroup(group);
entry->setGroup(root);
@@ -155,6 +150,4 @@ void TestDeletedObjects::testDatabaseChange()
QCOMPARE(db2->deletedObjects().size(), delObjectsSize2);
delete group;
- delete db;
- delete db2;
}
diff --git a/tests/TestDeletedObjects.h b/tests/TestDeletedObjects.h
index d96452093..61d864a67 100644
--- a/tests/TestDeletedObjects.h
+++ b/tests/TestDeletedObjects.h
@@ -27,7 +27,7 @@ class TestDeletedObjects : public QObject
Q_OBJECT
private:
- void createAndDelete(Database* db, int delObjectsSize);
+ void createAndDelete(QSharedPointer<Database> db, int delObjectsSize);
private slots:
void initTestCase();
diff --git a/tests/TestKdbx2.cpp b/tests/TestKdbx2.cpp
index ef944f7fd..2ddcbf11f 100644
--- a/tests/TestKdbx2.cpp
+++ b/tests/TestKdbx2.cpp
@@ -37,7 +37,7 @@ void TestKdbx2::initTestCase()
/**
* Helper method for verifying contents of the sample KDBX 2 file.
*/
-void TestKdbx2::verifyKdbx2Db(Database* db)
+void TestKdbx2::verifyKdbx2Db(QSharedPointer<Database> db)
{
QVERIFY(db);
QCOMPARE(db->rootGroup()->name(), QString("Format200"));
@@ -67,12 +67,13 @@ void TestKdbx2::testFormat200()
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("a"));
+ auto db = QSharedPointer<Database>::create();
KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
+ QVERIFY(reader.readDatabase(filename, key, db.data()));
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_2 & KeePass2::FILE_VERSION_CRITICAL_MASK);
QVERIFY2(!reader.hasError(), reader.errorString().toStdString().c_str());
- verifyKdbx2Db(db.data());
+ verifyKdbx2Db(db);
}
void TestKdbx2::testFormat200Upgrade()
@@ -80,8 +81,9 @@ void TestKdbx2::testFormat200Upgrade()
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("a"));
+ auto db = QSharedPointer<Database>::create();
KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
+ reader.readDatabase(filename, key, db.data());
QVERIFY2(!reader.hasError(), reader.errorString().toStdString().c_str());
QVERIFY(!db.isNull());
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_2 & KeePass2::FILE_VERSION_CRITICAL_MASK);
@@ -92,20 +94,21 @@ void TestKdbx2::testFormat200Upgrade()
// write KDBX 3 to upgrade it
KeePass2Writer writer;
- writer.writeDatabase(&buffer, db.data());
+ QVERIFY(writer.writeDatabase(&buffer, db.data()));
if (writer.hasError()) {
QFAIL(qPrintable(QString("Error while writing database: %1").arg(writer.errorString())));
}
// read buffer back
buffer.seek(0);
- QScopedPointer<Database> targetDb(reader.readDatabase(&buffer, key));
+ auto targetDb = QSharedPointer<Database>::create();
+ QVERIFY(reader.readDatabase(&buffer, key, targetDb.data()));
if (reader.hasError()) {
QFAIL(qPrintable(QString("Error while reading database: %1").arg(reader.errorString())));
}
// database should now be upgraded to KDBX 3 without data loss
- verifyKdbx2Db(targetDb.data());
+ verifyKdbx2Db(targetDb);
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK);
QCOMPARE(targetDb->kdf()->uuid(), KeePass2::KDF_AES_KDBX3);
}
diff --git a/tests/TestKdbx2.h b/tests/TestKdbx2.h
index 9eb0e415e..7386270be 100644
--- a/tests/TestKdbx2.h
+++ b/tests/TestKdbx2.h
@@ -32,7 +32,7 @@ private slots:
void testFormat200Upgrade();
private:
- void verifyKdbx2Db(Database* db);
+ void verifyKdbx2Db(QSharedPointer<Database> db);
};
#endif // KEEPASSXC_TEST_KDBX2_H
diff --git a/tests/TestKdbx3.cpp b/tests/TestKdbx3.cpp
index 5fdfe0241..bf9a1ec8b 100644
--- a/tests/TestKdbx3.cpp
+++ b/tests/TestKdbx3.cpp
@@ -33,7 +33,7 @@ void TestKdbx3::initTestCaseImpl()
{
}
-Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
+QSharedPointer<Database> TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
{
KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
reader.setStrictMode(strictMode);
@@ -43,7 +43,7 @@ Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasErro
return db;
}
-Database* TestKdbx3::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
+QSharedPointer<Database> TestKdbx3::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
{
KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
reader.setStrictMode(strictMode);
@@ -63,12 +63,12 @@ void TestKdbx3::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& er
void TestKdbx3::readKdbx(QIODevice* device,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
{
KeePass2Reader reader;
- db.reset(reader.readDatabase(device, key));
+ reader.readDatabase(device, key, db.data());
hasError = reader.hasError();
if (hasError) {
errorString = reader.errorString();
@@ -78,12 +78,12 @@ void TestKdbx3::readKdbx(QIODevice* device,
void TestKdbx3::readKdbx(const QString& path,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
{
KeePass2Reader reader;
- db.reset(reader.readDatabase(path, key));
+ reader.readDatabase(path, key, db.data());
hasError = reader.hasError();
if (hasError) {
errorString = reader.errorString();
@@ -108,7 +108,8 @@ void TestKdbx3::testFormat300()
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("a"));
KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(reader.readDatabase(filename, key, db.data()));
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3);
QVERIFY(db.data());
QVERIFY(!reader.hasError());
@@ -123,11 +124,12 @@ void TestKdbx3::testNonAscii()
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6")));
KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(filename, key, nullptr, false));
QVERIFY(db.data());
QVERIFY(!reader.hasError());
QCOMPARE(db->metadata()->name(), QString("NonAsciiTest"));
- QCOMPARE(db->compressionAlgo(), Database::CompressionNone);
+ QCOMPARE(db->compressionAlgorithm(), Database::CompressionNone);
}
void TestKdbx3::testCompressed()
@@ -136,11 +138,12 @@ void TestKdbx3::testCompressed()
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create(""));
KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(filename, key, nullptr, false));
QVERIFY(db.data());
QVERIFY(!reader.hasError());
QCOMPARE(db->metadata()->name(), QString("Compressed"));
- QCOMPARE(db->compressionAlgo(), Database::CompressionGZip);
+ QCOMPARE(db->compressionAlgorithm(), Database::CompressionGZip);
}
void TestKdbx3::testProtectedStrings()
@@ -149,7 +152,8 @@ void TestKdbx3::testProtectedStrings()
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("masterpw"));
KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(filename, key, nullptr, false));
QVERIFY(db.data());
QVERIFY(!reader.hasError());
QCOMPARE(db->metadata()->name(), QString("Protected Strings Test"));
@@ -175,8 +179,6 @@ void TestKdbx3::testBrokenHeaderHash()
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/BrokenHeaderHash.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create(""));
- KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
- QVERIFY(!db.data());
- QVERIFY(reader.hasError());
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(!db->open(filename, key, nullptr, false));
}
diff --git a/tests/TestKdbx3.h b/tests/TestKdbx3.h
index aadc50aa8..2442a25b6 100644
--- a/tests/TestKdbx3.h
+++ b/tests/TestKdbx3.h
@@ -34,18 +34,18 @@ private slots:
protected:
void initTestCaseImpl() override;
- Database* readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
- Database* readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) override;
+ QSharedPointer<Database> readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
+ QSharedPointer<Database> readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) override;
void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
void readKdbx(QIODevice* device,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString) override;
void readKdbx(const QString& path,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString) override;
void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) override;
diff --git a/tests/TestKdbx4.cpp b/tests/TestKdbx4.cpp
index 297cde284..a7fd52e0e 100644
--- a/tests/TestKdbx4.cpp
+++ b/tests/TestKdbx4.cpp
@@ -37,7 +37,7 @@ void TestKdbx4::initTestCaseImpl()
m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
}
-Database* TestKdbx4::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
+QSharedPointer<Database> TestKdbx4::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
{
KdbxXmlReader reader(KeePass2::FILE_VERSION_4);
reader.setStrictMode(strictMode);
@@ -47,7 +47,7 @@ Database* TestKdbx4::readXml(const QString& path, bool strictMode, bool& hasErro
return db;
}
-Database* TestKdbx4::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
+QSharedPointer<Database> TestKdbx4::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
{
KdbxXmlReader reader(KeePass2::FILE_VERSION_4);
reader.setStrictMode(strictMode);
@@ -67,12 +67,12 @@ void TestKdbx4::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& er
void TestKdbx4::readKdbx(QIODevice* device,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
{
KeePass2Reader reader;
- db.reset(reader.readDatabase(device, key));
+ reader.readDatabase(device, key, db.data());
hasError = reader.hasError();
if (hasError) {
errorString = reader.errorString();
@@ -82,12 +82,12 @@ void TestKdbx4::readKdbx(QIODevice* device,
void TestKdbx4::readKdbx(const QString& path,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
{
KeePass2Reader reader;
- db.reset(reader.readDatabase(path, key));
+ reader.readDatabase(path, key, db.data());
hasError = reader.hasError();
if (hasError) {
errorString = reader.errorString();
@@ -116,7 +116,8 @@ void TestKdbx4::testFormat400()
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("t"));
KeePass2Reader reader;
- QScopedPointer<Database> db(reader.readDatabase(filename, key));
+ auto db = QSharedPointer<Database>::create();
+ reader.readDatabase(filename, key, db.data());
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_4);
QVERIFY(db.data());
QVERIFY(!reader.hasError());
@@ -174,7 +175,8 @@ void TestKdbx4::testFormat400Upgrade()
// read buffer back
buffer.seek(0);
KeePass2Reader reader;
- QScopedPointer<Database> targetDb(reader.readDatabase(&buffer, key));
+ auto targetDb = QSharedPointer<Database>::create();
+ reader.readDatabase(&buffer, key, targetDb.data());
if (reader.hasError()) {
QFAIL(qPrintable(QString("Error while reading database: %1").arg(reader.errorString())));
}
@@ -292,14 +294,15 @@ void TestKdbx4::testUpgradeMasterKeyIntegrity()
// paranoid check that we cannot decrypt the database without a key
buffer.seek(0);
KeePass2Reader reader;
- QScopedPointer<Database> db2;
- db2.reset(reader.readDatabase(&buffer, QSharedPointer<CompositeKey>::create()));
+ auto db2 = QSharedPointer<Database>::create();
+ reader.readDatabase(&buffer, QSharedPointer<CompositeKey>::create(), db2.data());
QVERIFY(reader.hasError());
// check that we can read back the database with the original composite key,
// i.e., no components have been lost on the way
buffer.seek(0);
- db2.reset(reader.readDatabase(&buffer, compositeKey));
+ db2 = QSharedPointer<Database>::create();
+ reader.readDatabase(&buffer, compositeKey, db2.data());
if (reader.hasError()) {
QFAIL(qPrintable(reader.errorString()));
}
@@ -396,7 +399,8 @@ void TestKdbx4::testCustomData()
// read buffer back
buffer.seek(0);
KeePass2Reader reader;
- QSharedPointer<Database> newDb(reader.readDatabase(&buffer, QSharedPointer<CompositeKey>::create()));
+ auto newDb = QSharedPointer<Database>::create();
+ reader.readDatabase(&buffer, QSharedPointer<CompositeKey>::create(), newDb.data());
// test all custom data are read back successfully from KDBX
QCOMPARE(newDb->publicCustomData(), publicCustomData);
diff --git a/tests/TestKdbx4.h b/tests/TestKdbx4.h
index edf319a96..e5fd85ae3 100644
--- a/tests/TestKdbx4.h
+++ b/tests/TestKdbx4.h
@@ -35,18 +35,18 @@ private slots:
protected:
void initTestCaseImpl() override;
- Database* readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
- Database* readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) override;
+ QSharedPointer<Database> readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
+ QSharedPointer<Database> readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) override;
void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
void readKdbx(const QString& path,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString) override;
void readKdbx(QIODevice* device,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString) override;
void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) override;
diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp
index a3c148324..bb9e07a42 100644
--- a/tests/TestKeePass1Reader.cpp
+++ b/tests/TestKeePass1Reader.cpp
@@ -177,15 +177,13 @@ void TestKeePass1Reader::testFileKey()
QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
QString keyFilename = QString("%1/%2.key").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
- Database* db = reader.readDatabase(dbFilename, QString(), keyFilename);
+ auto db = reader.readDatabase(dbFilename, QString(), keyFilename);
QVERIFY(db);
QVERIFY(!reader.hasError());
QCOMPARE(db->rootGroup()->children().size(), 1);
QCOMPARE(db->rootGroup()->children().at(0)->name(), name);
reopenDatabase(db, QString(), keyFilename);
-
- delete db;
}
void TestKeePass1Reader::testFileKey_data()
@@ -205,15 +203,13 @@ void TestKeePass1Reader::testCompositeKey()
QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
QString keyFilename = QString("%1/FileKeyHex.key").arg(QString(KEEPASSX_TEST_DATA_DIR));
- Database* db = reader.readDatabase(dbFilename, "mypassword", keyFilename);
+ auto db = reader.readDatabase(dbFilename, "mypassword", keyFilename);
QVERIFY(db);
QVERIFY(!reader.hasError());
QCOMPARE(db->rootGroup()->children().size(), 1);
QCOMPARE(db->rootGroup()->children().at(0)->name(), name);
reopenDatabase(db, "mypassword", keyFilename);
-
- delete db;
}
void TestKeePass1Reader::testTwofish()
@@ -224,13 +220,11 @@ void TestKeePass1Reader::testTwofish()
QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
- Database* db = reader.readDatabase(dbFilename, "masterpw", 0);
+ auto db = reader.readDatabase(dbFilename, "masterpw", 0);
QVERIFY(db);
QVERIFY(!reader.hasError());
QCOMPARE(db->rootGroup()->children().size(), 1);
QCOMPARE(db->rootGroup()->children().at(0)->name(), name);
-
- delete db;
}
void TestKeePass1Reader::testCP1252Password()
@@ -242,18 +236,15 @@ void TestKeePass1Reader::testCP1252Password()
QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
QString password = QString::fromUtf8("\xe2\x80\x9e\x70\x61\x73\x73\x77\x6f\x72\x64\xe2\x80\x9d");
- Database* db = reader.readDatabase(dbFilename, password, 0);
+ auto db = reader.readDatabase(dbFilename, password, 0);
QVERIFY(db);
QVERIFY(!reader.hasError());
QCOMPARE(db->rootGroup()->children().size(), 1);
QCOMPARE(db->rootGroup()->children().at(0)->name(), name);
-
- delete db;
}
void TestKeePass1Reader::cleanupTestCase()
{
- delete m_db;
}
QDateTime TestKeePass1Reader::genDT(int year, int month, int day, int hour, int min)
@@ -263,13 +254,15 @@ QDateTime TestKeePass1Reader::genDT(int year, int month, int day, int hour, int
return QDateTime(date, time, Qt::UTC);
}
-void TestKeePass1Reader::reopenDatabase(Database* db, const QString& password, const QString& keyfileName)
+void TestKeePass1Reader::reopenDatabase(QSharedPointer<Database> db,
+ const QString& password,
+ const QString& keyfileName)
{
QBuffer buffer;
buffer.open(QIODevice::ReadWrite);
KeePass2Writer writer;
- writer.writeDatabase(&buffer, db);
+ writer.writeDatabase(&buffer, db.data());
QVERIFY(!writer.hasError());
QVERIFY(buffer.seek(0));
@@ -284,7 +277,7 @@ void TestKeePass1Reader::reopenDatabase(Database* db, const QString& password, c
}
KeePass2Reader reader;
- QScopedPointer<Database> newDb(reader.readDatabase(&buffer, key));
- QVERIFY(newDb);
+ auto newDb = QSharedPointer<Database>::create();
+ QVERIFY(reader.readDatabase(&buffer, key, newDb.data()));
QVERIFY(!reader.hasError());
}
diff --git a/tests/TestKeePass1Reader.h b/tests/TestKeePass1Reader.h
index 9a5ab9e49..b847d910c 100644
--- a/tests/TestKeePass1Reader.h
+++ b/tests/TestKeePass1Reader.h
@@ -20,6 +20,7 @@
#include <QDateTime>
#include <QObject>
+#include <QSharedPointer>
class Database;
@@ -43,9 +44,9 @@ private slots:
private:
static QDateTime genDT(int year, int month, int day, int hour, int min);
- static void reopenDatabase(Database* db, const QString& password, const QString& keyfileName);
+ static void reopenDatabase(QSharedPointer<Database> db, const QString& password, const QString& keyfileName);
- Database* m_db;
+ QSharedPointer<Database> m_db;
};
#endif // KEEPASSX_TESTKEEPASS1READER_H
diff --git a/tests/TestKeePass2Format.cpp b/tests/TestKeePass2Format.cpp
index c2c3f75d3..35bbfed3e 100644
--- a/tests/TestKeePass2Format.cpp
+++ b/tests/TestKeePass2Format.cpp
@@ -34,7 +34,7 @@ void TestKeePass2Format::initTestCase()
// read raw XML database
bool hasError;
QString errorString;
- m_xmlDb.reset(readXml(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"), true, hasError, errorString));
+ m_xmlDb = readXml(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"), true, hasError, errorString);
if (hasError) {
QFAIL(qPrintable(QString("Error while reading XML: ").append(errorString)));
}
@@ -44,7 +44,7 @@ void TestKeePass2Format::initTestCase()
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("test"));
- m_kdbxSourceDb.reset(new Database());
+ m_kdbxSourceDb = QSharedPointer<Database>::create();
m_kdbxSourceDb->setKey(key);
m_kdbxSourceDb->metadata()->setName("TESTDB");
Group* group = m_kdbxSourceDb->rootGroup();
@@ -351,7 +351,7 @@ void TestKeePass2Format::testXmlBroken()
QVERIFY(QFile::exists(xmlFile));
bool hasError;
QString errorString;
- QScopedPointer<Database> db(readXml(xmlFile, strictMode, hasError, errorString));
+ auto db = readXml(xmlFile, strictMode, hasError, errorString);
if (hasError) {
qWarning("Reader error: %s", qPrintable(errorString));
}
@@ -392,7 +392,7 @@ void TestKeePass2Format::testXmlEmptyUuids()
QVERIFY(QFile::exists(xmlFile));
bool hasError;
QString errorString;
- QScopedPointer<Database> dbp(readXml(xmlFile, true, hasError, errorString));
+ auto db = readXml(xmlFile, true, hasError, errorString);
if (hasError) {
qWarning("Reader error: %s", qPrintable(errorString));
}
@@ -446,7 +446,7 @@ void TestKeePass2Format::testXmlInvalidXmlChars()
QVERIFY(!hasError);
buffer.seek(0);
- QScopedPointer<Database> dbRead(readXml(&buffer, true, hasError, errorString));
+ auto dbRead = readXml(&buffer, true, hasError, errorString);
if (hasError) {
qWarning("Database read error: %s", qPrintable(errorString));
}
@@ -474,7 +474,7 @@ void TestKeePass2Format::testXmlRepairUuidHistoryItem()
QVERIFY(QFile::exists(xmlFile));
bool hasError;
QString errorString;
- QScopedPointer<Database> db(readXml(xmlFile, false, hasError, errorString));
+ auto db = readXml(xmlFile, false, hasError, errorString);
if (hasError) {
qWarning("Database read error: %s", qPrintable(errorString));
}
@@ -503,6 +503,7 @@ void TestKeePass2Format::testReadBackTargetDb()
QString errorString;
m_kdbxTargetBuffer.seek(0);
+ m_kdbxTargetDb = QSharedPointer<Database>::create();
readKdbx(&m_kdbxTargetBuffer, key, m_kdbxTargetDb, hasError, errorString);
if (hasError) {
QFAIL(qPrintable(QString("Error while reading database: ").append(errorString)));
@@ -548,7 +549,7 @@ void TestKeePass2Format::testKdbxDeviceFailure()
QScopedPointer<Database> db(new Database());
db->setKey(key);
// Disable compression so we write a predictable number of bytes.
- db->setCompressionAlgo(Database::CompressionNone);
+ db->setCompressionAlgorithm(Database::CompressionNone);
auto entry = new Entry();
entry->setParent(db->rootGroup());
@@ -569,7 +570,7 @@ void TestKeePass2Format::testKdbxDeviceFailure()
*/
void TestKeePass2Format::testDuplicateAttachments()
{
- QScopedPointer<Database> db(new Database());
+ auto db = QSharedPointer<Database>::create();
db->setKey(QSharedPointer<CompositeKey>::create());
const QByteArray attachment1("abc");
diff --git a/tests/TestKeePass2Format.h b/tests/TestKeePass2Format.h
index 19b0fb649..5d85a73b8 100644
--- a/tests/TestKeePass2Format.h
+++ b/tests/TestKeePass2Format.h
@@ -21,7 +21,7 @@
#include <QBuffer>
#include <QDateTime>
#include <QObject>
-#include <QScopedPointer>
+#include <QSharedPointer>
#include "core/Database.h"
@@ -67,25 +67,25 @@ private slots:
protected:
virtual void initTestCaseImpl() = 0;
- virtual Database* readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) = 0;
- virtual Database* readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) = 0;
+ virtual QSharedPointer<Database> readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) = 0;
+ virtual QSharedPointer<Database> readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) = 0;
virtual void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) = 0;
virtual void readKdbx(QIODevice* device,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString) = 0;
virtual void readKdbx(const QString& path,
QSharedPointer<const CompositeKey> key,
- QScopedPointer<Database>& db,
+ QSharedPointer<Database> db,
bool& hasError,
QString& errorString) = 0;
virtual void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) = 0;
- QScopedPointer<Database> m_xmlDb;
- QScopedPointer<Database> m_kdbxSourceDb;
- QScopedPointer<Database> m_kdbxTargetDb;
+ QSharedPointer<Database> m_xmlDb;
+ QSharedPointer<Database> m_kdbxSourceDb;
+ QSharedPointer<Database> m_kdbxTargetDb;
private:
QBuffer m_kdbxTargetBuffer;
diff --git a/tests/TestKeys.cpp b/tests/TestKeys.cpp
index 84c202914..c2e9aa196 100644
--- a/tests/TestKeys.cpp
+++ b/tests/TestKeys.cpp
@@ -87,8 +87,8 @@ void TestKeys::testFileKey()
compositeKey->addKey(fileKey);
- QScopedPointer<Database> db(reader.readDatabase(dbFilename, compositeKey));
- QVERIFY(db);
+ auto db = QSharedPointer<Database>::create();
+ QVERIFY(db->open(dbFilename, compositeKey, nullptr, false));
QVERIFY(!reader.hasError());
QCOMPARE(db->metadata()->name(), QString("%1 Database").arg(name));
}
@@ -152,7 +152,8 @@ void TestKeys::testCreateAndOpenFileKey()
dbBuffer.reset();
KeePass2Reader reader;
- QScopedPointer<Database> dbRead(reader.readDatabase(&dbBuffer, compositeKey));
+ auto dbRead = QSharedPointer<Database>::create();
+ reader.readDatabase(&dbBuffer, compositeKey, dbRead.data());
if (reader.hasError()) {
QFAIL(reader.errorString().toUtf8().constData());
}
@@ -236,7 +237,7 @@ void TestKeys::testCompositeKeyComponents()
compositeKeyEnc->addKey(fileKeyEnc);
compositeKeyEnc->addChallengeResponseKey(challengeResponseKeyEnc);
- QScopedPointer<Database> db1(new Database());
+ auto db1 = QSharedPointer<Database>::create();
db1->setKey(compositeKeyEnc);
KeePass2Writer writer;
@@ -245,27 +246,27 @@ void TestKeys::testCompositeKeyComponents()
QVERIFY(writer.writeDatabase(&buffer, db1.data()));
buffer.seek(0);
- QScopedPointer<Database> db2;
+ auto db2 = QSharedPointer<Database>::create();
KeePass2Reader reader;
auto compositeKeyDec1 = QSharedPointer<CompositeKey>::create();
// try decryption and subsequently add key components until decryption is successful
- db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
+ QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
QVERIFY(reader.hasError());
compositeKeyDec1->addKey(passwordKeyEnc);
buffer.seek(0);
- db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
+ QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
QVERIFY(reader.hasError());
compositeKeyDec1->addKey(fileKeyEnc);
buffer.seek(0);
- db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
+ QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
QVERIFY(reader.hasError());
compositeKeyDec1->addChallengeResponseKey(challengeResponseKeyEnc);
buffer.seek(0);
- db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
+ QVERIFY(reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
// now we should be able to open the database
if (reader.hasError()) {
QFAIL(qPrintable(reader.errorString()));
@@ -277,7 +278,7 @@ void TestKeys::testCompositeKeyComponents()
compositeKeyDec2->addKey(fileKeyEnc);
compositeKeyDec2->addChallengeResponseKey(challengeResponseKeyEnc);
buffer.seek(0);
- db2.reset(reader.readDatabase(&buffer, compositeKeyDec2));
+ QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec2, db2.data()));
QVERIFY(reader.hasError());
auto compositeKeyDec3 = QSharedPointer<CompositeKey>::create();
@@ -290,7 +291,7 @@ void TestKeys::testCompositeKeyComponents()
compositeKeyDec3->addKey(fileKeyWrong);
compositeKeyDec3->addChallengeResponseKey(challengeResponseKeyEnc);
buffer.seek(0);
- db2.reset(reader.readDatabase(&buffer, compositeKeyDec3));
+ QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec3, db2.data()));
QVERIFY(reader.hasError());
auto compositeKeyDec4 = QSharedPointer<CompositeKey>::create();
@@ -298,6 +299,6 @@ void TestKeys::testCompositeKeyComponents()
compositeKeyDec4->addKey(fileKeyEnc);
compositeKeyDec4->addChallengeResponseKey(QSharedPointer<MockChallengeResponseKey>::create(QByteArray(16, 0x20)));
buffer.seek(0);
- db2.reset(reader.readDatabase(&buffer, compositeKeyDec4));
+ QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec4, db2.data()));
QVERIFY(reader.hasError());
}
diff --git a/tests/TestMerge.cpp b/tests/TestMerge.cpp
index 60e3a9ea8..3e41d7d50 100644
--- a/tests/TestMerge.cpp
+++ b/tests/TestMerge.cpp
@@ -1363,10 +1363,10 @@ void TestMerge::testMergeNotModified()
QScopedPointer<Database> dbSource(
createTestDatabaseStructureClone(dbDestination.data(), Entry::CloneNoFlags, Group::CloneIncludeEntries));
- QSignalSpy modifiedSignalSpy(dbDestination.data(), SIGNAL(modified()));
+ QSignalSpy modifiedSignalSpy(dbDestination.data(), SIGNAL(databaseModified()));
Merger merger(dbSource.data(), dbDestination.data());
merger.merge();
- QVERIFY(modifiedSignalSpy.empty());
+ QTRY_VERIFY(modifiedSignalSpy.empty());
}
void TestMerge::testMergeModified()
@@ -1375,7 +1375,7 @@ void TestMerge::testMergeModified()
QScopedPointer<Database> dbSource(
createTestDatabaseStructureClone(dbDestination.data(), Entry::CloneNoFlags, Group::CloneIncludeEntries));
- QSignalSpy modifiedSignalSpy(dbDestination.data(), SIGNAL(modified()));
+ QSignalSpy modifiedSignalSpy(dbDestination.data(), SIGNAL(databaseModified()));
// Make sure the two changes have a different timestamp.
QTest::qSleep(1);
Entry* entry = dbSource->rootGroup()->findEntryByPath("entry1");
@@ -1385,7 +1385,7 @@ void TestMerge::testMergeModified()
Merger merger(dbSource.data(), dbDestination.data());
merger.merge();
- QVERIFY(!modifiedSignalSpy.empty());
+ QTRY_VERIFY(!modifiedSignalSpy.empty());
}
Database* TestMerge::createTestDatabase()
diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp
index 63013fd5e..fff558b22 100644
--- a/tests/TestModified.cpp
+++ b/tests/TestModified.cpp
@@ -61,67 +61,83 @@ void TestModified::testSignals()
QScopedPointer<Database> db(new Database());
auto* root = db->rootGroup();
- QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
db->setKey(compositeKey);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
auto* group1 = new Group();
group1->setParent(root);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
auto* group2 = new Group();
group2->setParent(root);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
group2->setParent(root, 0);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
auto* entry1 = new Entry();
entry1->setGroup(group1);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
QScopedPointer<Database> db2(new Database());
auto* root2 = db2->rootGroup();
- QSignalSpy spyModified2(db2.data(), SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified2(db2.data(), SIGNAL(databaseModified()));
group1->setParent(root2);
- QCOMPARE(spyModified.count(), ++spyCount);
- QCOMPARE(spyModified2.count(), ++spyCount2);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
+ ++spyCount2;
+ QTRY_COMPARE(spyModified2.count(), spyCount2);
entry1->setTitle("test");
- QCOMPARE(spyModified.count(), spyCount);
- QCOMPARE(spyModified2.count(), ++spyCount2);
+ QTRY_COMPARE(spyModified.count(), spyCount);
+ ++spyCount2;
+ QTRY_COMPARE(spyModified2.count(), spyCount2);
auto* entry2 = new Entry();
entry2->setGroup(group2);
- QCOMPARE(spyModified.count(), ++spyCount);
- QCOMPARE(spyModified2.count(), spyCount2);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified2.count(), spyCount2);
entry2->setGroup(root2);
- QCOMPARE(spyModified.count(), ++spyCount);
- QCOMPARE(spyModified2.count(), ++spyCount2);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
+ ++spyCount2;
+ QTRY_COMPARE(spyModified2.count(), spyCount2);
entry2->setTitle("test2");
- QCOMPARE(spyModified.count(), spyCount);
- QCOMPARE(spyModified2.count(), ++spyCount2);
+ QTRY_COMPARE(spyModified.count(), spyCount);
+ ++spyCount2;
+ QTRY_COMPARE(spyModified2.count(), spyCount2);
auto* group3 = new Group();
group3->setParent(root);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
auto* group4 = new Group();
group4->setParent(group3);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
delete group4;
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
delete entry2;
- QCOMPARE(spyModified2.count(), ++spyCount2);
+ ++spyCount2;
+ QTRY_COMPARE(spyModified2.count(), spyCount2);
- QCOMPARE(spyModified.count(), spyCount);
- QCOMPARE(spyModified2.count(), spyCount2);
+ QTRY_COMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified2.count(), spyCount2);
}
void TestModified::testGroupSets()
@@ -133,58 +149,68 @@ void TestModified::testGroupSets()
auto* group = new Group();
group->setParent(root);
- QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
root->setUuid(QUuid::createUuid());
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setUuid(root->uuid());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setName("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setName(root->name());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setNotes("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setNotes(root->notes());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setIcon(1);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setIcon(root->iconNumber());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setIcon(QUuid::createUuid());
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
root->setIcon(root->iconUuid());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setUuid(QUuid::createUuid());
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setUuid(group->uuid());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setName("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setName(group->name());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setNotes("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setNotes(group->notes());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setIcon(1);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setIcon(group->iconNumber());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setIcon(QUuid::createUuid());
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->setIcon(group->iconUuid());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
}
void TestModified::testEntrySets()
@@ -198,106 +224,127 @@ void TestModified::testEntrySets()
auto* entry = new Entry();
entry->setGroup(group);
- QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
entry->setUuid(QUuid::createUuid());
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setUuid(entry->uuid());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setTitle("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setTitle(entry->title());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setUrl("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setUrl(entry->url());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setUsername("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setUsername(entry->username());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setPassword("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setPassword(entry->password());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setNotes("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setNotes(entry->notes());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setIcon(1);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setIcon(entry->iconNumber());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setIcon(QUuid::createUuid());
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setIcon(entry->iconUuid());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setTags("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setTags(entry->tags());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setExpires(true);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setExpires(entry->timeInfo().expires());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setExpiryTime(Clock::currentDateTimeUtc().addYears(1));
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setExpiryTime(entry->timeInfo().expiryTime());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setAutoTypeEnabled(false);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setAutoTypeEnabled(entry->autoTypeEnabled());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setAutoTypeObfuscation(1);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setAutoTypeObfuscation(entry->autoTypeObfuscation());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setDefaultAutoTypeSequence("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setDefaultAutoTypeSequence(entry->defaultAutoTypeSequence());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setForegroundColor(Qt::red);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setForegroundColor(entry->foregroundColor());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setBackgroundColor(Qt::red);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setBackgroundColor(entry->backgroundColor());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setOverrideUrl("test");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->setOverrideUrl(entry->overrideUrl());
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->attributes()->set("test key", "test value", false);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->attributes()->set("test key", entry->attributes()->value("test key"), false);
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->attributes()->set("test key", entry->attributes()->value("test key"), true);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->attributes()->set("test key", "new test value", true);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->attributes()->set("test key2", "test value2", true);
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->attributes()->set("test key2", entry->attributes()->value("test key2"), true);
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
}
void TestModified::testHistoryItems()
@@ -601,20 +648,23 @@ void TestModified::testCustomData()
auto* entry = new Entry();
entry->setGroup(group);
- QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
db->metadata()->customData()->set("Key", "Value");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
db->metadata()->customData()->set("Key", "Value");
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->customData()->set("Key", "Value");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
entry->customData()->set("Key", "Value");
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->customData()->set("Key", "Value");
- QCOMPARE(spyModified.count(), ++spyCount);
+ ++spyCount;
+ QTRY_COMPARE(spyModified.count(), spyCount);
group->customData()->set("Key", "Value");
- QCOMPARE(spyModified.count(), spyCount);
+ QTRY_COMPARE(spyModified.count(), spyCount);
}
diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp
index 0adeabd95..41499d16e 100644
--- a/tests/gui/TestGui.cpp
+++ b/tests/gui/TestGui.cpp
@@ -110,37 +110,36 @@ void TestGui::init()
m_dbFilePath = m_dbFile->fileName();
m_dbFile->close();
+ // make sure window is activated or focus tests may fail
+ m_mainWindow->activateWindow();
+ QApplication::processEvents();
+
fileDialog()->setNextFileName(m_dbFilePath);
triggerAction("actionDatabaseOpen");
- auto* databaseOpenWidget = m_mainWindow->findChild<QWidget*>("databaseOpenWidget");
+ auto* databaseOpenWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("databaseOpenWidget");
+ QVERIFY(databaseOpenWidget);
auto* editPassword = databaseOpenWidget->findChild<QLineEdit*>("editPassword");
QVERIFY(editPassword);
+ editPassword->setFocus();
QTest::keyClicks(editPassword, "a");
QTest::keyClick(editPassword, Qt::Key_Enter);
- QTRY_VERIFY(m_tabWidget->currentDatabaseWidget());
-
m_dbWidget = m_tabWidget->currentDatabaseWidget();
m_db = m_dbWidget->database();
-
- // make sure window is activated or focus tests may fail
- m_mainWindow->activateWindow();
- QApplication::processEvents();
}
// Every test ends with closing the temp database without saving
void TestGui::cleanup()
{
// DO NOT save the database
+ m_db->markAsClean();
MessageBox::setNextAnswer(QMessageBox::No);
triggerAction("actionDatabaseClose");
QApplication::processEvents();
+ MessageBox::setNextAnswer(QMessageBox::NoButton);
- if (m_db) {
- delete m_db;
- }
if (m_dbWidget) {
delete m_dbWidget;
}
@@ -301,13 +300,14 @@ void TestGui::createDatabaseCallback()
void TestGui::testMergeDatabase()
{
// It is safe to ignore the warning this line produces
- QSignalSpy dbMergeSpy(m_dbWidget.data(), SIGNAL(databaseMerged(Database*)));
+ QSignalSpy dbMergeSpy(m_dbWidget.data(), SIGNAL(databaseMerged(QSharedPointer<Database>)));
+ QApplication::processEvents();
// set file to merge from
fileDialog()->setNextFileName(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx"));
triggerAction("actionDatabaseMerge");
- auto* databaseOpenMergeWidget = m_mainWindow->findChild<QWidget*>("databaseOpenMergeWidget");
+ auto* databaseOpenMergeWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("databaseOpenMergeWidget");
auto* editPasswordMerge = databaseOpenMergeWidget->findChild<QLineEdit*>("editPassword");
QVERIFY(editPasswordMerge->isVisible());
@@ -317,7 +317,7 @@ void TestGui::testMergeDatabase()
QTest::keyClick(editPasswordMerge, Qt::Key_Enter);
QTRY_COMPARE(dbMergeSpy.count(), 1);
- QTRY_VERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).contains("*"));
+ QTRY_VERIFY(m_tabWidget->tabName(m_tabWidget->currentIndex()).contains("*"));
m_db = m_tabWidget->currentDatabaseWidget()->database();
@@ -352,7 +352,7 @@ void TestGui::testAutoreloadDatabase()
// the General group contains one entry from the new db data
QCOMPARE(m_db->rootGroup()->findChildByName("General")->entries().size(), 1);
- QVERIFY(!m_tabWidget->tabText(m_tabWidget->currentIndex()).endsWith("*"));
+ QVERIFY(!m_tabWidget->tabName(m_tabWidget->currentIndex()).endsWith("*"));
// Reset the state
cleanup();
@@ -370,7 +370,7 @@ void TestGui::testAutoreloadDatabase()
// Ensure the merge did not take place
QCOMPARE(m_db->rootGroup()->findChildByName("General")->entries().size(), 0);
- QVERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).endsWith("*"));
+ QVERIFY(m_tabWidget->tabName(m_tabWidget->currentIndex()).endsWith("*"));
// Reset the state
cleanup();
@@ -393,13 +393,13 @@ void TestGui::testAutoreloadDatabase()
m_db = m_dbWidget->database();
QCOMPARE(m_db->rootGroup()->findChildByName("General")->entries().size(), 1);
- QVERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).endsWith("*"));
+ QTRY_VERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).endsWith("*"));
}
void TestGui::testTabs()
{
QCOMPARE(m_tabWidget->count(), 1);
- QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), m_dbFileName);
+ QCOMPARE(m_tabWidget->tabName(m_tabWidget->currentIndex()), m_dbFileName);
}
void TestGui::testEditEntry()
@@ -424,15 +424,16 @@ void TestGui::testEditEntry()
// Edit the first entry ("Sample Entry")
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
auto* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
QTest::keyClicks(titleEdit, "_test");
// Apply the edit
auto* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
+ QVERIFY(editEntryWidgetButtonBox);
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
QCOMPARE(entry->title(), QString("Sample Entry_test"));
QCOMPARE(entry->historyItems().size(), ++editCount);
@@ -473,15 +474,16 @@ void TestGui::testEditEntry()
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
auto* messageWiget = editEntryWidget->findChild<MessageWidget*>("messageWidget");
QTRY_VERIFY(messageWiget->isVisible());
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
QCOMPARE(passwordEdit->text(), QString("newpass"));
passwordEdit->setText(originalPassword);
// Save the edit (press OK)
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
+ QApplication::processEvents();
// Confirm edit was made
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
QCOMPARE(entry->title(), QString("Sample Entry_test"));
QCOMPARE(entry->foregroundColor(), fgColor);
QCOMPARE(entryItem.data(Qt::ForegroundRole), QVariant(fgColor));
@@ -490,11 +492,11 @@ void TestGui::testEditEntry()
QCOMPARE(entry->historyItems().size(), ++editCount);
// Confirm modified indicator is showing
- QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("%1*").arg(m_dbFileName));
+ QTRY_COMPARE(m_tabWidget->tabName(m_tabWidget->currentIndex()), QString("%1*").arg(m_dbFileName));
// Test copy & paste newline sanitization
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
titleEdit->setText("multiline\ntitle");
editEntryWidget->findChild<QLineEdit*>("usernameEdit")->setText("multiline\nusername");
editEntryWidget->findChild<QLineEdit*>("passwordEdit")->setText("multiline\npassword");
@@ -550,11 +552,11 @@ void TestGui::testSearchEditEntry()
auto* searchTextEdit = searchWidget->findChild<QLineEdit*>("searchEdit");
QTest::mouseClick(searchTextEdit, Qt::LeftButton);
QTest::keyClicks(searchTextEdit, "Doggy");
- QTRY_VERIFY(m_dbWidget->isInSearchMode());
+ QTRY_VERIFY(m_dbWidget->isSearchActive());
// Goto "Doggy"'s edit view
QTest::keyClick(searchTextEdit, Qt::Key_Return);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
// Check the path in header is "parent-group > entry"
QCOMPARE(m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget")->findChild<QLabel*>("headerLabel")->text(),
@@ -577,7 +579,7 @@ void TestGui::testAddEntry()
// Click the new entry button and check that we enter edit mode
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
// Add entry "test" and confirm added
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
@@ -586,7 +588,7 @@ void TestGui::testAddEntry()
auto* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
QModelIndex item = entryView->model()->index(1, 1);
Entry* entry = entryView->entryFromIndex(item);
@@ -629,7 +631,7 @@ void TestGui::testPasswordEntryEntropy()
// Click the new entry button and check that we enter edit mode
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
// Add entry "test" and confirm added
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
@@ -701,7 +703,7 @@ void TestGui::testDicewareEntryEntropy()
// Click the new entry button and check that we enter edit mode
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
// Add entry "test" and confirm added
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
@@ -736,7 +738,7 @@ void TestGui::testTotp()
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView");
QCOMPARE(entryView->model()->rowCount(), 1);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
QModelIndex item = entryView->model()->index(0, 1);
Entry* entry = entryView->entryFromIndex(item);
clickIndex(item, entryView, Qt::LeftButton);
@@ -766,7 +768,7 @@ void TestGui::testTotp()
QVERIFY(entryEditWidget->isVisible());
QVERIFY(entryEditWidget->isEnabled());
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
editEntryWidget->setCurrentPage(1);
@@ -822,7 +824,7 @@ void TestGui::testSearch()
QTest::keyClicks(searchTextEdit, "ZZZ");
QTRY_COMPARE(searchTextEdit->text(), QString("ZZZ"));
QTRY_VERIFY(clearButton->isVisible());
- QTRY_VERIFY(m_dbWidget->isInSearchMode());
+ QTRY_VERIFY(m_dbWidget->isSearchActive());
QTRY_COMPARE(entryView->model()->rowCount(), 0);
// Press the search clear button
clearButton->trigger();
@@ -834,10 +836,10 @@ void TestGui::testSearch()
QTest::keyClick(searchTextEdit, Qt::Key_Escape);
QTRY_VERIFY(searchTextEdit->text().isEmpty());
QTRY_VERIFY(searchTextEdit->hasFocus());
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
// Search for "some"
QTest::keyClicks(searchTextEdit, "some");
- QTRY_VERIFY(m_dbWidget->isInSearchMode());
+ QTRY_VERIFY(m_dbWidget->isSearchActive());
QTRY_COMPARE(entryView->model()->rowCount(), 3);
// Search for "someTHING"
QTest::keyClicks(searchTextEdit, "THING");
@@ -894,12 +896,12 @@ void TestGui::testSearch()
// Refocus back to search edit
QTest::mouseClick(searchTextEdit, Qt::LeftButton);
QTRY_VERIFY(searchTextEdit->hasFocus());
- QVERIFY(m_dbWidget->isInSearchMode());
+ QVERIFY(m_dbWidget->isSearchActive());
QModelIndex item = entryView->model()->index(0, 1);
Entry* entry = entryView->entryFromIndex(item);
QTest::keyClick(searchTextEdit, Qt::Key_Return);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
// Perform the edit and save it
EditEntryWidget* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
@@ -910,12 +912,12 @@ void TestGui::testSearch()
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
// Confirm the edit was made and we are back in search mode
- QTRY_VERIFY(m_dbWidget->isInSearchMode());
+ QTRY_VERIFY(m_dbWidget->isSearchActive());
QCOMPARE(entry->title(), origTitle.append("_edited"));
// Cancel search, should return to normal view
QTest::keyClick(m_mainWindow.data(), Qt::Key_Escape);
- QTRY_COMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
+ QTRY_COMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
}
void TestGui::testDeleteEntry()
@@ -929,7 +931,7 @@ void TestGui::testDeleteEntry()
auto* entryDeleteAction = m_mainWindow->findChild<QAction*>("actionEntryDelete");
QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
clickIndex(entryView->model()->index(1, 1), entryView, Qt::LeftButton);
QVERIFY(entryDeleteWidget->isVisible());
QVERIFY(entryDeleteWidget->isEnabled());
@@ -1022,7 +1024,7 @@ void TestGui::testEntryPlaceholders()
// Click the new entry button and check that we enter edit mode
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
// Add entry "test" and confirm added
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
@@ -1037,7 +1039,7 @@ void TestGui::testEntryPlaceholders()
QCOMPARE(entryView->model()->rowCount(), 2);
- QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
+ QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
QModelIndex item = entryView->model()->index(1, 1);
Entry* entry = entryView->entryFromIndex(item);
@@ -1105,7 +1107,7 @@ void TestGui::testSaveAs()
triggerAction("actionDatabaseSaveAs");
- QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testSaveAs"));
+ QCOMPARE(m_tabWidget->tabName(m_tabWidget->currentIndex()), QString("testSaveAs"));
checkDatabase(tmpFileName);
@@ -1122,7 +1124,7 @@ void TestGui::testSave()
QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testSave*"));
triggerAction("actionDatabaseSave");
- QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testSave"));
+ QCOMPARE(m_tabWidget->tabName(m_tabWidget->currentIndex()), QString("testSave"));
checkDatabase();
}
@@ -1159,15 +1161,15 @@ void TestGui::testKeePass1Import()
fileDialog()->setNextFileName(QString(KEEPASSX_TEST_DATA_DIR).append("/basic.kdb"));
triggerAction("actionImportKeePass1");
- auto* keepass1OpenWidget = m_mainWindow->findChild<QWidget*>("keepass1OpenWidget");
+ auto* keepass1OpenWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("keepass1OpenWidget");
auto* editPassword = keepass1OpenWidget->findChild<QLineEdit*>("editPassword");
QVERIFY(editPassword);
QTest::keyClicks(editPassword, "masterpw");
QTest::keyClick(editPassword, Qt::Key_Enter);
- QCOMPARE(m_tabWidget->count(), 2);
- QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("basic [New database]*"));
+ QTRY_COMPARE(m_tabWidget->count(), 2);
+ QTRY_COMPARE(m_tabWidget->tabName(m_tabWidget->currentIndex()), QString("basic [New Database]*"));
// Close the KeePass1 Database
MessageBox::setNextAnswer(QMessageBox::No);
@@ -1182,7 +1184,7 @@ void TestGui::testDatabaseLocking()
MessageBox::setNextAnswer(QMessageBox::Cancel);
triggerAction("actionLockDatabases");
- QCOMPARE(m_tabWidget->tabText(0).remove('&'), origDbName + " [locked]");
+ QCOMPARE(m_tabWidget->tabName(0), origDbName + " [Locked]");
auto* actionDatabaseMerge = m_mainWindow->findChild<QAction*>("actionDatabaseMerge", Qt::FindChildrenRecursively);
QCOMPARE(actionDatabaseMerge->isEnabled(), false);
@@ -1197,7 +1199,7 @@ void TestGui::testDatabaseLocking()
QTest::keyClicks(editPassword, "a");
QTest::keyClick(editPassword, Qt::Key_Enter);
- QCOMPARE(m_tabWidget->tabText(0).remove('&'), origDbName);
+ QCOMPARE(m_tabWidget->tabName(0), origDbName);
actionDatabaseMerge = m_mainWindow->findChild<QAction*>("actionDatabaseMerge", Qt::FindChildrenRecursively);
QCOMPARE(actionDatabaseMerge->isEnabled(), true);
@@ -1304,10 +1306,8 @@ void TestGui::checkDatabase(QString dbFileName)
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("a"));
- KeePass2Reader reader;
- QScopedPointer<Database> dbSaved(reader.readDatabase(dbFileName, key));
- QVERIFY(dbSaved);
- QVERIFY(!reader.hasError());
+ auto dbSaved = QSharedPointer<Database>::create();
+ QVERIFY(dbSaved->open(dbFileName, key, nullptr, false));
QCOMPARE(dbSaved->metadata()->name(), m_db->metadata()->name());
}
diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h
index 4df606f4a..532600bdc 100644
--- a/tests/gui/TestGui.h
+++ b/tests/gui/TestGui.h
@@ -26,6 +26,7 @@
#include <QObject>
#include <QPointer>
#include <QScopedPointer>
+#include <QSharedPointer>
class Database;
class DatabaseTabWidget;
@@ -88,7 +89,7 @@ private:
QScopedPointer<MainWindow> m_mainWindow;
QPointer<DatabaseTabWidget> m_tabWidget;
QPointer<DatabaseWidget> m_dbWidget;
- QPointer<Database> m_db;
+ QSharedPointer<Database> m_db;
QByteArray m_dbData;
QScopedPointer<TemporaryFile> m_dbFile;
QString m_dbFileName;