From aa7216b9d9fd177062d6b68a8634be2dc60ca982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20V=C3=A4nttinen?= Date: Wed, 3 Oct 2018 15:16:32 +0300 Subject: [PATCH 01/84] Fix TouchID compiling on macOS (#2332) Pull request #1952 introduced a compilation error on macOS due to missing header includes. --- src/gui/dbsettings/DatabaseSettingsDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp index b2624a425..8b28ced04 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp +++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp @@ -25,6 +25,7 @@ #include "core/Config.h" #include "core/FilePath.h" #include "core/Database.h" +#include "touchid/TouchID.h" DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent) : DialogyWidget(parent) From 4ff63c2bf55f7801c7e3386f46c3a7f4b05a5952 Mon Sep 17 00:00:00 2001 From: vi-n Date: Wed, 3 Oct 2018 19:18:34 +0200 Subject: [PATCH 02/84] Add a CLI option to list elements recursively (#2345) --- src/cli/List.cpp | 17 ++++++++++++----- src/cli/List.h | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/cli/List.cpp b/src/cli/List.cpp index bdedaf210..b39b3fc14 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -50,6 +50,11 @@ int List::execute(const QStringList& arguments) QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); + + QCommandLineOption recursiveOption(QStringList() << "R" + << "recursive", + QObject::tr("Recursive mode, list elements recursively")); + parser.addOption(recursiveOption); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -58,22 +63,24 @@ int List::execute(const QStringList& arguments) return EXIT_FAILURE; } + bool recursive = parser.isSet(recursiveOption); + Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); if (db == nullptr) { return EXIT_FAILURE; } if (args.size() == 2) { - return this->listGroup(db, args.at(1)); + return this->listGroup(db, recursive, args.at(1)); } - return this->listGroup(db); + return this->listGroup(db, recursive); } -int List::listGroup(Database* database, QString groupPath) +int List::listGroup(Database* database, bool recursive, QString groupPath) { QTextStream outputTextStream(stdout, QIODevice::WriteOnly); if (groupPath.isEmpty()) { - outputTextStream << database->rootGroup()->print(); + outputTextStream << database->rootGroup()->print(recursive); outputTextStream.flush(); return EXIT_SUCCESS; } @@ -84,7 +91,7 @@ int List::listGroup(Database* database, QString groupPath) return EXIT_FAILURE; } - outputTextStream << group->print(); + outputTextStream << group->print(recursive); outputTextStream.flush(); return EXIT_SUCCESS; } diff --git a/src/cli/List.h b/src/cli/List.h index 98b8b5a45..00c376972 100644 --- a/src/cli/List.h +++ b/src/cli/List.h @@ -26,7 +26,7 @@ public: List(); ~List(); int execute(const QStringList& arguments); - int listGroup(Database* database, QString groupPath = QString("")); + int listGroup(Database* database, bool recursive, QString groupPath = QString("")); }; #endif // KEEPASSXC_LIST_H From 265325057a6545d6f3cecdc411f03dbe03c6b36e Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Mon, 8 Oct 2018 18:11:44 -0700 Subject: [PATCH 03/84] Create config file deprecation mechanism & rename Details->Preview (#2371) Rename UI elements involved with the bottom preview panel to use the terminology "entry preview panel" instead of "details view" or "preview panel". Change all associated variables, widgets, and classes to reference EntryPreviewPanel. Create Config::upgrade() function and deprecation mapping to help rename/remove previous config settings in a clean manner. Fixes #2327 --- src/CMakeLists.txt | 2 +- src/core/Config.cpp | 33 +++++++++++- src/core/Config.h | 1 + src/gui/ApplicationSettingsWidget.cpp | 8 +-- src/gui/ApplicationSettingsWidgetGeneral.ui | 4 +- src/gui/ApplicationSettingsWidgetSecurity.ui | 4 +- src/gui/DatabaseWidget.cpp | 42 +++++++-------- src/gui/DatabaseWidget.h | 12 ++--- src/gui/DatabaseWidgetStateSync.cpp | 12 ++--- src/gui/DatabaseWidgetStateSync.h | 2 +- ...tailsWidget.cpp => EntryPreviewWidget.cpp} | 52 +++++++++---------- .../{DetailsWidget.h => EntryPreviewWidget.h} | 10 ++-- ...DetailsWidget.ui => EntryPreviewWidget.ui} | 4 +- 13 files changed, 109 insertions(+), 77 deletions(-) rename src/gui/{DetailsWidget.cpp => EntryPreviewWidget.cpp} (87%) rename src/gui/{DetailsWidget.h => EntryPreviewWidget.h} (89%) rename src/gui/{DetailsWidget.ui => EntryPreviewWidget.ui} (99%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3621067e8..51add8968 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -107,7 +107,7 @@ set(keepassx_SOURCES gui/DatabaseTabWidget.cpp gui/DatabaseWidget.cpp gui/DatabaseWidgetStateSync.cpp - gui/DetailsWidget.cpp + gui/EntryPreviewWidget.cpp gui/DialogyWidget.cpp gui/DragTabBar.cpp gui/EditWidget.cpp diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 8f1dc55fd..2c92d47a0 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -24,6 +24,23 @@ #include #include +/* + * Map of configuration file settings that are either deprecated, or have + * had their name changed. Entries in the map are of the form + * {oldName, newName} + * Set newName to empty string to remove the setting from the file. + */ +static const QMap deprecationMap = { + // >2.3.4 + {"security/hidepassworddetails", "security/HidePasswordPreviewPanel"}, + // >2.3.4 + {"GUI/HideDetailsView", "GUI/HidePreviewPanel"}, + // >2.3.4 + {"GUI/DetailSplitterState", "GUI/PreviewSplitterState"}, + // >2.3.4 + {"security/IconDownloadFallbackToGoogle", "security/IconDownloadFallback"}, +}; + Config* Config::m_instance(nullptr); QVariant Config::get(const QString& key) @@ -63,6 +80,19 @@ void Config::sync() m_settings->sync(); } +void Config::upgrade() +{ + for (const auto& setting : deprecationMap.keys()) { + if (m_settings->contains(setting)) { + if(!deprecationMap.value(setting).isEmpty()) { + // Add entry with new name and old entry's value + m_settings->setValue(deprecationMap.value(setting), m_settings->value(setting)); + } + m_settings->remove(setting); + } + } +} + Config::Config(const QString& fileName, QObject* parent) : QObject(parent) { @@ -118,6 +148,7 @@ Config::~Config() void Config::init(const QString& fileName) { m_settings.reset(new QSettings(fileName, QSettings::IniFormat)); + upgrade(); connect(qApp, &QCoreApplication::aboutToQuit, this, &Config::sync); m_defaults.insert("SingleInstance", true); @@ -147,7 +178,7 @@ void Config::init(const QString& fileName) m_defaults.insert("security/passwordsrepeat", false); m_defaults.insert("security/passwordscleartext", false); m_defaults.insert("security/passwordemptynodots", true); - m_defaults.insert("security/hidepassworddetails", true); + m_defaults.insert("security/HidePasswordPreviewPanel", true); m_defaults.insert("security/autotypeask", true); m_defaults.insert("security/IconDownloadFallback", false); m_defaults.insert("security/resettouchid", false); diff --git a/src/core/Config.h b/src/core/Config.h index fcb27e2ca..c08e9b35b 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -47,6 +47,7 @@ private: Config(const QString& fileName, QObject* parent); explicit Config(QObject* parent); void init(const QString& fileName); + void upgrade(); static Config* m_instance; diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 285462042..0e80f2fc5 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -154,7 +154,7 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->languageComboBox->setCurrentIndex(defaultIndex); } - m_generalUi->detailsHideCheckBox->setChecked(config()->get("GUI/HideDetailsView").toBool()); + m_generalUi->previewHideCheckBox->setChecked(config()->get("GUI/HidePreviewPanel").toBool()); m_generalUi->toolbarHideCheckBox->setChecked(config()->get("GUI/HideToolbar").toBool()); m_generalUi->systrayShowCheckBox->setChecked(config()->get("GUI/ShowTrayIcon").toBool()); m_generalUi->systrayDarkIconCheckBox->setChecked(config()->get("GUI/DarkTrayIcon").toBool()); @@ -187,7 +187,7 @@ void ApplicationSettingsWidget::loadSettings() m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool()); m_secUi->passwordShowDotsCheckBox->setChecked(config()->get("security/passwordemptynodots").toBool()); - m_secUi->passwordDetailsCleartextCheckBox->setChecked(config()->get("security/hidepassworddetails").toBool()); + m_secUi->passwordPreviewCleartextCheckBox->setChecked(config()->get("security/HidePasswordPreviewPanel").toBool()); m_secUi->passwordRepeatCheckBox->setChecked(config()->get("security/passwordsrepeat").toBool()); m_secUi->hideNotesCheckBox->setChecked(config()->get("security/hidenotes").toBool()); @@ -230,7 +230,7 @@ void ApplicationSettingsWidget::saveSettings() config()->set("GUI/Language", m_generalUi->languageComboBox->itemData(currentLangIndex).toString()); - config()->set("GUI/HideDetailsView", m_generalUi->detailsHideCheckBox->isChecked()); + config()->set("GUI/HidePreviewPanel", m_generalUi->previewHideCheckBox->isChecked()); config()->set("GUI/HideToolbar", m_generalUi->toolbarHideCheckBox->isChecked()); config()->set("GUI/ShowTrayIcon", m_generalUi->systrayShowCheckBox->isChecked()); config()->set("GUI/DarkTrayIcon", m_generalUi->systrayDarkIconCheckBox->isChecked()); @@ -259,7 +259,7 @@ void ApplicationSettingsWidget::saveSettings() config()->set("security/passwordscleartext", m_secUi->passwordCleartextCheckBox->isChecked()); config()->set("security/passwordemptynodots", m_secUi->passwordShowDotsCheckBox->isChecked()); - config()->set("security/hidepassworddetails", m_secUi->passwordDetailsCleartextCheckBox->isChecked()); + config()->set("security/HidePasswordPreviewPanel", m_secUi->passwordPreviewCleartextCheckBox->isChecked()); config()->set("security/passwordsrepeat", m_secUi->passwordRepeatCheckBox->isChecked()); config()->set("security/hidenotes", m_secUi->hideNotesCheckBox->isChecked()); diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index f88206d60..157e52299 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -164,9 +164,9 @@ - + - Hide the Details view + Hide the entry preview panel diff --git a/src/gui/ApplicationSettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui index 45d2f954f..4052e5d63 100644 --- a/src/gui/ApplicationSettingsWidgetSecurity.ui +++ b/src/gui/ApplicationSettingsWidgetSecurity.ui @@ -187,9 +187,9 @@ - + - Hide passwords in the preview panel + Hide passwords in the entry preview panel diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index aae6527a1..1e3854998 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -44,7 +44,7 @@ #include "gui/CloneDialog.h" #include "gui/DatabaseOpenWidget.h" #include "gui/dbsettings/DatabaseSettingsDialog.h" -#include "gui/DetailsWidget.h" +#include "gui/EntryPreviewWidget.h" #include "gui/KeePass1OpenWidget.h" #include "gui/MessageBox.h" #include "gui/TotpSetupDialog.h" @@ -85,9 +85,9 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) mainLayout->addLayout(layout); m_mainSplitter = new QSplitter(m_mainWidget); m_mainSplitter->setChildrenCollapsible(false); - m_detailSplitter = new QSplitter(m_mainWidget); - m_detailSplitter->setOrientation(Qt::Vertical); - m_detailSplitter->setChildrenCollapsible(true); + m_previewSplitter = new QSplitter(m_mainWidget); + m_previewSplitter->setOrientation(Qt::Vertical); + m_previewSplitter->setChildrenCollapsible(true); QWidget* rightHandSideWidget = new QWidget(m_mainSplitter); @@ -111,27 +111,27 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) "border: 2px solid rgb(190, 190, 190);" "border-radius: 5px;"); - m_detailsView = new DetailsWidget(this); - m_detailsView->hide(); - connect(this, SIGNAL(pressedEntry(Entry*)), m_detailsView, SLOT(setEntry(Entry*))); - connect(this, SIGNAL(pressedGroup(Group*)), m_detailsView, SLOT(setGroup(Group*))); + m_previewView = new EntryPreviewWidget(this); + m_previewView->hide(); + 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_detailsView, + m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode))); - connect(m_detailsView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString))); + 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_detailSplitter); + vLayout->addWidget(m_previewSplitter); - m_detailSplitter->addWidget(m_entryView); - m_detailSplitter->addWidget(m_detailsView); + m_previewSplitter->addWidget(m_entryView); + m_previewSplitter->addWidget(m_previewView); - m_detailSplitter->setStretchFactor(0, 100); - m_detailSplitter->setStretchFactor(1, 0); - m_detailSplitter->setSizes({1, 1}); + m_previewSplitter->setStretchFactor(0, 100); + m_previewSplitter->setStretchFactor(1, 0); + m_previewSplitter->setSizes({1, 1}); m_searchingLabel->setVisible(false); @@ -179,7 +179,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) addWidget(m_unlockDatabaseWidget); connect(m_mainSplitter, SIGNAL(splitterMoved(int, int)), SIGNAL(mainSplitterSizesChanged())); - connect(m_detailSplitter, SIGNAL(splitterMoved(int, int)), SIGNAL(detailSplitterSizesChanged())); + connect(m_previewSplitter, SIGNAL(splitterMoved(int, int)), SIGNAL(previewSplitterSizesChanged())); 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())); @@ -276,14 +276,14 @@ void DatabaseWidget::setMainSplitterSizes(const QList& sizes) m_mainSplitter->setSizes(sizes); } -QList DatabaseWidget::detailSplitterSizes() const +QList DatabaseWidget::previewSplitterSizes() const { - return m_detailSplitter->sizes(); + return m_previewSplitter->sizes(); } -void DatabaseWidget::setDetailSplitterSizes(const QList& sizes) +void DatabaseWidget::setPreviewSplitterSizes(const QList& sizes) { - m_detailSplitter->setSizes(sizes); + m_previewSplitter->setSizes(sizes); } /** diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 896703eb6..d1598041f 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -46,7 +46,7 @@ class QSplitter; class QLabel; class UnlockDatabaseWidget; class MessageWidget; -class DetailsWidget; +class EntryPreviewWidget; class UnlockDatabaseDialog; class QFileSystemWatcher; @@ -90,8 +90,8 @@ public: bool isEditWidgetModified() const; QList mainSplitterSizes() const; void setMainSplitterSizes(const QList& sizes); - QList detailSplitterSizes() const; - void setDetailSplitterSizes(const QList& sizes); + QList previewSplitterSizes() const; + void setPreviewSplitterSizes(const QList& sizes); bool isUsernamesHidden() const; void setUsernamesHidden(const bool hide); bool isPasswordsHidden() const; @@ -130,7 +130,7 @@ signals: void searchModeAboutToActivate(); void searchModeActivated(); void mainSplitterSizesChanged(); - void detailSplitterSizesChanged(); + void previewSplitterSizesChanged(); void entryViewStateChanged(); void updateSearch(QString text); @@ -224,7 +224,7 @@ private: UnlockDatabaseWidget* m_unlockDatabaseWidget; UnlockDatabaseDialog* m_unlockDatabaseDialog; QSplitter* m_mainSplitter; - QSplitter* m_detailSplitter; + QSplitter* m_previewSplitter; GroupView* m_groupView; EntryView* m_entryView; QLabel* m_searchingLabel; @@ -235,7 +235,7 @@ private: QUuid m_groupBeforeLock; QUuid m_entryBeforeLock; MessageWidget* m_messageWidget; - DetailsWidget* m_detailsView; + EntryPreviewWidget* m_previewView; // Search state QString m_lastSearchText; diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp index 1a1fae7af..1172569f5 100644 --- a/src/gui/DatabaseWidgetStateSync.cpp +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -28,7 +28,7 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent) , m_blockUpdates(false) { m_mainSplitterSizes = variantToIntList(config()->get("GUI/SplitterState")); - m_detailSplitterSizes = variantToIntList(config()->get("GUI/DetailSplitterState")); + m_previewSplitterSizes = variantToIntList(config()->get("GUI/PreviewSplitterState")); m_hideUsernames = config()->get("GUI/HideUsernames").toBool(); m_hidePasswords = config()->get("GUI/HidePasswords").toBool(); m_listViewState = config()->get("GUI/ListViewState").toByteArray(); @@ -47,7 +47,7 @@ DatabaseWidgetStateSync::~DatabaseWidgetStateSync() void DatabaseWidgetStateSync::sync() { config()->set("GUI/SplitterState", intListToVariant(m_mainSplitterSizes)); - config()->set("GUI/DetailSplitterState", intListToVariant(m_detailSplitterSizes)); + config()->set("GUI/PreviewSplitterState", intListToVariant(m_previewSplitterSizes)); config()->set("GUI/HideUsernames", m_hideUsernames); config()->set("GUI/HidePasswords", m_hidePasswords); config()->set("GUI/ListViewState", m_listViewState); @@ -70,8 +70,8 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) m_activeDbWidget->setMainSplitterSizes(m_mainSplitterSizes); } - if (!m_detailSplitterSizes.isEmpty()) { - m_activeDbWidget->setDetailSplitterSizes(m_detailSplitterSizes); + if (!m_previewSplitterSizes.isEmpty()) { + m_activeDbWidget->setPreviewSplitterSizes(m_previewSplitterSizes); } if (m_activeDbWidget->isInSearchMode()) { @@ -83,7 +83,7 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) m_blockUpdates = false; connect(m_activeDbWidget, SIGNAL(mainSplitterSizesChanged()), SLOT(updateSplitterSizes())); - connect(m_activeDbWidget, SIGNAL(detailSplitterSizesChanged()), SLOT(updateSplitterSizes())); + connect(m_activeDbWidget, SIGNAL(previewSplitterSizesChanged()), SLOT(updateSplitterSizes())); connect(m_activeDbWidget, SIGNAL(entryViewStateChanged()), SLOT(updateViewState())); connect(m_activeDbWidget, SIGNAL(listModeActivated()), SLOT(restoreListView())); connect(m_activeDbWidget, SIGNAL(searchModeActivated()), SLOT(restoreSearchView())); @@ -158,7 +158,7 @@ void DatabaseWidgetStateSync::updateSplitterSizes() } m_mainSplitterSizes = m_activeDbWidget->mainSplitterSizes(); - m_detailSplitterSizes = m_activeDbWidget->detailSplitterSizes(); + m_previewSplitterSizes = m_activeDbWidget->previewSplitterSizes(); } /** diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h index 0b1c9b7cb..bf254e1f5 100644 --- a/src/gui/DatabaseWidgetStateSync.h +++ b/src/gui/DatabaseWidgetStateSync.h @@ -49,7 +49,7 @@ private: bool m_blockUpdates; QList m_mainSplitterSizes; - QList m_detailSplitterSizes; + QList m_previewSplitterSizes; bool m_hideUsernames; bool m_hidePasswords; diff --git a/src/gui/DetailsWidget.cpp b/src/gui/EntryPreviewWidget.cpp similarity index 87% rename from src/gui/DetailsWidget.cpp rename to src/gui/EntryPreviewWidget.cpp index ff8861172..c80e4ddcf 100644 --- a/src/gui/DetailsWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -#include "DetailsWidget.h" -#include "ui_DetailsWidget.h" +#include "EntryPreviewWidget.h" +#include "ui_EntryPreviewWidget.h" #include #include @@ -33,9 +33,9 @@ namespace constexpr int GeneralTabIndex = 0; } -DetailsWidget::DetailsWidget(QWidget* parent) +EntryPreviewWidget::EntryPreviewWidget(QWidget* parent) : QWidget(parent) - , m_ui(new Ui::DetailsWidget()) + , m_ui(new Ui::EntryPreviewWidget()) , m_locked(false) , m_currentEntry(nullptr) , m_currentGroup(nullptr) @@ -62,11 +62,11 @@ DetailsWidget::DetailsWidget(QWidget* parent) connect(m_ui->groupTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection); } -DetailsWidget::~DetailsWidget() +EntryPreviewWidget::~EntryPreviewWidget() { } -void DetailsWidget::setEntry(Entry* selectedEntry) +void EntryPreviewWidget::setEntry(Entry* selectedEntry) { if (!selectedEntry) { hide(); @@ -83,7 +83,7 @@ void DetailsWidget::setEntry(Entry* selectedEntry) updateEntryAttachmentsTab(); updateEntryAutotypeTab(); - setVisible(!config()->get("GUI/HideDetailsView").toBool()); + setVisible(!config()->get("GUI/HidePreviewPanel").toBool()); m_ui->stackedWidget->setCurrentWidget(m_ui->pageEntry); const int tabIndex = m_ui->entryTabWidget->isTabEnabled(m_selectedTabEntry) ? m_selectedTabEntry : GeneralTabIndex; @@ -91,7 +91,7 @@ void DetailsWidget::setEntry(Entry* selectedEntry) m_ui->entryTabWidget->setCurrentIndex(tabIndex); } -void DetailsWidget::setGroup(Group* selectedGroup) +void EntryPreviewWidget::setGroup(Group* selectedGroup) { if (!selectedGroup) { hide(); @@ -103,7 +103,7 @@ void DetailsWidget::setGroup(Group* selectedGroup) updateGroupGeneralTab(); updateGroupNotesTab(); - setVisible(!config()->get("GUI/HideDetailsView").toBool()); + setVisible(!config()->get("GUI/HidePreviewPanel").toBool()); m_ui->stackedWidget->setCurrentWidget(m_ui->pageGroup); const int tabIndex = m_ui->groupTabWidget->isTabEnabled(m_selectedTabGroup) ? m_selectedTabGroup : GeneralTabIndex; @@ -111,7 +111,7 @@ void DetailsWidget::setGroup(Group* selectedGroup) m_ui->groupTabWidget->setCurrentIndex(tabIndex); } -void DetailsWidget::setDatabaseMode(DatabaseWidget::Mode mode) +void EntryPreviewWidget::setDatabaseMode(DatabaseWidget::Mode mode) { m_locked = mode == DatabaseWidget::LockedMode; if (m_locked) { @@ -127,7 +127,7 @@ void DetailsWidget::setDatabaseMode(DatabaseWidget::Mode mode) } } -void DetailsWidget::updateEntryHeaderLine() +void EntryPreviewWidget::updateEntryHeaderLine() { Q_ASSERT(m_currentEntry); const QString title = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->title()); @@ -135,7 +135,7 @@ void DetailsWidget::updateEntryHeaderLine() m_ui->entryIcon->setPixmap(preparePixmap(m_currentEntry->iconPixmap(), 16)); } -void DetailsWidget::updateEntryTotp() +void EntryPreviewWidget::updateEntryTotp() { Q_ASSERT(m_currentEntry); const bool hasTotp = m_currentEntry->hasTotp(); @@ -152,12 +152,12 @@ void DetailsWidget::updateEntryTotp() } } -void DetailsWidget::updateEntryGeneralTab() +void EntryPreviewWidget::updateEntryGeneralTab() { Q_ASSERT(m_currentEntry); m_ui->entryUsernameLabel->setText(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->username())); - if (!config()->get("security/hidepassworddetails").toBool()) { + if (!config()->get("security/HidePasswordPreviewPanel").toBool()) { const QString password = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()); m_ui->entryPasswordLabel->setRawText(password); m_ui->entryPasswordLabel->setToolTip(password); @@ -183,7 +183,7 @@ void DetailsWidget::updateEntryGeneralTab() m_ui->entryExpirationLabel->setText(expires); } -void DetailsWidget::updateEntryNotesTab() +void EntryPreviewWidget::updateEntryNotesTab() { Q_ASSERT(m_currentEntry); const QString notes = m_currentEntry->notes(); @@ -191,7 +191,7 @@ void DetailsWidget::updateEntryNotesTab() m_ui->entryNotesEdit->setText(notes); } -void DetailsWidget::updateEntryAttributesTab() +void EntryPreviewWidget::updateEntryAttributesTab() { Q_ASSERT(m_currentEntry); m_ui->entryAttributesEdit->clear(); @@ -212,7 +212,7 @@ void DetailsWidget::updateEntryAttributesTab() } } -void DetailsWidget::updateEntryAttachmentsTab() +void EntryPreviewWidget::updateEntryAttachmentsTab() { Q_ASSERT(m_currentEntry); const bool hasAttachments = !m_currentEntry->attachments()->isEmpty(); @@ -220,7 +220,7 @@ void DetailsWidget::updateEntryAttachmentsTab() m_ui->entryAttachmentsWidget->setEntryAttachments(m_currentEntry->attachments()); } -void DetailsWidget::updateEntryAutotypeTab() +void EntryPreviewWidget::updateEntryAutotypeTab() { Q_ASSERT(m_currentEntry); m_ui->entryAutotypeTree->clear(); @@ -237,14 +237,14 @@ void DetailsWidget::updateEntryAutotypeTab() setTabEnabled(m_ui->entryTabWidget, m_ui->entryAutotypeTab, !items.isEmpty()); } -void DetailsWidget::updateGroupHeaderLine() +void EntryPreviewWidget::updateGroupHeaderLine() { Q_ASSERT(m_currentGroup); m_ui->groupTitleLabel->setRawText(hierarchy(m_currentGroup, {})); m_ui->groupIcon->setPixmap(preparePixmap(m_currentGroup->iconPixmap(), 32)); } -void DetailsWidget::updateGroupGeneralTab() +void EntryPreviewWidget::updateGroupGeneralTab() { Q_ASSERT(m_currentGroup); const QString searchingText = m_currentGroup->resolveSearchingEnabled() ? tr("Enabled") : tr("Disabled"); @@ -259,7 +259,7 @@ void DetailsWidget::updateGroupGeneralTab() m_ui->groupExpirationLabel->setText(expiresText); } -void DetailsWidget::updateGroupNotesTab() +void EntryPreviewWidget::updateGroupNotesTab() { Q_ASSERT(m_currentGroup); const QString notes = m_currentGroup->notes(); @@ -267,7 +267,7 @@ void DetailsWidget::updateGroupNotesTab() m_ui->groupNotesEdit->setText(notes); } -void DetailsWidget::updateTotpLabel() +void EntryPreviewWidget::updateTotpLabel() { if (!m_locked && m_currentEntry && m_currentEntry->hasTotp()) { const QString totpCode = m_currentEntry->totp(); @@ -280,20 +280,20 @@ void DetailsWidget::updateTotpLabel() } } -void DetailsWidget::updateTabIndexes() +void EntryPreviewWidget::updateTabIndexes() { m_selectedTabEntry = m_ui->entryTabWidget->currentIndex(); m_selectedTabGroup = m_ui->groupTabWidget->currentIndex(); } -void DetailsWidget::setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled) +void EntryPreviewWidget::setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled) { const int tabIndex = tabWidget->indexOf(widget); Q_ASSERT(tabIndex != -1); tabWidget->setTabEnabled(tabIndex, enabled); } -QPixmap DetailsWidget::preparePixmap(const QPixmap& pixmap, int size) +QPixmap EntryPreviewWidget::preparePixmap(const QPixmap& pixmap, int size) { if (pixmap.width() > size || pixmap.height() > size) { return pixmap.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); @@ -301,7 +301,7 @@ QPixmap DetailsWidget::preparePixmap(const QPixmap& pixmap, int size) return pixmap; } -QString DetailsWidget::hierarchy(const Group* group, const QString& title) +QString EntryPreviewWidget::hierarchy(const Group* group, const QString& title) { const QString separator(" / "); QStringList hierarchy = group->hierarchy(); diff --git a/src/gui/DetailsWidget.h b/src/gui/EntryPreviewWidget.h similarity index 89% rename from src/gui/DetailsWidget.h rename to src/gui/EntryPreviewWidget.h index ba42e5278..88c729626 100644 --- a/src/gui/DetailsWidget.h +++ b/src/gui/EntryPreviewWidget.h @@ -24,16 +24,16 @@ namespace Ui { - class DetailsWidget; + class EntryPreviewWidget; } -class DetailsWidget : public QWidget +class EntryPreviewWidget : public QWidget { Q_OBJECT public: - explicit DetailsWidget(QWidget* parent = nullptr); - ~DetailsWidget() override; + explicit EntryPreviewWidget(QWidget* parent = nullptr); + ~EntryPreviewWidget() override; public slots: void setEntry(Entry* selectedEntry); @@ -65,7 +65,7 @@ private: static QPixmap preparePixmap(const QPixmap& pixmap, int size); static QString hierarchy(const Group* group, const QString& title); - const QScopedPointer m_ui; + const QScopedPointer m_ui; bool m_locked; Entry* m_currentEntry; Group* m_currentGroup; diff --git a/src/gui/DetailsWidget.ui b/src/gui/EntryPreviewWidget.ui similarity index 99% rename from src/gui/DetailsWidget.ui rename to src/gui/EntryPreviewWidget.ui index 38906150e..b7b8445a8 100644 --- a/src/gui/DetailsWidget.ui +++ b/src/gui/EntryPreviewWidget.ui @@ -1,7 +1,7 @@ - DetailsWidget - + EntryPreviewWidget + 0 From ca328242bce505e0d3e53914ecbbaa4350b9dbfd Mon Sep 17 00:00:00 2001 From: Jake Howard Date: Mon, 8 Oct 2018 17:29:08 +0100 Subject: [PATCH 04/84] Don't show application if we're only showing the help / version --- src/main.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 687988762..903974fa7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -95,7 +95,7 @@ int main(int argc, char** argv) QCoreApplication::translate("main", "Parent window handle"), "handle"); - parser.addHelpOption(); + QCommandLineOption helpOption = parser.addHelpOption(); QCommandLineOption versionOption = parser.addVersionOption(); parser.addOption(configOption); parser.addOption(keyfileOption); @@ -103,6 +103,12 @@ int main(int argc, char** argv) parser.addOption(parentWindowOption); parser.process(app); + + // Don't try and do anything with the application if we're only showing the help / version + if (parser.isSet(versionOption) || parser.isSet(helpOption)) { + return 0; + } + const QStringList fileNames = parser.positionalArguments(); if (app.isAlreadyRunning() && !parser.isSet(versionOption)) { From d3069eb7341eae4d6d95a37f7fa14912e4c98c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E5=8D=9A=E4=BB=81=28Buo-ren=2C=20Lin=29?= Date: Tue, 16 Oct 2018 23:46:12 +0800 Subject: [PATCH 05/84] Add Git tracking ignore rules for snapcraft This patch implements gitignore(5) rules for snapcraft, this prevents snap build artifacts from listed as untracked files. [ci skip] --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index 903910a37..4fdc8d70a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,11 @@ release*/ .DS_Store .version \.scannerwork/ + +/snap/.snapcraft/ +/parts/ +/stage/ +/prime/ +/*.snap +/*_source.tar.bz2 + From 80749958b7c909644269540863dd78b7435e6a6d Mon Sep 17 00:00:00 2001 From: varjolintu Date: Thu, 30 Aug 2018 13:40:41 +0300 Subject: [PATCH 06/84] Fix checking subdomains --- src/browser/BrowserService.cpp | 27 +++++++++++++++++++++++++-- src/browser/BrowserService.h | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 086c15062..25c9f21b2 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -388,7 +388,7 @@ QList BrowserService::searchEntries(Database* db, const QString& hostnam return entries; } - for (Entry* entry : EntrySearcher().search(hostname, rootGroup, Qt::CaseInsensitive)) { + for (Entry* entry : EntrySearcher().search(baseDomain(hostname), rootGroup, Qt::CaseInsensitive)) { QString entryUrl = entry->url(); QUrl entryQUrl(entryUrl); QString entryScheme = entryQUrl.scheme(); @@ -401,7 +401,7 @@ QList BrowserService::searchEntries(Database* db, const QString& hostnam } // Filter to match hostname in URL field - if ((!entryUrl.isEmpty() && hostname.contains(entryUrl)) + if ((!entryUrl.isEmpty() && hostname.contains(entryUrl)) || (matchUrlScheme(entryUrl) && hostname.endsWith(entryQUrl.host()))) { entries.append(entry); } @@ -780,6 +780,29 @@ bool BrowserService::removeFirstDomain(QString& hostname) return false; } +/** + * Gets the base domain of URL. + * + * Returns the base domain, e.g. https://another.example.co.uk -> example.co.uk + */ +QString BrowserService::baseDomain(const QString& url) const +{ + QUrl qurl = QUrl::fromUserInput(url); + QString hostname = qurl.host(); + + if (hostname.isEmpty() || !hostname.contains(qurl.topLevelDomain())) { + return {}; + } + + // Remove the top level domain part from the hostname, e.g. https://another.example.co.uk -> https://another.example + hostname.chop(qurl.topLevelDomain().length()); + // Split the URL and select the last part, e.g. https://another.example -> example + QString baseDomain = hostname.split('.').last(); + // Append the top level domain back to the URL, e.g. example -> example.co.uk + baseDomain.append(qurl.topLevelDomain()); + return baseDomain; +} + Database* BrowserService::getDatabase() { if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) { diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 10c3a786e..56f74bd38 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -101,6 +101,7 @@ private: 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(); private: From bb16dc6d016b53d06a951724916bc41498ae63df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20E=2E=20Garc=C3=ADa?= Date: Fri, 19 Oct 2018 12:42:49 -0600 Subject: [PATCH 07/84] Add QR code generator for TOTP export (#1167) * Resolves #764 * Add libqrencode and qtsvg dependencies * Ensure QR code remains square * Auto-close QR code dialog when database is locked * Add databaseLocked() Signal to databaseWidget * Correct otpauth URI output in Totp::writeSettings(...) --- CMakeLists.txt | 8 +- Dockerfile | 6 +- ci/trusty/Dockerfile | 4 +- cmake/FindQREncode.cmake | 22 +++++ snapcraft.yaml | 2 + src/CMakeLists.txt | 11 ++- src/core/Entry.cpp | 2 +- src/gui/DatabaseWidget.cpp | 14 +++ src/gui/DatabaseWidget.h | 2 + src/gui/MainWindow.cpp | 2 + src/gui/MainWindow.ui | 8 +- src/gui/SquareSvgWidget.cpp | 28 ++++++ src/gui/SquareSvgWidget.h | 33 +++++++ src/gui/TotpExportSettingsDialog.cpp | 117 ++++++++++++++++++++++++ src/gui/TotpExportSettingsDialog.h | 57 ++++++++++++ src/qrcode/CMakeLists.txt | 21 +++++ src/qrcode/QrCode.cpp | 132 +++++++++++++++++++++++++++ src/qrcode/QrCode.h | 78 ++++++++++++++++ src/qrcode/QrCode_p.h | 33 +++++++ src/totp/totp.cpp | 17 +++- src/totp/totp.h | 3 +- 21 files changed, 584 insertions(+), 16 deletions(-) create mode 100644 cmake/FindQREncode.cmake create mode 100644 src/gui/SquareSvgWidget.cpp create mode 100644 src/gui/SquareSvgWidget.h create mode 100644 src/gui/TotpExportSettingsDialog.cpp create mode 100644 src/gui/TotpExportSettingsDialog.h create mode 100644 src/qrcode/CMakeLists.txt create mode 100644 src/qrcode/QrCode.cpp create mode 100644 src/qrcode/QrCode.h create mode 100644 src/qrcode/QrCode_p.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0edd766ff..41a6b9403 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,16 +294,16 @@ endif(WITH_TESTS) include(CLangFormat) if(UNIX AND NOT APPLE) - find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Test LinguistTools DBus REQUIRED) + find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools DBus REQUIRED) elseif(APPLE) - find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Test LinguistTools REQUIRED + find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools REQUIRED HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH ) find_package(Qt5 COMPONENTS MacExtras HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH ) else() - find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Test LinguistTools REQUIRED) + find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools REQUIRED) endif() if(Qt5Core_VERSION VERSION_LESS "5.2.0") @@ -339,6 +339,8 @@ find_package(Argon2 REQUIRED) find_package(ZLIB REQUIRED) +find_package(QREncode REQUIRED) + set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR}) if(ZLIB_VERSION_STRING VERSION_LESS "1.2.0") diff --git a/Dockerfile b/Dockerfile index 89ee04464..1054872ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ FROM ubuntu:14.04 -ENV REBUILD_COUNTER=8 +ENV REBUILD_COUNTER=10 ENV QT5_VERSION=qt510 ENV QT5_PPA_VERSION=qt-5.10.1 @@ -50,12 +50,14 @@ RUN set -x \ ${QT5_VERSION}x11extras \ ${QT5_VERSION}translations \ ${QT5_VERSION}imageformats \ + ${QT5_VERSION}svg \ zlib1g-dev \ libxi-dev \ libxtst-dev \ mesa-common-dev \ libyubikey-dev \ - libykpers-1-dev + libykpers-1-dev \ + libqrencode-dev ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}" ENV CMAKE_PREFIX_PATH="/opt/${QT5_VERSION}/lib/cmake" diff --git a/ci/trusty/Dockerfile b/ci/trusty/Dockerfile index 04aee25a5..cd69639d2 100644 --- a/ci/trusty/Dockerfile +++ b/ci/trusty/Dockerfile @@ -18,7 +18,7 @@ FROM ubuntu:14.04 -ENV REBUILD_COUNTER=4 +ENV REBUILD_COUNTER=5 ENV QT5_VERSION=qt53 ENV QT5_PPA_VERSION=${QT5_VERSION}2 @@ -49,11 +49,13 @@ RUN set -x \ ${QT5_VERSION}tools \ ${QT5_VERSION}x11extras \ ${QT5_VERSION}translations \ + ${QT5_VERSION}svg \ zlib1g-dev \ libyubikey-dev \ libykpers-1-dev \ libxi-dev \ libxtst-dev \ + libqrencode-dev \ xvfb ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}" diff --git a/cmake/FindQREncode.cmake b/cmake/FindQREncode.cmake new file mode 100644 index 000000000..6328d9699 --- /dev/null +++ b/cmake/FindQREncode.cmake @@ -0,0 +1,22 @@ +# Copyright (C) 2017 KeePassXC Team +# +# 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 +# the Free Software Foundation, either version 2 or (at your option) +# version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +find_path(QRENCODE_INCLUDE_DIR qrencode.h) +find_library(QRENCODE_LIBRARY qrencode) + +mark_as_advanced(QRENCODE_LIBRARY QRENCODE_INCLUDE_DIR) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QREncode DEFAULT_MSG QRENCODE_LIBRARY QRENCODE_INCLUDE_DIR) diff --git a/snapcraft.yaml b/snapcraft.yaml index d9321d748..d9c08ec9c 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -39,6 +39,7 @@ parts: - libgcrypt20-dev - libqt5x11extras5-dev - qtbase5-dev + - qtsvg5-dev - qttools5-dev - qttools5-dev-tools - zlib1g-dev @@ -48,6 +49,7 @@ parts: - libykpers-1-dev - libsodium-dev - libargon2-0-dev + - libqrencode-dev stage-packages: - dbus - qttranslations5-l10n # common translations diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51add8968..c70de316b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,8 +127,10 @@ set(keepassx_SOURCES gui/ApplicationSettingsWidget.cpp gui/SearchWidget.cpp gui/SortFilterHideProxyModel.cpp + gui/SquareSvgWidget.cpp gui/TotpSetupDialog.cpp gui/TotpDialog.cpp + gui/TotpExportSettingsDialog.cpp gui/UnlockDatabaseWidget.cpp gui/UnlockDatabaseDialog.cpp gui/WelcomeWidget.cpp @@ -225,6 +227,8 @@ endif() add_subdirectory(autotype) add_subdirectory(cli) +add_subdirectory(qrcode) +set(qrcode_LIB qrcode) add_subdirectory(sshagent) if(WITH_XC_SSHAGENT) @@ -270,9 +274,10 @@ target_link_libraries(keepassx_core autotype ${keepassxcbrowser_LIB} ${sshagent_LIB} + ${qrcode_LIB} Qt5::Core - Qt5::Network Qt5::Concurrent + Qt5::Network Qt5::Widgets ${CURL_LIBRARIES} ${YUBIKEY_LIBRARIES} @@ -280,7 +285,9 @@ target_link_libraries(keepassx_core ${ARGON2_LIBRARIES} ${GCRYPT_LIBRARIES} ${GPGERROR_LIBRARIES} - ${ZLIB_LIBRARIES}) + ${YUBIKEY_LIBRARIES} + ${ZLIB_LIBRARIES} + ${ZXCVBN_LIBRARIES}) if(APPLE) target_link_libraries(keepassx_core "-framework Foundation") diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 929447f9c..6ce613d91 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -369,7 +369,7 @@ void Entry::setTotp(QSharedPointer settings) beginUpdate(); m_data.totpSettings = settings; - auto text = Totp::writeSettings(m_data.totpSettings); + auto text = Totp::writeSettings(m_data.totpSettings, title(), username()); if (m_attributes->hasKey(Totp::ATTRIBUTE_OTP)) { m_attributes->set(Totp::ATTRIBUTE_OTP, text, true); } else { diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 1e3854998..869afbd24 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -49,6 +49,7 @@ #include "gui/MessageBox.h" #include "gui/TotpSetupDialog.h" #include "gui/TotpDialog.h" +#include "gui/TotpExportSettingsDialog.h" #include "gui/UnlockDatabaseDialog.h" #include "gui/UnlockDatabaseWidget.h" #include "gui/entry/EditEntryWidget.h" @@ -572,6 +573,18 @@ void DatabaseWidget::copyAttribute(QAction* action) currentEntry->resolveMultiplePlaceholders(currentEntry->attributes()->value(action->data().toString()))); } +void DatabaseWidget::showTotpKeyQrCode() +{ + Entry* currentEntry = m_entryView->currentEntry(); + Q_ASSERT(currentEntry); + if (!currentEntry) { + return; + } + + auto totpDisplayDialog = new TotpExportSettingsDialog(this, currentEntry); + totpDisplayDialog->open(); +} + void DatabaseWidget::setClipboardTextAndMinimize(const QString& text) { clipboard()->setText(text); @@ -1171,6 +1184,7 @@ void DatabaseWidget::lock() Database* newDb = new Database(); newDb->metadata()->setName(m_db->metadata()->name()); replaceDatabase(newDb); + emit lockedDatabase(); } void DatabaseWidget::updateFilePath(const QString& filePath) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index d1598041f..a15158c2c 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -125,6 +125,7 @@ signals: void pressedEntry(Entry* selectedEntry); void pressedGroup(Group* selectedGroup); void unlockedDatabase(); + void lockedDatabase(); void listModeAboutToActivate(); void listModeActivated(); void searchModeAboutToActivate(); @@ -146,6 +147,7 @@ public slots: void copyNotes(); void copyAttribute(QAction* action); void showTotp(); + void showTotpKeyQrCode(); void copyTotp(); void setupTotp(); void performAutoType(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 733ac0163..a729f588e 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -287,6 +287,7 @@ MainWindow::MainWindow() m_actionMultiplexer.connect(m_ui->actionEntrySetupTotp, SIGNAL(triggered()), SLOT(setupTotp())); m_actionMultiplexer.connect(m_ui->actionEntryCopyTotp, SIGNAL(triggered()), SLOT(copyTotp())); + m_actionMultiplexer.connect(m_ui->actionEntryTotpQRCode, SIGNAL(triggered()), SLOT(showTotpKeyQrCode())); m_actionMultiplexer.connect(m_ui->actionEntryCopyTitle, SIGNAL(triggered()), SLOT(copyTitle())); m_actionMultiplexer.connect(m_ui->actionEntryCopyUsername, SIGNAL(triggered()), SLOT(copyUsername())); m_actionMultiplexer.connect(m_ui->actionEntryCopyPassword, SIGNAL(triggered()), SLOT(copyPassword())); @@ -478,6 +479,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp()); m_ui->actionEntryCopyTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp()); m_ui->actionEntrySetupTotp->setEnabled(singleEntrySelected); + m_ui->actionEntryTotpQRCode->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp()); m_ui->actionGroupNew->setEnabled(groupSelected); m_ui->actionGroupEdit->setEnabled(groupSelected); m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup()); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 9f6041312..2c74c706e 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -251,6 +251,7 @@ + @@ -577,7 +578,12 @@ - Show TOTP + Show TOTP... + + + + + Show TOTP QR Code... diff --git a/src/gui/SquareSvgWidget.cpp b/src/gui/SquareSvgWidget.cpp new file mode 100644 index 000000000..5a907e95b --- /dev/null +++ b/src/gui/SquareSvgWidget.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "SquareSvgWidget.h" + +bool SquareSvgWidget::hasHeightForWidth() const +{ + return true; +} + +int SquareSvgWidget::heightForWidth(int width) const +{ + return width; +} diff --git a/src/gui/SquareSvgWidget.h b/src/gui/SquareSvgWidget.h new file mode 100644 index 000000000..3fcbbbffb --- /dev/null +++ b/src/gui/SquareSvgWidget.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_SquareSvgWidget_H +#define KEEPASSX_SquareSvgWidget_H + +#include + +class SquareSvgWidget : public QSvgWidget +{ +public: + SquareSvgWidget() = default; + ~SquareSvgWidget() override = default; + + bool hasHeightForWidth() const override; + int heightForWidth(int width) const override; +}; + +#endif // KEEPASSX_SquareSvgWidget_H diff --git a/src/gui/TotpExportSettingsDialog.cpp b/src/gui/TotpExportSettingsDialog.cpp new file mode 100644 index 000000000..dc7fcafd7 --- /dev/null +++ b/src/gui/TotpExportSettingsDialog.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TotpExportSettingsDialog.h" +#include "core/Config.h" +#include "core/Entry.h" +#include "gui/Clipboard.h" +#include "gui/DatabaseWidget.h" +#include "gui/SquareSvgWidget.h" +#include "qrcode/QrCode.h" +#include "totp/totp.h" + +#include +#include +#include +#include +#include +#include +#include + +TotpExportSettingsDialog::TotpExportSettingsDialog(DatabaseWidget* parent, Entry* entry) + : QDialog(parent) + , m_timer(new QTimer(this)) + , m_verticalLayout(new QVBoxLayout()) + , m_totpSvgWidget(new SquareSvgWidget()) + , m_countDown(new QLabel()) + , m_warningLabel(new QLabel()) + , m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Close | QDialogButtonBox::Ok)) +{ + m_verticalLayout->addWidget(m_warningLabel); + m_verticalLayout->addItem(new QSpacerItem(0, 0)); + + m_verticalLayout->addStretch(0); + m_verticalLayout->addWidget(m_totpSvgWidget); + m_verticalLayout->addStretch(0); + m_verticalLayout->addWidget(m_countDown); + m_verticalLayout->addWidget(m_buttonBox); + + setLayout(m_verticalLayout); + setAttribute(Qt::WA_DeleteOnClose); + + connect(m_buttonBox, SIGNAL(rejected()), SLOT(close())); + connect(m_buttonBox, SIGNAL(accepted()), SLOT(copyToClipboard())); + connect(m_timer, SIGNAL(timeout()), this, SLOT(autoClose())); + connect(parent, SIGNAL(lockedDatabase()), this, SLOT(close())); + + m_buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Copy")); + m_countDown->setAlignment(Qt::AlignCenter); + + m_secTillClose = 45; + autoClose(); + m_timer->start(1000); + + const auto totpSettings = entry->totpSettings(); + if (totpSettings->custom || !totpSettings->encoder.shortName.isEmpty()) { + m_warningLabel->setWordWrap(true); + m_warningLabel->setMargin(5); + m_warningLabel->setText(tr("NOTE: These TOTP settings are custom and may not work with other authenticators.", + "TOTP QR code dialog warning")); + } else { + m_warningLabel->hide(); + } + + m_totpUri = Totp::writeSettings(entry->totpSettings(), entry->title(), entry->username(), true); + const QrCode qrc(m_totpUri); + + if (qrc.isValid()) { + QBuffer buffer; + qrc.writeSvg(&buffer, logicalDpiX()); + m_totpSvgWidget->load(buffer.data()); + const int minsize = static_cast(logicalDpiX() * 2.5); + m_totpSvgWidget->setMinimumSize(minsize, minsize); + } else { + auto errorBox = new QMessageBox(parent); + errorBox->setAttribute(Qt::WA_DeleteOnClose); + errorBox->setIcon(QMessageBox::Warning); + errorBox->setText(tr("There was an error creating the QR code.")); + errorBox->exec(); + close(); + } + + show(); +} + +void TotpExportSettingsDialog::copyToClipboard() +{ + clipboard()->setText(m_totpUri); + if (config()->get("MinimizeOnCopy").toBool()) { + static_cast(parent())->window()->showMinimized(); + } +} + +void TotpExportSettingsDialog::autoClose() +{ + if (--m_secTillClose > 0) { + m_countDown->setText(tr("Closing in %1 seconds.").arg(m_secTillClose)); + } else { + m_timer->stop(); + close(); + } +} + +TotpExportSettingsDialog::~TotpExportSettingsDialog() = default; diff --git a/src/gui/TotpExportSettingsDialog.h b/src/gui/TotpExportSettingsDialog.h new file mode 100644 index 000000000..7797533d0 --- /dev/null +++ b/src/gui/TotpExportSettingsDialog.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_TotpExportSettingsDialog_H +#define KEEPASSX_TotpExportSettingsDialog_H + +#include "core/Database.h" +#include "core/Entry.h" +#include "gui/DatabaseWidget.h" +#include +#include +#include + +class QVBoxLayout; +class SquareSvgWidget; +class QLabel; +class QDialogButtonBox; + +class TotpExportSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TotpExportSettingsDialog(DatabaseWidget* parent = nullptr, Entry* entry = nullptr); + ~TotpExportSettingsDialog(); + +private slots: + void copyToClipboard(); + void autoClose(); + +private: + int m_secTillClose; + QString m_totpUri; + QTimer* m_timer; + + QVBoxLayout* m_verticalLayout; + SquareSvgWidget* m_totpSvgWidget; + QLabel* m_countDown; + QLabel* m_warningLabel; + QDialogButtonBox* m_buttonBox; +}; + +#endif // KEEPASSX_TOTPEXPORTSETTINGSDIALOG_H diff --git a/src/qrcode/CMakeLists.txt b/src/qrcode/CMakeLists.txt new file mode 100644 index 000000000..9ffcf5808 --- /dev/null +++ b/src/qrcode/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2017 KeePassXC Team +# +# 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 +# the Free Software Foundation, either version 2 or (at your option) +# version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(qrcode_SOURCES + QrCode.cpp +) + +add_library(qrcode STATIC ${qrcode_SOURCES}) +target_link_libraries(qrcode Qt5::Core Qt5::Widgets Qt5::Svg ${QRENCODE_LIBRARY}) diff --git a/src/qrcode/QrCode.cpp b/src/qrcode/QrCode.cpp new file mode 100644 index 000000000..961bcfa8d --- /dev/null +++ b/src/qrcode/QrCode.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "QrCode.h" +#include "QrCode_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QrCodePrivate::QrCodePrivate() + : m_qrcode(nullptr) +{ +} + +QrCodePrivate::~QrCodePrivate() +{ + if (m_qrcode) { + QRcode_free(m_qrcode); + } +} + +QrCode::QrCode() + : d_ptr(new QrCodePrivate()) +{ +} + +QrCode::QrCode(const QString& data, const Version version, const ErrorCorrectionLevel ecl, const bool caseSensitive) + : d_ptr(new QrCodePrivate()) +{ + init(data, version, ecl, caseSensitive); +} + +QrCode::QrCode(const QByteArray& data, const Version version, const ErrorCorrectionLevel ecl) + : d_ptr(new QrCodePrivate()) +{ + init(data, version, ecl); +} + +QrCode::~QrCode() = default; + +void QrCode::init(const QString& data, const Version version, const ErrorCorrectionLevel ecl, bool caseSensitive) +{ + if (data.isEmpty()) { + return; + } + + d_ptr->m_qrcode = QRcode_encodeString(data.toLocal8Bit().data(), + static_cast(version), + static_cast(ecl), + QR_MODE_8, + caseSensitive ? 1 : 0); +} + +void QrCode::init(const QByteArray& data, const Version version, const ErrorCorrectionLevel ecl) +{ + if (data.isEmpty()) { + return; + } + + d_ptr->m_qrcode = QRcode_encodeData(data.size(), + reinterpret_cast(data.data()), + static_cast(version), + static_cast(ecl)); +} + +bool QrCode::isValid() const +{ + return d_ptr->m_qrcode != nullptr; +} + +void QrCode::writeSvg(QIODevice* outputDevice, const int dpi, const int margin) const +{ + if (margin < 0 || d_ptr->m_qrcode == nullptr || outputDevice == nullptr) { + return; + } + + const int width = d_ptr->m_qrcode->width + margin * 2; + + QSvgGenerator generator; + generator.setSize(QSize(width, width)); + generator.setViewBox(QRect(0, 0, width, width)); + generator.setResolution(dpi); + generator.setOutputDevice(outputDevice); + + QPainter painter; + painter.begin(&generator); + + // Background + painter.setClipRect(QRect(0, 0, width, width)); + painter.fillRect(QRect(0, 0, width, width), Qt::white); + + // Foreground + // "Dots" are stored in a quint8 x quint8 array using row-major order. + // A dot is black if the LSB of its corresponding quint8 is 1. + const QPen pen(Qt::black, 0, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin); + const QBrush brush(Qt::black); + painter.setPen(pen); + painter.setBrush(brush); + + const int rowSize = d_ptr->m_qrcode->width; + unsigned char* dot = d_ptr->m_qrcode->data; + for (int y = 0; y < rowSize; ++y) { + for (int x = 0; x < rowSize; ++x) { + if (quint8(0x01) == (static_cast(*dot++) & quint8(0x01))) { + painter.drawRect(margin + x, margin + y, 1, 1); + } + } + } + + painter.end(); +} diff --git a/src/qrcode/QrCode.h b/src/qrcode/QrCode.h new file mode 100644 index 000000000..ffb49d139 --- /dev/null +++ b/src/qrcode/QrCode.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_QRCODE_H +#define KEEPASSX_QRCODE_H + +#include +#include + +class QImage; +class QIODevice; +class QString; +class QByteArray; + +struct QrCodePrivate; + +class QrCode +{ + +public: + enum class ErrorCorrectionLevel : int + { + LOW = 0, + MEDIUM, + QUARTILE, + HIGH + }; + + // See: http://www.qrcode.com/en/about/version.html + // clang-format off + enum class Version : int + { + AUTO = 0, + V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, + V21, V22, V23, V24, V25, V26, V27, V28, V29, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V40 + }; + // clang-format on + + // Uses QRcode_encodeString (can't contain NUL characters) + explicit QrCode(const QString& data, + const Version version = Version::AUTO, + const ErrorCorrectionLevel ecl = ErrorCorrectionLevel::HIGH, + const bool caseSensitive = true); + + // Uses QRcode_encodeData (can contain NUL characters) + explicit QrCode(const QByteArray& data, + const Version version = Version::AUTO, + const ErrorCorrectionLevel ecl = ErrorCorrectionLevel::HIGH); + + QrCode(); + ~QrCode(); + + bool isValid() const; + void writeSvg(QIODevice* outputDevice, const int dpi, const int margin = 4) const; + +private: + void init(const QString& data, const Version version, const ErrorCorrectionLevel ecl, const bool caseSensitive); + + void init(const QByteArray& data, const Version version, const ErrorCorrectionLevel ecl); + + QScopedPointer d_ptr; +}; + +#endif // KEEPASSX_QRCODE_H diff --git a/src/qrcode/QrCode_p.h b/src/qrcode/QrCode_p.h new file mode 100644 index 000000000..0161b7916 --- /dev/null +++ b/src/qrcode/QrCode_p.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This class exists to isolate from the rest of the code base. */ + +#ifndef KEEPASSX_QRCODEPRIVATE_H +#define KEEPASSX_QRCODEPRIVATE_H + +#include + +struct QrCodePrivate +{ + QRcode* m_qrcode; + + QrCodePrivate(); + ~QrCodePrivate(); +}; + +#endif // KEEPASSX_QRCODEPRIVATE_H diff --git a/src/totp/totp.cpp b/src/totp/totp.cpp index efd83c8aa..4140993c7 100644 --- a/src/totp/totp.cpp +++ b/src/totp/totp.cpp @@ -22,7 +22,8 @@ #include #include -#include +#include +#include #include #include #include @@ -79,7 +80,7 @@ QSharedPointer Totp::parseSettings(const QString& rawSettings, c settings->step = qBound(1u, settings->step, 60u); // Detect custom settings, used by setup GUI - if (settings->encoder.shortName != STEAM_SHORTNAME + if (settings->encoder.shortName.isEmpty() && (settings->digits != DEFAULT_DIGITS || settings->step != DEFAULT_STEP)) { settings->custom = true; } @@ -96,15 +97,21 @@ QSharedPointer Totp::createSettings(const QString& key, const ui }); } -QString Totp::writeSettings(const QSharedPointer settings) +QString Totp::writeSettings(const QSharedPointer settings, const QString& title, const QString& username, bool forceOtp) { if (settings.isNull()) { return {}; } // OTP Url output - if (settings->otpUrl) { - auto urlstring = QString("key=%1&step=%2&size=%3").arg(settings->key).arg(settings->step).arg(settings->digits); + if (settings->otpUrl || forceOtp) { + auto urlstring = QString("otpauth://totp/%1:%2?secret=%3&period=%4&digits=%5&issuer=%1") + .arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title))) + .arg(username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username))) + .arg(QString(Base32::sanitizeInput(settings->key.toLatin1()))) + .arg(settings->step) + .arg(settings->digits); + if (!settings->encoder.name.isEmpty()) { urlstring.append("&encoder=").append(settings->encoder.name); } diff --git a/src/totp/totp.h b/src/totp/totp.h index b4a592918..ba11ba2b0 100644 --- a/src/totp/totp.h +++ b/src/totp/totp.h @@ -60,7 +60,8 @@ static const QString ATTRIBUTE_SETTINGS = "TOTP Settings"; QSharedPointer parseSettings(const QString& rawSettings, const QString& key = {}); QSharedPointer createSettings(const QString& key, const uint digits, const uint step, const QString& encoderShortName = {}); -QString writeSettings(const QSharedPointer settings); +QString writeSettings(const QSharedPointer settings, const QString& title = {}, + const QString& username = {}, bool forceOtp = false); QString generateTotp(const QSharedPointer settings, const quint64 time = 0ull); From b8d2d5d877a407477ade9c26809211c40367ed9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20V=C3=A4nttinen?= Date: Fri, 19 Oct 2018 21:44:08 +0300 Subject: [PATCH 08/84] Choose database for saving or updating entries from KeePassXC-Browser (#2391) --- src/browser/BrowserAccessControlDialog.cpp | 16 ++--- src/browser/BrowserAccessControlDialog.h | 2 +- src/browser/BrowserAction.cpp | 2 +- src/browser/BrowserEntrySaveDialog.cpp | 79 ++++++++++++++++++++++ src/browser/BrowserEntrySaveDialog.h | 49 ++++++++++++++ src/browser/BrowserEntrySaveDialog.ui | 62 +++++++++++++++++ src/browser/BrowserService.cpp | 76 ++++++++++++++++++--- src/browser/BrowserService.h | 9 ++- src/browser/CMakeLists.txt | 1 + src/gui/DatabaseTabWidget.cpp | 5 ++ src/gui/DatabaseTabWidget.h | 2 +- src/gui/DatabaseWidget.cpp | 20 ++++++ src/gui/DatabaseWidget.h | 6 ++ 13 files changed, 307 insertions(+), 22 deletions(-) create mode 100644 src/browser/BrowserEntrySaveDialog.cpp create mode 100644 src/browser/BrowserEntrySaveDialog.h create mode 100755 src/browser/BrowserEntrySaveDialog.ui diff --git a/src/browser/BrowserAccessControlDialog.cpp b/src/browser/BrowserAccessControlDialog.cpp index 51abf2e95..e4205f811 100644 --- a/src/browser/BrowserAccessControlDialog.cpp +++ b/src/browser/BrowserAccessControlDialog.cpp @@ -22,13 +22,13 @@ BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent) : QDialog(parent) - , ui(new Ui::BrowserAccessControlDialog()) + , m_ui(new Ui::BrowserAccessControlDialog()) { this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); - ui->setupUi(this); - connect(ui->allowButton, SIGNAL(clicked()), this, SLOT(accept())); - connect(ui->denyButton, SIGNAL(clicked()), this, SLOT(reject())); + m_ui->setupUi(this); + connect(m_ui->allowButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_ui->denyButton, SIGNAL(clicked()), this, SLOT(reject())); } BrowserAccessControlDialog::~BrowserAccessControlDialog() @@ -37,7 +37,7 @@ BrowserAccessControlDialog::~BrowserAccessControlDialog() void BrowserAccessControlDialog::setUrl(const QString& url) { - ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n" + m_ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n" "Please select whether you want to allow access.")) .arg(QUrl(url).host())); } @@ -45,16 +45,16 @@ void BrowserAccessControlDialog::setUrl(const QString& url) void BrowserAccessControlDialog::setItems(const QList& items) { for (Entry* entry : items) { - ui->itemsList->addItem(entry->title() + " - " + entry->username()); + m_ui->itemsList->addItem(entry->title() + " - " + entry->username()); } } bool BrowserAccessControlDialog::remember() const { - return ui->rememberDecisionCheckBox->isChecked(); + return m_ui->rememberDecisionCheckBox->isChecked(); } void BrowserAccessControlDialog::setRemember(bool r) { - ui->rememberDecisionCheckBox->setChecked(r); + m_ui->rememberDecisionCheckBox->setChecked(r); } diff --git a/src/browser/BrowserAccessControlDialog.h b/src/browser/BrowserAccessControlDialog.h index 9f24a1e16..65cfe5e97 100644 --- a/src/browser/BrowserAccessControlDialog.h +++ b/src/browser/BrowserAccessControlDialog.h @@ -43,7 +43,7 @@ public: void setRemember(bool r); private: - QScopedPointer ui; + QScopedPointer m_ui; }; #endif // BROWSERACCESSCONTROLDIALOG_H diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 60cf8506a..c073bb4b2 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -322,7 +322,7 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString if (uuid.isEmpty()) { m_browserService.addEntry(id, login, password, url, submitUrl, realm); } else { - m_browserService.updateEntry(id, uuid, login, password, url); + m_browserService.updateEntry(id, uuid, login, password, url, submitUrl); } const QString newNonce = incrementNonce(nonce); diff --git a/src/browser/BrowserEntrySaveDialog.cpp b/src/browser/BrowserEntrySaveDialog.cpp new file mode 100644 index 000000000..384482b84 --- /dev/null +++ b/src/browser/BrowserEntrySaveDialog.cpp @@ -0,0 +1,79 @@ +/* +* Copyright (C) 2013 Francois Ferrand +* Copyright (C) 2018 KeePassXC Team +* +* 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 +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#include "BrowserEntrySaveDialog.h" +#include "ui_BrowserEntrySaveDialog.h" + +BrowserEntrySaveDialog::BrowserEntrySaveDialog(QWidget* parent) + : QDialog(parent) + , m_ui(new Ui::BrowserEntrySaveDialog()) +{ + this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + + m_ui->setupUi(this); + connect(m_ui->okButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + + m_ui->itemsList->setSelectionMode(QAbstractItemView::SingleSelection); + m_ui->label->setText(QString(tr("You have multiple databases open.\n" + "Please select the correct database for saving credentials."))); +} + +BrowserEntrySaveDialog::~BrowserEntrySaveDialog() +{ +} + +int BrowserEntrySaveDialog::setItems(QList& databaseWidgets, DatabaseWidget* currentWidget) const +{ + uint counter = 0; + int activeIndex = -1; + for (const auto dbWidget : databaseWidgets) { + QString databaseName = dbWidget->getDatabaseName(); + QString databaseFileName = dbWidget->getDatabaseFileName(); + + QListWidgetItem* item = new QListWidgetItem(); + item->setData(Qt::UserRole, counter); + + // Show database name (and filename if the name has been set in metadata) + if (databaseName == databaseFileName) { + item->setText(databaseFileName); + } else { + item->setText(QString("%1 (%2)").arg(databaseName, databaseFileName)); + } + + if (currentWidget == dbWidget) { + activeIndex = counter; + } + + m_ui->itemsList->addItem(item); + ++counter; + } + + // This must be done after the whole list is filled + if (activeIndex >= 0) { + m_ui->itemsList->item(activeIndex)->setSelected(true); + } + + m_ui->itemsList->selectAll(); + return databaseWidgets.length(); +} + + QList BrowserEntrySaveDialog::getSelected() const + { + return m_ui->itemsList->selectedItems(); + } diff --git a/src/browser/BrowserEntrySaveDialog.h b/src/browser/BrowserEntrySaveDialog.h new file mode 100644 index 000000000..0b1db7271 --- /dev/null +++ b/src/browser/BrowserEntrySaveDialog.h @@ -0,0 +1,49 @@ +/* +* Copyright (C) 2013 Francois Ferrand +* Copyright (C) 2018 KeePassXC Team +* +* 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 +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#ifndef BROWSERENTRYSAVEDIALOG_H +#define BROWSERENTRYSAVEDIALOG_H + +#include +#include +#include +#include "gui/DatabaseTabWidget.h" + +class Entry; + +namespace Ui +{ + class BrowserEntrySaveDialog; +} + +class BrowserEntrySaveDialog : public QDialog +{ + Q_OBJECT + +public: + explicit BrowserEntrySaveDialog(QWidget* parent = nullptr); + ~BrowserEntrySaveDialog() override; + + int setItems(QList& databaseWidgets, DatabaseWidget* currentWidget) const; + QList getSelected() const; + +private: + QScopedPointer m_ui; +}; + +#endif // BROWSERENTRYSAVEDIALOG_H diff --git a/src/browser/BrowserEntrySaveDialog.ui b/src/browser/BrowserEntrySaveDialog.ui new file mode 100755 index 000000000..5beb6fab8 --- /dev/null +++ b/src/browser/BrowserEntrySaveDialog.ui @@ -0,0 +1,62 @@ + + + BrowserEntrySaveDialog + + + + 0 + 0 + 400 + 221 + + + + KeePassXC-Browser Save Entry + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ok + + + + + + + Cancel + + + + + + + + + + diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 25c9f21b2..9a0f07f28 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -26,6 +26,7 @@ #include "BrowserService.h" #include "BrowserAccessControlDialog.h" #include "BrowserEntryConfig.h" +#include "BrowserEntrySaveDialog.h" #include "BrowserSettings.h" #include "core/Database.h" #include "core/EntrySearcher.h" @@ -289,14 +290,33 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, return result; } -void BrowserService::addEntry(const QString&, +void BrowserService::addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, - const QString& realm) + const QString& realm, + Database* selectedDb) { - Group* group = findCreateAddEntryGroup(); + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, + "addEntry", + Qt::BlockingQueuedConnection, + Q_ARG(const QString&, id), + Q_ARG(const QString&, login), + Q_ARG(const QString&, password), + Q_ARG(const QString&, url), + Q_ARG(const QString&, submitUrl), + Q_ARG(const QString&, realm), + Q_ARG(Database*, selectedDb)); + } + + Database* db = selectedDb ? selectedDb : selectedDatabase(); + if (!db) { + return; + } + + Group* group = findCreateAddEntryGroup(db); if (!group) { return; } @@ -328,7 +348,8 @@ void BrowserService::updateEntry(const QString& id, const QString& uuid, const QString& login, const QString& password, - const QString& url) + const QString& url, + const QString& submitUrl) { if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, @@ -338,16 +359,19 @@ void BrowserService::updateEntry(const QString& id, Q_ARG(const QString&, uuid), Q_ARG(const QString&, login), Q_ARG(const QString&, password), - Q_ARG(const QString&, url)); + Q_ARG(const QString&, url), + Q_ARG(const QString&, submitUrl)); } - Database* db = getDatabase(); + Database* db = selectedDatabase(); if (!db) { return; } Entry* entry = db->resolveEntry(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); return; } @@ -679,9 +703,9 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri return Unknown; } -Group* BrowserService::findCreateAddEntryGroup() +Group* BrowserService::findCreateAddEntryGroup(Database* selectedDb) { - Database* db = getDatabase(); + Database* db = selectedDb ? selectedDb : getDatabase(); if (!db) { return nullptr; } @@ -813,6 +837,42 @@ Database* BrowserService::getDatabase() return nullptr; } +Database* BrowserService::selectedDatabase() +{ + QList databaseWidgets; + for (int i = 0;; ++i) { + const auto dbStruct = m_dbTabWidget->indexDatabaseManagerStruct(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); + continue; + } + + // Break out if dbStruct.dbWidget is nullptr + break; + } + + BrowserEntrySaveDialog browserEntrySaveDialog; + int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_dbTabWidget->currentDatabaseWidget()); + if (openDatabaseCount > 1) { + int res = browserEntrySaveDialog.exec(); + if (res == QDialog::Accepted) { + const auto selectedDatabase = browserEntrySaveDialog.getSelected(); + if (selectedDatabase.length() > 0) { + int index = selectedDatabase[0]->data(Qt::UserRole).toUInt(); + return databaseWidgets[index]->database(); + } + } else { + return nullptr; + } + } + + // Return current database + return getDatabase(); +} + void BrowserService::databaseLocked(DatabaseWidget* dbWidget) { if (dbWidget) { diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 56f74bd38..165901a87 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -51,7 +51,8 @@ public: const QString& password, const QString& url, const QString& submitUrl, - const QString& realm); + const QString& realm, + Database* selectedDb = nullptr); QList searchEntries(Database* db, const QString& hostname, const QString& url); QList searchEntries(const QString& url, const StringPairList& keyList); void removeSharedEncryptionKeys(); @@ -68,7 +69,8 @@ public slots: const QString& uuid, const QString& login, const QString& password, - const QString& url); + const QString& url, + const QString& submitUrl); void databaseLocked(DatabaseWidget* dbWidget); void databaseUnlocked(DatabaseWidget* dbWidget); void activateDatabaseChanged(DatabaseWidget* dbWidget); @@ -96,13 +98,14 @@ 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(); + Group* findCreateAddEntryGroup(Database* selectedDb = nullptr); 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(); private: DatabaseTabWidget* const m_dbTabWidget; diff --git a/src/browser/CMakeLists.txt b/src/browser/CMakeLists.txt index 61215c181..bb5a01908 100755 --- a/src/browser/CMakeLists.txt +++ b/src/browser/CMakeLists.txt @@ -23,6 +23,7 @@ if(WITH_XC_BROWSER) BrowserAction.cpp BrowserClients.cpp BrowserEntryConfig.cpp + BrowserEntrySaveDialog.cpp BrowserOptionDialog.cpp BrowserService.cpp BrowserSettings.cpp diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 71bd74814..dd36ddae4 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -555,6 +555,7 @@ void DatabaseTabWidget::updateTabName(Database* db) const DatabaseManagerStruct& dbStruct = m_dbList.value(db); QString tabName; + QString fileName; if (dbStruct.fileInfo.exists()) { if (db->metadata()->name().isEmpty()) { @@ -563,6 +564,7 @@ void DatabaseTabWidget::updateTabName(Database* db) tabName = db->metadata()->name(); } + fileName = dbStruct.fileInfo.fileName(); setTabToolTip(index, dbStruct.fileInfo.absoluteFilePath()); } else { if (db->metadata()->name().isEmpty()) { @@ -580,6 +582,9 @@ void DatabaseTabWidget::updateTabName(Database* db) tabName.append("*"); } + dbStruct.dbWidget->setDatabaseName(tabName); + dbStruct.dbWidget->setDatabaseFileName(fileName); + setTabText(index, tabName); emit tabNameChanged(); } diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index dcb4a62da..87e171ab0 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -56,6 +56,7 @@ public: void mergeDatabase(const QString& fileName); DatabaseWidget* currentDatabaseWidget(); bool hasLockableDatabases() const; + DatabaseManagerStruct indexDatabaseManagerStruct(int index); static const int LastDatabasesCount; @@ -110,7 +111,6 @@ private: void deleteDatabase(Database* db); int databaseIndex(Database* db); Database* indexDatabase(int index); - DatabaseManagerStruct indexDatabaseManagerStruct(int index); Database* databaseFromDatabaseWidget(DatabaseWidget* dbWidget); void insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct); void updateLastDatabases(const QString& filename); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 869afbd24..c90fc52f7 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1487,6 +1487,26 @@ 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 a15158c2c..0e2bc96e8 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -112,6 +112,10 @@ public: 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); signals: void closeRequest(); @@ -238,6 +242,8 @@ private: QUuid m_entryBeforeLock; MessageWidget* m_messageWidget; EntryPreviewWidget* m_previewView; + QString m_databaseName; + QString m_databaseFileName; // Search state QString m_lastSearchText; From 18b22834c1d9657ab98ca5160571e51837bb6366 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 29 Sep 2018 14:55:33 +0200 Subject: [PATCH 09/84] Update and fix test coverage report generation Generation of unit test coverage reports used to be quite complicated and required a lot of different settings, including a custom CMake build type. This patch updates the coverage CMake module to only require -DWITH_COVERAGE=ON to be set on a normal Debug build in order to create a coverage target. This patch also moves away from lcov in favor of gcovr, since lcov appears to be broken in GCC 8. However, the routines for generating lcov reports still exist, so provided lcov receives updates and there is sufficient reason to switch back, it is easy to do so. --- CMakeLists.txt | 21 ++- cmake/CodeCoverage.cmake | 330 +++++++++++++++++++++++++-------------- src/crypto/Crypto.cpp | 2 +- 3 files changed, 232 insertions(+), 121 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41a6b9403..0f8a4b672 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,12 +204,6 @@ endif() if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align") - - if(WITH_COVERAGE) - # Include code coverage, use with -DCMAKE_BUILD_TYPE=Coverage - include(CodeCoverage) - setup_target_for_coverage(kp_coverage "make test" coverage) - endif() endif() if(CMAKE_COMPILER_IS_GNUCC) @@ -291,6 +285,21 @@ if(WITH_TESTS) enable_testing() endif(WITH_TESTS) +if(WITH_COVERAGE) + # Include code coverage, use with -DCMAKE_BUILD_TYPE=Debug + include(CodeCoverage) + set(COVERAGE_GCOVR_EXCLUDES + "\\(.+/\\)?tests/.\\*" + ".\\*/moc_\\[^/\\]+\\.cpp" + ".\\*/ui_\\[^/\\]+\\.h" + "\\(.+/\\)?zxcvbn/.\\*") + append_coverage_compiler_flags() + setup_target_for_coverage_gcovr_html( + NAME coverage + EXECUTABLE $(MAKE) && $(MAKE) test + ) +endif() + include(CLangFormat) if(UNIX AND NOT APPLE) diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index d10f79723..d10791745 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2012 - 2015, Lars Bilke +# Copyright (c) 2012 - 2017, Lars Bilke # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -26,7 +26,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -# +# CHANGES: # # 2012-01-31, Lars Bilke # - Enable Code Coverage @@ -35,167 +35,269 @@ # - Added support for Clang. # - Some additional usage instructions. # +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# # USAGE: - -# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here: -# http://stackoverflow.com/a/22404544/80480 # # 1. Copy this file into your cmake modules path. # # 2. Add the following line to your CMakeLists.txt: -# INCLUDE(CodeCoverage) +# include(CodeCoverage) # -# 3. Set compiler flags to turn off optimization and enable coverage: -# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") -# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") +# 3. Append necessary compiler flags: +# APPEND_COVERAGE_COMPILER_FLAGS() # -# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target -# which runs your test executable and produces a lcov code coverage report: +# 4. If you need to exclude additional directories from the report, specify them +# using the COVERAGE_LCOV_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE_LCOV. # Example: -# SETUP_TARGET_FOR_COVERAGE( -# my_coverage_target # Name for custom target. -# test_driver # Name of the test driver executable that runs the tests. -# # NOTE! This should always have a ZERO as exit code -# # otherwise the coverage generation will not complete. -# coverage # Name of output directory. -# ) +# set(COVERAGE_LCOV_EXCLUDES 'dir1/*' 'dir2/*') # -# 4. Build a Debug build: -# cmake -DCMAKE_BUILD_TYPE=Debug .. -# make -# make my_coverage_target +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. # +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target # +include(CMakeParseArguments) + # Check prereqs -FIND_PROGRAM( GCOV_PATH gcov ) -FIND_PROGRAM( LCOV_PATH lcov ) -FIND_PROGRAM( GENHTML_PATH genhtml ) -FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests) +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( SIMPLE_PYTHON_EXECUTABLE python ) -IF(NOT GCOV_PATH) - MESSAGE(FATAL_ERROR "gcov not found! Aborting...") -ENDIF() # NOT GCOV_PATH +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH -IF("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") - IF("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) - MESSAGE(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") - ENDIF() -ELSEIF(NOT CMAKE_COMPILER_IS_GNUCXX) - MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") -ENDIF() # CHECK VALID COMPILER +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() +elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") +endif() -SET(CMAKE_CXX_FLAGS_COVERAGE - "-g -O0 --coverage -fprofile-arcs -ftest-coverage" +set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage" + CACHE INTERNAL "") + +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE ) -SET(CMAKE_C_FLAGS_COVERAGE - "-g -O0 --coverage -fprofile-arcs -ftest-coverage" +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the C compiler during coverage builds." FORCE ) -SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used for linking binaries during coverage builds." FORCE ) -SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used by the shared libraries linker during coverage builds." FORCE ) -MARK_AS_ADVANCED( +mark_as_advanced( CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) -IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage")) - MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) -ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" +if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") +endif() -# Param _targetname The name of new the custom make target -# Param _testrunner The name of the target which runs the tests. -# MUST return ZERO always, even on errors. -# If not, no coverage report will be created! -# Param _outputname lcov output is generated as _outputname.info -# HTML report is generated in _outputname/index.html -# Optional fourth parameter is passed as arguments to _testrunner -# Pass them in list form, e.g.: "-j;2" for -j 2 -FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname) +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# SETUP_TARGET_FOR_COVERAGE_LCOV( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# ) +function(SETUP_TARGET_FOR_COVERAGE_LCOV) - IF(NOT LCOV_PATH) - MESSAGE(FATAL_ERROR "lcov not found! Aborting...") - ENDIF() # NOT LCOV_PATH + set(options NONE) + set(oneValueArgs NAME) + set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - IF(NOT GENHTML_PATH) - MESSAGE(FATAL_ERROR "genhtml not found! Aborting...") - ENDIF() # NOT GENHTML_PATH + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH - SET(coverage_info "${CMAKE_BINARY_DIR}/${_outputname}.info") - IF(MINGW) - # Replace C:/ with /C for MINGW - STRING(REGEX REPLACE "^([a-zA-Z]):" "/\\1" coverage_info ${coverage_info}) - ENDIF() - SET(coverage_cleaned "${coverage_info}.cleaned") + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH - SEPARATE_ARGUMENTS(test_command UNIX_COMMAND "${_testrunner}") + # Setup target + add_custom_target(${Coverage_NAME} - # Setup target - ADD_CUSTOM_TARGET(${_targetname} + # Cleanup lcov + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} -directory . --zerocounters + # Create baseline to make sure untouched files show up in the report + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base - # Cleanup lcov - ${LCOV_PATH} --directory . --zerocounters + # Run tests + COMMAND ${Coverage_EXECUTABLE} - # Run tests - COMMAND ${test_command} ${ARGV3} + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info + # add baseline counters + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total + COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_LCOV_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned + COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned + COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned - # Capturing lcov counters and generating report - COMMAND ${LCOV_PATH} --directory . --capture --output-file ${coverage_info} - COMMAND ${LCOV_PATH} --remove ${coverage_info} 'tests/*' '/usr/*' --output-file ${coverage_cleaned} - COMMAND ${GENHTML_PATH} -o ${_outputname} ${coverage_cleaned} - COMMAND ${CMAKE_COMMAND} -E remove ${coverage_info} ${coverage_cleaned} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." - ) + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ) - # Show info where to find the report - ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD - COMMAND ; - COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report." - ) + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) -ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE +endfunction() # SETUP_TARGET_FOR_COVERAGE_LCOV -# Param _targetname The name of new the custom make target -# Param _testrunner The name of the target which runs the tests -# Param _outputname cobertura output is generated as _outputname.xml -# Optional fourth parameter is passed as arguments to _testrunner -# Pass them in list form, e.g.: "-j;2" for -j 2 -FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname) +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# SETUP_TARGET_FOR_COVERAGE_GCOVR_XML( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# ) +function(SETUP_TARGET_FOR_COVERAGE_GCOVR_XML) - IF(NOT PYTHON_EXECUTABLE) - MESSAGE(FATAL_ERROR "Python not found! Aborting...") - ENDIF() # NOT PYTHON_EXECUTABLE + set(options NONE) + set(oneValueArgs NAME) + set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - IF(NOT GCOVR_PATH) - MESSAGE(FATAL_ERROR "gcovr not found! Aborting...") - ENDIF() # NOT GCOVR_PATH + if(NOT SIMPLE_PYTHON_EXECUTABLE) + message(FATAL_ERROR "python not found! Aborting...") + endif() # NOT SIMPLE_PYTHON_EXECUTABLE - ADD_CUSTOM_TARGET(${_targetname} + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH - # Run tests - ${_testrunner} ${ARGV3} + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDES "-e") + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() - # Running gcovr - COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Running gcovr to produce Cobertura code coverage report." - ) + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} - # Show info where to find the report - ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD - COMMAND ; - COMMENT "Cobertura code coverage report saved in ${_outputname}.xml." - ) + # Running gcovr + COMMAND ${GCOVR_PATH} --xml + -r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) -ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) + +endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_XML + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# ) +function(SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML) + + set(options NONE) + set(oneValueArgs NAME) + set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT SIMPLE_PYTHON_EXECUTABLE) + message(FATAL_ERROR "python not found! Aborting...") + endif() # NOT SIMPLE_PYTHON_EXECUTABLE + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDES "-e") + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} + + # Create folder + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + + # Running gcovr + COMMAND ${GCOVR_PATH} --html --html-details + -r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}/index.html + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML + +function(APPEND_COVERAGE_COMPILER_FLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # APPEND_COVERAGE_COMPILER_FLAGS \ No newline at end of file diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index c8bac11e5..0ed6f003a 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -50,7 +50,7 @@ bool Crypto::init() // has to be set before testing Crypto classes m_initalized = true; - if (!selfTest()) { + if (!backendSelfTest() || !selfTest()) { m_initalized = false; return false; } From 113c8eb702dbdfefaf23a76bfe69603b54522b28 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 29 Sep 2018 19:00:47 +0200 Subject: [PATCH 10/84] Add CLI tests and improve coding style and i18n The CLI module was lacking unit test coverage and showed some severe coding style violations, which this patch addresses. In addition, all uses of qCritical() with untranslatble raw char* sequences were removed in favor of proper locale strings. These are written to STDERR through QTextStreams and support output redirection for testing purposes. With this change, error messages don't depend on the global Qt logging settings and targets anymore and go directly to the terminal or into a file if needed. This patch also fixes a bug discovered during unit test development, where the extract command would just dump the raw XML contents without decrypting embedded Salsa20-protected values first, making the XML export mostly useless, since passwords are scrambled. Lastly, all CLI commands received a dedicated -h/--help option. --- CMakeLists.txt | 8 +- Dockerfile | 4 +- ci/trusty/Dockerfile | 2 + src/CMakeLists.txt | 1 + src/browser/BrowserAction.cpp | 1 - src/browser/BrowserSettings.cpp | 5 - src/browser/BrowserSettings.h | 1 - src/cli/Add.cpp | 46 +- src/cli/Clip.cpp | 37 +- src/cli/Command.cpp | 11 +- src/cli/Command.h | 2 +- src/cli/Diceware.cpp | 22 +- src/cli/Edit.cpp | 52 +- src/cli/Estimate.cpp | 84 +- src/cli/Extract.cpp | 40 +- src/cli/Generate.cpp | 39 +- src/cli/List.cpp | 39 +- src/cli/List.h | 2 +- src/cli/Locate.cpp | 29 +- src/cli/Locate.h | 2 +- src/cli/Merge.cpp | 38 +- src/cli/Remove.cpp | 33 +- src/cli/Remove.h | 2 +- src/cli/Show.cpp | 41 +- src/cli/Show.h | 2 +- src/cli/Utils.cpp | 77 +- src/cli/Utils.h | 15 +- src/cli/keepassxc-cli.cpp | 14 +- src/core/Bootstrap.cpp | 236 ++++++ src/core/Bootstrap.h | 34 + src/core/Database.cpp | 24 +- src/core/Database.h | 3 +- src/core/PasswordGenerator.cpp | 36 +- src/core/PasswordGenerator.h | 1 - src/core/Tools.cpp | 396 +++------ src/core/Tools.h | 40 +- src/crypto/CryptoHash.cpp | 7 - src/crypto/CryptoHash.h | 1 - src/crypto/SymmetricCipher.cpp | 8 +- src/crypto/SymmetricCipherGcrypt.cpp | 2 - src/format/Kdbx3Reader.cpp | 8 - src/format/Kdbx4Reader.cpp | 8 - src/format/KdbxReader.cpp | 31 +- src/format/KdbxReader.h | 2 + src/format/KdbxXmlReader.cpp | 1 - src/format/KdbxXmlWriter.cpp | 31 +- src/format/KdbxXmlWriter.h | 4 + src/format/KeePass2.cpp | 5 +- src/format/KeePass2.h | 3 +- src/format/KeePass2Reader.cpp | 2 +- src/gui/Application.h | 2 +- src/gui/DatabaseWidget.h | 2 +- .../DatabaseSettingsWidgetEncryption.cpp | 2 +- src/main.cpp | 87 +- tests/CMakeLists.txt | 12 +- tests/TestCli.cpp | 756 ++++++++++++++++++ tests/TestCli.h | 69 ++ tests/TestKdbx4.cpp | 12 +- tests/TestPasswordGenerator.cpp | 131 +++ tests/TestPasswordGenerator.h | 33 + tests/TestSymmetricCipher.cpp | 331 ++++---- tests/TestSymmetricCipher.h | 13 +- tests/gui/CMakeLists.txt | 2 +- tests/gui/TemporaryFile.cpp | 93 --- tests/gui/TemporaryFile.h | 65 -- tests/gui/TestGui.cpp | 358 +++++---- tests/gui/TestGui.h | 9 +- 67 files changed, 2259 insertions(+), 1250 deletions(-) create mode 100644 src/core/Bootstrap.cpp create mode 100644 src/core/Bootstrap.h create mode 100644 tests/TestCli.cpp create mode 100644 tests/TestCli.h create mode 100644 tests/TestPasswordGenerator.cpp create mode 100644 tests/TestPasswordGenerator.h delete mode 100644 tests/gui/TemporaryFile.cpp delete mode 100644 tests/gui/TemporaryFile.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f8a4b672..f14b363b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,16 +303,16 @@ endif() include(CLangFormat) if(UNIX AND NOT APPLE) - find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools DBus REQUIRED) + find_package(Qt5 COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools DBus REQUIRED) elseif(APPLE) - find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools REQUIRED + find_package(Qt5 COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools REQUIRED HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH ) find_package(Qt5 COMPONENTS MacExtras HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH ) else() - find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools REQUIRED) + find_package(Qt5 COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools REQUIRED) endif() if(Qt5Core_VERSION VERSION_LESS "5.2.0") @@ -345,9 +345,7 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG) find_package(LibGPGError REQUIRED) find_package(Gcrypt 1.7.0 REQUIRED) find_package(Argon2 REQUIRED) - find_package(ZLIB REQUIRED) - find_package(QREncode REQUIRED) set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR}) diff --git a/Dockerfile b/Dockerfile index 1054872ad..e1fd26d40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,7 +57,9 @@ RUN set -x \ mesa-common-dev \ libyubikey-dev \ libykpers-1-dev \ - libqrencode-dev + libqrencode-dev \ + xclip \ + xvfb ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}" ENV CMAKE_PREFIX_PATH="/opt/${QT5_VERSION}/lib/cmake" diff --git a/ci/trusty/Dockerfile b/ci/trusty/Dockerfile index cd69639d2..6a3d0567f 100644 --- a/ci/trusty/Dockerfile +++ b/ci/trusty/Dockerfile @@ -39,6 +39,7 @@ RUN set -x \ clang-3.6 \ libclang-common-3.6-dev \ clang-format-3.6 \ + llvm-3.6 \ cmake3 \ make \ libgcrypt20-18-dev \ @@ -56,6 +57,7 @@ RUN set -x \ libxi-dev \ libxtst-dev \ libqrencode-dev \ + xclip \ xvfb ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c70de316b..3b41b3878 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,7 @@ set(keepassx_SOURCES core/EntryAttributes.cpp core/EntrySearcher.cpp core/FilePath.cpp + core/Bootstrap.cpp core/Global.h core/Group.cpp core/InactivityTimer.cpp diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index c073bb4b2..6001acb18 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -271,7 +271,6 @@ QJsonObject BrowserAction::handleGeneratePassword(const QJsonObject& json, const { const QString nonce = json.value("nonce").toString(); const QString password = browserSettings()->generatePassword(); - const QString bits = QString::number(browserSettings()->getbits()); // For some reason this always returns 1140 bits? if (nonce.isEmpty() || password.isEmpty()) { return QJsonObject(); diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index f7abdece9..96d8f98d7 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -485,11 +485,6 @@ QString BrowserSettings::generatePassword() } } -int BrowserSettings::getbits() -{ - return m_passwordGenerator.getbits(); -} - void BrowserSettings::updateBinaryPaths(QString customProxyLocation) { bool isProxy = supportBrowserProxy(); diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index 3e84ba37d..5dc28593a 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -112,7 +112,6 @@ public: PasswordGenerator::CharClasses passwordCharClasses(); PasswordGenerator::GeneratorFlags passwordGeneratorFlags(); QString generatePassword(); - int getbits(); void updateBinaryPaths(QString customProxyLocation = QString()); private: diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index 5c97299b8..81a5cad13 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -41,22 +41,20 @@ Add::~Add() int Add::execute(const QStringList& arguments) { - - QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly); + QTextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); - QCommandLineOption username(QStringList() << "u" - << "username", + QCommandLineOption username(QStringList() << "u" << "username", QObject::tr("Username for the entry."), QObject::tr("username")); parser.addOption(username); @@ -64,23 +62,22 @@ int Add::execute(const QStringList& arguments) QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL")); parser.addOption(url); - QCommandLineOption prompt(QStringList() << "p" - << "password-prompt", + QCommandLineOption prompt(QStringList() << "p" << "password-prompt", QObject::tr("Prompt for the entry's password.")); parser.addOption(prompt); - QCommandLineOption generate(QStringList() << "g" - << "generate", + QCommandLineOption generate(QStringList() << "g" << "generate", QObject::tr("Generate a password for the entry.")); parser.addOption(generate); - QCommandLineOption length(QStringList() << "l" - << "password-length", + QCommandLineOption length(QStringList() << "l" << "password-length", QObject::tr("Length for the generated password."), QObject::tr("length")); parser.addOption(length); parser.addPositionalArgument("entry", QObject::tr("Path of the entry to add.")); + + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -89,11 +86,11 @@ int Add::execute(const QStringList& arguments) return EXIT_FAILURE; } - QString databasePath = args.at(0); - QString entryPath = args.at(1); + const QString& databasePath = args.at(0); + const QString& entryPath = args.at(1); - Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile)); - if (db == nullptr) { + Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + if (!db) { return EXIT_FAILURE; } @@ -101,13 +98,13 @@ int Add::execute(const QStringList& arguments) // the entry. QString passwordLength = parser.value(length); if (!passwordLength.isEmpty() && !passwordLength.toInt()) { - qCritical("Invalid value for password length %s.", qPrintable(passwordLength)); + errorTextStream << QObject::tr("Invalid value for password length %1.").arg(passwordLength) << endl; return EXIT_FAILURE; } Entry* entry = db->rootGroup()->addEntryWithPath(entryPath); if (!entry) { - qCritical("Could not create entry with path %s.", qPrintable(entryPath)); + errorTextStream << QObject::tr("Could not create entry with path %1.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -120,8 +117,7 @@ int Add::execute(const QStringList& arguments) } if (parser.isSet(prompt)) { - outputTextStream << "Enter password for new entry: "; - outputTextStream.flush(); + outputTextStream << QObject::tr("Enter password for new entry: ") << flush; QString password = Utils::getPassword(); entry->setPassword(password); } else if (parser.isSet(generate)) { @@ -130,7 +126,7 @@ int Add::execute(const QStringList& arguments) if (passwordLength.isEmpty()) { passwordGenerator.setLength(PasswordGenerator::DefaultLength); } else { - passwordGenerator.setLength(passwordLength.toInt()); + passwordGenerator.setLength(static_cast(passwordLength.toInt())); } passwordGenerator.setCharClasses(PasswordGenerator::DefaultCharset); @@ -141,10 +137,10 @@ int Add::execute(const QStringList& arguments) QString errorMessage = db->saveToFile(databasePath); if (!errorMessage.isEmpty()) { - qCritical("Writing the database failed %s.", qPrintable(errorMessage)); + errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl; return EXIT_FAILURE; } - outputTextStream << "Successfully added entry " << entry->title() << "." << endl; + outputTextStream << QObject::tr("Successfully added entry %1.").arg(entry->title()) << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 886f8ecc7..0b78a24b4 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -42,20 +42,19 @@ Clip::~Clip() int Clip::execute(const QStringList& arguments) { - - QTextStream out(stdout); + QTextStream out(Utils::STDOUT); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); parser.addPositionalArgument("entry", QObject::tr("Path of the entry to clip.", "clip = copy to clipboard")); - parser.addPositionalArgument( - "timeout", QObject::tr("Timeout in seconds before clearing the clipboard."), QString("[timeout]")); + parser.addPositionalArgument("timeout", + QObject::tr("Timeout in seconds before clearing the clipboard."), "[timeout]"); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -64,29 +63,30 @@ int Clip::execute(const QStringList& arguments) return EXIT_FAILURE; } - Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); + Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR); if (!db) { return EXIT_FAILURE; } - return this->clipEntry(db, args.at(1), args.value(2)); + return clipEntry(db, args.at(1), args.value(2)); } int Clip::clipEntry(Database* database, QString entryPath, QString timeout) { + QTextStream err(Utils::STDERR); int timeoutSeconds = 0; if (!timeout.isEmpty() && !timeout.toInt()) { - qCritical("Invalid timeout value %s.", qPrintable(timeout)); + err << QObject::tr("Invalid timeout value %1.").arg(timeout) << endl; return EXIT_FAILURE; } else if (!timeout.isEmpty()) { timeoutSeconds = timeout.toInt(); } - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntry(entryPath); if (!entry) { - qCritical("Entry %s not found.", qPrintable(entryPath)); + err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -95,20 +95,23 @@ int Clip::clipEntry(Database* database, QString entryPath, QString timeout) return exitCode; } - outputTextStream << "Entry's password copied to the clipboard!" << endl; + outputTextStream << QObject::tr("Entry's password copied to the clipboard!") << endl; if (!timeoutSeconds) { return exitCode; } + QString lastLine = ""; while (timeoutSeconds > 0) { - outputTextStream << "\rClearing the clipboard in " << timeoutSeconds << " seconds..."; - outputTextStream.flush(); + outputTextStream << '\r' << QString(lastLine.size(), ' ') << '\r'; + lastLine = QObject::tr("Clearing the clipboard in %1 second(s)...", "", timeoutSeconds).arg(timeoutSeconds); + outputTextStream << lastLine << flush; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - timeoutSeconds--; + --timeoutSeconds; } Utils::clipText(""); - outputTextStream << "\nClipboard cleared!" << endl; + outputTextStream << '\r' << QString(lastLine.size(), ' ') << '\r'; + outputTextStream << QObject::tr("Clipboard cleared!") << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Command.cpp b/src/cli/Command.cpp index ef6948888..c85e5d95d 100644 --- a/src/cli/Command.cpp +++ b/src/cli/Command.cpp @@ -41,19 +41,14 @@ Command::~Command() { } -int Command::execute(const QStringList&) -{ - return EXIT_FAILURE; -} - QString Command::getDescriptionLine() { - QString response = this->name; + QString response = name; QString space(" "); - QString spaces = space.repeated(15 - this->name.length()); + QString spaces = space.repeated(15 - name.length()); response = response.append(spaces); - response = response.append(this->description); + response = response.append(description); response = response.append("\n"); return response; } diff --git a/src/cli/Command.h b/src/cli/Command.h index 2ebdd77b9..7ad49440a 100644 --- a/src/cli/Command.h +++ b/src/cli/Command.h @@ -29,7 +29,7 @@ class Command { public: virtual ~Command(); - virtual int execute(const QStringList& arguments); + virtual int execute(const QStringList& arguments) = 0; QString name; QString description; QString getDescriptionLine(); diff --git a/src/cli/Diceware.cpp b/src/cli/Diceware.cpp index 4cdda0a73..72cbd1960 100644 --- a/src/cli/Diceware.cpp +++ b/src/cli/Diceware.cpp @@ -24,6 +24,7 @@ #include #include "core/PassphraseGenerator.h" +#include "Utils.h" Diceware::Diceware() { @@ -37,26 +38,25 @@ Diceware::~Diceware() int Diceware::execute(const QStringList& arguments) { - QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream in(Utils::STDIN, QIODevice::ReadOnly); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(this->description); - QCommandLineOption words(QStringList() << "W" - << "words", + parser.setApplicationDescription(description); + QCommandLineOption words(QStringList() << "W" << "words", QObject::tr("Word count for the diceware passphrase."), - QObject::tr("count")); + QObject::tr("count", "CLI parameter")); parser.addOption(words); - QCommandLineOption wordlistFile(QStringList() << "w" - << "word-list", + QCommandLineOption wordlistFile(QStringList() << "w" << "word-list", QObject::tr("Wordlist for the diceware generator.\n[Default: EFF English]"), QObject::tr("path")); parser.addOption(wordlistFile); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); if (!args.isEmpty()) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); + out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); return EXIT_FAILURE; } @@ -76,12 +76,12 @@ int Diceware::execute(const QStringList& arguments) } if (!dicewareGenerator.isValid()) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); + out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); return EXIT_FAILURE; } QString password = dicewareGenerator.generatePassphrase(); - outputTextStream << password << endl; + out << password << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index 056a0a595..c2f067794 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -41,22 +41,20 @@ Edit::~Edit() int Edit::execute(const QStringList& arguments) { - - QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream in(Utils::STDIN, QIODevice::ReadOnly); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream err(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); - QCommandLineOption username(QStringList() << "u" - << "username", + QCommandLineOption username(QStringList() << "u" << "username", QObject::tr("Username for the entry."), QObject::tr("username")); parser.addOption(username); @@ -64,61 +62,58 @@ int Edit::execute(const QStringList& arguments) QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL")); parser.addOption(url); - QCommandLineOption title(QStringList() << "t" - << "title", + QCommandLineOption title(QStringList() << "t" << "title", QObject::tr("Title for the entry."), QObject::tr("title")); parser.addOption(title); - QCommandLineOption prompt(QStringList() << "p" - << "password-prompt", + QCommandLineOption prompt(QStringList() << "p" << "password-prompt", QObject::tr("Prompt for the entry's password.")); parser.addOption(prompt); - QCommandLineOption generate(QStringList() << "g" - << "generate", + QCommandLineOption generate(QStringList() << "g" << "generate", QObject::tr("Generate a password for the entry.")); parser.addOption(generate); - QCommandLineOption length(QStringList() << "l" - << "password-length", + QCommandLineOption length(QStringList() << "l" << "password-length", QObject::tr("Length for the generated password."), QObject::tr("length")); parser.addOption(length); parser.addPositionalArgument("entry", QObject::tr("Path of the entry to edit.")); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit"); + out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit"); return EXIT_FAILURE; } - QString databasePath = args.at(0); - QString entryPath = args.at(1); + const QString& databasePath = args.at(0); + const QString& entryPath = args.at(1); - Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile)); - if (db == nullptr) { + Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + if (!db) { return EXIT_FAILURE; } QString passwordLength = parser.value(length); if (!passwordLength.isEmpty() && !passwordLength.toInt()) { - qCritical("Invalid value for password length %s.", qPrintable(passwordLength)); + err << QObject::tr("Invalid value for password length: %1").arg(passwordLength) << endl; return EXIT_FAILURE; } Entry* entry = db->rootGroup()->findEntryByPath(entryPath); if (!entry) { - qCritical("Could not find entry with path %s.", qPrintable(entryPath)); + err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; return EXIT_FAILURE; } if (parser.value("username").isEmpty() && parser.value("url").isEmpty() && parser.value("title").isEmpty() && !parser.isSet(prompt) && !parser.isSet(generate)) { - qCritical("Not changing any field for entry %s.", qPrintable(entryPath)); + err << QObject::tr("Not changing any field for entry %1.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -137,8 +132,7 @@ int Edit::execute(const QStringList& arguments) } if (parser.isSet(prompt)) { - outputTextStream << "Enter new password for entry: "; - outputTextStream.flush(); + out << QObject::tr("Enter new password for entry: ") << flush; QString password = Utils::getPassword(); entry->setPassword(password); } else if (parser.isSet(generate)) { @@ -147,7 +141,7 @@ int Edit::execute(const QStringList& arguments) if (passwordLength.isEmpty()) { passwordGenerator.setLength(PasswordGenerator::DefaultLength); } else { - passwordGenerator.setLength(passwordLength.toInt()); + passwordGenerator.setLength(static_cast(passwordLength.toInt())); } passwordGenerator.setCharClasses(PasswordGenerator::DefaultCharset); @@ -160,10 +154,10 @@ int Edit::execute(const QStringList& arguments) QString errorMessage = db->saveToFile(databasePath); if (!errorMessage.isEmpty()) { - qCritical("Writing the database failed %s.", qPrintable(errorMessage)); + err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl; return EXIT_FAILURE; } - outputTextStream << "Successfully edited entry " << entry->title() << "." << endl; + out << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Estimate.cpp b/src/cli/Estimate.cpp index 9a2ab0b0f..c249d7b1f 100644 --- a/src/cli/Estimate.cpp +++ b/src/cli/Estimate.cpp @@ -16,6 +16,7 @@ */ #include "Estimate.h" +#include "cli/Utils.h" #include #include @@ -44,117 +45,126 @@ Estimate::~Estimate() static void estimate(const char* pwd, bool advanced) { - double e; - int len = strlen(pwd); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + + double e = 0.0; + int len = static_cast(strlen(pwd)); if (!advanced) { - e = ZxcvbnMatch(pwd, 0, 0); - printf("Length %d\tEntropy %.3f\tLog10 %.3f\n", len, e, e * 0.301029996); + e = ZxcvbnMatch(pwd, nullptr, nullptr); + out << QObject::tr("Length %1").arg(len, 0) << '\t' + << QObject::tr("Entropy %1").arg(e, 0, 'f', 3) << '\t' + << QObject::tr("Log10 %1").arg(e * 0.301029996, 0, 'f', 3) << endl; } else { - int ChkLen; + int ChkLen = 0; ZxcMatch_t *info, *p; double m = 0.0; - e = ZxcvbnMatch(pwd, 0, &info); + e = ZxcvbnMatch(pwd, nullptr, &info); for (p = info; p; p = p->Next) { m += p->Entrpy; } m = e - m; - printf("Length %d\tEntropy %.3f\tLog10 %.3f\n Multi-word extra bits %.1f\n", len, e, e * 0.301029996, m); + out << QObject::tr("Length %1").arg(len) << '\t' + << QObject::tr("Entropy %1").arg(e, 0, 'f', 3) << '\t' + << QObject::tr("Log10 %1").arg(e * 0.301029996, 0, 'f', 3) << "\n " + << QObject::tr("Multi-word extra bits %1").arg(m, 0, 'f', 1) << endl; p = info; ChkLen = 0; while (p) { int n; switch (static_cast(p->Type)) { case BRUTE_MATCH: - printf(" Type: Bruteforce "); + out << " " << QObject::tr("Type: Bruteforce") << " "; break; case DICTIONARY_MATCH: - printf(" Type: Dictionary "); + out << " " << QObject::tr("Type: Dictionary") << " "; break; case DICT_LEET_MATCH: - printf(" Type: Dict+Leet "); + out << " " << QObject::tr("Type: Dict+Leet") << " "; break; case USER_MATCH: - printf(" Type: User Words "); + out << " " << QObject::tr("Type: User Words") << " "; break; case USER_LEET_MATCH: - printf(" Type: User+Leet "); + out << " " << QObject::tr("Type: User+Leet") << " "; break; case REPEATS_MATCH: - printf(" Type: Repeated "); + out << " " << QObject::tr("Type: Repeated") << " "; break; case SEQUENCE_MATCH: - printf(" Type: Sequence "); + out << " " << QObject::tr("Type: Sequence") << " "; break; case SPATIAL_MATCH: - printf(" Type: Spatial "); + out << " " << QObject::tr("Type: Spatial") << " "; break; case DATE_MATCH: - printf(" Type: Date "); + out << " " << QObject::tr("Type: Date") << " "; break; case BRUTE_MATCH + MULTIPLE_MATCH: - printf(" Type: Bruteforce(Rep)"); + out << " " << QObject::tr("Type: Bruteforce(Rep)") << " "; break; case DICTIONARY_MATCH + MULTIPLE_MATCH: - printf(" Type: Dictionary(Rep)"); + out << " " << QObject::tr("Type: Dictionary(Rep)") << " "; break; case DICT_LEET_MATCH + MULTIPLE_MATCH: - printf(" Type: Dict+Leet(Rep) "); + out << " " << QObject::tr("Type: Dict+Leet(Rep)") << " "; break; case USER_MATCH + MULTIPLE_MATCH: - printf(" Type: User Words(Rep)"); + out << " " << QObject::tr("Type: User Words(Rep)") << " "; break; case USER_LEET_MATCH + MULTIPLE_MATCH: - printf(" Type: User+Leet(Rep) "); + out << " " << QObject::tr("Type: User+Leet(Rep)") << " "; break; case REPEATS_MATCH + MULTIPLE_MATCH: - printf(" Type: Repeated(Rep) "); + out << " " << QObject::tr("Type: Repeated(Rep)") << " "; break; case SEQUENCE_MATCH + MULTIPLE_MATCH: - printf(" Type: Sequence(Rep) "); + out << " " << QObject::tr("Type: Sequence(Rep)") << " "; break; case SPATIAL_MATCH + MULTIPLE_MATCH: - printf(" Type: Spatial(Rep) "); + out << " " << QObject::tr("Type: Spatial(Rep)") << " "; break; case DATE_MATCH + MULTIPLE_MATCH: - printf(" Type: Date(Rep) "); + out << " " << QObject::tr("Type: Date(Rep)") << " "; break; default: - printf(" Type: Unknown%d ", p->Type); + out << " " << QObject::tr("Type: Unknown%1").arg(p->Type) << " "; break; } ChkLen += p->Length; - printf(" Length %d Entropy %6.3f (%.2f) ", p->Length, p->Entrpy, p->Entrpy * 0.301029996); + + out << QObject::tr("Length %1").arg(p->Length) << '\t' + << QObject::tr("Entropy %1 (%2)").arg(p->Entrpy, 6, 'f', 3).arg(p->Entrpy * 0.301029996, 0, 'f', 2) << '\t'; for (n = 0; n < p->Length; ++n, ++pwd) { - printf("%c", *pwd); + out << *pwd; } - printf("\n"); + out << endl; p = p->Next; } ZxcvbnFreeInfo(info); if (ChkLen != len) { - printf("*** Password length (%d) != sum of length of parts (%d) ***\n", len, ChkLen); + out << QObject::tr("*** Password length (%1) != sum of length of parts (%2) ***").arg(len).arg(ChkLen) << endl; } } } int Estimate::execute(const QStringList& arguments) { - QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream in(Utils::STDIN, QIODevice::ReadOnly); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("password", QObject::tr("Password for which to estimate the entropy."), "[password]"); - QCommandLineOption advancedOption(QStringList() << "a" - << "advanced", + QCommandLineOption advancedOption(QStringList() << "a" << "advanced", QObject::tr("Perform advanced analysis on the password.")); parser.addOption(advancedOption); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); if (args.size() > 1) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate"); + out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate"); return EXIT_FAILURE; } @@ -162,7 +172,7 @@ int Estimate::execute(const QStringList& arguments) if (args.size() == 1) { password = args.at(0); } else { - password = inputTextStream.readLine(); + password = in.readLine(); } estimate(password.toLatin1(), parser.isSet(advancedOption)); diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index f0004e688..cc39c469a 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -43,17 +43,17 @@ Extract::~Extract() int Extract::execute(const QStringList& arguments) { - QTextStream out(stdout); - QTextStream errorTextStream(stderr); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream err(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database to extract.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -62,8 +62,7 @@ int Extract::execute(const QStringList& arguments) return EXIT_FAILURE; } - out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)); - out.flush(); + out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush; auto compositeKey = QSharedPointer::create(); @@ -74,52 +73,51 @@ int Extract::execute(const QStringList& arguments) QString keyFilePath = parser.value(keyFile); if (!keyFilePath.isEmpty()) { + // LCOV_EXCL_START auto fileKey = QSharedPointer::create(); QString errorMsg; if (!fileKey->load(keyFilePath, &errorMsg)) { - errorTextStream << QObject::tr("Failed to load key file %1 : %2").arg(keyFilePath).arg(errorMsg); - errorTextStream << endl; + err << QObject::tr("Failed to load key file %1: %2").arg(keyFilePath).arg(errorMsg) << endl; return EXIT_FAILURE; } if (fileKey->type() != FileKey::Hashed) { - errorTextStream << QObject::tr("WARNING: You are using a legacy key file format which may become\n" - "unsupported in the future.\n\n" - "Please consider generating a new key file."); - errorTextStream << endl; + err << QObject::tr("WARNING: You are using a legacy key file format which may become\n" + "unsupported in the future.\n\n" + "Please consider generating a new key file.") << endl; } + // LCOV_EXCL_STOP compositeKey->addKey(fileKey); } - QString databaseFilename = args.at(0); + const QString& databaseFilename = args.at(0); QFile dbFile(databaseFilename); if (!dbFile.exists()) { - qCritical("File %s does not exist.", qPrintable(databaseFilename)); + err << QObject::tr("File %1 does not exist.").arg(databaseFilename) << endl; return EXIT_FAILURE; } if (!dbFile.open(QIODevice::ReadOnly)) { - qCritical("Unable to open file %s.", qPrintable(databaseFilename)); + err << QObject::tr("Unable to open file %1.").arg(databaseFilename) << endl; return EXIT_FAILURE; } KeePass2Reader reader; reader.setSaveXml(true); - Database* db = reader.readDatabase(&dbFile, compositeKey); - delete db; + QScopedPointer db(reader.readDatabase(&dbFile, compositeKey)); QByteArray xmlData = reader.reader()->xmlData(); if (reader.hasError()) { if (xmlData.isEmpty()) { - qCritical("Error while reading the database:\n%s", qPrintable(reader.errorString())); + err << QObject::tr("Error while reading the database:\n%1").arg(reader.errorString()) << endl; } else { - qWarning("Error while parsing the database:\n%s\n", qPrintable(reader.errorString())); + err << QObject::tr("Error while parsing the database:\n%1").arg(reader.errorString()) << endl; } return EXIT_FAILURE; } - out << xmlData.constData() << "\n"; + out << xmlData.constData() << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Generate.cpp b/src/cli/Generate.cpp index 6a5be3f07..7780aa829 100644 --- a/src/cli/Generate.cpp +++ b/src/cli/Generate.cpp @@ -19,6 +19,7 @@ #include #include "Generate.h" +#include "cli/Utils.h" #include #include @@ -37,38 +38,32 @@ Generate::~Generate() int Generate::execute(const QStringList& arguments) { - QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream in(Utils::STDIN, QIODevice::ReadOnly); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + out.setCodec("UTF-8"); // force UTF-8 to prevent ??? characters in extended-ASCII passwords QCommandLineParser parser; - parser.setApplicationDescription(this->description); - QCommandLineOption len(QStringList() << "L" - << "length", + parser.setApplicationDescription(description); + QCommandLineOption len(QStringList() << "L" << "length", QObject::tr("Length of the generated password"), QObject::tr("length")); parser.addOption(len); - QCommandLineOption lower(QStringList() << "l" - << "lower", + QCommandLineOption lower(QStringList() << "l" << "lower", QObject::tr("Use lowercase characters")); parser.addOption(lower); - QCommandLineOption upper(QStringList() << "u" - << "upper", + QCommandLineOption upper(QStringList() << "u" << "upper", QObject::tr("Use uppercase characters")); parser.addOption(upper); - QCommandLineOption numeric(QStringList() << "n" - << "numeric", + QCommandLineOption numeric(QStringList() << "n" << "numeric", QObject::tr("Use numbers.")); parser.addOption(numeric); - QCommandLineOption special(QStringList() << "s" - << "special", + QCommandLineOption special(QStringList() << "s" << "special", QObject::tr("Use special characters")); parser.addOption(special); - QCommandLineOption extended(QStringList() << "e" - << "extended", + QCommandLineOption extended(QStringList() << "e" << "extended", QObject::tr("Use extended ASCII")); parser.addOption(extended); - QCommandLineOption exclude(QStringList() << "x" - << "exclude", + QCommandLineOption exclude(QStringList() << "x" << "exclude", QObject::tr("Exclude character set"), QObject::tr("chars")); parser.addOption(exclude); @@ -78,12 +73,12 @@ int Generate::execute(const QStringList& arguments) QCommandLineOption every_group(QStringList() << "every-group", QObject::tr("Include characters from every selected group")); parser.addOption(every_group); - + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); if (!args.isEmpty()) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); + out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); return EXIT_FAILURE; } @@ -93,7 +88,7 @@ int Generate::execute(const QStringList& arguments) passwordGenerator.setLength(PasswordGenerator::DefaultLength); } else { int length = parser.value(len).toInt(); - passwordGenerator.setLength(length); + passwordGenerator.setLength(static_cast(length)); } PasswordGenerator::CharClasses classes = 0x0; @@ -128,12 +123,12 @@ int Generate::execute(const QStringList& arguments) passwordGenerator.setExcludedChars(parser.value(exclude)); if (!passwordGenerator.isValid()) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); + out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); return EXIT_FAILURE; } QString password = passwordGenerator.generatePassword(); - outputTextStream << password << endl; + out << password << endl; return EXIT_SUCCESS; } diff --git a/src/cli/List.cpp b/src/cli/List.cpp index b39b3fc14..4d1ebcfc5 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -19,6 +19,7 @@ #include #include "List.h" +#include "cli/Utils.h" #include #include @@ -39,22 +40,20 @@ List::~List() int List::execute(const QStringList& arguments) { - QTextStream out(stdout); + QTextStream out(Utils::STDOUT); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); - parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), QString("[group]")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), "[group]"); + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); - - QCommandLineOption recursiveOption(QStringList() << "R" - << "recursive", + QCommandLineOption recursiveOption(QStringList() << "R" << "recursive", QObject::tr("Recursive mode, list elements recursively")); parser.addOption(recursiveOption); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -65,33 +64,33 @@ int List::execute(const QStringList& arguments) bool recursive = parser.isSet(recursiveOption); - Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); - if (db == nullptr) { + QScopedPointer db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR)); + if (!db) { return EXIT_FAILURE; } if (args.size() == 2) { - return this->listGroup(db, recursive, args.at(1)); + return listGroup(db.data(), recursive, args.at(1)); } - return this->listGroup(db, recursive); + return listGroup(db.data(), recursive); } -int List::listGroup(Database* database, bool recursive, QString groupPath) +int List::listGroup(Database* database, bool recursive, const QString& groupPath) { - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + if (groupPath.isEmpty()) { - outputTextStream << database->rootGroup()->print(recursive); - outputTextStream.flush(); + out << database->rootGroup()->print(recursive) << flush; return EXIT_SUCCESS; } Group* group = database->rootGroup()->findGroupByPath(groupPath); - if (group == nullptr) { - qCritical("Cannot find group %s.", qPrintable(groupPath)); + if (!group) { + err << QObject::tr("Cannot find group %1.").arg(groupPath) << endl; return EXIT_FAILURE; } - outputTextStream << group->print(recursive); - outputTextStream.flush(); + out << group->print(recursive) << flush; return EXIT_SUCCESS; } diff --git a/src/cli/List.h b/src/cli/List.h index 00c376972..5697d9390 100644 --- a/src/cli/List.h +++ b/src/cli/List.h @@ -26,7 +26,7 @@ public: List(); ~List(); int execute(const QStringList& arguments); - int listGroup(Database* database, bool recursive, QString groupPath = QString("")); + int listGroup(Database* database, bool recursive, const QString& groupPath = {}); }; #endif // KEEPASSXC_LIST_H diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index f80372885..3bca8ae1d 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -1,3 +1,5 @@ +#include + /* * Copyright (C) 2017 KeePassXC Team * @@ -25,6 +27,7 @@ #include #include "cli/Utils.h" +#include "core/Global.h" #include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" @@ -41,18 +44,17 @@ Locate::~Locate() int Locate::execute(const QStringList& arguments) { - - QTextStream out(stdout); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addPositionalArgument("term", QObject::tr("Search term.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -61,26 +63,27 @@ int Locate::execute(const QStringList& arguments) return EXIT_FAILURE; } - Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); + QScopedPointer db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR)); if (!db) { return EXIT_FAILURE; } - return this->locateEntry(db, args.at(1)); + return locateEntry(db.data(), args.at(1)); } -int Locate::locateEntry(Database* database, QString searchTerm) +int Locate::locateEntry(Database* database, const QString& searchTerm) { + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream err(Utils::STDERR, QIODevice::WriteOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); QStringList results = database->rootGroup()->locate(searchTerm); if (results.isEmpty()) { - outputTextStream << "No results for that search term" << endl; - return EXIT_SUCCESS; + err << "No results for that search term." << endl; + return EXIT_FAILURE; } - for (QString result : results) { - outputTextStream << result << endl; + for (const QString& result : asConst(results)) { + out << result << endl; } return EXIT_SUCCESS; } diff --git a/src/cli/Locate.h b/src/cli/Locate.h index 3677a034d..3355d41ec 100644 --- a/src/cli/Locate.h +++ b/src/cli/Locate.h @@ -26,7 +26,7 @@ public: Locate(); ~Locate(); int execute(const QStringList& arguments); - int locateEntry(Database* database, QString searchTerm); + int locateEntry(Database* database, const QString& searchTerm); }; #endif // KEEPASSXC_LOCATE_H diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index ea7e6636a..a5b4a2cb7 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -15,8 +15,6 @@ * along with this program. If not, see . */ -#include - #include "Merge.h" #include @@ -24,6 +22,9 @@ #include "core/Database.h" #include "core/Merger.h" +#include "cli/Utils.h" + +#include Merge::Merge() { @@ -37,29 +38,28 @@ Merge::~Merge() int Merge::execute(const QStringList& arguments) { - QTextStream out(stdout); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream err(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database1", QObject::tr("Path of the database to merge into.")); parser.addPositionalArgument("database2", QObject::tr("Path of the database to merge from.")); - QCommandLineOption samePasswordOption(QStringList() << "s" - << "same-credentials", + QCommandLineOption samePasswordOption(QStringList() << "s" << "same-credentials", QObject::tr("Use the same credentials for both database files.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); - QCommandLineOption keyFileFrom(QStringList() << "f" - << "key-file-from", + QCommandLineOption keyFileFrom(QStringList() << "f" << "key-file-from", QObject::tr("Key file of the database to merge from."), QObject::tr("path")); parser.addOption(keyFileFrom); parser.addOption(samePasswordOption); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -68,30 +68,30 @@ int Merge::execute(const QStringList& arguments) return EXIT_FAILURE; } - Database* db1 = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); - if (db1 == nullptr) { + QScopedPointer db1(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR)); + if (!db1) { return EXIT_FAILURE; } - Database* db2; + QScopedPointer db2; if (!parser.isSet("same-credentials")) { - db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom)); + db2.reset(Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom), Utils::STDOUT, Utils::STDERR)); } else { - db2 = Database::openDatabaseFile(args.at(1), db1->key()); + db2.reset(Database::openDatabaseFile(args.at(1), db1->key())); } - if (db2 == nullptr) { + if (!db2) { return EXIT_FAILURE; } - Merger merger(db2, db1); + Merger merger(db2.data(), db1.data()); merger.merge(); QString errorMessage = db1->saveToFile(args.at(0)); if (!errorMessage.isEmpty()) { - qCritical("Unable to save database to file : %s", qPrintable(errorMessage)); + err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl; return EXIT_FAILURE; } - out << "Successfully merged the database files.\n"; + out << "Successfully merged the database files." << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 64a5976e9..5bbfd67e4 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -44,40 +44,41 @@ Remove::~Remove() int Remove::execute(const QStringList& arguments) { - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream out(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; - parser.setApplicationDescription(QCoreApplication::translate("main", "Remove an entry from the database.")); - parser.addPositionalArgument("database", QCoreApplication::translate("main", "Path of the database.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + parser.setApplicationDescription(QCoreApplication::tr("main", "Remove an entry from the database.")); + parser.addPositionalArgument("database", QCoreApplication::tr("main", "Path of the database.")); + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); - parser.addPositionalArgument("entry", QCoreApplication::translate("main", "Path of the entry to remove.")); + parser.addPositionalArgument("entry", QCoreApplication::tr("main", "Path of the entry to remove.")); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm"); + out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm"); return EXIT_FAILURE; } - Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); - if (db == nullptr) { + QScopedPointer db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR)); + if (!db) { return EXIT_FAILURE; } - return this->removeEntry(db, args.at(0), args.at(1)); + return removeEntry(db.data(), args.at(0), args.at(1)); } -int Remove::removeEntry(Database* database, QString databasePath, QString entryPath) +int Remove::removeEntry(Database* database, const QString& databasePath, const QString& entryPath) { + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream err(Utils::STDERR, QIODevice::WriteOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { - qCritical("Entry %s not found.", qPrintable(entryPath)); + err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -92,14 +93,14 @@ int Remove::removeEntry(Database* database, QString databasePath, QString entryP QString errorMessage = database->saveToFile(databasePath); if (!errorMessage.isEmpty()) { - qCritical("Unable to save database to file : %s", qPrintable(errorMessage)); + err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl; return EXIT_FAILURE; } if (recycled) { - outputTextStream << "Successfully recycled entry " << entryTitle << "." << endl; + out << QObject::tr("Successfully recycled entry %1.").arg(entryTitle) << endl; } else { - outputTextStream << "Successfully deleted entry " << entryTitle << "." << endl; + out << QObject::tr("Successfully deleted entry %1.").arg(entryTitle) << endl; } return EXIT_SUCCESS; diff --git a/src/cli/Remove.h b/src/cli/Remove.h index 5465530ed..33d62f4bd 100644 --- a/src/cli/Remove.h +++ b/src/cli/Remove.h @@ -28,7 +28,7 @@ public: Remove(); ~Remove(); int execute(const QStringList& arguments); - int removeEntry(Database* database, QString databasePath, QString entryPath); + int removeEntry(Database* database, const QString& databasePath, const QString& entryPath); }; #endif // KEEPASSXC_REMOVE_H diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index be188c75c..5e2ec14b4 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -15,17 +15,19 @@ * along with this program. If not, see . */ +#include "Show.h" + #include #include -#include "Show.h" - #include #include #include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" +#include "core/Global.h" +#include "Utils.h" Show::Show() { @@ -39,19 +41,17 @@ Show::~Show() int Show::execute(const QStringList& arguments) { - QTextStream out(stdout); + QTextStream out(Utils::STDOUT); QCommandLineParser parser; - parser.setApplicationDescription(this->description); + parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); - QCommandLineOption keyFile(QStringList() << "k" - << "key-file", + QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); QCommandLineOption attributes( - QStringList() << "a" - << "attributes", + QStringList() << "a" << "attributes", QObject::tr( "Names of the attributes to show. " "This option can be specified more than once, with each attribute shown one-per-line in the given order. " @@ -59,6 +59,7 @@ int Show::execute(const QStringList& arguments) QObject::tr("attribute")); parser.addOption(attributes); parser.addPositionalArgument("entry", QObject::tr("Name of the entry to show.")); + parser.addHelpOption(); parser.process(arguments); const QStringList args = parser.positionalArguments(); @@ -67,23 +68,23 @@ int Show::execute(const QStringList& arguments) return EXIT_FAILURE; } - Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); - if (db == nullptr) { + QScopedPointer db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR)); + if (!db) { return EXIT_FAILURE; } - return this->showEntry(db, parser.values(attributes), args.at(1)); + return showEntry(db.data(), parser.values(attributes), args.at(1)); } -int Show::showEntry(Database* database, QStringList attributes, QString entryPath) +int Show::showEntry(Database* database, QStringList attributes, const QString& entryPath) { - - QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + QTextStream in(Utils::STDIN, QIODevice::ReadOnly); + QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + QTextStream err(Utils::STDERR, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntry(entryPath); if (!entry) { - qCritical("Could not find entry with path %s.", qPrintable(entryPath)); + err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -95,16 +96,16 @@ int Show::showEntry(Database* database, QStringList attributes, QString entryPat // Iterate over the attributes and output them line-by-line. bool sawUnknownAttribute = false; - for (QString attribute : attributes) { + for (const QString& attribute : asConst(attributes)) { if (!entry->attributes()->contains(attribute)) { sawUnknownAttribute = true; - qCritical("ERROR: unknown attribute '%s'.", qPrintable(attribute)); + err << QObject::tr("ERROR: unknown attribute %1.").arg(attribute) << endl; continue; } if (showAttributeNames) { - outputTextStream << attribute << ": "; + out << attribute << ": "; } - outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl; + out << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl; } return sawUnknownAttribute ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cli/Show.h b/src/cli/Show.h index 18b6d7049..fe16546c3 100644 --- a/src/cli/Show.h +++ b/src/cli/Show.h @@ -26,7 +26,7 @@ public: Show(); ~Show(); int execute(const QStringList& arguments); - int showEntry(Database* database, QStringList attributes, QString entryPath); + int showEntry(Database* database, QStringList attributes, const QString& entryPath); }; #endif // KEEPASSXC_SHOW_H diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index 35e7cce38..8a0f5abe3 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -25,9 +25,25 @@ #endif #include -#include -void Utils::setStdinEcho(bool enable = true) +namespace Utils +{ +/** + * STDOUT file handle for the CLI. + */ +FILE* STDOUT = stdout; + +/** + * STDERR file handle for the CLI. + */ +FILE* STDERR = stderr; + +/** + * STDIN file handle for the CLI. + */ +FILE* STDIN = stdin; + +void setStdinEcho(bool enable = true) { #ifdef Q_OS_WIN HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); @@ -56,28 +72,55 @@ void Utils::setStdinEcho(bool enable = true) #endif } -QString Utils::getPassword() +QStringList nextPasswords = {}; + +/** + * Set the next password returned by \link getPassword() instead of reading it from STDIN. + * Multiple calls to this method will fill a queue of passwords. + * This function is intended for testing purposes. + * + * @param password password to return next + */ +void setNextPassword(const QString& password) { - static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - static QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + nextPasswords.append(password); +} + +/** + * Read a user password from STDIN or return a password previously + * set by \link setNextPassword(). + * + * @return the password + */ +QString getPassword() +{ + QTextStream out(STDOUT, QIODevice::WriteOnly); + + // return preset password if one is set + if (!nextPasswords.isEmpty()) { + auto password = nextPasswords.takeFirst(); + // simulate user entering newline + out << endl; + return password; + } + + QTextStream in(STDIN, QIODevice::ReadOnly); setStdinEcho(false); - QString line = inputTextStream.readLine(); + QString line = in.readLine(); setStdinEcho(true); - - // The new line was also not echoed, but we do want to echo it. - outputTextStream << "\n"; - outputTextStream.flush(); + out << endl; return line; } -/* +/** * A valid and running event loop is needed to use the global QClipboard, * so we need to use this from the CLI. */ -int Utils::clipText(const QString& text) +int clipText(const QString& text) { + QTextStream err(Utils::STDERR); QString programName = ""; QStringList arguments; @@ -98,16 +141,18 @@ int Utils::clipText(const QString& text) #endif if (programName.isEmpty()) { - qCritical("No program defined for clipboard manipulation"); + err << QObject::tr("No program defined for clipboard manipulation"); + err.flush(); return EXIT_FAILURE; } - QProcess* clipProcess = new QProcess(nullptr); + auto* clipProcess = new QProcess(nullptr); clipProcess->start(programName, arguments); clipProcess->waitForStarted(); if (clipProcess->state() != QProcess::Running) { - qCritical("Unable to start program %s", qPrintable(programName)); + err << QObject::tr("Unable to start program %1").arg(programName); + err.flush(); return EXIT_FAILURE; } @@ -120,3 +165,5 @@ int Utils::clipText(const QString& text) return clipProcess->exitCode(); } + +} // namespace Utils diff --git a/src/cli/Utils.h b/src/cli/Utils.h index 1f8051183..868ccdef5 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -19,13 +19,18 @@ #define KEEPASSXC_UTILS_H #include +#include -class Utils +namespace Utils { -public: - static void setStdinEcho(bool enable); - static QString getPassword(); - static int clipText(const QString& text); +extern FILE* STDOUT; +extern FILE* STDERR; +extern FILE* STDIN; + +void setStdinEcho(bool enable); +QString getPassword(); +void setNextPassword(const QString& password); +int clipText(const QString& text); }; #endif // KEEPASSXC_UTILS_H diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 1462f92b9..97efd8c08 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -25,7 +25,7 @@ #include #include "config-keepassx.h" -#include "core/Tools.h" +#include "core/Bootstrap.h" #include "crypto/Crypto.h" #if defined(WITH_ASAN) && defined(WITH_LSAN) @@ -34,17 +34,17 @@ int main(int argc, char** argv) { -#ifdef QT_NO_DEBUG - Tools::disableCoreDumps(); -#endif - if (!Crypto::init()) { qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); return EXIT_FAILURE; } QCoreApplication app(argc, argv); - app.setApplicationVersion(KEEPASSX_VERSION); + QCoreApplication::setApplicationVersion(KEEPASSX_VERSION); + +#ifdef QT_NO_DEBUG + Bootstrap::bootstrapApplication(); +#endif QTextStream out(stdout); QStringList arguments; @@ -69,7 +69,7 @@ int main(int argc, char** argv) // recognized by this parser. parser.parse(arguments); - if (parser.positionalArguments().size() < 1) { + if (parser.positionalArguments().empty()) { if (parser.isSet("version")) { // Switch to parser.showVersion() when available (QT 5.4). out << KEEPASSX_VERSION << endl; diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp new file mode 100644 index 000000000..2c25b2505 --- /dev/null +++ b/src/core/Bootstrap.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "Bootstrap.h" +#include "core/Config.h" +#include "core/Translator.h" + +#ifdef Q_OS_WIN +#include // for createWindowsDACL() +#include // for Sleep(), SetDllDirectoryA(), SetSearchPathMode(), ... +#endif + +namespace Bootstrap +{ +/** + * When QNetworkAccessManager is instantiated it regularly starts polling + * all network interfaces to see if anything changes and if so, what. This + * creates a latency spike every 10 seconds on Mac OS 10.12+ and Windows 7 >= + * when on a wifi connection. + * So here we disable it for lack of better measure. + * This will also cause this message: QObject::startTimer: Timers cannot + * have negative intervals + * For more info see: + * - https://bugreports.qt.io/browse/QTBUG-40332 + * - https://bugreports.qt.io/browse/QTBUG-46015 + */ +static inline void applyEarlyQNetworkAccessManagerWorkaround() +{ + qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); +} + +/** + * Perform early application bootstrapping such as setting up search paths, + * configuration OS security properties, and loading translators. + * A QApplication object has to be instantiated before calling this function. + */ +void bootstrapApplication() +{ +#ifdef QT_NO_DEBUG + disableCoreDumps(); +#endif + setupSearchPaths(); + applyEarlyQNetworkAccessManagerWorkaround(); + Translator::installTranslators(); + +#ifdef Q_OS_MAC + // Don't show menu icons on OSX + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif +} + +/** + * Restore the main window's state after launch + * + * @param mainWindow the main window whose state to restore + */ +void restoreMainWindowState(MainWindow& mainWindow) +{ + // start minimized if configured + bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool(); + bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool(); +#ifndef Q_OS_LINUX + if (minimizeOnStartup) { +#else + // On some Linux systems, the window should NOT be minimized and hidden (i.e. not shown), at + // the same time (which would happen if both minimize on startup and minimize to tray are set) + // since otherwise it causes problems on restore as seen on issue #1595. Hiding it is enough. + if (minimizeOnStartup && !minimizeToTray) { +#endif + mainWindow.setWindowState(Qt::WindowMinimized); + } + if (!(minimizeOnStartup && minimizeToTray)) { + mainWindow.show(); + } + + if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) { + const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList(); + for (const QString& filename : fileNames) { + if (!filename.isEmpty() && QFile::exists(filename)) { + mainWindow.openDatabase(filename); + } + } + } +} + +// LCOV_EXCL_START +void disableCoreDumps() +{ + // default to true + // there is no point in printing a warning if this is not implemented on the platform + bool success = true; + +#if defined(HAVE_RLIMIT_CORE) + struct rlimit limit; + limit.rlim_cur = 0; + limit.rlim_max = 0; + success = success && (setrlimit(RLIMIT_CORE, &limit) == 0); +#endif + +#if defined(HAVE_PR_SET_DUMPABLE) + success = success && (prctl(PR_SET_DUMPABLE, 0) == 0); +#endif + +// Mac OS X +#ifdef HAVE_PT_DENY_ATTACH + success = success && (ptrace(PT_DENY_ATTACH, 0, 0, 0) == 0); +#endif + +#ifdef Q_OS_WIN + success = success && createWindowsDACL(); +#endif + + if (!success) { + qWarning("Unable to disable core dumps."); + } +} + +// +// This function grants the user associated with the process token minimal access rights and +// denies everything else on Windows. This includes PROCESS_QUERY_INFORMATION and +// PROCESS_VM_READ access rights that are required for MiniDumpWriteDump() or ReadProcessMemory(). +// We do this using a discretionary access control list (DACL). Effectively this prevents +// crash dumps and disallows other processes from accessing our memory. This works as long +// as you do not have admin privileges, since then you are able to grant yourself the +// SeDebugPrivilege or SeTakeOwnershipPrivilege and circumvent the DACL. +// +bool createWindowsDACL() +{ + bool bSuccess = false; + +#ifdef Q_OS_WIN + // Process token and user + HANDLE hToken = nullptr; + PTOKEN_USER pTokenUser = nullptr; + DWORD cbBufferSize = 0; + + // Access control list + PACL pACL = nullptr; + DWORD cbACL = 0; + + // Open the access token associated with the calling process + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + goto Cleanup; + } + + // Retrieve the token information in a TOKEN_USER structure + GetTokenInformation(hToken, TokenUser, nullptr, 0, &cbBufferSize); + + pTokenUser = static_cast(HeapAlloc(GetProcessHeap(), 0, cbBufferSize)); + if (pTokenUser == nullptr) { + goto Cleanup; + } + + if (!GetTokenInformation(hToken, TokenUser, pTokenUser, cbBufferSize, &cbBufferSize)) { + goto Cleanup; + } + + if (!IsValidSid(pTokenUser->User.Sid)) { + goto Cleanup; + } + + // Calculate the amount of memory that must be allocated for the DACL + cbACL = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pTokenUser->User.Sid); + + // Create and initialize an ACL + pACL = static_cast(HeapAlloc(GetProcessHeap(), 0, cbACL)); + if (pACL == nullptr) { + goto Cleanup; + } + + if (!InitializeAcl(pACL, cbACL, ACL_REVISION)) { + goto Cleanup; + } + + // Add allowed access control entries, everything else is denied + if (!AddAccessAllowedAce( + pACL, + ACL_REVISION, + SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, // same as protected process + pTokenUser->User.Sid // pointer to the trustee's SID + )) { + goto Cleanup; + } + + // Set discretionary access control list + bSuccess = ERROR_SUCCESS + == SetSecurityInfo(GetCurrentProcess(), // object handle + SE_KERNEL_OBJECT, // type of object + DACL_SECURITY_INFORMATION, // change only the objects DACL + nullptr, + nullptr, // do not change owner or group + pACL, // DACL specified + nullptr // do not change SACL + ); + +Cleanup: + + if (pACL != nullptr) { + HeapFree(GetProcessHeap(), 0, pACL); + } + if (pTokenUser != nullptr) { + HeapFree(GetProcessHeap(), 0, pTokenUser); + } + if (hToken != nullptr) { + CloseHandle(hToken); + } +#endif + + return bSuccess; +} +// LCOV_EXCL_STOP + +void setupSearchPaths() +{ +#ifdef Q_OS_WIN + // Make sure Windows doesn't load DLLs from the current working directory + SetDllDirectoryA(""); + SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE); +#endif +} + +} // namespace Bootstrap diff --git a/src/core/Bootstrap.h b/src/core/Bootstrap.h new file mode 100644 index 000000000..0e9db155a --- /dev/null +++ b/src/core/Bootstrap.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef KEEPASSXC_BOOTSTRAP_H +#define KEEPASSXC_BOOTSTRAP_H + +#include "gui/MainWindow.h" + +namespace Bootstrap +{ +void bootstrapApplication(); +void restoreMainWindowState(MainWindow& mainWindow); +void disableCoreDumps(); +bool createWindowsDACL(); +void setupSearchPaths(); +}; + + +#endif //KEEPASSXC_BOOTSTRAP_H diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 5b7a3c07d..5116fd199 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -47,7 +47,7 @@ Database::Database() , m_emitModified(false) , m_uuid(QUuid::createUuid()) { - m_data.cipher = KeePass2::CIPHER_AES; + m_data.cipher = KeePass2::CIPHER_AES256; m_data.compressionAlgo = CompressionGZip; // instantiate default AES-KDF with legacy KDBX3 flag set @@ -501,14 +501,14 @@ Database* Database::openDatabaseFile(const QString& fileName, QSharedPointer::create(); - QTextStream outputTextStream(stdout); - QTextStream errorTextStream(stderr); + QTextStream out(outputDescriptor); + QTextStream err(errorDescriptor); - outputTextStream << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename); - outputTextStream.flush(); + out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename); + out.flush(); QString line = Utils::getPassword(); auto passwordKey = QSharedPointer::create(); @@ -518,11 +518,19 @@ Database* Database::unlockFromStdin(QString databaseFilename, QString keyFilenam if (!keyFilename.isEmpty()) { auto fileKey = QSharedPointer::create(); QString errorMessage; + // LCOV_EXCL_START if (!fileKey->load(keyFilename, &errorMessage)) { - errorTextStream << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage); - errorTextStream << endl; + err << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage)<< endl; return nullptr; } + + if (fileKey->type() != FileKey::Hashed) { + err << QObject::tr("WARNING: You are using a legacy key file format which may become\n" + "unsupported in the future.\n\n" + "Please consider generating a new key file.") << endl; + } + // LCOV_EXCL_STOP + compositeKey->addKey(fileKey); } diff --git a/src/core/Database.h b/src/core/Database.h index a5ae3effa..7108ded31 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -131,7 +131,8 @@ public: static Database* databaseByUuid(const QUuid& uuid); static Database* openDatabaseFile(const QString& fileName, QSharedPointer key); - static Database* unlockFromStdin(QString databaseFilename, QString keyFilename = QString("")); + static Database* unlockFromStdin(QString databaseFilename, QString keyFilename = {}, + FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr); signals: void groupDataChanged(Group* group); diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp index 01b715072..3dbcdaad8 100644 --- a/src/core/PasswordGenerator.cpp +++ b/src/core/PasswordGenerator.cpp @@ -80,21 +80,21 @@ QString PasswordGenerator::generatePassword() const QString password; if (m_flags & CharFromEveryGroup) { - for (int i = 0; i < groups.size(); i++) { - int pos = randomGen()->randomUInt(groups[i].size()); + for (const auto& group : groups) { + int pos = randomGen()->randomUInt(static_cast(group.size())); - password.append(groups[i][pos]); + password.append(group[pos]); } for (int i = groups.size(); i < m_length; i++) { - int pos = randomGen()->randomUInt(passwordChars.size()); + int pos = randomGen()->randomUInt(static_cast(passwordChars.size())); password.append(passwordChars[pos]); } // shuffle chars for (int i = (password.size() - 1); i >= 1; i--) { - int j = randomGen()->randomUInt(i + 1); + int j = randomGen()->randomUInt(static_cast(i + 1)); QChar tmp = password[i]; password[i] = password[j]; @@ -102,7 +102,7 @@ QString PasswordGenerator::generatePassword() const } } else { for (int i = 0; i < m_length; i++) { - int pos = randomGen()->randomUInt(passwordChars.size()); + int pos = randomGen()->randomUInt(static_cast(passwordChars.size())); password.append(passwordChars[pos]); } @@ -111,21 +111,6 @@ QString PasswordGenerator::generatePassword() const return password; } -int PasswordGenerator::getbits() const -{ - const QVector groups = passwordGroups(); - - int bits = 0; - QVector passwordChars; - for (const PasswordGroup& group : groups) { - bits += group.size(); - } - - bits *= m_length; - - return bits; -} - bool PasswordGenerator::isValid() const { if (m_classes == 0) { @@ -138,11 +123,8 @@ bool PasswordGenerator::isValid() const return false; } - if (passwordGroups().size() == 0) { - return false; - } + return !passwordGroups().isEmpty(); - return true; } QVector PasswordGenerator::passwordGroups() const @@ -298,9 +280,9 @@ QVector PasswordGenerator::passwordGroups() const j = group.indexOf(ch); } } - if (group.size() > 0) { + if (!group.isEmpty()) { passwordGroups.replace(i, group); - i++; + ++i; } else { passwordGroups.remove(i); } diff --git a/src/core/PasswordGenerator.h b/src/core/PasswordGenerator.h index 1d6ac73f6..cb6402d0f 100644 --- a/src/core/PasswordGenerator.h +++ b/src/core/PasswordGenerator.h @@ -66,7 +66,6 @@ public: bool isValid() const; QString generatePassword() const; - int getbits() const; static const int DefaultLength = 16; static const char* DefaultExcludedChars; diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 458d42988..60c50b451 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -18,19 +18,20 @@ */ #include "Tools.h" +#include "core/Config.h" +#include "core/Translator.h" #include #include #include #include #include -#include - #include +#include + #ifdef Q_OS_WIN -#include // for SetSecurityInfo() -#include // for Sleep(), SetDllDirectoryA(), SetSearchPathMode(), ... +#include // for Sleep() #endif #ifdef Q_OS_UNIX @@ -56,294 +57,161 @@ namespace Tools { +QString humanReadableFileSize(qint64 bytes, quint32 precision) +{ + constexpr auto kibibyte = 1024; + double size = bytes; - QString humanReadableFileSize(qint64 bytes, quint32 precision) - { - constexpr auto kibibyte = 1024; - double size = bytes; + QStringList units = QStringList() << "B" + << "KiB" + << "MiB" + << "GiB"; + int i = 0; + int maxI = units.size() - 1; - QStringList units = QStringList() << "B" - << "KiB" - << "MiB" - << "GiB"; - int i = 0; - int maxI = units.size() - 1; - - while ((size >= kibibyte) && (i < maxI)) { - size /= kibibyte; - i++; - } - - return QString("%1 %2").arg(QLocale().toString(size, 'f', precision), units.at(i)); + while ((size >= kibibyte) && (i < maxI)) { + size /= kibibyte; + i++; } - bool hasChild(const QObject* parent, const QObject* child) - { - if (!parent || !child) { - return false; - } + return QString("%1 %2").arg(QLocale().toString(size, 'f', precision), units.at(i)); +} - const QObjectList children = parent->children(); - for (QObject* c : children) { - if (child == c || hasChild(c, child)) { - return true; - } - } +bool hasChild(const QObject* parent, const QObject* child) +{ + if (!parent || !child) { return false; } - bool readFromDevice(QIODevice* device, QByteArray& data, int size) - { - QByteArray buffer; - buffer.resize(size); - - qint64 readResult = device->read(buffer.data(), size); - if (readResult == -1) { - return false; - } else { - buffer.resize(readResult); - data = buffer; + const QObjectList children = parent->children(); + for (QObject* c : children) { + if (child == c || hasChild(c, child)) { return true; } } + return false; +} - bool readAllFromDevice(QIODevice* device, QByteArray& data) - { - QByteArray result; - qint64 readBytes = 0; - qint64 readResult; - do { - result.resize(result.size() + 16384); - readResult = device->read(result.data() + readBytes, result.size() - readBytes); - if (readResult > 0) { - readBytes += readResult; - } - } while (readResult > 0); - - if (readResult == -1) { - return false; - } else { - result.resize(static_cast(readBytes)); - data = result; - return true; - } - } - - QString imageReaderFilter() - { - const QList formats = QImageReader::supportedImageFormats(); - QStringList formatsStringList; - - for (const QByteArray& format : formats) { - for (int i = 0; i < format.size(); i++) { - if (!QChar(format.at(i)).isLetterOrNumber()) { - continue; - } - } - - formatsStringList.append("*." + QString::fromLatin1(format).toLower()); - } - - return formatsStringList.join(" "); - } - - bool isHex(const QByteArray& ba) - { - for (const unsigned char c : ba) { - if (!std::isxdigit(c)) { - return false; - } - } +bool readFromDevice(QIODevice* device, QByteArray& data, int size) +{ + QByteArray buffer; + buffer.resize(size); + qint64 readResult = device->read(buffer.data(), size); + if (readResult == -1) { + return false; + } else { + buffer.resize(readResult); + data = buffer; return true; } +} - bool isBase64(const QByteArray& ba) - { - constexpr auto pattern = R"(^(?:[a-z0-9+]{4})*(?:[a-z0-9+]{3}=|[a-z0-9+]{2}==)?$)"; - QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::RegExp2); - - QString base64 = QString::fromLatin1(ba.constData(), ba.size()); - - return regexp.exactMatch(base64); +bool readAllFromDevice(QIODevice* device, QByteArray& data) +{ + QByteArray result; + qint64 readBytes = 0; + qint64 readResult; + do { + result.resize(result.size() + 16384); + readResult = device->read(result.data() + readBytes, result.size() - readBytes); + if (readResult > 0) { + readBytes += readResult; + } } + while (readResult > 0); - void sleep(int ms) - { - Q_ASSERT(ms >= 0); + if (readResult == -1) { + return false; + } else { + result.resize(static_cast(readBytes)); + data = result; + return true; + } +} - if (ms == 0) { - return; +QString imageReaderFilter() +{ + const QList formats = QImageReader::supportedImageFormats(); + QStringList formatsStringList; + + for (const QByteArray& format : formats) { + for (int i = 0; i < format.size(); i++) { + if (!QChar(format.at(i)).isLetterOrNumber()) { + continue; + } } + formatsStringList.append("*." + QString::fromLatin1(format).toLower()); + } + + return formatsStringList.join(" "); +} + +bool isHex(const QByteArray& ba) +{ + for (const unsigned char c : ba) { + if (!std::isxdigit(c)) { + return false; + } + } + + return true; +} + +bool isBase64(const QByteArray& ba) +{ + constexpr auto pattern = R"(^(?:[a-z0-9+]{4})*(?:[a-z0-9+]{3}=|[a-z0-9+]{2}==)?$)"; + QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::RegExp2); + + QString base64 = QString::fromLatin1(ba.constData(), ba.size()); + + return regexp.exactMatch(base64); +} + +void sleep(int ms) +{ + Q_ASSERT(ms >= 0); + + if (ms == 0) { + return; + } + #ifdef Q_OS_WIN - Sleep(uint(ms)); + Sleep(uint(ms)); #else - timespec ts; - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000 * 1000; - nanosleep(&ts, nullptr); + timespec ts; + ts.tv_sec = ms/1000; + ts.tv_nsec = (ms%1000)*1000*1000; + nanosleep(&ts, nullptr); #endif +} + +void wait(int ms) +{ + Q_ASSERT(ms >= 0); + + if (ms == 0) { + return; } - void wait(int ms) - { - Q_ASSERT(ms >= 0); + QElapsedTimer timer; + timer.start(); - if (ms == 0) { - return; - } - - QElapsedTimer timer; - timer.start(); - - if (ms <= 50) { - QCoreApplication::processEvents(QEventLoop::AllEvents, ms); - sleep(qMax(ms - static_cast(timer.elapsed()), 0)); - } else { - int timeLeft; - do { - timeLeft = ms - timer.elapsed(); - if (timeLeft > 0) { - QCoreApplication::processEvents(QEventLoop::AllEvents, timeLeft); - sleep(10); - } - } while (!timer.hasExpired(ms)); + if (ms <= 50) { + QCoreApplication::processEvents(QEventLoop::AllEvents, ms); + sleep(qMax(ms - static_cast(timer.elapsed()), 0)); + } else { + int timeLeft; + do { + timeLeft = ms - timer.elapsed(); + if (timeLeft > 0) { + QCoreApplication::processEvents(QEventLoop::AllEvents, timeLeft); + sleep(10); + } } + while (!timer.hasExpired(ms)); } - - void disableCoreDumps() - { - // default to true - // there is no point in printing a warning if this is not implemented on the platform - bool success = true; - -#if defined(HAVE_RLIMIT_CORE) - struct rlimit limit; - limit.rlim_cur = 0; - limit.rlim_max = 0; - success = success && (setrlimit(RLIMIT_CORE, &limit) == 0); -#endif - -#if defined(HAVE_PR_SET_DUMPABLE) - success = success && (prctl(PR_SET_DUMPABLE, 0) == 0); -#endif - -// Mac OS X -#ifdef HAVE_PT_DENY_ATTACH - success = success && (ptrace(PT_DENY_ATTACH, 0, 0, 0) == 0); -#endif - -#ifdef Q_OS_WIN - success = success && createWindowsDACL(); -#endif - - if (!success) { - qWarning("Unable to disable core dumps."); - } - } - - void setupSearchPaths() - { -#ifdef Q_OS_WIN - // Make sure Windows doesn't load DLLs from the current working directory - SetDllDirectoryA(""); - SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE); -#endif - } - - // - // This function grants the user associated with the process token minimal access rights and - // denies everything else on Windows. This includes PROCESS_QUERY_INFORMATION and - // PROCESS_VM_READ access rights that are required for MiniDumpWriteDump() or ReadProcessMemory(). - // We do this using a discretionary access control list (DACL). Effectively this prevents - // crash dumps and disallows other processes from accessing our memory. This works as long - // as you do not have admin privileges, since then you are able to grant yourself the - // SeDebugPrivilege or SeTakeOwnershipPrivilege and circumvent the DACL. - // - bool createWindowsDACL() - { - bool bSuccess = false; - -#ifdef Q_OS_WIN - // Process token and user - HANDLE hToken = nullptr; - PTOKEN_USER pTokenUser = nullptr; - DWORD cbBufferSize = 0; - - // Access control list - PACL pACL = nullptr; - DWORD cbACL = 0; - - // Open the access token associated with the calling process - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { - goto Cleanup; - } - - // Retrieve the token information in a TOKEN_USER structure - GetTokenInformation(hToken, TokenUser, nullptr, 0, &cbBufferSize); - - pTokenUser = static_cast(HeapAlloc(GetProcessHeap(), 0, cbBufferSize)); - if (pTokenUser == nullptr) { - goto Cleanup; - } - - if (!GetTokenInformation(hToken, TokenUser, pTokenUser, cbBufferSize, &cbBufferSize)) { - goto Cleanup; - } - - if (!IsValidSid(pTokenUser->User.Sid)) { - goto Cleanup; - } - - // Calculate the amount of memory that must be allocated for the DACL - cbACL = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pTokenUser->User.Sid); - - // Create and initialize an ACL - pACL = static_cast(HeapAlloc(GetProcessHeap(), 0, cbACL)); - if (pACL == nullptr) { - goto Cleanup; - } - - if (!InitializeAcl(pACL, cbACL, ACL_REVISION)) { - goto Cleanup; - } - - // Add allowed access control entries, everything else is denied - if (!AddAccessAllowedAce( - pACL, - ACL_REVISION, - SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, // same as protected process - pTokenUser->User.Sid // pointer to the trustee's SID - )) { - goto Cleanup; - } - - // Set discretionary access control list - bSuccess = ERROR_SUCCESS - == SetSecurityInfo(GetCurrentProcess(), // object handle - SE_KERNEL_OBJECT, // type of object - DACL_SECURITY_INFORMATION, // change only the objects DACL - nullptr, - nullptr, // do not change owner or group - pACL, // DACL specified - nullptr // do not change SACL - ); - - Cleanup: - - if (pACL != nullptr) { - HeapFree(GetProcessHeap(), 0, pACL); - } - if (pTokenUser != nullptr) { - HeapFree(GetProcessHeap(), 0, pTokenUser); - } - if (hToken != nullptr) { - CloseHandle(hToken); - } -#endif - - return bSuccess; - } +} } // namespace Tools diff --git a/src/core/Tools.h b/src/core/Tools.h index 4f75b750b..aec937304 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -30,31 +30,27 @@ class QIODevice; namespace Tools { +QString humanReadableFileSize(qint64 bytes, quint32 precision = 2); +bool hasChild(const QObject* parent, const QObject* child); +bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384); +bool readAllFromDevice(QIODevice* device, QByteArray& data); +QString imageReaderFilter(); +bool isHex(const QByteArray& ba); +bool isBase64(const QByteArray& ba); +void sleep(int ms); +void wait(int ms); - QString humanReadableFileSize(qint64 bytes, quint32 precision = 2); - bool hasChild(const QObject* parent, const QObject* child); - bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384); - bool readAllFromDevice(QIODevice* device, QByteArray& data); - QString imageReaderFilter(); - bool isHex(const QByteArray& ba); - bool isBase64(const QByteArray& ba); - void sleep(int ms); - void wait(int ms); - void disableCoreDumps(); - void setupSearchPaths(); - bool createWindowsDACL(); +template +RandomAccessIterator binaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T& value) +{ + RandomAccessIterator it = std::lower_bound(begin, end, value); - template - RandomAccessIterator binaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T& value) - { - RandomAccessIterator it = std::lower_bound(begin, end, value); - - if ((it == end) || (value < *it)) { - return end; - } else { - return it; - } + if ((it == end) || (value < *it)) { + return end; + } else { + return it; } +} } // namespace Tools diff --git a/src/crypto/CryptoHash.cpp b/src/crypto/CryptoHash.cpp index 12c6bf791..3c5e4c284 100644 --- a/src/crypto/CryptoHash.cpp +++ b/src/crypto/CryptoHash.cpp @@ -98,13 +98,6 @@ void CryptoHash::setKey(const QByteArray& data) Q_ASSERT(error == 0); } -void CryptoHash::reset() -{ - Q_D(CryptoHash); - - gcry_md_reset(d->ctx); -} - QByteArray CryptoHash::result() const { Q_D(const CryptoHash); diff --git a/src/crypto/CryptoHash.h b/src/crypto/CryptoHash.h index bd312121a..02f90eb4d 100644 --- a/src/crypto/CryptoHash.h +++ b/src/crypto/CryptoHash.h @@ -34,7 +34,6 @@ public: explicit CryptoHash(Algorithm algo, bool hmac = false); ~CryptoHash(); void addData(const QByteArray& data); - void reset(); QByteArray result() const; void setKey(const QByteArray& data); diff --git a/src/crypto/SymmetricCipher.cpp b/src/crypto/SymmetricCipher.cpp index 0467ad7c2..828d3a998 100644 --- a/src/crypto/SymmetricCipher.cpp +++ b/src/crypto/SymmetricCipher.cpp @@ -94,7 +94,7 @@ QString SymmetricCipher::errorString() const SymmetricCipher::Algorithm SymmetricCipher::cipherToAlgorithm(const QUuid& cipher) { - if (cipher == KeePass2::CIPHER_AES) { + if (cipher == KeePass2::CIPHER_AES256) { return Aes256; } else if (cipher == KeePass2::CIPHER_CHACHA20) { return ChaCha20; @@ -109,15 +109,17 @@ SymmetricCipher::Algorithm SymmetricCipher::cipherToAlgorithm(const QUuid& ciphe QUuid SymmetricCipher::algorithmToCipher(Algorithm algo) { switch (algo) { + case Aes128: + return KeePass2::CIPHER_AES128; case Aes256: - return KeePass2::CIPHER_AES; + return KeePass2::CIPHER_AES256; case ChaCha20: return KeePass2::CIPHER_CHACHA20; case Twofish: return KeePass2::CIPHER_TWOFISH; default: qWarning("SymmetricCipher::algorithmToCipher: invalid algorithm %d", algo); - return QUuid(); + return {}; } } diff --git a/src/crypto/SymmetricCipherGcrypt.cpp b/src/crypto/SymmetricCipherGcrypt.cpp index c7a5e6a07..e3bc88cbf 100644 --- a/src/crypto/SymmetricCipherGcrypt.cpp +++ b/src/crypto/SymmetricCipherGcrypt.cpp @@ -185,8 +185,6 @@ bool SymmetricCipherGcrypt::processInPlace(QByteArray& data) bool SymmetricCipherGcrypt::processInPlace(QByteArray& data, quint64 rounds) { - // TODO: check block size - gcry_error_t error; char* rawData = data.data(); diff --git a/src/format/Kdbx3Reader.cpp b/src/format/Kdbx3Reader.cpp index aeacaad3d..0114a0b76 100644 --- a/src/format/Kdbx3Reader.cpp +++ b/src/format/Kdbx3Reader.cpp @@ -110,14 +110,6 @@ Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device, return nullptr; } - QBuffer buffer; - if (saveXml()) { - m_xmlData = xmlDevice->readAll(); - buffer.setBuffer(&m_xmlData); - buffer.open(QIODevice::ReadOnly); - xmlDevice = &buffer; - } - Q_ASSERT(xmlDevice); KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_3_1); diff --git a/src/format/Kdbx4Reader.cpp b/src/format/Kdbx4Reader.cpp index 7b94d34f8..5a024a254 100644 --- a/src/format/Kdbx4Reader.cpp +++ b/src/format/Kdbx4Reader.cpp @@ -124,14 +124,6 @@ Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device, return nullptr; } - QBuffer buffer; - if (saveXml()) { - m_xmlData = xmlDevice->readAll(); - buffer.setBuffer(&m_xmlData); - buffer.open(QIODevice::ReadOnly); - xmlDevice = &buffer; - } - Q_ASSERT(xmlDevice); KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_4, binaryPool()); diff --git a/src/format/KdbxReader.cpp b/src/format/KdbxReader.cpp index 5e14b8a9f..13a792fd5 100644 --- a/src/format/KdbxReader.cpp +++ b/src/format/KdbxReader.cpp @@ -1,3 +1,5 @@ +#include + /* * Copyright (C) 2018 KeePassXC Team * @@ -18,6 +20,9 @@ #include "KdbxReader.h" #include "core/Database.h" #include "core/Endian.h" +#include "format/KdbxXmlWriter.h" + +#include #define UUID_LENGTH 16 @@ -92,7 +97,14 @@ Database* KdbxReader::readDatabase(QIODevice* device, QSharedPointer m_db; QPair m_kdbxSignature; diff --git a/src/format/KdbxXmlReader.cpp b/src/format/KdbxXmlReader.cpp index 76fa03221..c9e5c31af 100644 --- a/src/format/KdbxXmlReader.cpp +++ b/src/format/KdbxXmlReader.cpp @@ -82,7 +82,6 @@ Database* KdbxXmlReader::readDatabase(QIODevice* device) * @param db database to read into * @param randomStream random stream to use for decryption */ -#include "QDebug" void KdbxXmlReader::readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream) { m_error = false; diff --git a/src/format/KdbxXmlWriter.cpp b/src/format/KdbxXmlWriter.cpp index 5ad1e34ae..c26d316dc 100644 --- a/src/format/KdbxXmlWriter.cpp +++ b/src/format/KdbxXmlWriter.cpp @@ -358,10 +358,10 @@ void KdbxXmlWriter::writeEntry(const Entry* entry) bool protect = (((key == "Title") && m_meta->protectTitle()) || ((key == "UserName") && m_meta->protectUsername()) - || ((key == "Password") && m_meta->protectPassword()) - || ((key == "URL") && m_meta->protectUrl()) - || ((key == "Notes") && m_meta->protectNotes()) - || entry->attributes()->isProtected(key)); + || ((key == "Password") && m_meta->protectPassword()) + || ((key == "URL") && m_meta->protectUrl()) + || ((key == "Notes") && m_meta->protectNotes()) + || entry->attributes()->isProtected(key)); writeString("Key", key); @@ -369,7 +369,7 @@ void KdbxXmlWriter::writeEntry(const Entry* entry) QString value; if (protect) { - if (m_randomStream) { + if (!m_innerStreamProtectionDisabled && m_randomStream) { m_xml.writeAttribute("Protected", "True"); bool ok; QByteArray rawData = m_randomStream->process(entry->attributes()->value(key).toUtf8(), &ok); @@ -596,3 +596,24 @@ void KdbxXmlWriter::raiseError(const QString& errorMessage) m_error = true; m_errorStr = errorMessage; } + +/** + * Disable inner stream protection and write protected fields + * in plaintext instead. This is useful for plaintext XML exports + * where the inner stream key is not available. + * + * @param disable true to disable protection + */ +void KdbxXmlWriter::disableInnerStreamProtection(bool disable) +{ + m_innerStreamProtectionDisabled = disable; +} + +/** + * @return true if inner stream protection is disabled and protected + * fields will be saved in plaintext + */ +bool KdbxXmlWriter::innerStreamProtectionDisabled() const +{ + return m_innerStreamProtectionDisabled; +} diff --git a/src/format/KdbxXmlWriter.h b/src/format/KdbxXmlWriter.h index 51a803497..2f7215b46 100644 --- a/src/format/KdbxXmlWriter.h +++ b/src/format/KdbxXmlWriter.h @@ -41,6 +41,8 @@ public: KeePass2RandomStream* randomStream = nullptr, const QByteArray& headerHash = QByteArray()); void writeDatabase(const QString& filename, Database* db); + void disableInnerStreamProtection(bool disable); + bool innerStreamProtectionDisabled() const; bool hasError(); QString errorString(); @@ -81,6 +83,8 @@ private: const quint32 m_kdbxVersion; + bool m_innerStreamProtectionDisabled = false; + QXmlStreamWriter m_xml; QPointer m_db; QPointer m_meta; diff --git a/src/format/KeePass2.cpp b/src/format/KeePass2.cpp index 5aad1f7f2..9c0714484 100644 --- a/src/format/KeePass2.cpp +++ b/src/format/KeePass2.cpp @@ -23,7 +23,8 @@ #define UUID_LENGTH 16 -const QUuid KeePass2::CIPHER_AES = QUuid("31c1f2e6-bf71-4350-be58-05216afc5aff"); +const QUuid KeePass2::CIPHER_AES128 = QUuid("61ab05a1-9464-41c3-8d74-3a563df8dd35"); +const QUuid KeePass2::CIPHER_AES256 = QUuid("31c1f2e6-bf71-4350-be58-05216afc5aff"); const QUuid KeePass2::CIPHER_TWOFISH = QUuid("ad68f29f-576f-4bb9-a36a-d47af965346c"); const QUuid KeePass2::CIPHER_CHACHA20 = QUuid("d6038a2b-8b6f-4cb5-a524-339a31dbb59a"); @@ -47,7 +48,7 @@ const QString KeePass2::KDFPARAM_ARGON2_SECRET("K"); const QString KeePass2::KDFPARAM_ARGON2_ASSOCDATA("A"); const QList> KeePass2::CIPHERS{ - qMakePair(KeePass2::CIPHER_AES, QObject::tr("AES: 256-bit")), + qMakePair(KeePass2::CIPHER_AES256, QObject::tr("AES: 256-bit")), qMakePair(KeePass2::CIPHER_TWOFISH, QObject::tr("Twofish: 256-bit")), qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20: 256-bit")) }; diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h index a8e97c5bd..02fe635ca 100644 --- a/src/format/KeePass2.h +++ b/src/format/KeePass2.h @@ -46,7 +46,8 @@ namespace KeePass2 const QSysInfo::Endian BYTEORDER = QSysInfo::LittleEndian; -extern const QUuid CIPHER_AES; +extern const QUuid CIPHER_AES128; +extern const QUuid CIPHER_AES256; extern const QUuid CIPHER_TWOFISH; extern const QUuid CIPHER_CHACHA20; diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 2a6d61118..8cdb8ff43 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -93,7 +93,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, QSharedPointersetSaveXml(m_saveXml); - return m_reader->readDatabase(device, key, keepDatabase); + return m_reader->readDatabase(device, std::move(key), keepDatabase); } bool KeePass2Reader::hasError() const diff --git a/src/gui/Application.h b/src/gui/Application.h index a6c6fdf90..3fdd8af90 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -22,8 +22,8 @@ #include #include -class QLockFile; +class QLockFile; class QSocketNotifier; class Application : public QApplication diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 0e2bc96e8..828aace51 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -216,7 +216,7 @@ private: void setIconFromParent(); void replaceDatabase(Database* db); - Database* m_db; + QPointer m_db; QWidget* m_mainWidget; EditEntryWidget* m_editEntryWidget; EditEntryWidget* m_historyEditEntryWidget; diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp index 2a3cf7cbb..63a1ccef8 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp @@ -81,7 +81,7 @@ void DatabaseSettingsWidgetEncryption::initialize() } if (!m_db->key()) { m_db->setKey(QSharedPointer::create()); - m_db->setCipher(KeePass2::CIPHER_AES); + m_db->setCipher(KeePass2::CIPHER_AES256); isDirty = true; } diff --git a/src/main.cpp b/src/main.cpp index 903974fa7..74af06953 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,19 +16,18 @@ * along with this program. If not, see . */ -#include #include #include +#include #include "config-keepassx.h" -#include "core/Config.h" +#include "core/Bootstrap.h" #include "core/Tools.h" -#include "core/Translator.h" +#include "core/Config.h" #include "crypto/Crypto.h" #include "gui/Application.h" #include "gui/MainWindow.h" #include "gui/MessageBox.h" - #include "cli/Utils.h" #if defined(WITH_ASAN) && defined(WITH_LSAN) @@ -45,55 +44,29 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) #endif #endif -static inline void earlyQNetworkAccessManagerWorkaround() -{ - // When QNetworkAccessManager is instantiated it regularly starts polling - // all network interfaces to see if anything changes and if so, what. This - // creates a latency spike every 10 seconds on Mac OS 10.12+ and Windows 7 >= - // when on a wifi connection. - // So here we disable it for lack of better measure. - // This will also cause this message: QObject::startTimer: Timers cannot - // have negative intervals - // For more info see: - // - https://bugreports.qt.io/browse/QTBUG-40332 - // - https://bugreports.qt.io/browse/QTBUG-46015 - qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); -} - int main(int argc, char** argv) { -#ifdef QT_NO_DEBUG - Tools::disableCoreDumps(); -#endif - Tools::setupSearchPaths(); - - earlyQNetworkAccessManagerWorkaround(); - Application app(argc, argv); Application::setApplicationName("keepassxc"); Application::setApplicationVersion(KEEPASSX_VERSION); // don't set organizationName as that changes the return value of // QStandardPaths::writableLocation(QDesktopServices::DataLocation) + Bootstrap::bootstrapApplication(); QCommandLineParser parser; - parser.setApplicationDescription( - QCoreApplication::translate("main", "KeePassXC - cross-platform password manager")); - parser.addPositionalArgument( - "filename", - QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), - "[filename(s)]"); + parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC - cross-platform password manager")); + parser.addPositionalArgument("filename", + QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), "[filename(s)]"); QCommandLineOption configOption( "config", QCoreApplication::translate("main", "path to a custom config file"), "config"); QCommandLineOption keyfileOption( "keyfile", QCoreApplication::translate("main", "key file of the database"), "keyfile"); QCommandLineOption pwstdinOption("pw-stdin", - QCoreApplication::translate("main", "read password of the database from stdin")); + QCoreApplication::translate("main", "read password of the database from stdin")); // This is needed under Windows where clients send --parent-window parameter with Native Messaging connect method - QCommandLineOption parentWindowOption(QStringList() << "pw" - << "parent-window", - QCoreApplication::translate("main", "Parent window handle"), - "handle"); + QCommandLineOption parentWindowOption( + QStringList() << "pw" << "parent-window", QCoreApplication::translate("main", "Parent window handle"), "handle"); QCommandLineOption helpOption = parser.addHelpOption(); QCommandLineOption versionOption = parser.addVersionOption(); @@ -115,9 +88,7 @@ int main(int argc, char** argv) if (!fileNames.isEmpty()) { app.sendFileNamesToRunningInstance(fileNames); } - qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.") - .toUtf8() - .constData(); + qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.").toUtf8().constData(); return 0; } @@ -135,46 +106,14 @@ int main(int argc, char** argv) Config::createConfigFromFile(parser.value(configOption)); } - Translator::installTranslators(); - -#ifdef Q_OS_MAC - // Don't show menu icons on OSX - QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); -#endif - MainWindow mainWindow; app.setMainWindow(&mainWindow); - QObject::connect(&app, SIGNAL(anotherInstanceStarted()), &mainWindow, SLOT(bringToFront())); QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront())); QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString))); QObject::connect(&app, SIGNAL(quitSignalReceived()), &mainWindow, SLOT(appExit()), Qt::DirectConnection); - // start minimized if configured - bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool(); - bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool(); -#ifndef Q_OS_LINUX - if (minimizeOnStartup) { -#else - // On some Linux systems, the window should NOT be minimized and hidden (i.e. not shown), at - // the same time (which would happen if both minimize on startup and minimize to tray are set) - // since otherwise it causes problems on restore as seen on issue #1595. Hiding it is enough. - if (minimizeOnStartup && !minimizeToTray) { -#endif - mainWindow.setWindowState(Qt::WindowMinimized); - } - if (!(minimizeOnStartup && minimizeToTray)) { - mainWindow.show(); - } - - if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) { - const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList(); - for (const QString& filename : fileNames) { - if (!filename.isEmpty() && QFile::exists(filename)) { - mainWindow.openDatabase(filename); - } - } - } + Bootstrap::restoreMainWindowState(mainWindow); const bool pwstdin = parser.isSet(pwstdinOption); for (const QString& filename : fileNames) { @@ -193,7 +132,7 @@ int main(int argc, char** argv) } } - int exitCode = app.exec(); + int exitCode = Application::exec(); #if defined(WITH_ASAN) && defined(WITH_LSAN) // do leak check here to prevent massive tail of end-of-process leak errors from third-party libraries diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 73262bae0..562b45e4d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -163,7 +163,10 @@ add_unit_test(NAME testentry SOURCES TestEntry.cpp LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testmerge SOURCES TestMerge.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) + +add_unit_test(NAME testpasswordgenerator SOURCES TestPasswordGenerator.cpp + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testtotp SOURCES TestTotp.cpp LIBS ${TEST_LIBRARIES}) @@ -180,7 +183,7 @@ add_unit_test(NAME testrandom SOURCES TestRandom.cpp add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp LIBS ${TEST_LIBRARIES}) -add_unit_test(NAME testcsvexporter SOURCES TestCsvExporter.cpp +add_unit_test(NAME testcsveporter SOURCES TestCsvExporter.cpp LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testykchallengeresponsekey @@ -193,6 +196,11 @@ add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp add_unit_test(NAME testtools SOURCES TestTools.cpp LIBS ${TEST_LIBRARIES}) + if(WITH_GUI_TESTS) + # CLI clip tests need X environment on Linux + add_unit_test(NAME testcli SOURCES TestCli.cpp + LIBS testsupport cli ${TEST_LIBRARIES}) + add_subdirectory(gui) endif(WITH_GUI_TESTS) diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp new file mode 100644 index 000000000..02bc0ba3f --- /dev/null +++ b/tests/TestCli.cpp @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TestCli.h" +#include "config-keepassx-tests.h" +#include "core/Global.h" +#include "core/Config.h" +#include "core/Bootstrap.h" +#include "core/Tools.h" +#include "core/PasswordGenerator.h" +#include "crypto/Crypto.h" +#include "format/KeePass2.h" +#include "format/Kdbx3Reader.h" +#include "format/Kdbx4Reader.h" +#include "format/Kdbx4Writer.h" +#include "format/Kdbx3Writer.h" +#include "format/KdbxXmlReader.h" + +#include "cli/Command.h" +#include "cli/Utils.h" +#include "cli/Add.h" +#include "cli/Clip.h" +#include "cli/Diceware.h" +#include "cli/Edit.h" +#include "cli/Estimate.h" +#include "cli/Extract.h" +#include "cli/Generate.h" +#include "cli/List.h" +#include "cli/Locate.h" +#include "cli/Merge.h" +#include "cli/Remove.h" +#include "cli/Show.h" + +#include +#include +#include +#include +#include + +#include + +QTEST_MAIN(TestCli) + +void TestCli::initTestCase() +{ + QVERIFY(Crypto::init()); + + Config::createTempFileInstance(); + Bootstrap::bootstrapApplication(); + + // Load the NewDatabase.kdbx file into temporary storage + QFile sourceDbFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx")); + QVERIFY(sourceDbFile.open(QIODevice::ReadOnly)); + QVERIFY(Tools::readAllFromDevice(&sourceDbFile, m_dbData)); + sourceDbFile.close(); +} + +void TestCli::init() +{ + m_dbFile.reset(new QTemporaryFile()); + m_dbFile->open(); + m_dbFile->write(m_dbData); + m_dbFile->flush(); + + m_stdinFile.reset(new QTemporaryFile()); + m_stdinFile->open(); + m_stdinHandle = fdopen(m_stdinFile->handle(), "r+"); + Utils::STDIN = m_stdinHandle; + + m_stdoutFile.reset(new QTemporaryFile()); + m_stdoutFile->open(); + m_stdoutHandle = fdopen(m_stdoutFile->handle(), "r+"); + Utils::STDOUT = m_stdoutHandle; + + m_stderrFile.reset(new QTemporaryFile()); + m_stderrFile->open(); + m_stderrHandle = fdopen(m_stderrFile->handle(), "r+"); + Utils::STDERR = m_stderrHandle; +} + +void TestCli::cleanup() +{ + m_dbFile.reset(); + + m_stdinFile.reset(); + m_stdinHandle = stdin; + Utils::STDIN = stdin; + + m_stdoutFile.reset(); + Utils::STDOUT = stdout; + m_stdoutHandle = stdout; + + m_stderrFile.reset(); + m_stderrHandle = stderr; + Utils::STDERR = stderr; +} + +void TestCli::cleanupTestCase() +{ +} + +QSharedPointer TestCli::readTestDatabase() const +{ + Utils::setNextPassword("a"); + auto db = QSharedPointer(Database::unlockFromStdin(m_dbFile->fileName(), "", m_stdoutHandle)); + m_stdoutFile->seek(ftell(m_stdoutHandle)); // re-synchronize handles + return db; +} + +void TestCli::testCommand() +{ + QCOMPARE(Command::getCommands().size(), 12); + QVERIFY(Command::getCommand("add")); + QVERIFY(Command::getCommand("clip")); + QVERIFY(Command::getCommand("diceware")); + QVERIFY(Command::getCommand("edit")); + QVERIFY(Command::getCommand("estimate")); + QVERIFY(Command::getCommand("extract")); + QVERIFY(Command::getCommand("generate")); + QVERIFY(Command::getCommand("locate")); + QVERIFY(Command::getCommand("ls")); + QVERIFY(Command::getCommand("merge")); + QVERIFY(Command::getCommand("rm")); + QVERIFY(Command::getCommand("show")); + QVERIFY(!Command::getCommand("doesnotexist")); +} + +void TestCli::testAdd() +{ + Add addCmd; + QVERIFY(!addCmd.name.isEmpty()); + QVERIFY(addCmd.getDescriptionLine().contains(addCmd.name)); + + Utils::setNextPassword("a"); + addCmd.execute({"add", "-u", "newuser", "--url", "https://example.com/", "-g", "-l", "20", m_dbFile->fileName(), "/newuser-entry"}); + + auto db = readTestDatabase(); + auto* entry = db->rootGroup()->findEntryByPath("/newuser-entry"); + QVERIFY(entry); + QCOMPARE(entry->username(), QString("newuser")); + QCOMPARE(entry->url(), QString("https://example.com/")); + QCOMPARE(entry->password().size(), 20); + + Utils::setNextPassword("a"); + Utils::setNextPassword("newpassword"); + addCmd.execute({"add", "-u", "newuser2", "--url", "https://example.net/", "-g", "-l", "20", "-p", m_dbFile->fileName(), "/newuser-entry2"}); + + db = readTestDatabase(); + entry = db->rootGroup()->findEntryByPath("/newuser-entry2"); + QVERIFY(entry); + QCOMPARE(entry->username(), QString("newuser2")); + QCOMPARE(entry->url(), QString("https://example.net/")); + QCOMPARE(entry->password(), QString("newpassword")); +} + +void TestCli::testClip() +{ + QClipboard* clipboard = QGuiApplication::clipboard(); + clipboard->clear(); + + Clip clipCmd; + QVERIFY(!clipCmd.name.isEmpty()); + QVERIFY(clipCmd.getDescriptionLine().contains(clipCmd.name)); + + Utils::setNextPassword("a"); + clipCmd.execute({"clip", m_dbFile->fileName(), "/Sample Entry"}); + + m_stderrFile->reset(); + QString errorOutput(m_stderrFile->readAll()); + + if (errorOutput.contains("Unable to start program") + || errorOutput.contains("No program defined for clipboard manipulation")) { + QSKIP("Clip test skipped due to missing clipboard tool"); + } + + QCOMPARE(clipboard->text(), QString("Password")); + + Utils::setNextPassword("a"); + QFuture future = QtConcurrent::run(&clipCmd, &Clip::execute, QStringList{"clip", m_dbFile->fileName(), "/Sample Entry", "1"}); + + QTRY_COMPARE_WITH_TIMEOUT(clipboard->text(), QString("Password"), 500); + QTRY_COMPARE_WITH_TIMEOUT(clipboard->text(), QString(""), 1500); + + future.waitForFinished(); +} + +void TestCli::testDiceware() +{ + Diceware dicewareCmd; + QVERIFY(!dicewareCmd.name.isEmpty()); + QVERIFY(dicewareCmd.getDescriptionLine().contains(dicewareCmd.name)); + + dicewareCmd.execute({"diceware"}); + m_stdoutFile->reset(); + QString passphrase(m_stdoutFile->readLine()); + QVERIFY(!passphrase.isEmpty()); + + dicewareCmd.execute({"diceware", "-W", "2"}); + m_stdoutFile->seek(passphrase.toLatin1().size()); + passphrase = m_stdoutFile->readLine(); + QCOMPARE(passphrase.split(" ").size(), 2); + + auto pos = m_stdoutFile->pos(); + dicewareCmd.execute({"diceware", "-W", "10"}); + m_stdoutFile->seek(pos); + passphrase = m_stdoutFile->readLine(); + QCOMPARE(passphrase.split(" ").size(), 10); + + QTemporaryFile wordFile; + wordFile.open(); + for (int i = 0; i < 4500; ++i) { + wordFile.write(QString("word" + QString::number(i) + "\n").toLatin1()); + } + wordFile.close(); + + pos = m_stdoutFile->pos(); + dicewareCmd.execute({"diceware", "-W", "11", "-w", wordFile.fileName()}); + m_stdoutFile->seek(pos); + passphrase = m_stdoutFile->readLine(); + const auto words = passphrase.split(" "); + QCOMPARE(words.size(), 11); + QRegularExpression regex("^word\\d+$"); + for (const auto& word: words) { + QVERIFY2(regex.match(word).hasMatch(), qPrintable("Word " + word + " was not on the word list")); + } +} + +void TestCli::testEdit() +{ + Edit editCmd; + QVERIFY(!editCmd.name.isEmpty()); + QVERIFY(editCmd.getDescriptionLine().contains(editCmd.name)); + + Utils::setNextPassword("a"); + editCmd.execute({"edit", "-u", "newuser", "--url", "https://otherurl.example.com/", "-t", "newtitle", m_dbFile->fileName(), "/Sample Entry"}); + + auto db = readTestDatabase(); + auto* entry = db->rootGroup()->findEntryByPath("/newtitle"); + QVERIFY(entry); + QCOMPARE(entry->username(), QString("newuser")); + QCOMPARE(entry->url(), QString("https://otherurl.example.com/")); + QCOMPARE(entry->password(), QString("Password")); + + Utils::setNextPassword("a"); + editCmd.execute({"edit", "-g", m_dbFile->fileName(), "/newtitle"}); + db = readTestDatabase(); + entry = db->rootGroup()->findEntryByPath("/newtitle"); + QVERIFY(entry); + QCOMPARE(entry->username(), QString("newuser")); + QCOMPARE(entry->url(), QString("https://otherurl.example.com/")); + QVERIFY(!entry->password().isEmpty()); + QVERIFY(entry->password() != QString("Password")); + + Utils::setNextPassword("a"); + editCmd.execute({"edit", "-g", "-l", "34", "-t", "yet another title", m_dbFile->fileName(), "/newtitle"}); + db = readTestDatabase(); + entry = db->rootGroup()->findEntryByPath("/yet another title"); + QVERIFY(entry); + QCOMPARE(entry->username(), QString("newuser")); + QCOMPARE(entry->url(), QString("https://otherurl.example.com/")); + QVERIFY(entry->password() != QString("Password")); + QCOMPARE(entry->password().size(), 34); + + Utils::setNextPassword("a"); + Utils::setNextPassword("newpassword"); + editCmd.execute({"edit", "-p", m_dbFile->fileName(), "/yet another title"}); + db = readTestDatabase(); + entry = db->rootGroup()->findEntryByPath("/yet another title"); + QVERIFY(entry); + QCOMPARE(entry->password(), QString("newpassword")); +} + +void TestCli::testEstimate_data() +{ + QTest::addColumn("input"); + QTest::addColumn("length"); + QTest::addColumn("entropy"); + QTest::addColumn("log10"); + QTest::addColumn("searchStrings"); + + QTest::newRow("Dictionary") + << "password" << "8" << "1.0" << "0.3" + << QStringList{"Type: Dictionary", "\tpassword"}; + + QTest::newRow("Spatial") + << "zxcv" << "4" << "10.3" << "3.1" + << QStringList{"Type: Spatial", "\tzxcv"}; + + QTest::newRow("Spatial(Rep)") + << "sdfgsdfg" << "8" << "11.3" << "3.4" + << QStringList{"Type: Spatial(Rep)", "\tsdfgsdfg"}; + + QTest::newRow("Dictionary / Sequence") + << "password123" << "11" << "4.5" << "1.3" + << QStringList{"Type: Dictionary", "Type: Sequence", "\tpassword", "\t123"}; + + QTest::newRow("Dict+Leet") + << "p455w0rd" << "8" << "2.5" << "0.7" + << QStringList{"Type: Dict+Leet", "\tp455w0rd"}; + + QTest::newRow("Dictionary(Rep)") + << "hellohello" << "10" << "7.3" << "2.2" + << QStringList{"Type: Dictionary(Rep)", "\thellohello"}; + + QTest::newRow("Sequence(Rep) / Dictionary") + << "456456foobar" << "12" << "16.7" << "5.0" + << QStringList{"Type: Sequence(Rep)", "Type: Dictionary", "\t456456", "\tfoobar"}; + + QTest::newRow("Bruteforce(Rep) / Bruteforce") + << "xzxzy" << "5" << "16.1" << "4.8" + << QStringList{"Type: Bruteforce(Rep)", "Type: Bruteforce", "\txzxz", "\ty"}; + + QTest::newRow("Dictionary / Date(Rep)") + << "pass20182018" << "12" << "15.1" << "4.56" + << QStringList{"Type: Dictionary", "Type: Date(Rep)", "\tpass", "\t20182018"}; + + QTest::newRow("Dictionary / Date / Bruteforce") + << "mypass2018-2" << "12" << "32.9" << "9.9" + << QStringList{"Type: Dictionary", "Type: Date", "Type: Bruteforce", "\tmypass", "\t2018", "\t-2"}; + + QTest::newRow("Strong Password") + << "E*!%.Qw{t.X,&bafw)\"Q!ah$%;U/" << "28" << "165.7" << "49.8" + << QStringList{"Type: Bruteforce", "\tE*"}; + + // TODO: detect passphrases and adjust entropy calculation accordingly (issue #2347) + QTest::newRow("Strong Passphrase") + << "squint wooing resupply dangle isolation axis headsman" << "53" << "151.2" << "45.5" + << QStringList{"Type: Dictionary", "Type: Bruteforce", "Multi-word extra bits 22.0", "\tsquint", "\t ", "\twooing"}; +} + +void TestCli::testEstimate() +{ + QFETCH(QString, input); + QFETCH(QString, length); + QFETCH(QString, entropy); + QFETCH(QString, log10); + QFETCH(QStringList, searchStrings); + + Estimate estimateCmd; + QVERIFY(!estimateCmd.name.isEmpty()); + QVERIFY(estimateCmd.getDescriptionLine().contains(estimateCmd.name)); + + QTextStream in(m_stdinFile.data()); + QTextStream out(m_stdoutFile.data()); + + in << input << endl; + auto inEnd = in.pos(); + in.seek(0); + estimateCmd.execute({"estimate"}); + auto outEnd = out.pos(); + out.seek(0); + auto result = out.readAll(); + QVERIFY(result.startsWith("Length " + length)); + QVERIFY(result.contains("Entropy " + entropy)); + QVERIFY(result.contains("Log10 " + log10)); + + // seek to end of stream + in.seek(inEnd); + out.seek(outEnd); + + in << input << endl; + in.seek(inEnd); + estimateCmd.execute({"estimate", "-a"}); + out.seek(outEnd); + result = out.readAll(); + QVERIFY(result.startsWith("Length " + length)); + QVERIFY(result.contains("Entropy " + entropy)); + QVERIFY(result.contains("Log10 " + log10)); + for (const auto& string: asConst(searchStrings)) { + QVERIFY2(result.contains(string), qPrintable("String " + string + " missing")); + } +} + +void TestCli::testExtract() +{ + Extract extractCmd; + QVERIFY(!extractCmd.name.isEmpty()); + QVERIFY(extractCmd.getDescriptionLine().contains(extractCmd.name)); + + Utils::setNextPassword("a"); + extractCmd.execute({"extract", m_dbFile->fileName()}); + + m_stdoutFile->seek(0); + m_stdoutFile->readLine(); // skip prompt line + + KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1); + QScopedPointer db(new Database()); + reader.readDatabase(m_stdoutFile.data(), db.data()); + QVERIFY(!reader.hasError()); + QVERIFY(db.data()); + auto* entry = db->rootGroup()->findEntryByPath("/Sample Entry"); + QVERIFY(entry); + QCOMPARE(entry->password(), QString("Password")); +} + +void TestCli::testGenerate_data() +{ + QTest::addColumn("parameters"); + QTest::addColumn("pattern"); + + QTest::newRow("default") << QStringList{"generate"} << "^[^\r\n]+$"; + QTest::newRow("length") << QStringList{"generate", "-L", "13"} << "^.{13}$"; + QTest::newRow("lowercase") << QStringList{"generate", "-L", "14", "-l"} << "^[a-z]{14}$"; + QTest::newRow("uppercase") << QStringList{"generate", "-L", "15", "-u"} << "^[A-Z]{15}$"; + QTest::newRow("numbers")<< QStringList{"generate", "-L", "16", "-n"} << "^[0-9]{16}$"; + QTest::newRow("special") + << QStringList{"generate", "-L", "200", "-s"} + << R"(^[\(\)\[\]\{\}\.\-*|\\,:;"'\/\_!+-<=>?#$%&^`@~]{200}$)"; + QTest::newRow("special (exclude)") + << QStringList{"generate", "-L", "200", "-s" , "-x", "+.?@&"} + << R"(^[\(\)\[\]\{\}\.\-*|\\,:;"'\/\_!-<=>#$%^`~]{200}$)"; + QTest::newRow("extended") + << QStringList{"generate", "-L", "50", "-e"} + << R"(^[^a-zA-Z0-9\(\)\[\]\{\}\.\-\*\|\\,:;"'\/\_!+-<=>?#$%&^`@~]{50}$)"; + QTest::newRow("numbers + lowercase + uppercase") + << QStringList{"generate", "-L", "16", "-n", "-u", "-l"} + << "^[0-9a-zA-Z]{16}$"; + QTest::newRow("numbers + lowercase + uppercase (exclude)") + << QStringList{"generate", "-L", "500", "-n", "-u", "-l", "-x", "abcdefg0123@"} + << "^[^abcdefg0123@]{500}$"; + QTest::newRow("numbers + lowercase + uppercase (exclude similar)") + << QStringList{"generate", "-L", "200", "-n", "-u", "-l", "--exclude-similar"} + << "^[^l1IO0]{200}$"; + QTest::newRow("uppercase + lowercase (every)") + << QStringList{"generate", "-L", "2", "-u", "-l", "--every-group"} + << "^[a-z][A-Z]|[A-Z][a-z]$"; + QTest::newRow("numbers + lowercase (every)") + << QStringList{"generate", "-L", "2", "-n", "-l", "--every-group"} + << "^[a-z][0-9]|[0-9][a-z]$"; +} + +void TestCli::testGenerate() +{ + QFETCH(QStringList, parameters); + QFETCH(QString, pattern); + + Generate generateCmd; + QVERIFY(!generateCmd.name.isEmpty()); + QVERIFY(generateCmd.getDescriptionLine().contains(generateCmd.name)); + + qint64 pos = 0; + // run multiple times to make accidental passes unlikely + for (int i = 0; i < 10; ++i) { + generateCmd.execute(parameters); + m_stdoutFile->seek(pos); + QRegularExpression regex(pattern); + QString password = QString::fromUtf8(m_stdoutFile->readLine()); + pos = m_stdoutFile->pos(); + QVERIFY2(regex.match(password).hasMatch(), qPrintable("Password " + password + " does not match pattern " + pattern)); + } +} + +void TestCli::testList() +{ + List listCmd; + QVERIFY(!listCmd.name.isEmpty()); + QVERIFY(listCmd.getDescriptionLine().contains(listCmd.name)); + + Utils::setNextPassword("a"); + listCmd.execute({"ls", m_dbFile->fileName()}); + m_stdoutFile->reset(); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n" + "General/\n" + "Windows/\n" + "Network/\n" + "Internet/\n" + "eMail/\n" + "Homebanking/\n")); + + qint64 pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + listCmd.execute({"ls", "-R", m_dbFile->fileName()}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n" + "General/\n" + " [empty]\n" + "Windows/\n" + " [empty]\n" + "Network/\n" + " [empty]\n" + "Internet/\n" + " [empty]\n" + "eMail/\n" + " [empty]\n" + "Homebanking/\n" + " [empty]\n")); + + pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + listCmd.execute({"ls", m_dbFile->fileName(), "/General/"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("[empty]\n")); + + pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + listCmd.execute({"ls", m_dbFile->fileName(), "/DoesNotExist/"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->reset(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll(), QByteArray("Cannot find group /DoesNotExist/.\n")); +} + +void TestCli::testLocate() +{ + Locate locateCmd; + QVERIFY(!locateCmd.name.isEmpty()); + QVERIFY(locateCmd.getDescriptionLine().contains(locateCmd.name)); + + Utils::setNextPassword("a"); + locateCmd.execute({"locate", m_dbFile->fileName(), "Sample"}); + m_stdoutFile->reset(); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("/Sample Entry\n")); + + qint64 pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + locateCmd.execute({"locate", m_dbFile->fileName(), "Does Not Exist"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->reset(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll(), QByteArray("No results for that search term.\n")); + + // write a modified database + auto db = readTestDatabase(); + QVERIFY(db); + auto* group = db->rootGroup()->findGroupByPath("/General/"); + QVERIFY(group); + auto* entry = new Entry(); + entry->setUuid(QUuid::createUuid()); + entry->setTitle("New Entry"); + group->addEntry(entry); + QTemporaryFile tmpFile; + tmpFile.open(); + Kdbx4Writer writer; + writer.writeDatabase(&tmpFile, db.data()); + tmpFile.close(); + + pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + locateCmd.execute({"locate", tmpFile.fileName(), "New"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("/General/New Entry\n")); + + pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + locateCmd.execute({"locate", tmpFile.fileName(), "Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("/Sample Entry\n/General/New Entry\n")); +} + +void TestCli::testMerge() +{ + Merge mergeCmd; + QVERIFY(!mergeCmd.name.isEmpty()); + QVERIFY(mergeCmd.getDescriptionLine().contains(mergeCmd.name)); + + Kdbx4Writer writer; + Kdbx4Reader reader; + + // load test database and save a copy + auto db = readTestDatabase(); + QVERIFY(db); + QTemporaryFile targetFile1; + targetFile1.open(); + writer.writeDatabase(&targetFile1, db.data()); + targetFile1.close(); + + // save another copy with a different password + QTemporaryFile targetFile2; + targetFile2.open(); + auto oldKey = db->key(); + auto key = QSharedPointer::create(); + key->addKey(QSharedPointer::create("b")); + db->setKey(key); + writer.writeDatabase(&targetFile2, db.data()); + targetFile2.close(); + db->setKey(oldKey); + + // then add a new entry to the in-memory database and save another copy + auto* entry = new Entry(); + entry->setUuid(QUuid::createUuid()); + entry->setTitle("Some Website"); + entry->setPassword("secretsecretsecret"); + auto* group = db->rootGroup()->findGroupByPath("/Internet/"); + QVERIFY(group); + group->addEntry(entry); + QTemporaryFile sourceFile; + sourceFile.open(); + writer.writeDatabase(&sourceFile, db.data()); + sourceFile.close(); + + qint64 pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + mergeCmd.execute({"merge", "-s", targetFile1.fileName(), sourceFile.fileName()}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully merged the database files.\n")); + + QFile readBack(targetFile1.fileName()); + readBack.open(QIODevice::ReadOnly); + QScopedPointer mergedDb(reader.readDatabase(&readBack, oldKey)); + readBack.close(); + QVERIFY(mergedDb); + auto* entry1 = mergedDb->rootGroup()->findEntryByPath("/Internet/Some Website"); + QVERIFY(entry1); + QCOMPARE(entry1->title(), QString("Some Website")); + QCOMPARE(entry1->password(), QString("secretsecretsecret")); + + // try again with different passwords for both files + pos = m_stdoutFile->pos(); + Utils::setNextPassword("b"); + Utils::setNextPassword("a"); + mergeCmd.execute({"merge", targetFile2.fileName(), sourceFile.fileName()}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); + m_stdoutFile->readLine(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully merged the database files.\n")); + + readBack.setFileName(targetFile2.fileName()); + readBack.open(QIODevice::ReadOnly); + mergedDb.reset(reader.readDatabase(&readBack, key)); + readBack.close(); + QVERIFY(mergedDb); + entry1 = mergedDb->rootGroup()->findEntryByPath("/Internet/Some Website"); + QVERIFY(entry1); + QCOMPARE(entry1->title(), QString("Some Website")); + QCOMPARE(entry1->password(), QString("secretsecretsecret")); +} + +void TestCli::testRemove() +{ + Remove removeCmd; + QVERIFY(!removeCmd.name.isEmpty()); + QVERIFY(removeCmd.getDescriptionLine().contains(removeCmd.name)); + + Kdbx3Reader reader; + Kdbx3Writer writer; + + // load test database and save a copy with disabled recycle bin + auto db = readTestDatabase(); + QVERIFY(db); + QTemporaryFile fileCopy; + fileCopy.open(); + db->metadata()->setRecycleBinEnabled(false); + writer.writeDatabase(&fileCopy, db.data()); + fileCopy.close(); + + qint64 pos = m_stdoutFile->pos(); + + // delete entry and verify + Utils::setNextPassword("a"); + removeCmd.execute({"rm", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully recycled entry Sample Entry.\n")); + + auto key = QSharedPointer::create(); + key->addKey(QSharedPointer::create("a")); + QFile readBack(m_dbFile->fileName()); + readBack.open(QIODevice::ReadOnly); + QScopedPointer readBackDb(reader.readDatabase(&readBack, key)); + readBack.close(); + QVERIFY(readBackDb); + QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry")); + QVERIFY(readBackDb->rootGroup()->findEntryByPath("/Recycle Bin/Sample Entry")); + + pos = m_stdoutFile->pos(); + + // try again, this time without recycle bin + Utils::setNextPassword("a"); + removeCmd.execute({"rm", fileCopy.fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully deleted entry Sample Entry.\n")); + + readBack.setFileName(fileCopy.fileName()); + readBack.open(QIODevice::ReadOnly); + readBackDb.reset(reader.readDatabase(&readBack, key)); + readBack.close(); + QVERIFY(readBackDb); + QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry")); + QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Recycle Bin/Sample Entry")); + + pos = m_stdoutFile->pos(); + + // finally, try deleting a non-existent entry + Utils::setNextPassword("a"); + removeCmd.execute({"rm", fileCopy.fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->reset(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll(), QByteArray("Entry /Sample Entry not found.\n")); +} + +void TestCli::testShow() +{ + Show showCmd; + QVERIFY(!showCmd.name.isEmpty()); + QVERIFY(showCmd.getDescriptionLine().contains(showCmd.name)); + + Utils::setNextPassword("a"); + showCmd.execute({"show", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->reset(); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Title: Sample Entry\n" + "UserName: User Name\n" + "Password: Password\n" + "URL: http://www.somesite.com/\n" + "Notes: Notes\n")); + + qint64 pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + showCmd.execute({"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n")); + + pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + showCmd.execute({"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n" + "http://www.somesite.com/\n")); + + pos = m_stdoutFile->pos(); + Utils::setNextPassword("a"); + showCmd.execute({"show", "-a", "DoesNotExist", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->reset(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll(), QByteArray("ERROR: unknown attribute DoesNotExist.\n")); +} diff --git a/tests/TestCli.h b/tests/TestCli.h new file mode 100644 index 000000000..532d84a79 --- /dev/null +++ b/tests/TestCli.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_TESTCLI_H +#define KEEPASSXC_TESTCLI_H + +#include "core/Database.h" + +#include +#include +#include +#include +#include + +class TestCli : public QObject +{ + Q_OBJECT + +private: + QSharedPointer readTestDatabase() const; + +private slots: + void initTestCase(); + void init(); + void cleanup(); + void cleanupTestCase(); + + void testCommand(); + void testAdd(); + void testClip(); + void testDiceware(); + void testEdit(); + void testEstimate_data(); + void testEstimate(); + void testExtract(); + void testGenerate_data(); + void testGenerate(); + void testList(); + void testLocate(); + void testMerge(); + void testRemove(); + void testShow(); + +private: + QByteArray m_dbData; + QScopedPointer m_dbFile; + QScopedPointer m_stdoutFile; + QScopedPointer m_stderrFile; + QScopedPointer m_stdinFile; + FILE* m_stdoutHandle = stdout; + FILE* m_stderrHandle = stderr; + FILE* m_stdinHandle = stdin; +}; + +#endif //KEEPASSXC_TESTCLI_H diff --git a/tests/TestKdbx4.cpp b/tests/TestKdbx4.cpp index 72c2d4c83..297cde284 100644 --- a/tests/TestKdbx4.cpp +++ b/tests/TestKdbx4.cpp @@ -199,12 +199,12 @@ void TestKdbx4::testFormat400Upgrade_data() auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK; auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK; - QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << false << kdbx4; - QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << false << kdbx4; - QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << false << kdbx3; - QTest::newRow("Argon2 + AES + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << true << kdbx4; - QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << true << kdbx4; - QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << true << kdbx4; + QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES256 << false << kdbx4; + QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES256 << false << kdbx4; + QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES256 << false << kdbx3; + QTest::newRow("Argon2 + AES + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES256 << true << kdbx4; + QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES256 << true << kdbx4; + QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES256 << true << kdbx4; QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << false << kdbx4; QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << false << kdbx4; diff --git a/tests/TestPasswordGenerator.cpp b/tests/TestPasswordGenerator.cpp new file mode 100644 index 000000000..53cf25c31 --- /dev/null +++ b/tests/TestPasswordGenerator.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TestPasswordGenerator.h" +#include "core/PasswordGenerator.h" +#include "crypto/Crypto.h" + +#include +#include + +QTEST_GUILESS_MAIN(TestPasswordGenerator) + +void TestPasswordGenerator::initTestCase() +{ + QVERIFY(Crypto::init()); +} + +void TestPasswordGenerator::testCharClasses() +{ + PasswordGenerator generator; + QVERIFY(!generator.isValid()); + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters); + generator.setLength(16); + QVERIFY(generator.isValid()); + QCOMPARE(generator.generatePassword().size(), 16); + + generator.setLength(2000); + QString password = generator.generatePassword(); + QCOMPARE(password.size(), 2000); + QRegularExpression regex(R"(^[a-z]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::UpperLetters); + password = generator.generatePassword(); + regex.setPattern(R"(^[A-Z]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Numbers); + password = generator.generatePassword(); + regex.setPattern(R"(^\d+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Braces); + password = generator.generatePassword(); + regex.setPattern(R"(^[\(\)\[\]\{\}]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Punctuation); + password = generator.generatePassword(); + regex.setPattern(R"(^[\.,:;]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Quotes); + password = generator.generatePassword(); + regex.setPattern(R"(^["']+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Dashes); + password = generator.generatePassword(); + regex.setPattern(R"(^[\-/\\_|]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Math); + password = generator.generatePassword(); + regex.setPattern(R"(^[!\*\+\-<=>\?]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Logograms); + password = generator.generatePassword(); + regex.setPattern(R"(^[#`~%&^$@]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::EASCII); + password = generator.generatePassword(); + regex.setPattern(R"(^[^a-zA-Z0-9\.,:;"'\-/\\_|!\*\+\-<=>\?#`~%&^$@]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters + | PasswordGenerator::CharClass::UpperLetters | PasswordGenerator::CharClass::Braces); + password = generator.generatePassword(); + regex.setPattern(R"(^[a-zA-Z\(\)\[\]\{\}]+$)"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::Quotes + | PasswordGenerator::CharClass::Numbers | PasswordGenerator::CharClass::Dashes); + password = generator.generatePassword(); + regex.setPattern(R"(^["'\d\-/\\_|]+$)"); + QVERIFY(regex.match(password).hasMatch()); +} + +void TestPasswordGenerator::testLookalikeExclusion() +{ + PasswordGenerator generator; + generator.setLength(2000); + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters); + QVERIFY(generator.isValid()); + QString password = generator.generatePassword(); + QCOMPARE(password.size(), 2000); + + generator.setFlags(PasswordGenerator::GeneratorFlag::ExcludeLookAlike); + password = generator.generatePassword(); + QRegularExpression regex("^[^lI0]+$"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | + PasswordGenerator::CharClass::UpperLetters | PasswordGenerator::CharClass::Numbers); + password = generator.generatePassword(); + regex.setPattern("^[^lI01]+$"); + QVERIFY(regex.match(password).hasMatch()); + + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters + | PasswordGenerator::CharClass::UpperLetters | PasswordGenerator::CharClass::Numbers + | PasswordGenerator::CharClass::EASCII); + password = generator.generatePassword(); + regex.setPattern("^[^lI01﹒]+$"); + QVERIFY(regex.match(password).hasMatch()); +} diff --git a/tests/TestPasswordGenerator.h b/tests/TestPasswordGenerator.h new file mode 100644 index 000000000..5287e5bde --- /dev/null +++ b/tests/TestPasswordGenerator.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_TESTPASSWORDGENERATOR_H +#define KEEPASSXC_TESTPASSWORDGENERATOR_H + +#include + +class TestPasswordGenerator : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void testCharClasses(); + void testLookalikeExclusion(); +}; + +#endif //KEEPASSXC_TESTPASSWORDGENERATOR_H diff --git a/tests/TestSymmetricCipher.cpp b/tests/TestSymmetricCipher.cpp index bec894c06..b69e463b1 100644 --- a/tests/TestSymmetricCipher.cpp +++ b/tests/TestSymmetricCipher.cpp @@ -26,76 +26,171 @@ #include "streams/SymmetricCipherStream.h" QTEST_GUILESS_MAIN(TestSymmetricCipher) +Q_DECLARE_METATYPE(SymmetricCipher::Algorithm); +Q_DECLARE_METATYPE(SymmetricCipher::Mode); +Q_DECLARE_METATYPE(SymmetricCipher::Direction); void TestSymmetricCipher::initTestCase() { QVERIFY(Crypto::init()); } -void TestSymmetricCipher::testAes128CbcEncryption() +void TestSymmetricCipher::testAlgorithmToCipher() { + QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Aes128), KeePass2::CIPHER_AES128); + QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Aes256), KeePass2::CIPHER_AES256); + QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Twofish), KeePass2::CIPHER_TWOFISH); + QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::ChaCha20), KeePass2::CIPHER_CHACHA20); + QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::InvalidAlgorithm), QUuid()); +} + +void TestSymmetricCipher::testEncryptionDecryption_data() +{ + QTest::addColumn("algorithm"); + QTest::addColumn("mode"); + QTest::addColumn("direction"); + QTest::addColumn("key"); + QTest::addColumn("iv"); + QTest::addColumn("plainText"); + QTest::addColumn("cipherText"); + // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + QTest::newRow("AES128-CBC Encryption") + << SymmetricCipher::Aes128 + << SymmetricCipher::Cbc + << SymmetricCipher::Encrypt + << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2"); + + QTest::newRow("AES128-CBC Decryption") + << SymmetricCipher::Aes128 + << SymmetricCipher::Cbc + << SymmetricCipher::Decrypt + << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"); + + QTest::newRow("AES256-CBC Encryption") + << SymmetricCipher::Aes256 + << SymmetricCipher::Cbc + << SymmetricCipher::Encrypt + << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d"); + + QTest::newRow("AES256-CBC Decryption") + << SymmetricCipher::Aes256 + << SymmetricCipher::Cbc + << SymmetricCipher::Decrypt + << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"); + + QTest::newRow("AES256-CTR Encryption") + << SymmetricCipher::Aes256 + << SymmetricCipher::Ctr + << SymmetricCipher::Encrypt + << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") + << QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"); + + QTest::newRow("AES256-CTR Decryption") + << SymmetricCipher::Aes256 + << SymmetricCipher::Ctr + << SymmetricCipher::Decrypt + << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") + << QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") + << QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"); +} + +void TestSymmetricCipher::testEncryptionDecryption() +{ + QFETCH(SymmetricCipher::Algorithm, algorithm); + QFETCH(SymmetricCipher::Mode, mode); + QFETCH(SymmetricCipher::Direction, direction); + QFETCH(QByteArray, key); + QFETCH(QByteArray, iv); + QFETCH(QByteArray, plainText); + QFETCH(QByteArray, cipherText); - QByteArray key = QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c"); - QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - QByteArray cipherText = QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d"); - cipherText.append(QByteArray::fromHex("5086cb9b507219ee95db113a917678b2")); bool ok; - - SymmetricCipher cipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); + SymmetricCipher cipher(algorithm, mode, direction); QVERIFY(cipher.init(key, iv)); QCOMPARE(cipher.blockSize(), 16); QCOMPARE(cipher.process(plainText, &ok), cipherText); QVERIFY(ok); - QBuffer buffer; - SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - QVERIFY(stream.init(key, iv)); - buffer.open(QIODevice::WriteOnly); - QVERIFY(stream.open(QIODevice::WriteOnly)); - QVERIFY(stream.reset()); + if (mode == SymmetricCipher::Cbc) { + QBuffer buffer; + SymmetricCipherStream stream(&buffer, algorithm, mode, direction); + QVERIFY(stream.init(key, iv)); + buffer.open(QIODevice::WriteOnly); + QVERIFY(stream.open(QIODevice::WriteOnly)); + QVERIFY(stream.reset()); - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(16)), qint64(16)); - QCOMPARE(buffer.data(), cipherText.left(16)); - QVERIFY(stream.reset()); - // make sure padding is written - QCOMPARE(buffer.data().size(), 32); + buffer.reset(); + buffer.buffer().clear(); + QCOMPARE(stream.write(plainText.left(16)), qint64(16)); + QCOMPARE(buffer.data(), cipherText.left(16)); + QVERIFY(stream.reset()); + // make sure padding is written + QCOMPARE(buffer.data().size(), 32); - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(10)), qint64(10)); - QVERIFY(buffer.data().isEmpty()); + buffer.reset(); + buffer.buffer().clear(); + QCOMPARE(stream.write(plainText.left(10)), qint64(10)); + QVERIFY(buffer.data().isEmpty()); - QVERIFY(stream.reset()); - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(10)), qint64(10)); - stream.close(); - QCOMPARE(buffer.data().size(), 16); + QVERIFY(stream.reset()); + buffer.reset(); + buffer.buffer().clear(); + QCOMPARE(stream.write(plainText.left(10)), qint64(10)); + stream.close(); + QCOMPARE(buffer.data().size(), 16); + } } -void TestSymmetricCipher::testAes128CbcDecryption() +void TestSymmetricCipher::testAesCbcPadding_data() { - QByteArray key = QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c"); - QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); - QByteArray cipherText = QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d"); - cipherText.append(QByteArray::fromHex("5086cb9b507219ee95db113a917678b2")); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - bool ok; + QTest::addColumn("key"); + QTest::addColumn("iv"); + QTest::addColumn("cipherText"); + QTest::addColumn("plainText"); + QTest::addColumn("padding"); - SymmetricCipher cipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.blockSize(), 16); - QCOMPARE(cipher.process(cipherText, &ok), plainText); - QVERIFY(ok); + QTest::newRow("AES128") + << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("55e21d7100b988ffec32feeafaf23538"); + + QTest::newRow("AES256") + << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2"); +} + +void TestSymmetricCipher::testAesCbcPadding() +{ + QFETCH(QByteArray, key); + QFETCH(QByteArray, iv); + QFETCH(QByteArray, cipherText); + QFETCH(QByteArray, plainText); + QFETCH(QByteArray, padding); // padded with 16 0x10 bytes - QByteArray cipherTextPadded = cipherText + QByteArray::fromHex("55e21d7100b988ffec32feeafaf23538"); + QByteArray cipherTextPadded = cipherText + padding; + QBuffer buffer(&cipherTextPadded); SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); QVERIFY(stream.init(key, iv)); @@ -114,126 +209,48 @@ void TestSymmetricCipher::testAes128CbcDecryption() QCOMPARE(stream.read(100), plainText); } -void TestSymmetricCipher::testAes256CbcEncryption() +void TestSymmetricCipher::testInplaceEcb_data() { - // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + QTest::addColumn("key"); + QTest::addColumn("plainText"); + QTest::addColumn("cipherText"); - QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); - QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6"); - cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d")); - bool ok; - - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.blockSize(), 16); - - QCOMPARE(cipher.process(plainText, &ok), cipherText); - QVERIFY(ok); - - QBuffer buffer; - SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - QVERIFY(stream.init(key, iv)); - buffer.open(QIODevice::WriteOnly); - QVERIFY(stream.open(QIODevice::WriteOnly)); - QVERIFY(stream.reset()); - - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(16)), qint64(16)); - QCOMPARE(buffer.data(), cipherText.left(16)); - QVERIFY(stream.reset()); - // make sure padding is written - QCOMPARE(buffer.data().size(), 32); - - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(10)), qint64(10)); - QVERIFY(buffer.data().isEmpty()); - - QVERIFY(stream.reset()); - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(10)), qint64(10)); - stream.close(); - QCOMPARE(buffer.data().size(), 16); + QTest::newRow("AES128") + << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a") + << QByteArray::fromHex("3ad77bb40d7a3660a89ecaf32466ef97"); } -void TestSymmetricCipher::testAes256CbcDecryption() +void TestSymmetricCipher::testInplaceEcb() { - QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); - QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); - QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6"); - cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d")); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - bool ok; + QFETCH(QByteArray, key); + QFETCH(QByteArray, plainText); + QFETCH(QByteArray, cipherText); - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.blockSize(), 16); + SymmetricCipher cipherInPlaceEnc(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); + QVERIFY(cipherInPlaceEnc.init(key, QByteArray(16, 0))); + QCOMPARE(cipherInPlaceEnc.blockSize(), 16); + auto data = QByteArray(plainText); + QVERIFY(cipherInPlaceEnc.processInPlace(data)); + QCOMPARE(data, cipherText); - QCOMPARE(cipher.process(cipherText, &ok), plainText); - QVERIFY(ok); + SymmetricCipher cipherInPlaceDec(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Decrypt); + QVERIFY(cipherInPlaceDec.init(key, QByteArray(16, 0))); + QCOMPARE(cipherInPlaceDec.blockSize(), 16); + QVERIFY(cipherInPlaceDec.processInPlace(data)); + QCOMPARE(data, plainText); - // padded with 16 0x16 bytes - QByteArray cipherTextPadded = cipherText + QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2"); - QBuffer buffer(&cipherTextPadded); - SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - QVERIFY(stream.init(key, iv)); - buffer.open(QIODevice::ReadOnly); - QVERIFY(stream.open(QIODevice::ReadOnly)); + SymmetricCipher cipherInPlaceEnc2(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); + QVERIFY(cipherInPlaceEnc2.init(key, QByteArray(16, 0))); + QCOMPARE(cipherInPlaceEnc2.blockSize(), 16); + data = QByteArray(plainText); + QVERIFY(cipherInPlaceEnc2.processInPlace(data, 100)); - QCOMPARE(stream.read(10), plainText.left(10)); - buffer.reset(); - QVERIFY(stream.reset()); - QCOMPARE(stream.read(20), plainText.left(20)); - buffer.reset(); - QVERIFY(stream.reset()); - QCOMPARE(stream.read(16), plainText.left(16)); - buffer.reset(); - QVERIFY(stream.reset()); - QCOMPARE(stream.read(100), plainText); -} - -void TestSymmetricCipher::testAes256CtrEncryption() -{ - // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf - - QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); - QByteArray ctr = QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - QByteArray cipherText = QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228"); - cipherText.append(QByteArray::fromHex("f443e3ca4d62b59aca84e990cacaf5c5")); - bool ok; - - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ctr, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(key, ctr)); - QCOMPARE(cipher.blockSize(), 16); - - QCOMPARE(cipher.process(plainText, &ok), cipherText); - QVERIFY(ok); -} - -void TestSymmetricCipher::testAes256CtrDecryption() -{ - QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); - QByteArray ctr = QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"); - QByteArray cipherText = QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228"); - cipherText.append(QByteArray::fromHex("f443e3ca4d62b59aca84e990cacaf5c5")); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - bool ok; - - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ctr, SymmetricCipher::Decrypt); - QVERIFY(cipher.init(key, ctr)); - QCOMPARE(cipher.blockSize(), 16); - - QCOMPARE(cipher.process(cipherText, &ok), plainText); - QVERIFY(ok); + SymmetricCipher cipherInPlaceDec2(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Decrypt); + QVERIFY(cipherInPlaceDec2.init(key, QByteArray(16, 0))); + QCOMPARE(cipherInPlaceDec2.blockSize(), 16); + QVERIFY(cipherInPlaceDec2.processInPlace(data, 100)); + QCOMPARE(data, plainText); } void TestSymmetricCipher::testTwofish256CbcEncryption() diff --git a/tests/TestSymmetricCipher.h b/tests/TestSymmetricCipher.h index 9b82fd88a..5eede0953 100644 --- a/tests/TestSymmetricCipher.h +++ b/tests/TestSymmetricCipher.h @@ -27,12 +27,13 @@ class TestSymmetricCipher : public QObject private slots: void initTestCase(); - void testAes128CbcEncryption(); - void testAes128CbcDecryption(); - void testAes256CbcEncryption(); - void testAes256CbcDecryption(); - void testAes256CtrEncryption(); - void testAes256CtrDecryption(); + void testAlgorithmToCipher(); + void testEncryptionDecryption_data(); + void testEncryptionDecryption(); + void testAesCbcPadding_data(); + void testAesCbcPadding(); + void testInplaceEcb_data(); + void testInplaceEcb(); void testTwofish256CbcEncryption(); void testTwofish256CbcDecryption(); void testSalsa20(); diff --git a/tests/gui/CMakeLists.txt b/tests/gui/CMakeLists.txt index 6cae88830..a6e876f47 100644 --- a/tests/gui/CMakeLists.txt +++ b/tests/gui/CMakeLists.txt @@ -15,6 +15,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -add_unit_test(NAME testgui SOURCES TestGui.cpp TemporaryFile.cpp LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testgui SOURCES TestGui.cpp LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testguipixmaps SOURCES TestGuiPixmaps.cpp LIBS ${TEST_LIBRARIES}) diff --git a/tests/gui/TemporaryFile.cpp b/tests/gui/TemporaryFile.cpp deleted file mode 100644 index b6d20848b..000000000 --- a/tests/gui/TemporaryFile.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2016 Danny Su - * Copyright (C) 2017 KeePassXC Team - * - * 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 - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "TemporaryFile.h" - -#include - -#ifdef Q_OS_WIN -const QString TemporaryFile::SUFFIX = ".win"; - -TemporaryFile::~TemporaryFile() -{ - if (m_tempFile.autoRemove()) { - m_file.remove(); - } -} -#endif - -bool TemporaryFile::open() -{ -#ifdef Q_OS_WIN - // Still call QTemporaryFile::open() so that it figures out the temporary - // file name to use. Assuming that by appending the SUFFIX to whatever - // QTemporaryFile chooses is also an available file. - bool tempFileOpened = m_tempFile.open(); - if (tempFileOpened) { - m_file.setFileName(filePath()); - return m_file.open(QIODevice::WriteOnly); - } - return false; -#else - return m_tempFile.open(); -#endif -} - -void TemporaryFile::close() -{ - m_tempFile.close(); -#ifdef Q_OS_WIN - m_file.close(); -#endif -} - -qint64 TemporaryFile::write(const char* data, qint64 maxSize) -{ -#ifdef Q_OS_WIN - return m_file.write(data, maxSize); -#else - return m_tempFile.write(data, maxSize); -#endif -} - -qint64 TemporaryFile::write(const QByteArray& byteArray) -{ -#ifdef Q_OS_WIN - return m_file.write(byteArray); -#else - return m_tempFile.write(byteArray); -#endif -} - -QString TemporaryFile::fileName() const -{ -#ifdef Q_OS_WIN - return QFileInfo(m_tempFile).fileName() + TemporaryFile::SUFFIX; -#else - return QFileInfo(m_tempFile).fileName(); -#endif -} - -QString TemporaryFile::filePath() const -{ -#ifdef Q_OS_WIN - return m_tempFile.fileName() + TemporaryFile::SUFFIX; -#else - return m_tempFile.fileName(); -#endif -} diff --git a/tests/gui/TemporaryFile.h b/tests/gui/TemporaryFile.h deleted file mode 100644 index 8a98d9235..000000000 --- a/tests/gui/TemporaryFile.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 Danny Su - * Copyright (C) 2017 KeePassXC Team - * - * 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 - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_TEMPORARYFILE_H -#define KEEPASSX_TEMPORARYFILE_H - -#include -#include -#include - -/** - * QTemporaryFile::close() doesn't actually close the file according to - * http://doc.qt.io/qt-5/qtemporaryfile.html: "For as long as the - * QTemporaryFile object itself is not destroyed, the unique temporary file - * will exist and be kept open internally by QTemporaryFile." - * - * This behavior causes issues when running tests on Windows. If the file is - * not closed, the testSave test will fail due to Access Denied. The - * auto-reload test also fails from Windows not triggering file change - * notification because the file isn't actually closed by QTemporaryFile. - * - * This class isolates the Windows specific logic that uses QFile to really - * close the test file when requested to. - */ -class TemporaryFile : public QObject -{ - Q_OBJECT - -public: -#ifdef Q_OS_WIN - ~TemporaryFile(); -#endif - - bool open(); - void close(); - qint64 write(const char* data, qint64 maxSize); - qint64 write(const QByteArray& byteArray); - - QString fileName() const; - QString filePath() const; - -private: - QTemporaryFile m_tempFile; -#ifdef Q_OS_WIN - QFile m_file; - static const QString SUFFIX; -#endif -}; - -#endif // KEEPASSX_TEMPORARYFILE_H diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index e3671567c..ee53eb777 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -18,6 +18,7 @@ #include "TestGui.h" #include "TestGlobal.h" +#include "gui/Application.h" #include #include @@ -33,12 +34,12 @@ #include #include #include -#include #include #include #include #include "config-keepassx-tests.h" +#include "core/Bootstrap.h" #include "core/Config.h" #include "core/Database.h" #include "core/Entry.h" @@ -59,7 +60,6 @@ #include "gui/DatabaseTabWidget.h" #include "gui/DatabaseWidget.h" #include "gui/FileDialog.h" -#include "gui/MainWindow.h" #include "gui/MessageBox.h" #include "gui/PasswordEdit.h" #include "gui/SearchWidget.h" @@ -74,22 +74,23 @@ #include "gui/masterkey/KeyComponentWidget.h" #include "keys/PasswordKey.h" +QTEST_MAIN(TestGui) + void TestGui::initTestCase() { QVERIFY(Crypto::init()); Config::createTempFileInstance(); // Disable autosave so we can test the modified file indicator config()->set("AutoSaveAfterEveryChange", false); - // Enable the tray icon so we can test hiding/restoring the window + // Enable the tray icon so we can test hiding/restoring the windowQByteArray config()->set("GUI/ShowTrayIcon", true); // Disable advanced settings mode (activate within individual tests to test advanced settings) config()->set("GUI/AdvancedSettings", false); - m_mainWindow = new MainWindow(); + m_mainWindow.reset(new MainWindow()); + Bootstrap::restoreMainWindowState(*m_mainWindow); m_tabWidget = m_mainWindow->findChild("tabWidget"); m_mainWindow->show(); - m_mainWindow->activateWindow(); - Tools::wait(50); // Load the NewDatabase.kdbx file into temporary storage QFile sourceDbFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx")); @@ -101,29 +102,32 @@ void TestGui::initTestCase() // Every test starts with opening the temp database void TestGui::init() { + m_dbFile.reset(new QTemporaryFile()); // Write the temp storage to a temp database file for use in our tests - QVERIFY(m_dbFile.open()); - QCOMPARE(m_dbFile.write(m_dbData), static_cast((m_dbData.size()))); - m_dbFile.close(); - - m_dbFileName = m_dbFile.fileName(); - m_dbFilePath = m_dbFile.filePath(); + QVERIFY(m_dbFile->open()); + QCOMPARE(m_dbFile->write(m_dbData), static_cast((m_dbData.size()))); + m_dbFileName = QFileInfo(m_dbFile->fileName()).fileName(); + m_dbFilePath = m_dbFile->fileName(); + m_dbFile->close(); fileDialog()->setNextFileName(m_dbFilePath); triggerAction("actionDatabaseOpen"); - QWidget* databaseOpenWidget = m_mainWindow->findChild("databaseOpenWidget"); - QLineEdit* editPassword = databaseOpenWidget->findChild("editPassword"); + auto* databaseOpenWidget = m_mainWindow->findChild("databaseOpenWidget"); + auto* editPassword = databaseOpenWidget->findChild("editPassword"); QVERIFY(editPassword); QTest::keyClicks(editPassword, "a"); QTest::keyClick(editPassword, Qt::Key_Enter); - Tools::wait(100); - QVERIFY(m_tabWidget->currentDatabaseWidget()); + 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 @@ -132,17 +136,21 @@ void TestGui::cleanup() // DO NOT save the database MessageBox::setNextAnswer(QMessageBox::No); triggerAction("actionDatabaseClose"); - Tools::wait(100); + QApplication::processEvents(); if (m_db) { delete m_db; } - m_db = nullptr; - if (m_dbWidget) { delete m_dbWidget; } - m_dbWidget = nullptr; + + m_dbFile->remove(); +} + +void TestGui::cleanupTestCase() +{ + m_dbFile->remove(); } void TestGui::testSettingsDefaultTabOrder() @@ -187,8 +195,9 @@ void TestGui::testCreateDatabase() // check key and encryption QCOMPARE(m_db->key()->keys().size(), 2); + QCOMPARE(m_db->kdf()->rounds(), 2); QCOMPARE(m_db->kdf()->uuid(), KeePass2::KDF_ARGON2); - QCOMPARE(m_db->cipher(), KeePass2::CIPHER_AES); + QCOMPARE(m_db->cipher(), KeePass2::CIPHER_AES256); auto compositeKey = QSharedPointer::create(); compositeKey->addKey(QSharedPointer::create("test")); auto fileKey = QSharedPointer::create(); @@ -213,7 +222,40 @@ void TestGui::createDatabaseCallback() QTest::keyClick(wizard, Qt::Key_Enter); QCOMPARE(wizard->currentId(), 1); - QTest::keyClick(wizard, Qt::Key_Enter); + auto decryptionTimeSlider = wizard->currentPage()->findChild("decryptionTimeSlider"); + auto algorithmComboBox = wizard->currentPage()->findChild("algorithmComboBox"); + QTRY_VERIFY(decryptionTimeSlider->isVisible()); + QVERIFY(!algorithmComboBox->isVisible()); + auto advancedToggle = wizard->currentPage()->findChild("advancedSettingsButton"); + QTest::mouseClick(advancedToggle, Qt::MouseButton::LeftButton); + QTRY_VERIFY(!decryptionTimeSlider->isVisible()); + QVERIFY(algorithmComboBox->isVisible()); + + auto rounds = wizard->currentPage()->findChild("transformRoundsSpinBox"); + QVERIFY(rounds); + QVERIFY(rounds->isVisible()); + QTest::mouseClick(rounds, Qt::MouseButton::LeftButton); + QTest::keyClick(rounds, Qt::Key_A, Qt::ControlModifier); + QTest::keyClicks(rounds, "2"); + QTest::keyClick(rounds, Qt::Key_Tab); + QTest::keyClick(rounds, Qt::Key_Tab); + + auto memory = wizard->currentPage()->findChild("memorySpinBox"); + QVERIFY(memory); + QVERIFY(memory->isVisible()); + QTest::mouseClick(memory, Qt::MouseButton::LeftButton); + QTest::keyClick(memory, Qt::Key_A, Qt::ControlModifier); + QTest::keyClicks(memory, "50"); + QTest::keyClick(memory, Qt::Key_Tab); + + auto parallelism = wizard->currentPage()->findChild("parallelismSpinBox"); + QVERIFY(parallelism); + QVERIFY(parallelism->isVisible()); + QTest::mouseClick(parallelism, Qt::MouseButton::LeftButton); + QTest::keyClick(parallelism, Qt::Key_A, Qt::ControlModifier); + QTest::keyClicks(parallelism, "1"); + QTest::keyClick(parallelism, Qt::Key_Enter); + QCOMPARE(wizard->currentId(), 2); // enter password @@ -222,7 +264,7 @@ void TestGui::createDatabaseCallback() auto* passwordEdit = passwordWidget->findChild("enterPasswordEdit"); auto* passwordRepeatEdit = passwordWidget->findChild("repeatPasswordEdit"); QTRY_VERIFY(passwordEdit->isVisible()); - QVERIFY(passwordEdit->hasFocus()); + QTRY_VERIFY(passwordEdit->hasFocus()); QTest::keyClicks(passwordEdit, "test"); QTest::keyClick(passwordEdit, Qt::Key::Key_Tab); QTest::keyClicks(passwordRepeatEdit, "test"); @@ -247,25 +289,26 @@ void TestGui::createDatabaseCallback() QCOMPARE(fileCombo->currentText(), QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key")); // save database to temporary file - TemporaryFile tmpFile; + QTemporaryFile tmpFile; QVERIFY(tmpFile.open()); tmpFile.close(); - fileDialog()->setNextFileName(tmpFile.filePath()); + fileDialog()->setNextFileName(tmpFile.fileName()); QTest::keyClick(fileCombo, Qt::Key::Key_Enter); + tmpFile.remove(); } void TestGui::testMergeDatabase() { // It is safe to ignore the warning this line produces - QSignalSpy dbMergeSpy(m_dbWidget, SIGNAL(databaseMerged(Database*))); + QSignalSpy dbMergeSpy(m_dbWidget.data(), SIGNAL(databaseMerged(Database*))); // set file to merge from fileDialog()->setNextFileName(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx")); triggerAction("actionDatabaseMerge"); - QWidget* databaseOpenMergeWidget = m_mainWindow->findChild("databaseOpenMergeWidget"); - QLineEdit* editPasswordMerge = databaseOpenMergeWidget->findChild("editPassword"); + auto* databaseOpenMergeWidget = m_mainWindow->findChild("databaseOpenMergeWidget"); + auto* editPasswordMerge = databaseOpenMergeWidget->findChild("editPassword"); QVERIFY(editPasswordMerge->isVisible()); m_tabWidget->currentDatabaseWidget()->setCurrentWidget(databaseOpenMergeWidget); @@ -300,11 +343,11 @@ void TestGui::testAutoreloadDatabase() // Test accepting new file in autoreload MessageBox::setNextAnswer(QMessageBox::Yes); // Overwrite the current database with the temp data - QVERIFY(m_dbFile.open()); - QVERIFY(m_dbFile.write(unmodifiedMergeDatabase, static_cast(unmodifiedMergeDatabase.size()))); - m_dbFile.close(); - Tools::wait(1500); + QVERIFY(m_dbFile->open()); + QVERIFY(m_dbFile->write(unmodifiedMergeDatabase, static_cast(unmodifiedMergeDatabase.size()))); + m_dbFile->close(); + Tools::wait(800); m_db = m_dbWidget->database(); // the General group contains one entry from the new db data @@ -318,10 +361,10 @@ void TestGui::testAutoreloadDatabase() // Test rejecting new file in autoreload MessageBox::setNextAnswer(QMessageBox::No); // Overwrite the current temp database with a new file - m_dbFile.open(); - QVERIFY(m_dbFile.write(unmodifiedMergeDatabase, static_cast(unmodifiedMergeDatabase.size()))); - m_dbFile.close(); - Tools::wait(1500); + m_dbFile->open(); + QVERIFY(m_dbFile->write(unmodifiedMergeDatabase, static_cast(unmodifiedMergeDatabase.size()))); + m_dbFile->close(); + Tools::wait(800); m_db = m_dbWidget->database(); @@ -342,10 +385,10 @@ void TestGui::testAutoreloadDatabase() // This is saying yes to merging the entries MessageBox::setNextAnswer(QMessageBox::Yes); // Overwrite the current database with the temp data - QVERIFY(m_dbFile.open()); - QVERIFY(m_dbFile.write(unmodifiedMergeDatabase, static_cast(unmodifiedMergeDatabase.size()))); - m_dbFile.close(); - Tools::wait(1500); + QVERIFY(m_dbFile->open()); + QVERIFY(m_dbFile->write(unmodifiedMergeDatabase, static_cast(unmodifiedMergeDatabase.size()))); + m_dbFile->close(); + Tools::wait(800); m_db = m_dbWidget->database(); @@ -361,17 +404,17 @@ void TestGui::testTabs() void TestGui::testEditEntry() { - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + auto* toolBar = m_mainWindow->findChild("toolBar"); int editCount = 0; // Select the first entry in the database - EntryView* entryView = m_dbWidget->findChild("entryView"); + auto* entryView = m_dbWidget->findChild("entryView"); QModelIndex entryItem = entryView->model()->index(0, 1); Entry* entry = entryView->entryFromIndex(entryItem); clickIndex(entryItem, entryView, Qt::LeftButton); // Confirm the edit action button is enabled - QAction* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); + auto* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); QVERIFY(entryEditAction->isEnabled()); QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction); QVERIFY(entryEditWidget->isVisible()); @@ -380,12 +423,12 @@ void TestGui::testEditEntry() // Edit the first entry ("Sample Entry") QTest::mouseClick(entryEditWidget, Qt::LeftButton); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* titleEdit = editEntryWidget->findChild("titleEdit"); QTest::keyClicks(titleEdit, "_test"); // Apply the edit - QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); QCOMPARE(entry->title(), QString("Sample Entry_test")); @@ -410,7 +453,7 @@ void TestGui::testEditEntry() // Test protected attributes editEntryWidget->setCurrentPage(1); - QPlainTextEdit* attrTextEdit = editEntryWidget->findChild("attributesEdit"); + auto* attrTextEdit = editEntryWidget->findChild("attributesEdit"); QTest::mouseClick(editEntryWidget->findChild("addAttributeButton"), Qt::LeftButton); QString attrText = "TEST TEXT"; QTest::keyClicks(attrTextEdit, attrText); @@ -422,11 +465,11 @@ void TestGui::testEditEntry() editEntryWidget->setCurrentPage(0); // Test mismatch passwords - QLineEdit* passwordEdit = editEntryWidget->findChild("passwordEdit"); + auto* passwordEdit = editEntryWidget->findChild("passwordEdit"); QString originalPassword = passwordEdit->text(); passwordEdit->setText("newpass"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); - MessageWidget* messageWiget = editEntryWidget->findChild("messageWidget"); + auto* messageWiget = editEntryWidget->findChild("messageWidget"); QTRY_VERIFY(messageWiget->isVisible()); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); QCOMPARE(passwordEdit->text(), QString("newpass")); @@ -469,9 +512,9 @@ void TestGui::testSearchEditEntry() // Regression test for Issue #1447 -- Uses example from issue description // Find buttons for group creation - EditGroupWidget* editGroupWidget = m_dbWidget->findChild("editGroupWidget"); - QLineEdit* nameEdit = editGroupWidget->findChild("editName"); - QDialogButtonBox* editGroupWidgetButtonBox = editGroupWidget->findChild("buttonBox"); + auto* editGroupWidget = m_dbWidget->findChild("editGroupWidget"); + auto* nameEdit = editGroupWidget->findChild("editName"); + auto* editGroupWidgetButtonBox = editGroupWidget->findChild("buttonBox"); // Add groups "Good" and "Bad" m_dbWidget->createGroup(); @@ -484,11 +527,11 @@ void TestGui::testSearchEditEntry() m_dbWidget->groupView()->setCurrentGroup(m_db->rootGroup()); // Find buttons for entry creation - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + auto* toolBar = m_mainWindow->findChild("toolBar"); QWidget* entryNewWidget = toolBar->widgetForAction(m_mainWindow->findChild("actionEntryNew")); - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); - QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* titleEdit = editEntryWidget->findChild("titleEdit"); + auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); // Create "Doggy" in "Good" Group* goodGroup = m_dbWidget->currentGroup()->findChildByName(QString("Good")); @@ -501,8 +544,8 @@ void TestGui::testSearchEditEntry() m_dbWidget->groupView()->setCurrentGroup(badGroup); // Search for "Doggy" entry - SearchWidget* searchWidget = toolBar->findChild("SearchWidget"); - QLineEdit* searchTextEdit = searchWidget->findChild("searchEdit"); + auto* searchWidget = toolBar->findChild("SearchWidget"); + auto* searchTextEdit = searchWidget->findChild("searchEdit"); QTest::mouseClick(searchTextEdit, Qt::LeftButton); QTest::keyClicks(searchTextEdit, "Doggy"); QTRY_VERIFY(m_dbWidget->isInSearchMode()); @@ -518,11 +561,11 @@ void TestGui::testSearchEditEntry() void TestGui::testAddEntry() { - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); - EntryView* entryView = m_dbWidget->findChild("entryView"); + auto* toolBar = m_mainWindow->findChild("toolBar"); + auto* entryView = m_dbWidget->findChild("entryView"); // Find the new entry action - QAction* entryNewAction = m_mainWindow->findChild("actionEntryNew"); + auto* entryNewAction = m_mainWindow->findChild("actionEntryNew"); QVERIFY(entryNewAction->isEnabled()); // Find the button associated with the new entry action @@ -535,10 +578,10 @@ void TestGui::testAddEntry() QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); // Add entry "test" and confirm added - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* titleEdit = editEntryWidget->findChild("titleEdit"); QTest::keyClicks(titleEdit, "test"); - QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode); @@ -551,28 +594,12 @@ void TestGui::testAddEntry() // Add entry "something 2" QTest::mouseClick(entryNewWidget, Qt::LeftButton); QTest::keyClicks(titleEdit, "something 2"); - QLineEdit* passwordEdit = editEntryWidget->findChild("passwordEdit"); - QLineEdit* passwordRepeatEdit = editEntryWidget->findChild("passwordRepeatEdit"); + auto* passwordEdit = editEntryWidget->findChild("passwordEdit"); + auto* passwordRepeatEdit = editEntryWidget->findChild("passwordRepeatEdit"); QTest::keyClicks(passwordEdit, "something 2"); QTest::keyClicks(passwordRepeatEdit, "something 2"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); -/* All apply tests disabled due to data loss workaround - * that disables apply button on new entry creation - * - // Add entry "something 3" using the apply button then click ok - QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QTest::keyClicks(titleEdit, "something 3"); - QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton); - QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); - - // Add entry "something 4" using the apply button then click cancel - QTest::mouseClick(entryNewWidget, Qt::LeftButton); - QTest::keyClicks(titleEdit, "something 4"); - QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton); - QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Cancel), Qt::LeftButton); -*/ - // Add entry "something 5" but click cancel button (does NOT add entry) QTest::mouseClick(entryNewWidget, Qt::LeftButton); QTest::keyClicks(titleEdit, "something 5"); @@ -587,10 +614,10 @@ void TestGui::testAddEntry() void TestGui::testPasswordEntryEntropy() { - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + auto* toolBar = m_mainWindow->findChild("toolBar"); // Find the new entry action - QAction* entryNewAction = m_mainWindow->findChild("actionEntryNew"); + auto* entryNewAction = m_mainWindow->findChild("actionEntryNew"); QVERIFY(entryNewAction->isEnabled()); // Find the button associated with the new entry action @@ -603,18 +630,18 @@ void TestGui::testPasswordEntryEntropy() QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); // Add entry "test" and confirm added - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* titleEdit = editEntryWidget->findChild("titleEdit"); QTest::keyClicks(titleEdit, "test"); // Open the password generator - QToolButton* generatorButton = editEntryWidget->findChild("togglePasswordGeneratorButton"); + auto* generatorButton = editEntryWidget->findChild("togglePasswordGeneratorButton"); QTest::mouseClick(generatorButton, Qt::LeftButton); // Type in some password - QLineEdit* editNewPassword = editEntryWidget->findChild("editNewPassword"); - QLabel* entropyLabel = editEntryWidget->findChild("entropyLabel"); - QLabel* strengthLabel = editEntryWidget->findChild("strengthLabel"); + auto* editNewPassword = editEntryWidget->findChild("editNewPassword"); + auto* entropyLabel = editEntryWidget->findChild("entropyLabel"); + auto* strengthLabel = editEntryWidget->findChild("strengthLabel"); editNewPassword->setText(""); QTest::keyClicks(editNewPassword, "hello"); @@ -659,10 +686,10 @@ void TestGui::testPasswordEntryEntropy() void TestGui::testDicewareEntryEntropy() { - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + auto* toolBar = m_mainWindow->findChild("toolBar"); // Find the new entry action - QAction* entryNewAction = m_mainWindow->findChild("actionEntryNew"); + auto* entryNewAction = m_mainWindow->findChild("actionEntryNew"); QVERIFY(entryNewAction->isEnabled()); // Find the button associated with the new entry action @@ -675,27 +702,27 @@ void TestGui::testDicewareEntryEntropy() QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); // Add entry "test" and confirm added - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* titleEdit = editEntryWidget->findChild("titleEdit"); QTest::keyClicks(titleEdit, "test"); // Open the password generator - QToolButton* generatorButton = editEntryWidget->findChild("togglePasswordGeneratorButton"); + auto* generatorButton = editEntryWidget->findChild("togglePasswordGeneratorButton"); QTest::mouseClick(generatorButton, Qt::LeftButton); // Select Diceware - QTabWidget* tabWidget = editEntryWidget->findChild("tabWidget"); - QWidget* dicewareWidget = editEntryWidget->findChild("dicewareWidget"); + auto* tabWidget = editEntryWidget->findChild("tabWidget"); + auto* dicewareWidget = editEntryWidget->findChild("dicewareWidget"); tabWidget->setCurrentWidget(dicewareWidget); - QComboBox* comboBoxWordList = dicewareWidget->findChild("comboBoxWordList"); + auto* comboBoxWordList = dicewareWidget->findChild("comboBoxWordList"); comboBoxWordList->setCurrentText("eff_large.wordlist"); - QSpinBox* spinBoxWordCount = dicewareWidget->findChild("spinBoxWordCount"); + auto* spinBoxWordCount = dicewareWidget->findChild("spinBoxWordCount"); spinBoxWordCount->setValue(6); // Type in some password - QLabel* entropyLabel = editEntryWidget->findChild("entropyLabel"); - QLabel* strengthLabel = editEntryWidget->findChild("strengthLabel"); + auto* entropyLabel = editEntryWidget->findChild("entropyLabel"); + auto* strengthLabel = editEntryWidget->findChild("strengthLabel"); QCOMPARE(entropyLabel->text(), QString("Entropy: 77.55 bit")); QCOMPARE(strengthLabel->text(), QString("Password Quality: Good")); @@ -703,8 +730,8 @@ void TestGui::testDicewareEntryEntropy() void TestGui::testTotp() { - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); - EntryView* entryView = m_dbWidget->findChild("entryView"); + auto* toolBar = m_mainWindow->findChild("toolBar"); + auto* entryView = m_dbWidget->findChild("entryView"); QCOMPARE(entryView->model()->rowCount(), 1); @@ -716,36 +743,36 @@ void TestGui::testTotp() triggerAction("actionEntrySetupTotp"); - TotpSetupDialog* setupTotpDialog = m_dbWidget->findChild("TotpSetupDialog"); + auto* setupTotpDialog = m_dbWidget->findChild("TotpSetupDialog"); - Tools::wait(100); + QApplication::processEvents(); - QLineEdit* seedEdit = setupTotpDialog->findChild("seedEdit"); + auto* seedEdit = setupTotpDialog->findChild("seedEdit"); QString exampleSeed = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq"; QTest::keyClicks(seedEdit, exampleSeed); - QDialogButtonBox* setupTotpButtonBox = setupTotpDialog->findChild("buttonBox"); + auto* setupTotpButtonBox = setupTotpDialog->findChild("buttonBox"); QTest::mouseClick(setupTotpButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); - QAction* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); + auto* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction); QTest::mouseClick(entryEditWidget, Qt::LeftButton); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); editEntryWidget->setCurrentPage(1); - QPlainTextEdit* attrTextEdit = editEntryWidget->findChild("attributesEdit"); + auto* attrTextEdit = editEntryWidget->findChild("attributesEdit"); QTest::mouseClick(editEntryWidget->findChild("revealAttributeButton"), Qt::LeftButton); QCOMPARE(attrTextEdit->toPlainText(), exampleSeed); - QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); triggerAction("actionEntryTotp"); - TotpDialog* totpDialog = m_dbWidget->findChild("TotpDialog"); - QLabel* totpLabel = totpDialog->findChild("totpLabel"); + auto* totpDialog = m_dbWidget->findChild("TotpDialog"); + auto* totpLabel = totpDialog->findChild("totpLabel"); QCOMPARE(totpLabel->text().replace(" ", ""), entry->totp()); } @@ -755,16 +782,16 @@ void TestGui::testSearch() // Add canned entries for consistent testing Q_UNUSED(addCannedEntries()); - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + auto* toolBar = m_mainWindow->findChild("toolBar"); - SearchWidget* searchWidget = toolBar->findChild("SearchWidget"); + auto* searchWidget = toolBar->findChild("SearchWidget"); QVERIFY(searchWidget->isEnabled()); - QLineEdit* searchTextEdit = searchWidget->findChild("searchEdit"); + auto* searchTextEdit = searchWidget->findChild("searchEdit"); - EntryView* entryView = m_dbWidget->findChild("entryView"); + auto* entryView = m_dbWidget->findChild("entryView"); QVERIFY(entryView->isVisible()); - QAction* clearButton = searchWidget->findChild("clearIcon"); + auto* clearButton = searchWidget->findChild("clearIcon"); QVERIFY(!clearButton->isVisible()); // Enter search @@ -801,7 +828,7 @@ void TestGui::testSearch() QTest::keyClick(searchTextEdit, Qt::Key_Down); QTRY_VERIFY(entryView->hasFocus()); // Restore focus and search text selection - QTest::keyClick(m_mainWindow, Qt::Key_F, Qt::ControlModifier); + QTest::keyClick(m_mainWindow.data(), Qt::Key_F, Qt::ControlModifier); QTRY_COMPARE(searchTextEdit->selectedText(), QString("someTHING")); // Ensure Down focuses on entry view when search text is selected QTest::keyClick(searchTextEdit, Qt::Key_Down); @@ -862,7 +889,7 @@ void TestGui::testSearch() QCOMPARE(entry->title(), origTitle.append("_edited")); // Cancel search, should return to normal view - QTest::keyClick(m_mainWindow, Qt::Key_Escape); + QTest::keyClick(m_mainWindow.data(), Qt::Key_Escape); QTRY_COMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode); } @@ -871,10 +898,10 @@ void TestGui::testDeleteEntry() // Add canned entries for consistent testing Q_UNUSED(addCannedEntries()); - GroupView* groupView = m_dbWidget->findChild("groupView"); - EntryView* entryView = m_dbWidget->findChild("entryView"); - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); - QAction* entryDeleteAction = m_mainWindow->findChild("actionEntryDelete"); + auto* groupView = m_dbWidget->findChild("groupView"); + auto* entryView = m_dbWidget->findChild("entryView"); + auto* toolBar = m_mainWindow->findChild("toolBar"); + auto* entryDeleteAction = m_mainWindow->findChild("actionEntryDelete"); QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode); @@ -934,7 +961,7 @@ void TestGui::testDeleteEntry() void TestGui::testCloneEntry() { - EntryView* entryView = m_dbWidget->findChild("entryView"); + auto* entryView = m_dbWidget->findChild("entryView"); QCOMPARE(entryView->model()->rowCount(), 1); @@ -944,8 +971,8 @@ void TestGui::testCloneEntry() triggerAction("actionEntryClone"); - CloneDialog* cloneDialog = m_dbWidget->findChild("CloneDialog"); - QDialogButtonBox* cloneButtonBox = cloneDialog->findChild("buttonBox"); + auto* cloneDialog = m_dbWidget->findChild("CloneDialog"); + auto* cloneButtonBox = cloneDialog->findChild("buttonBox"); QTest::mouseClick(cloneButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); QCOMPARE(entryView->model()->rowCount(), 2); @@ -956,11 +983,11 @@ void TestGui::testCloneEntry() void TestGui::testEntryPlaceholders() { - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); - EntryView* entryView = m_dbWidget->findChild("entryView"); + auto* toolBar = m_mainWindow->findChild("toolBar"); + auto* entryView = m_dbWidget->findChild("entryView"); // Find the new entry action - QAction* entryNewAction = m_mainWindow->findChild("actionEntryNew"); + auto* entryNewAction = m_mainWindow->findChild("actionEntryNew"); QVERIFY(entryNewAction->isEnabled()); // Find the button associated with the new entry action @@ -973,14 +1000,14 @@ void TestGui::testEntryPlaceholders() QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); // Add entry "test" and confirm added - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* titleEdit = editEntryWidget->findChild("titleEdit"); QTest::keyClicks(titleEdit, "test"); QLineEdit* usernameEdit = editEntryWidget->findChild("usernameEdit"); QTest::keyClicks(usernameEdit, "john"); QLineEdit* urlEdit = editEntryWidget->findChild("urlEdit"); QTest::keyClicks(urlEdit, "{TITLE}.{USERNAME}"); - QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); QCOMPARE(entryView->model()->rowCount(), 2); @@ -1000,8 +1027,8 @@ void TestGui::testEntryPlaceholders() void TestGui::testDragAndDropEntry() { - EntryView* entryView = m_dbWidget->findChild("entryView"); - GroupView* groupView = m_dbWidget->findChild("groupView"); + auto* entryView = m_dbWidget->findChild("entryView"); + auto* groupView = m_dbWidget->findChild("groupView"); QAbstractItemModel* groupModel = groupView->model(); QModelIndex sourceIndex = entryView->model()->index(0, 1); @@ -1029,11 +1056,7 @@ void TestGui::testDragAndDropGroup() // dropping parent on child is supposed to fail dragAndDropGroup(groupModel->index(0, 0, rootIndex), - groupModel->index(0, 0, groupModel->index(0, 0, rootIndex)), - -1, - false, - "NewDatabase", - 0); + groupModel->index(0, 0, groupModel->index(0, 0, rootIndex)), -1, false, "NewDatabase", 0); dragAndDropGroup(groupModel->index(1, 0, rootIndex), rootIndex, 0, true, "NewDatabase", 0); @@ -1063,6 +1086,7 @@ void TestGui::testSaveAs() fileInfo.refresh(); QCOMPARE(fileInfo.lastModified(), lastModified); + tmpFile.remove(); } void TestGui::testSave() @@ -1123,7 +1147,7 @@ void TestGui::testKeePass1Import() // Close the KeePass1 Database MessageBox::setNextAnswer(QMessageBox::No); triggerAction("actionDatabaseClose"); - Tools::wait(100); + QApplication::processEvents(); } void TestGui::testDatabaseLocking() @@ -1135,13 +1159,13 @@ void TestGui::testDatabaseLocking() QCOMPARE(m_tabWidget->tabText(0).remove('&'), origDbName + " [locked]"); - QAction* actionDatabaseMerge = m_mainWindow->findChild("actionDatabaseMerge", Qt::FindChildrenRecursively); + auto* actionDatabaseMerge = m_mainWindow->findChild("actionDatabaseMerge", Qt::FindChildrenRecursively); QCOMPARE(actionDatabaseMerge->isEnabled(), false); - QAction* actionDatabaseSave = m_mainWindow->findChild("actionDatabaseSave", Qt::FindChildrenRecursively); + auto* actionDatabaseSave = m_mainWindow->findChild("actionDatabaseSave", Qt::FindChildrenRecursively); QCOMPARE(actionDatabaseSave->isEnabled(), false); QWidget* dbWidget = m_tabWidget->currentDatabaseWidget(); - QWidget* unlockDatabaseWidget = dbWidget->findChild("unlockDatabaseWidget"); + auto* unlockDatabaseWidget = dbWidget->findChild("unlockDatabaseWidget"); QWidget* editPassword = unlockDatabaseWidget->findChild("editPassword"); QVERIFY(editPassword); @@ -1162,11 +1186,11 @@ void TestGui::testDragAndDropKdbxFiles() QMimeData badMimeData; badMimeData.setUrls({QUrl::fromLocalFile(badDatabaseFilePath)}); QDragEnterEvent badDragEvent(QPoint(1, 1), Qt::LinkAction, &badMimeData, Qt::LeftButton, Qt::NoModifier); - qApp->notify(m_mainWindow, &badDragEvent); + qApp->notify(m_mainWindow.data(), &badDragEvent); QCOMPARE(badDragEvent.isAccepted(), false); QDropEvent badDropEvent(QPoint(1, 1), Qt::LinkAction, &badMimeData, Qt::LeftButton, Qt::NoModifier); - qApp->notify(m_mainWindow, &badDropEvent); + qApp->notify(m_mainWindow.data(), &badDropEvent); QCOMPARE(badDropEvent.isAccepted(), false); QCOMPARE(m_tabWidget->count(), openedDatabasesCount); @@ -1175,20 +1199,19 @@ void TestGui::testDragAndDropKdbxFiles() QMimeData goodMimeData; goodMimeData.setUrls({QUrl::fromLocalFile(goodDatabaseFilePath)}); QDragEnterEvent goodDragEvent(QPoint(1, 1), Qt::LinkAction, &goodMimeData, Qt::LeftButton, Qt::NoModifier); - qApp->notify(m_mainWindow, &goodDragEvent); + qApp->notify(m_mainWindow.data(), &goodDragEvent); QCOMPARE(goodDragEvent.isAccepted(), true); QDropEvent goodDropEvent(QPoint(1, 1), Qt::LinkAction, &goodMimeData, Qt::LeftButton, Qt::NoModifier); - qApp->notify(m_mainWindow, &goodDropEvent); + qApp->notify(m_mainWindow.data(), &goodDropEvent); QCOMPARE(goodDropEvent.isAccepted(), true); QCOMPARE(m_tabWidget->count(), openedDatabasesCount + 1); MessageBox::setNextAnswer(QMessageBox::No); triggerAction("actionDatabaseClose"); - Tools::wait(100); - QCOMPARE(m_tabWidget->count(), openedDatabasesCount); + QTRY_COMPARE(m_tabWidget->count(), openedDatabasesCount); } void TestGui::testTrayRestoreHide() @@ -1197,29 +1220,20 @@ void TestGui::testTrayRestoreHide() QSKIP("QSystemTrayIcon::isSystemTrayAvailable() = false, skipping tray restore/hide test..."); } - QSystemTrayIcon* trayIcon = m_mainWindow->findChild(); + auto* trayIcon = m_mainWindow->findChild(); QVERIFY(m_mainWindow->isVisible()); trayIcon->activated(QSystemTrayIcon::Trigger); - Tools::wait(100); - QVERIFY(!m_mainWindow->isVisible()); + QTRY_VERIFY(!m_mainWindow->isVisible()); trayIcon->activated(QSystemTrayIcon::Trigger); - Tools::wait(100); - QVERIFY(m_mainWindow->isVisible()); + QTRY_VERIFY(m_mainWindow->isVisible()); trayIcon->activated(QSystemTrayIcon::Trigger); - Tools::wait(100); - QVERIFY(!m_mainWindow->isVisible()); + QTRY_VERIFY(!m_mainWindow->isVisible()); trayIcon->activated(QSystemTrayIcon::Trigger); - Tools::wait(100); - QVERIFY(m_mainWindow->isVisible()); -} - -void TestGui::cleanupTestCase() -{ - delete m_mainWindow; + QTRY_VERIFY(m_mainWindow->isVisible()); } int TestGui::addCannedEntries() @@ -1227,17 +1241,17 @@ int TestGui::addCannedEntries() int entries_added = 0; // Find buttons - QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + auto* toolBar = m_mainWindow->findChild("toolBar"); QWidget* entryNewWidget = toolBar->widgetForAction(m_mainWindow->findChild("actionEntryNew")); - EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); - QLineEdit* passwordEdit = editEntryWidget->findChild("passwordEdit"); - QLineEdit* passwordRepeatEdit = editEntryWidget->findChild("passwordRepeatEdit"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* titleEdit = editEntryWidget->findChild("titleEdit"); + auto* passwordEdit = editEntryWidget->findChild("passwordEdit"); + auto* passwordRepeatEdit = editEntryWidget->findChild("passwordRepeatEdit"); // Add entry "test" and confirm added QTest::mouseClick(entryNewWidget, Qt::LeftButton); QTest::keyClicks(titleEdit, "test"); - QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); ++entries_added; @@ -1274,7 +1288,7 @@ void TestGui::checkDatabase(QString dbFileName) void TestGui::triggerAction(const QString& name) { - QAction* action = m_mainWindow->findChild(name); + auto* action = m_mainWindow->findChild(name); QVERIFY(action); QVERIFY(action->isEnabled()); action->trigger(); @@ -1312,5 +1326,3 @@ void TestGui::clickIndex(const QModelIndex& index, { QTest::mouseClick(view->viewport(), button, stateKey, view->visualRect(index).center()); } - -QTEST_MAIN(TestGui) diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index dc8a05e9b..5e0b441b1 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -19,17 +19,18 @@ #ifndef KEEPASSX_TESTGUI_H #define KEEPASSX_TESTGUI_H -#include "TemporaryFile.h" +#include "gui/MainWindow.h" #include #include #include +#include +#include class Database; class DatabaseTabWidget; class DatabaseWidget; class QAbstractItemView; -class MainWindow; class TestGui : public QObject { @@ -84,12 +85,12 @@ private: Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0); - QPointer m_mainWindow; + QScopedPointer m_mainWindow; QPointer m_tabWidget; QPointer m_dbWidget; QPointer m_db; QByteArray m_dbData; - TemporaryFile m_dbFile; + QScopedPointer m_dbFile; QString m_dbFileName; QString m_dbFilePath; }; From 108e4efc8ad280142653d2f6ce3a7e3a630ee2f2 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 19 Oct 2018 20:10:37 +0200 Subject: [PATCH 11/84] Fix tests on Windows --- tests/CMakeLists.txt | 2 +- tests/TestCli.cpp | 24 ++++++++-------- tests/TestCli.h | 9 +++--- tests/gui/CMakeLists.txt | 2 +- tests/gui/TestGui.cpp | 6 ++-- tests/gui/TestGui.h | 4 +-- tests/util/TemporaryFile.cpp | 55 ++++++++++++++++++++++++++++++++++++ tests/util/TemporaryFile.h | 50 ++++++++++++++++++++++++++++++++ 8 files changed, 130 insertions(+), 22 deletions(-) create mode 100644 tests/util/TemporaryFile.cpp create mode 100644 tests/util/TemporaryFile.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 562b45e4d..fc2df2f62 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -95,7 +95,7 @@ set(TEST_LIBRARIES ${ZLIB_LIBRARIES} ) -set(testsupport_SOURCES TestGlobal.h modeltest.cpp FailDevice.cpp stub/TestClock.cpp) +set(testsupport_SOURCES TestGlobal.h modeltest.cpp FailDevice.cpp stub/TestClock.cpp util/TemporaryFile.cpp) add_library(testsupport STATIC ${testsupport_SOURCES}) target_link_libraries(testsupport Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Test) diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index 02bc0ba3f..fd51aa2e4 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -71,22 +71,22 @@ void TestCli::initTestCase() void TestCli::init() { - m_dbFile.reset(new QTemporaryFile()); + m_dbFile.reset(new TemporaryFile()); m_dbFile->open(); m_dbFile->write(m_dbData); - m_dbFile->flush(); + m_dbFile->close(); - m_stdinFile.reset(new QTemporaryFile()); + m_stdinFile.reset(new TemporaryFile()); m_stdinFile->open(); m_stdinHandle = fdopen(m_stdinFile->handle(), "r+"); Utils::STDIN = m_stdinHandle; - m_stdoutFile.reset(new QTemporaryFile()); + m_stdoutFile.reset(new TemporaryFile()); m_stdoutFile->open(); m_stdoutHandle = fdopen(m_stdoutFile->handle(), "r+"); Utils::STDOUT = m_stdoutHandle; - m_stderrFile.reset(new QTemporaryFile()); + m_stderrFile.reset(new TemporaryFile()); m_stderrFile->open(); m_stderrHandle = fdopen(m_stderrFile->handle(), "r+"); Utils::STDERR = m_stderrHandle; @@ -147,6 +147,7 @@ void TestCli::testAdd() Utils::setNextPassword("a"); addCmd.execute({"add", "-u", "newuser", "--url", "https://example.com/", "-g", "-l", "20", m_dbFile->fileName(), "/newuser-entry"}); + m_stderrFile->reset(); auto db = readTestDatabase(); auto* entry = db->rootGroup()->findEntryByPath("/newuser-entry"); @@ -220,7 +221,7 @@ void TestCli::testDiceware() passphrase = m_stdoutFile->readLine(); QCOMPARE(passphrase.split(" ").size(), 10); - QTemporaryFile wordFile; + TemporaryFile wordFile; wordFile.open(); for (int i = 0; i < 4500; ++i) { wordFile.write(QString("word" + QString::number(i) + "\n").toLatin1()); @@ -548,7 +549,7 @@ void TestCli::testLocate() entry->setUuid(QUuid::createUuid()); entry->setTitle("New Entry"); group->addEntry(entry); - QTemporaryFile tmpFile; + TemporaryFile tmpFile; tmpFile.open(); Kdbx4Writer writer; writer.writeDatabase(&tmpFile, db.data()); @@ -581,13 +582,13 @@ void TestCli::testMerge() // load test database and save a copy auto db = readTestDatabase(); QVERIFY(db); - QTemporaryFile targetFile1; + TemporaryFile targetFile1; targetFile1.open(); writer.writeDatabase(&targetFile1, db.data()); targetFile1.close(); // save another copy with a different password - QTemporaryFile targetFile2; + TemporaryFile targetFile2; targetFile2.open(); auto oldKey = db->key(); auto key = QSharedPointer::create(); @@ -605,7 +606,7 @@ void TestCli::testMerge() auto* group = db->rootGroup()->findGroupByPath("/Internet/"); QVERIFY(group); group->addEntry(entry); - QTemporaryFile sourceFile; + TemporaryFile sourceFile; sourceFile.open(); writer.writeDatabase(&sourceFile, db.data()); sourceFile.close(); @@ -615,6 +616,7 @@ void TestCli::testMerge() mergeCmd.execute({"merge", "-s", targetFile1.fileName(), sourceFile.fileName()}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); + m_stderrFile->reset(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully merged the database files.\n")); QFile readBack(targetFile1.fileName()); @@ -660,7 +662,7 @@ void TestCli::testRemove() // load test database and save a copy with disabled recycle bin auto db = readTestDatabase(); QVERIFY(db); - QTemporaryFile fileCopy; + TemporaryFile fileCopy; fileCopy.open(); db->metadata()->setRecycleBinEnabled(false); writer.writeDatabase(&fileCopy, db.data()); diff --git a/tests/TestCli.h b/tests/TestCli.h index 532d84a79..691269840 100644 --- a/tests/TestCli.h +++ b/tests/TestCli.h @@ -19,6 +19,7 @@ #define KEEPASSXC_TESTCLI_H #include "core/Database.h" +#include "util/TemporaryFile.h" #include #include @@ -57,10 +58,10 @@ private slots: private: QByteArray m_dbData; - QScopedPointer m_dbFile; - QScopedPointer m_stdoutFile; - QScopedPointer m_stderrFile; - QScopedPointer m_stdinFile; + QScopedPointer m_dbFile; + QScopedPointer m_stdoutFile; + QScopedPointer m_stderrFile; + QScopedPointer m_stdinFile; FILE* m_stdoutHandle = stdout; FILE* m_stderrHandle = stderr; FILE* m_stdinHandle = stdin; diff --git a/tests/gui/CMakeLists.txt b/tests/gui/CMakeLists.txt index a6e876f47..8542e58cb 100644 --- a/tests/gui/CMakeLists.txt +++ b/tests/gui/CMakeLists.txt @@ -15,6 +15,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -add_unit_test(NAME testgui SOURCES TestGui.cpp LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testgui SOURCES TestGui.cpp ../util/TemporaryFile.cpp LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testguipixmaps SOURCES TestGuiPixmaps.cpp LIBS ${TEST_LIBRARIES}) diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index ee53eb777..450f09474 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -102,7 +102,7 @@ void TestGui::initTestCase() // Every test starts with opening the temp database void TestGui::init() { - m_dbFile.reset(new QTemporaryFile()); + m_dbFile.reset(new TemporaryFile()); // Write the temp storage to a temp database file for use in our tests QVERIFY(m_dbFile->open()); QCOMPARE(m_dbFile->write(m_dbData), static_cast((m_dbData.size()))); @@ -289,7 +289,7 @@ void TestGui::createDatabaseCallback() QCOMPARE(fileCombo->currentText(), QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key")); // save database to temporary file - QTemporaryFile tmpFile; + TemporaryFile tmpFile; QVERIFY(tmpFile.open()); tmpFile.close(); fileDialog()->setNextFileName(tmpFile.fileName()); @@ -1071,7 +1071,7 @@ void TestGui::testSaveAs() m_db->metadata()->setName("testSaveAs"); // open temporary file so it creates a filename - QTemporaryFile tmpFile; + TemporaryFile tmpFile; QVERIFY(tmpFile.open()); QString tmpFileName = tmpFile.fileName(); tmpFile.remove(); diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index 5e0b441b1..4df606f4a 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -20,12 +20,12 @@ #define KEEPASSX_TESTGUI_H #include "gui/MainWindow.h" +#include "util/TemporaryFile.h" #include #include #include #include -#include class Database; class DatabaseTabWidget; @@ -90,7 +90,7 @@ private: QPointer m_dbWidget; QPointer m_db; QByteArray m_dbData; - QScopedPointer m_dbFile; + QScopedPointer m_dbFile; QString m_dbFileName; QString m_dbFilePath; }; diff --git a/tests/util/TemporaryFile.cpp b/tests/util/TemporaryFile.cpp new file mode 100644 index 000000000..476313b02 --- /dev/null +++ b/tests/util/TemporaryFile.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TemporaryFile.h" + +#ifdef Q_OS_WIN + +TemporaryFile::TemporaryFile() + : TemporaryFile(nullptr) +{ +} + +TemporaryFile::TemporaryFile(const QString& templateName) + : TemporaryFile(templateName, nullptr) +{ +} + +TemporaryFile::TemporaryFile(QObject* parent) + : QFile(parent) +{ + QTemporaryFile tmp; + tmp.open(); + QFile::setFileName(tmp.fileName()); + tmp.close(); +} + +TemporaryFile::TemporaryFile(const QString& templateName, QObject* parent) + : QFile(parent) +{ + QTemporaryFile tmp(templateName); + tmp.open(); + QFile::setFileName(tmp.fileName()); + tmp.close(); +} + +bool TemporaryFile::open() +{ + return QFile::open(QIODevice::ReadWrite); +} + +#endif diff --git a/tests/util/TemporaryFile.h b/tests/util/TemporaryFile.h new file mode 100644 index 000000000..4e39a9ae7 --- /dev/null +++ b/tests/util/TemporaryFile.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_TEMPORARYFILE_H +#define KEEPASSXC_TEMPORARYFILE_H + +#include + +#ifdef Q_OS_WIN +/** + * QTemporaryFile does not actually close a file when close() is + * called, which causes the file to be locked on Windows. + * This class extends a QFile with the extra functionality + * of a QTemporaryFile to circumvent this problem. + */ +class TemporaryFile : public QFile +#else +class TemporaryFile : public QTemporaryFile +#endif +{ + Q_OBJECT + +#ifdef Q_OS_WIN +public: + TemporaryFile(); + explicit TemporaryFile(const QString& templateName); + explicit TemporaryFile(QObject* parent); + TemporaryFile(const QString& templateName, QObject* parent); + ~TemporaryFile() override = default; + + using QFile::open; + bool open(); +#endif +}; + +#endif //KEEPASSXC_TEMPORARYFILE_H From bea31f9bcc42010f6bbb72449292fb95f171cad7 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 19 Oct 2018 20:22:28 +0200 Subject: [PATCH 12/84] Rename TestClock to MockClock and move it to the mock directory --- tests/CMakeLists.txt | 2 +- tests/TestGroup.cpp | 10 ++--- tests/TestKeePass2Format.cpp | 42 +++++++++---------- tests/TestMerge.cpp | 10 ++--- tests/TestModified.cpp | 10 ++--- .../TestClock.cpp => mock/MockClock.cpp} | 26 ++++++------ tests/{stub/TestClock.h => mock/MockClock.h} | 6 +-- 7 files changed, 53 insertions(+), 53 deletions(-) rename tests/{stub/TestClock.cpp => mock/MockClock.cpp} (71%) rename tests/{stub/TestClock.h => mock/MockClock.h} (89%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fc2df2f62..96f684171 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -95,7 +95,7 @@ set(TEST_LIBRARIES ${ZLIB_LIBRARIES} ) -set(testsupport_SOURCES TestGlobal.h modeltest.cpp FailDevice.cpp stub/TestClock.cpp util/TemporaryFile.cpp) +set(testsupport_SOURCES TestGlobal.h modeltest.cpp FailDevice.cpp mock/MockClock.cpp util/TemporaryFile.cpp) add_library(testsupport STATIC ${testsupport_SOURCES}) target_link_libraries(testsupport Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Test) diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index e97f7ac25..9ee9389c0 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -18,7 +18,7 @@ #include "TestGroup.h" #include "TestGlobal.h" -#include "stub/TestClock.h" +#include "mock/MockClock.h" #include @@ -29,7 +29,7 @@ QTEST_GUILESS_MAIN(TestGroup) namespace { - TestClock* m_clock = nullptr; + MockClock* m_clock = nullptr; } void TestGroup::initTestCase() @@ -42,13 +42,13 @@ void TestGroup::initTestCase() void TestGroup::init() { Q_ASSERT(m_clock == nullptr); - m_clock = new TestClock(2010, 5, 5, 10, 30, 10); - TestClock::setup(m_clock); + m_clock = new MockClock(2010, 5, 5, 10, 30, 10); + MockClock::setup(m_clock); } void TestGroup::cleanup() { - TestClock::teardown(); + MockClock::teardown(); m_clock = nullptr; } diff --git a/tests/TestKeePass2Format.cpp b/tests/TestKeePass2Format.cpp index 201c4a64a..c2c3f75d3 100644 --- a/tests/TestKeePass2Format.cpp +++ b/tests/TestKeePass2Format.cpp @@ -17,7 +17,7 @@ #include "TestKeePass2Format.h" #include "TestGlobal.h" -#include "stub/TestClock.h" +#include "mock/MockClock.h" #include "core/Metadata.h" #include "crypto/Crypto.h" @@ -78,14 +78,14 @@ void TestKeePass2Format::testXmlMetadata() { QCOMPARE(m_xmlDb->metadata()->generator(), QString("KeePass")); QCOMPARE(m_xmlDb->metadata()->name(), QString("ANAME")); - QCOMPARE(m_xmlDb->metadata()->nameChanged(), TestClock::datetimeUtc(2010, 8, 8, 17, 24, 53)); + QCOMPARE(m_xmlDb->metadata()->nameChanged(), MockClock::datetimeUtc(2010, 8, 8, 17, 24, 53)); QCOMPARE(m_xmlDb->metadata()->description(), QString("ADESC")); - QCOMPARE(m_xmlDb->metadata()->descriptionChanged(), TestClock::datetimeUtc(2010, 8, 8, 17, 27, 12)); + QCOMPARE(m_xmlDb->metadata()->descriptionChanged(), MockClock::datetimeUtc(2010, 8, 8, 17, 27, 12)); QCOMPARE(m_xmlDb->metadata()->defaultUserName(), QString("DEFUSERNAME")); - QCOMPARE(m_xmlDb->metadata()->defaultUserNameChanged(), TestClock::datetimeUtc(2010, 8, 8, 17, 27, 45)); + QCOMPARE(m_xmlDb->metadata()->defaultUserNameChanged(), MockClock::datetimeUtc(2010, 8, 8, 17, 27, 45)); QCOMPARE(m_xmlDb->metadata()->maintenanceHistoryDays(), 127); QCOMPARE(m_xmlDb->metadata()->color(), QColor(0xff, 0xef, 0x00)); - QCOMPARE(m_xmlDb->metadata()->masterKeyChanged(), TestClock::datetimeUtc(2012, 4, 5, 17, 9, 34)); + QCOMPARE(m_xmlDb->metadata()->masterKeyChanged(), MockClock::datetimeUtc(2012, 4, 5, 17, 9, 34)); QCOMPARE(m_xmlDb->metadata()->masterKeyChangeRec(), 101); QCOMPARE(m_xmlDb->metadata()->masterKeyChangeForce(), -1); QCOMPARE(m_xmlDb->metadata()->protectTitle(), false); @@ -96,9 +96,9 @@ void TestKeePass2Format::testXmlMetadata() QCOMPARE(m_xmlDb->metadata()->recycleBinEnabled(), true); QVERIFY(m_xmlDb->metadata()->recycleBin() != nullptr); QCOMPARE(m_xmlDb->metadata()->recycleBin()->name(), QString("Recycle Bin")); - QCOMPARE(m_xmlDb->metadata()->recycleBinChanged(), TestClock::datetimeUtc(2010, 8, 25, 16, 12, 57)); + QCOMPARE(m_xmlDb->metadata()->recycleBinChanged(), MockClock::datetimeUtc(2010, 8, 25, 16, 12, 57)); QVERIFY(m_xmlDb->metadata()->entryTemplatesGroup() == nullptr); - QCOMPARE(m_xmlDb->metadata()->entryTemplatesGroupChanged(), TestClock::datetimeUtc(2010, 8, 8, 17, 24, 19)); + QCOMPARE(m_xmlDb->metadata()->entryTemplatesGroupChanged(), MockClock::datetimeUtc(2010, 8, 8, 17, 24, 19)); QVERIFY(m_xmlDb->metadata()->lastSelectedGroup() != nullptr); QCOMPARE(m_xmlDb->metadata()->lastSelectedGroup()->name(), QString("NewDatabase")); QVERIFY(m_xmlDb->metadata()->lastTopVisibleGroup() == m_xmlDb->metadata()->lastSelectedGroup()); @@ -136,13 +136,13 @@ void TestKeePass2Format::testXmlGroupRoot() QCOMPARE(group->iconUuid(), QUuid()); QVERIFY(group->isExpanded()); TimeInfo ti = group->timeInfo(); - QCOMPARE(ti.lastModificationTime(), TestClock::datetimeUtc(2010, 8, 8, 17, 24, 27)); - QCOMPARE(ti.creationTime(), TestClock::datetimeUtc(2010, 8, 7, 17, 24, 27)); - QCOMPARE(ti.lastAccessTime(), TestClock::datetimeUtc(2010, 8, 9, 9, 9, 44)); - QCOMPARE(ti.expiryTime(), TestClock::datetimeUtc(2010, 8, 8, 17, 24, 17)); + QCOMPARE(ti.lastModificationTime(), MockClock::datetimeUtc(2010, 8, 8, 17, 24, 27)); + QCOMPARE(ti.creationTime(), MockClock::datetimeUtc(2010, 8, 7, 17, 24, 27)); + QCOMPARE(ti.lastAccessTime(), MockClock::datetimeUtc(2010, 8, 9, 9, 9, 44)); + QCOMPARE(ti.expiryTime(), MockClock::datetimeUtc(2010, 8, 8, 17, 24, 17)); QVERIFY(!ti.expires()); QCOMPARE(ti.usageCount(), 52); - QCOMPARE(ti.locationChanged(), TestClock::datetimeUtc(2010, 8, 8, 17, 24, 27)); + QCOMPARE(ti.locationChanged(), MockClock::datetimeUtc(2010, 8, 8, 17, 24, 27)); QCOMPARE(group->defaultAutoTypeSequence(), QString("")); QCOMPARE(group->autoTypeEnabled(), Group::Inherit); QCOMPARE(group->searchingEnabled(), Group::Inherit); @@ -203,13 +203,13 @@ void TestKeePass2Format::testXmlEntry1() QCOMPARE(entry->tags(), QString("a b c")); const TimeInfo ti = entry->timeInfo(); - QCOMPARE(ti.lastModificationTime(), TestClock::datetimeUtc(2010, 8, 25, 16, 19, 25)); - QCOMPARE(ti.creationTime(), TestClock::datetimeUtc(2010, 8, 25, 16, 13, 54)); - QCOMPARE(ti.lastAccessTime(), TestClock::datetimeUtc(2010, 8, 25, 16, 19, 25)); - QCOMPARE(ti.expiryTime(), TestClock::datetimeUtc(2010, 8, 25, 16, 12, 57)); + QCOMPARE(ti.lastModificationTime(), MockClock::datetimeUtc(2010, 8, 25, 16, 19, 25)); + QCOMPARE(ti.creationTime(), MockClock::datetimeUtc(2010, 8, 25, 16, 13, 54)); + QCOMPARE(ti.lastAccessTime(), MockClock::datetimeUtc(2010, 8, 25, 16, 19, 25)); + QCOMPARE(ti.expiryTime(), MockClock::datetimeUtc(2010, 8, 25, 16, 12, 57)); QVERIFY(!ti.expires()); QCOMPARE(ti.usageCount(), 8); - QCOMPARE(ti.locationChanged(), TestClock::datetimeUtc(2010, 8, 25, 16, 13, 54)); + QCOMPARE(ti.locationChanged(), MockClock::datetimeUtc(2010, 8, 25, 16, 13, 54)); QList attrs = entry->attributes()->keys(); QCOMPARE(entry->attributes()->value("Notes"), QString("Notes")); @@ -308,7 +308,7 @@ void TestKeePass2Format::testXmlEntryHistory() const Entry* entry = entryMain->historyItems().at(0); QCOMPARE(entry->uuid(), entryMain->uuid()); QVERIFY(!entry->parent()); - QCOMPARE(entry->timeInfo().lastModificationTime(), TestClock::datetimeUtc(2010, 8, 25, 16, 13, 54)); + QCOMPARE(entry->timeInfo().lastModificationTime(), MockClock::datetimeUtc(2010, 8, 25, 16, 13, 54)); QCOMPARE(entry->timeInfo().usageCount(), 3); QCOMPARE(entry->title(), QString("Sample Entry")); QCOMPARE(entry->url(), QString("http://www.somesite.com/")); @@ -318,7 +318,7 @@ void TestKeePass2Format::testXmlEntryHistory() const Entry* entry = entryMain->historyItems().at(1); QCOMPARE(entry->uuid(), entryMain->uuid()); QVERIFY(!entry->parent()); - QCOMPARE(entry->timeInfo().lastModificationTime(), TestClock::datetimeUtc(2010, 8, 25, 16, 15, 43)); + QCOMPARE(entry->timeInfo().lastModificationTime(), MockClock::datetimeUtc(2010, 8, 25, 16, 15, 43)); QCOMPARE(entry->timeInfo().usageCount(), 7); QCOMPARE(entry->title(), QString("Sample Entry 1")); QCOMPARE(entry->url(), QString("http://www.somesite.com/")); @@ -332,11 +332,11 @@ void TestKeePass2Format::testXmlDeletedObjects() delObj = objList.takeFirst(); QCOMPARE(delObj.uuid, QUuid::fromRfc4122(QByteArray::fromBase64("5K/bzWCSmkCv5OZxYl4N/w=="))); - QCOMPARE(delObj.deletionTime, TestClock::datetimeUtc(2010, 8, 25, 16, 14, 12)); + QCOMPARE(delObj.deletionTime, MockClock::datetimeUtc(2010, 8, 25, 16, 14, 12)); delObj = objList.takeFirst(); QCOMPARE(delObj.uuid, QUuid::fromRfc4122(QByteArray::fromBase64("80h8uSNWgkKhKCp1TgXF7g=="))); - QCOMPARE(delObj.deletionTime, TestClock::datetimeUtc(2010, 8, 25, 16, 14, 14)); + QCOMPARE(delObj.deletionTime, MockClock::datetimeUtc(2010, 8, 25, 16, 14, 14)); QVERIFY(objList.isEmpty()); } diff --git a/tests/TestMerge.cpp b/tests/TestMerge.cpp index 0da304f07..4a5081777 100644 --- a/tests/TestMerge.cpp +++ b/tests/TestMerge.cpp @@ -17,7 +17,7 @@ #include "TestMerge.h" #include "TestGlobal.h" -#include "stub/TestClock.h" +#include "mock/MockClock.h" #include "core/Merger.h" #include "core/Metadata.h" @@ -34,7 +34,7 @@ namespace return timeInfo; } - TestClock* m_clock = nullptr; + MockClock* m_clock = nullptr; } void TestMerge::initTestCase() @@ -47,13 +47,13 @@ void TestMerge::initTestCase() void TestMerge::init() { Q_ASSERT(m_clock == nullptr); - m_clock = new TestClock(2010, 5, 5, 10, 30, 10); - TestClock::setup(m_clock); + m_clock = new MockClock(2010, 5, 5, 10, 30, 10); + MockClock::setup(m_clock); } void TestMerge::cleanup() { - TestClock::teardown(); + MockClock::teardown(); m_clock = nullptr; } diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp index b1ad09443..63013fd5e 100644 --- a/tests/TestModified.cpp +++ b/tests/TestModified.cpp @@ -16,7 +16,7 @@ */ #include "TestModified.h" -#include "stub/TestClock.h" +#include "mock/MockClock.h" #include #include @@ -31,7 +31,7 @@ QTEST_GUILESS_MAIN(TestModified) namespace { - TestClock* m_clock = nullptr; + MockClock* m_clock = nullptr; } void TestModified::initTestCase() @@ -42,13 +42,13 @@ void TestModified::initTestCase() void TestModified::init() { Q_ASSERT(m_clock == nullptr); - m_clock = new TestClock(2010, 5, 5, 10, 30, 10); - TestClock::setup(m_clock); + m_clock = new MockClock(2010, 5, 5, 10, 30, 10); + MockClock::setup(m_clock); } void TestModified::cleanup() { - TestClock::teardown(); + MockClock::teardown(); m_clock = nullptr; } diff --git a/tests/stub/TestClock.cpp b/tests/mock/MockClock.cpp similarity index 71% rename from tests/stub/TestClock.cpp rename to tests/mock/MockClock.cpp index d3222febd..36873cd69 100644 --- a/tests/stub/TestClock.cpp +++ b/tests/mock/MockClock.cpp @@ -15,72 +15,72 @@ * along with this program. If not, see . */ -#include "TestClock.h" +#include "MockClock.h" -TestClock::TestClock(int year, int month, int day, int hour, int min, int second) +MockClock::MockClock(int year, int month, int day, int hour, int min, int second) : Clock() , m_utcCurrent(datetimeUtc(year, month, day, hour, min, second)) { } -TestClock::TestClock(QDateTime utcBase) +MockClock::MockClock(QDateTime utcBase) : Clock() , m_utcCurrent(utcBase) { } -const QDateTime& TestClock::advanceSecond(int seconds) +const QDateTime& MockClock::advanceSecond(int seconds) { m_utcCurrent = m_utcCurrent.addSecs(seconds); return m_utcCurrent; } -const QDateTime& TestClock::advanceMinute(int minutes) +const QDateTime& MockClock::advanceMinute(int minutes) { m_utcCurrent = m_utcCurrent.addSecs(minutes * 60); return m_utcCurrent; } -const QDateTime& TestClock::advanceHour(int hours) +const QDateTime& MockClock::advanceHour(int hours) { m_utcCurrent = m_utcCurrent.addSecs(hours * 60 * 60); return m_utcCurrent; } -const QDateTime& TestClock::advanceDay(int days) +const QDateTime& MockClock::advanceDay(int days) { m_utcCurrent = m_utcCurrent.addDays(days); return m_utcCurrent; } -const QDateTime& TestClock::advanceMonth(int months) +const QDateTime& MockClock::advanceMonth(int months) { m_utcCurrent = m_utcCurrent.addMonths(months); return m_utcCurrent; } -const QDateTime& TestClock::advanceYear(int years) +const QDateTime& MockClock::advanceYear(int years) { m_utcCurrent = m_utcCurrent.addYears(years); return m_utcCurrent; } -void TestClock::setup(Clock* clock) +void MockClock::setup(Clock* clock) { Clock::setInstance(clock); } -void TestClock::teardown() +void MockClock::teardown() { Clock::resetInstance(); } -QDateTime TestClock::currentDateTimeUtcImpl() const +QDateTime MockClock::currentDateTimeUtcImpl() const { return m_utcCurrent; } -QDateTime TestClock::currentDateTimeImpl() const +QDateTime MockClock::currentDateTimeImpl() const { return m_utcCurrent.toLocalTime(); } diff --git a/tests/stub/TestClock.h b/tests/mock/MockClock.h similarity index 89% rename from tests/stub/TestClock.h rename to tests/mock/MockClock.h index 02405edcb..4bc61b70a 100644 --- a/tests/stub/TestClock.h +++ b/tests/mock/MockClock.h @@ -22,12 +22,12 @@ #include -class TestClock : public Clock +class MockClock : public Clock { public: - TestClock(int year, int month, int day, int hour, int min, int second); + MockClock(int year, int month, int day, int hour, int min, int second); - TestClock(QDateTime utcBase = QDateTime::currentDateTimeUtc()); + MockClock(QDateTime utcBase = QDateTime::currentDateTimeUtc()); const QDateTime& advanceSecond(int seconds); const QDateTime& advanceMinute(int minutes); From 0ca7fd369aaa364ad437de694714b9488e5e5636 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 19 Oct 2018 21:41:42 +0200 Subject: [PATCH 13/84] Implement review feedback --- src/browser/BrowserAction.cpp | 2 +- src/cli/Remove.cpp | 5 +-- src/cli/Utils.cpp | 7 ++-- src/cli/Utils.h | 6 +++- src/cli/keepassxc-cli.cpp | 4 +-- src/config-keepassx.h.cmake | 2 +- src/core/Tools.cpp | 15 --------- src/core/Tools.h | 1 - src/gui/AboutDialog.cpp | 4 +-- src/gui/DatabaseWidget.cpp | 8 +++-- src/gui/WelcomeWidget.cpp | 2 +- src/gui/group/GroupModel.cpp | 6 ++-- src/main.cpp | 2 +- tests/TestCli.cpp | 60 +++++++++++++++++------------------ 14 files changed, 59 insertions(+), 65 deletions(-) diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 6001acb18..e0c3a8501 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -376,7 +376,7 @@ QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorC QJsonObject BrowserAction::buildMessage(const QString& nonce) const { QJsonObject message; - message["version"] = KEEPASSX_VERSION; + message["version"] = KEEPASSXC_VERSION; message["success"] = "true"; message["nonce"] = nonce; return message; diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 5bbfd67e4..6523cff97 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -76,7 +76,7 @@ int Remove::removeEntry(Database* database, const QString& databasePath, const Q QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); QTextStream err(Utils::STDERR, QIODevice::WriteOnly); - Entry* entry = database->rootGroup()->findEntryByPath(entryPath); + QPointer entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; return EXIT_FAILURE; @@ -84,7 +84,8 @@ int Remove::removeEntry(Database* database, const QString& databasePath, const Q QString entryTitle = entry->title(); bool recycled = true; - if (Tools::hasChild(database->metadata()->recycleBin(), entry) || !database->metadata()->recycleBinEnabled()) { + auto* recycleBin = database->metadata()->recycleBin(); + if (!database->metadata()->recycleBinEnabled() || (recycleBin && recycleBin->findEntryByUuid(entry->uuid()))) { delete entry; recycled = false; } else { diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index 8a0f5abe3..a0f75bc8e 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -72,6 +72,8 @@ void setStdinEcho(bool enable = true) #endif } +namespace Test +{ QStringList nextPasswords = {}; /** @@ -85,6 +87,7 @@ void setNextPassword(const QString& password) { nextPasswords.append(password); } +} // namespace Test /** * Read a user password from STDIN or return a password previously @@ -97,8 +100,8 @@ QString getPassword() QTextStream out(STDOUT, QIODevice::WriteOnly); // return preset password if one is set - if (!nextPasswords.isEmpty()) { - auto password = nextPasswords.takeFirst(); + if (!Test::nextPasswords.isEmpty()) { + auto password = Test::nextPasswords.takeFirst(); // simulate user entering newline out << endl; return password; diff --git a/src/cli/Utils.h b/src/cli/Utils.h index 868ccdef5..1d5e0f356 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -29,8 +29,12 @@ extern FILE* STDIN; void setStdinEcho(bool enable); QString getPassword(); -void setNextPassword(const QString& password); int clipText(const QString& text); + +namespace Test +{ +void setNextPassword(const QString& password); +} }; #endif // KEEPASSXC_UTILS_H diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 97efd8c08..041908663 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -40,7 +40,7 @@ int main(int argc, char** argv) } QCoreApplication app(argc, argv); - QCoreApplication::setApplicationVersion(KEEPASSX_VERSION); + QCoreApplication::setApplicationVersion(KEEPASSXC_VERSION); #ifdef QT_NO_DEBUG Bootstrap::bootstrapApplication(); @@ -72,7 +72,7 @@ int main(int argc, char** argv) if (parser.positionalArguments().empty()) { if (parser.isSet("version")) { // Switch to parser.showVersion() when available (QT 5.4). - out << KEEPASSX_VERSION << endl; + out << KEEPASSXC_VERSION << endl; return EXIT_SUCCESS; } parser.showHelp(); diff --git a/src/config-keepassx.h.cmake b/src/config-keepassx.h.cmake index d1f0723b4..52d14ce94 100644 --- a/src/config-keepassx.h.cmake +++ b/src/config-keepassx.h.cmake @@ -3,7 +3,7 @@ #ifndef KEEPASSX_CONFIG_KEEPASSX_H #define KEEPASSX_CONFIG_KEEPASSX_H -#define KEEPASSX_VERSION "@KEEPASSXC_VERSION@" +#define KEEPASSXC_VERSION "@KEEPASSXC_VERSION@" #define KEEPASSX_SOURCE_DIR "@CMAKE_SOURCE_DIR@" #define KEEPASSX_BINARY_DIR "@CMAKE_BINARY_DIR@" diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 60c50b451..8467fb416 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -77,21 +77,6 @@ QString humanReadableFileSize(qint64 bytes, quint32 precision) return QString("%1 %2").arg(QLocale().toString(size, 'f', precision), units.at(i)); } -bool hasChild(const QObject* parent, const QObject* child) -{ - if (!parent || !child) { - return false; - } - - const QObjectList children = parent->children(); - for (QObject* c : children) { - if (child == c || hasChild(c, child)) { - return true; - } - } - return false; -} - bool readFromDevice(QIODevice* device, QByteArray& data, int size) { QByteArray buffer; diff --git a/src/core/Tools.h b/src/core/Tools.h index aec937304..13d9869f7 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -31,7 +31,6 @@ class QIODevice; namespace Tools { QString humanReadableFileSize(qint64 bytes, quint32 precision = 2); -bool hasChild(const QObject* parent, const QObject* child); bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384); bool readAllFromDevice(QIODevice* device, QByteArray& data); QString imageReaderFilter(); diff --git a/src/gui/AboutDialog.cpp b/src/gui/AboutDialog.cpp index c7c75a11e..f6a9d15f9 100644 --- a/src/gui/AboutDialog.cpp +++ b/src/gui/AboutDialog.cpp @@ -37,7 +37,7 @@ AboutDialog::AboutDialog(QWidget* parent) setWindowFlags(Qt::Sheet); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - m_ui->nameLabel->setText(m_ui->nameLabel->text().replace("${VERSION}", KEEPASSX_VERSION)); + m_ui->nameLabel->setText(m_ui->nameLabel->text().replace("${VERSION}", KEEPASSXC_VERSION)); QFont nameLabelFont = m_ui->nameLabel->font(); nameLabelFont.setPointSize(nameLabelFont.pointSize() + 4); m_ui->nameLabel->setFont(nameLabelFont); @@ -52,7 +52,7 @@ AboutDialog::AboutDialog(QWidget* parent) } QString debugInfo = "KeePassXC - "; - debugInfo.append(tr("Version %1").arg(KEEPASSX_VERSION).append("\n")); + debugInfo.append(tr("Version %1").arg(KEEPASSXC_VERSION).append("\n")); #ifndef KEEPASSXC_BUILD_TYPE_RELEASE debugInfo.append(tr("Build Type: %1").arg(KEEPASSXC_BUILD_TYPE).append("\n")); #endif diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index c90fc52f7..effae45c1 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -460,7 +460,8 @@ void DatabaseWidget::deleteEntries() selectedEntries.append(m_entryView->entryFromIndex(index)); } - bool inRecycleBin = Tools::hasChild(m_db->metadata()->recycleBin(), selectedEntries.first()); + auto* recycleBin = m_db->metadata()->recycleBin(); + bool inRecycleBin = recycleBin && recycleBin->findEntryByUuid(selectedEntries.first()->uuid()); if (inRecycleBin || !m_db->metadata()->recycleBinEnabled()) { QString prompt; if (selected.size() == 1) { @@ -688,9 +689,10 @@ void DatabaseWidget::deleteGroup() return; } - bool inRecycleBin = Tools::hasChild(m_db->metadata()->recycleBin(), currentGroup); + auto* recycleBin = m_db->metadata()->recycleBin(); + bool inRecycleBin = recycleBin && recycleBin->findGroupByUuid(currentGroup->uuid()); bool isRecycleBin = (currentGroup == m_db->metadata()->recycleBin()); - bool isRecycleBinSubgroup = Tools::hasChild(currentGroup, m_db->metadata()->recycleBin()); + bool isRecycleBinSubgroup = currentGroup->findGroupByUuid(m_db->metadata()->recycleBin()->uuid()); if (inRecycleBin || isRecycleBin || isRecycleBinSubgroup || !m_db->metadata()->recycleBinEnabled()) { QMessageBox::StandardButton result = MessageBox::question( this, diff --git a/src/gui/WelcomeWidget.cpp b/src/gui/WelcomeWidget.cpp index 7bb4484c7..ed0ca2936 100644 --- a/src/gui/WelcomeWidget.cpp +++ b/src/gui/WelcomeWidget.cpp @@ -29,7 +29,7 @@ WelcomeWidget::WelcomeWidget(QWidget* parent) { m_ui->setupUi(this); - m_ui->welcomeLabel->setText(tr("Welcome to KeePassXC %1").arg(KEEPASSX_VERSION)); + m_ui->welcomeLabel->setText(tr("Welcome to KeePassXC %1").arg(KEEPASSXC_VERSION)); QFont welcomeLabelFont = m_ui->welcomeLabel->font(); welcomeLabelFont.setBold(true); welcomeLabelFont.setPointSize(welcomeLabelFont.pointSize() + 4); diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp index e8f51909f..a3c72b792 100644 --- a/src/gui/group/GroupModel.cpp +++ b/src/gui/group/GroupModel.cpp @@ -234,11 +234,11 @@ bool GroupModel::dropMimeData(const QMimeData* data, } Group* dragGroup = db->resolveGroup(groupUuid); - if (!dragGroup || !Tools::hasChild(db, dragGroup) || dragGroup == db->rootGroup()) { + if (!dragGroup || !db->rootGroup()->findGroupByUuid(dragGroup->uuid()) || dragGroup == db->rootGroup()) { return false; } - if (dragGroup == parentGroup || Tools::hasChild(dragGroup, parentGroup)) { + if (dragGroup == parentGroup || dragGroup->findGroupByUuid(parentGroup->uuid())) { return false; } @@ -278,7 +278,7 @@ bool GroupModel::dropMimeData(const QMimeData* data, } Entry* dragEntry = db->resolveEntry(entryUuid); - if (!dragEntry || !Tools::hasChild(db, dragEntry)) { + if (!dragEntry || !db->rootGroup()->findEntryByUuid(dragEntry->uuid())) { continue; } diff --git a/src/main.cpp b/src/main.cpp index 74af06953..a37648ec1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) { Application app(argc, argv); Application::setApplicationName("keepassxc"); - Application::setApplicationVersion(KEEPASSX_VERSION); + Application::setApplicationVersion(KEEPASSXC_VERSION); // don't set organizationName as that changes the return value of // QStandardPaths::writableLocation(QDesktopServices::DataLocation) Bootstrap::bootstrapApplication(); diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index fd51aa2e4..e10a651d7 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -115,7 +115,7 @@ void TestCli::cleanupTestCase() QSharedPointer TestCli::readTestDatabase() const { - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); auto db = QSharedPointer(Database::unlockFromStdin(m_dbFile->fileName(), "", m_stdoutHandle)); m_stdoutFile->seek(ftell(m_stdoutHandle)); // re-synchronize handles return db; @@ -145,7 +145,7 @@ void TestCli::testAdd() QVERIFY(!addCmd.name.isEmpty()); QVERIFY(addCmd.getDescriptionLine().contains(addCmd.name)); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); addCmd.execute({"add", "-u", "newuser", "--url", "https://example.com/", "-g", "-l", "20", m_dbFile->fileName(), "/newuser-entry"}); m_stderrFile->reset(); @@ -156,8 +156,8 @@ void TestCli::testAdd() QCOMPARE(entry->url(), QString("https://example.com/")); QCOMPARE(entry->password().size(), 20); - Utils::setNextPassword("a"); - Utils::setNextPassword("newpassword"); + Utils::Test::setNextPassword("a"); + Utils::Test::setNextPassword("newpassword"); addCmd.execute({"add", "-u", "newuser2", "--url", "https://example.net/", "-g", "-l", "20", "-p", m_dbFile->fileName(), "/newuser-entry2"}); db = readTestDatabase(); @@ -177,7 +177,7 @@ void TestCli::testClip() QVERIFY(!clipCmd.name.isEmpty()); QVERIFY(clipCmd.getDescriptionLine().contains(clipCmd.name)); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); clipCmd.execute({"clip", m_dbFile->fileName(), "/Sample Entry"}); m_stderrFile->reset(); @@ -190,7 +190,7 @@ void TestCli::testClip() QCOMPARE(clipboard->text(), QString("Password")); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); QFuture future = QtConcurrent::run(&clipCmd, &Clip::execute, QStringList{"clip", m_dbFile->fileName(), "/Sample Entry", "1"}); QTRY_COMPARE_WITH_TIMEOUT(clipboard->text(), QString("Password"), 500); @@ -246,7 +246,7 @@ void TestCli::testEdit() QVERIFY(!editCmd.name.isEmpty()); QVERIFY(editCmd.getDescriptionLine().contains(editCmd.name)); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); editCmd.execute({"edit", "-u", "newuser", "--url", "https://otherurl.example.com/", "-t", "newtitle", m_dbFile->fileName(), "/Sample Entry"}); auto db = readTestDatabase(); @@ -256,7 +256,7 @@ void TestCli::testEdit() QCOMPARE(entry->url(), QString("https://otherurl.example.com/")); QCOMPARE(entry->password(), QString("Password")); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); editCmd.execute({"edit", "-g", m_dbFile->fileName(), "/newtitle"}); db = readTestDatabase(); entry = db->rootGroup()->findEntryByPath("/newtitle"); @@ -266,7 +266,7 @@ void TestCli::testEdit() QVERIFY(!entry->password().isEmpty()); QVERIFY(entry->password() != QString("Password")); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); editCmd.execute({"edit", "-g", "-l", "34", "-t", "yet another title", m_dbFile->fileName(), "/newtitle"}); db = readTestDatabase(); entry = db->rootGroup()->findEntryByPath("/yet another title"); @@ -276,8 +276,8 @@ void TestCli::testEdit() QVERIFY(entry->password() != QString("Password")); QCOMPARE(entry->password().size(), 34); - Utils::setNextPassword("a"); - Utils::setNextPassword("newpassword"); + Utils::Test::setNextPassword("a"); + Utils::Test::setNextPassword("newpassword"); editCmd.execute({"edit", "-p", m_dbFile->fileName(), "/yet another title"}); db = readTestDatabase(); entry = db->rootGroup()->findEntryByPath("/yet another title"); @@ -392,7 +392,7 @@ void TestCli::testExtract() QVERIFY(!extractCmd.name.isEmpty()); QVERIFY(extractCmd.getDescriptionLine().contains(extractCmd.name)); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); extractCmd.execute({"extract", m_dbFile->fileName()}); m_stdoutFile->seek(0); @@ -471,7 +471,7 @@ void TestCli::testList() QVERIFY(!listCmd.name.isEmpty()); QVERIFY(listCmd.getDescriptionLine().contains(listCmd.name)); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); listCmd.execute({"ls", m_dbFile->fileName()}); m_stdoutFile->reset(); m_stdoutFile->readLine(); // skip password prompt @@ -484,7 +484,7 @@ void TestCli::testList() "Homebanking/\n")); qint64 pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); listCmd.execute({"ls", "-R", m_dbFile->fileName()}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -503,14 +503,14 @@ void TestCli::testList() " [empty]\n")); pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); listCmd.execute({"ls", m_dbFile->fileName(), "/General/"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("[empty]\n")); pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); listCmd.execute({"ls", m_dbFile->fileName(), "/DoesNotExist/"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -525,14 +525,14 @@ void TestCli::testLocate() QVERIFY(!locateCmd.name.isEmpty()); QVERIFY(locateCmd.getDescriptionLine().contains(locateCmd.name)); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", m_dbFile->fileName(), "Sample"}); m_stdoutFile->reset(); m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("/Sample Entry\n")); qint64 pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", m_dbFile->fileName(), "Does Not Exist"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -556,14 +556,14 @@ void TestCli::testLocate() tmpFile.close(); pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", tmpFile.fileName(), "New"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("/General/New Entry\n")); pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", tmpFile.fileName(), "Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -612,7 +612,7 @@ void TestCli::testMerge() sourceFile.close(); qint64 pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); mergeCmd.execute({"merge", "-s", targetFile1.fileName(), sourceFile.fileName()}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); @@ -631,8 +631,8 @@ void TestCli::testMerge() // try again with different passwords for both files pos = m_stdoutFile->pos(); - Utils::setNextPassword("b"); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("b"); + Utils::Test::setNextPassword("a"); mergeCmd.execute({"merge", targetFile2.fileName(), sourceFile.fileName()}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); @@ -671,7 +671,7 @@ void TestCli::testRemove() qint64 pos = m_stdoutFile->pos(); // delete entry and verify - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); removeCmd.execute({"rm", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -690,7 +690,7 @@ void TestCli::testRemove() pos = m_stdoutFile->pos(); // try again, this time without recycle bin - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); removeCmd.execute({"rm", fileCopy.fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -707,7 +707,7 @@ void TestCli::testRemove() pos = m_stdoutFile->pos(); // finally, try deleting a non-existent entry - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); removeCmd.execute({"rm", fileCopy.fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -722,7 +722,7 @@ void TestCli::testShow() QVERIFY(!showCmd.name.isEmpty()); QVERIFY(showCmd.getDescriptionLine().contains(showCmd.name)); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); showCmd.execute({"show", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->reset(); m_stdoutFile->readLine(); // skip password prompt @@ -733,14 +733,14 @@ void TestCli::testShow() "Notes: Notes\n")); qint64 pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n")); pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -748,7 +748,7 @@ void TestCli::testShow() "http://www.somesite.com/\n")); pos = m_stdoutFile->pos(); - Utils::setNextPassword("a"); + Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "DoesNotExist", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt From 77adbef401caa02bd78e478cb0736063c0211b7c Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 19 Oct 2018 21:41:56 +0200 Subject: [PATCH 14/84] Reformat CMakeLists.txt files --- CMakeLists.txt | 291 +++++++------ cmake/CLangFormat.cmake | 62 ++- cmake/FindArgon2.cmake | 28 +- cmake/FindGcrypt.cmake | 2 +- src/CMakeLists.txt | 623 ++++++++++++++-------------- src/autotype/CMakeLists.txt | 36 +- src/autotype/mac/CMakeLists.txt | 26 +- src/autotype/test/CMakeLists.txt | 4 +- src/autotype/windows/CMakeLists.txt | 6 +- src/autotype/xcb/CMakeLists.txt | 4 +- src/browser/CMakeLists.txt | 25 +- src/cli/CMakeLists.txt | 68 +-- tests/CMakeLists.txt | 160 +++---- 13 files changed, 659 insertions(+), 676 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f14b363b3..0c1ca4292 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2017 KeePassXC Team +# Copyright (C) 2018 KeePassXC Team # Copyright (C) 2010 Felix Geyer # # This program is free software: you can redistribute it and/or modify @@ -19,9 +19,9 @@ cmake_minimum_required(VERSION 3.1.0) project(KeePassXC) if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo Debug DebugFull Profile MinSizeRel." - FORCE) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo Debug DebugFull Profile MinSizeRel." + FORCE) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) @@ -48,19 +48,19 @@ option(WITH_XC_BROWSER "Include browser integration with keepassxc-browser." OFF option(WITH_XC_YUBIKEY "Include YubiKey support." OFF) option(WITH_XC_SSHAGENT "Include SSH agent support." OFF) if(APPLE) - option(WITH_XC_TOUCHID "Include TouchID support for macOS." OFF) + option(WITH_XC_TOUCHID "Include TouchID support for macOS." OFF) endif() if(WITH_XC_ALL) - # Enable all options - set(WITH_XC_AUTOTYPE ON) - set(WITH_XC_NETWORKING ON) - set(WITH_XC_BROWSER ON) - set(WITH_XC_YUBIKEY ON) - set(WITH_XC_SSHAGENT ON) - if(APPLE) - set(WITH_XC_TOUCHID ON) - endif() + # Enable all options + set(WITH_XC_AUTOTYPE ON) + set(WITH_XC_NETWORKING ON) + set(WITH_XC_BROWSER ON) + set(WITH_XC_YUBIKEY ON) + set(WITH_XC_SSHAGENT ON) + if(APPLE) + set(WITH_XC_TOUCHID ON) + endif() endif() set(KEEPASSXC_VERSION_MAJOR "2") @@ -76,34 +76,34 @@ execute_process(COMMAND git tag --points-at HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_TAG) if(GIT_TAG) - set(OVERRIDE_VERSION ${GIT_TAG}) + set(OVERRIDE_VERSION ${GIT_TAG}) elseif(EXISTS ${CMAKE_SOURCE_DIR}/.version) - file(READ ${CMAKE_SOURCE_DIR}/.version OVERRIDE_VERSION) + file(READ ${CMAKE_SOURCE_DIR}/.version OVERRIDE_VERSION) endif() string(REGEX REPLACE "(\r?\n)+" "" OVERRIDE_VERSION "${OVERRIDE_VERSION}") if(OVERRIDE_VERSION) - if(OVERRIDE_VERSION MATCHES "^[\\.0-9]+-(alpha|beta)[0-9]+$") - set(KEEPASSXC_BUILD_TYPE PreRelease) - set(KEEPASSXC_VERSION ${OVERRIDE_VERSION}) - elseif(OVERRIDE_VERSION MATCHES "^[\\.0-9]+$") - set(KEEPASSXC_BUILD_TYPE Release) - set(KEEPASSXC_VERSION ${OVERRIDE_VERSION}) - endif() + if(OVERRIDE_VERSION MATCHES "^[\\.0-9]+-(alpha|beta)[0-9]+$") + set(KEEPASSXC_BUILD_TYPE PreRelease) + set(KEEPASSXC_VERSION ${OVERRIDE_VERSION}) + elseif(OVERRIDE_VERSION MATCHES "^[\\.0-9]+$") + set(KEEPASSXC_BUILD_TYPE Release) + set(KEEPASSXC_VERSION ${OVERRIDE_VERSION}) + endif() endif() if(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease" AND NOT OVERRIDE_VERSION) - set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-preview") + set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-preview") elseif(KEEPASSXC_BUILD_TYPE STREQUAL "Snapshot") - set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-snapshot") + set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-snapshot") endif() if(KEEPASSXC_BUILD_TYPE STREQUAL "Release") - set(KEEPASSXC_BUILD_TYPE_RELEASE ON) + set(KEEPASSXC_BUILD_TYPE_RELEASE ON) elseif(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease") - set(KEEPASSXC_BUILD_TYPE_PRE_RELEASE ON) + set(KEEPASSXC_BUILD_TYPE_PRE_RELEASE ON) else() - set(KEEPASSXC_BUILD_TYPE_SNAPSHOT ON) + set(KEEPASSXC_BUILD_TYPE_SNAPSHOT ON) endif() message(STATUS "Setting up build for KeePassXC v${KEEPASSXC_VERSION}\n") @@ -113,46 +113,46 @@ set(KEEPASSXC_DIST ON) set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution Type") set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Other) if(KEEPASSXC_DIST_TYPE STREQUAL "Snap") - set(KEEPASSXC_DIST_SNAP ON) + set(KEEPASSXC_DIST_SNAP ON) elseif(KEEPASSXC_DIST_TYPE STREQUAL "AppImage") - set(KEEPASSXC_DIST_APPIMAGE ON) + set(KEEPASSXC_DIST_APPIMAGE ON) elseif(KEEPASSXC_DIST_TYPE STREQUAL "Other") - unset(KEEPASSXC_DIST) + unset(KEEPASSXC_DIST) endif() if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") - set(IS_32BIT TRUE) + set(IS_32BIT TRUE) endif() if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_COMPILER_IS_CLANG 1) + set(CMAKE_COMPILER_IS_CLANG 1) endif() if("${CMAKE_CXX_COMPILER}" MATCHES "clang(\\+\\+)?$" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_COMPILER_IS_CLANGXX 1) + set(CMAKE_COMPILER_IS_CLANGXX 1) endif() macro(add_gcc_compiler_cxxflags FLAGS) - if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") - endif() + if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") + endif() endmacro(add_gcc_compiler_cxxflags) macro(add_gcc_compiler_cflags FLAGS) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") - endif() + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") + endif() endmacro(add_gcc_compiler_cflags) macro(add_gcc_compiler_flags FLAGS) - add_gcc_compiler_cxxflags("${FLAGS}") - add_gcc_compiler_cflags("${FLAGS}") + add_gcc_compiler_cxxflags("${FLAGS}") + add_gcc_compiler_cflags("${FLAGS}") endmacro(add_gcc_compiler_flags) add_definitions(-DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII) if(WITH_APP_BUNDLE) - add_definitions(-DWITH_APP_BUNDLE) + add_definitions(-DWITH_APP_BUNDLE) endif() add_gcc_compiler_flags("-fno-common") @@ -162,7 +162,7 @@ add_gcc_compiler_flags("-fvisibility=hidden") add_gcc_compiler_cxxflags("-fvisibility-inlines-hidden") if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_gcc_compiler_flags("-Werror") + add_gcc_compiler_flags("-Werror") endif() if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8.999) OR CMAKE_COMPILER_IS_CLANGXX) @@ -176,147 +176,144 @@ add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virt add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") if(WITH_ASAN) - if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR APPLE)) - message(FATAL_ERROR "WITH_ASAN is only supported on Linux / macOS at the moment.") - endif() - - add_gcc_compiler_flags("-fsanitize=address -DWITH_ASAN") - - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - if(NOT (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)) - add_gcc_compiler_flags("-fsanitize=leak -DWITH_LSAN") + if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR APPLE)) + message(FATAL_ERROR "WITH_ASAN is only supported on Linux / macOS at the moment.") + endif() + + add_gcc_compiler_flags("-fsanitize=address -DWITH_ASAN") + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(NOT (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)) + add_gcc_compiler_flags("-fsanitize=leak -DWITH_LSAN") + endif() endif() - endif() endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) -if (CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)") - add_gcc_compiler_flags("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") +if(CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)") + add_gcc_compiler_flags("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") endif() check_c_compiler_flag("-Werror=format-security -Werror=implicit-function-declaration" WERROR_C_AVAILABLE) check_cxx_compiler_flag("-Werror=format-security" WERROR_CXX_AVAILABLE) if(WERROR_C_AVAILABLE AND WERROR_CXX_AVAILABLE) - add_gcc_compiler_flags("-Werror=format-security") - add_gcc_compiler_cflags("-Werror=implicit-function-declaration") + add_gcc_compiler_flags("-Werror=format-security") + add_gcc_compiler_cflags("-Werror=implicit-function-declaration") endif() if(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align") endif() if(CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-align") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-align") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - if (CMAKE_COMPILER_IS_CLANGXX) - add_gcc_compiler_flags("-Qunused-arguments") - endif() - add_gcc_compiler_flags("-pie -fPIE") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now") + if(CMAKE_COMPILER_IS_CLANGXX) + add_gcc_compiler_flags("-Qunused-arguments") + endif() + add_gcc_compiler_flags("-pie -fPIE") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now") endif() add_gcc_compiler_cflags("-std=c99") add_gcc_compiler_cxxflags("-std=c++11") if(APPLE) - add_gcc_compiler_cxxflags("-stdlib=libc++") + add_gcc_compiler_cxxflags("-stdlib=libc++") endif() if(WITH_DEV_BUILD) - add_definitions(-DQT_DEPRECATED_WARNINGS -DGCRYPT_NO_DEPRECATED) + add_definitions(-DQT_DEPRECATED_WARNINGS -DGCRYPT_NO_DEPRECATED) endif() if(MINGW) - set(CMAKE_RC_COMPILER_INIT windres) - enable_language(RC) - set(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") - if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - # Enable DEP and ASLR - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") - # Enable high entropy ASLR for 64-bit builds - if(NOT IS_32BIT) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va") + set(CMAKE_RC_COMPILER_INIT windres) + enable_language(RC) + set(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") + if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + # Enable DEP and ASLR + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") + # Enable high entropy ASLR for 64-bit builds + if(NOT IS_32BIT) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va") + endif() endif() - endif() endif() if(APPLE AND WITH_APP_BUNDLE OR MINGW) - set(PROGNAME KeePassXC) + set(PROGNAME KeePassXC) else() - set(PROGNAME keepassxc) + set(PROGNAME keepassxc) endif() if(APPLE AND WITH_APP_BUNDLE AND "${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") - set(CMAKE_INSTALL_PREFIX "/Applications") - set(CMAKE_INSTALL_MANDIR "/usr/local/share/man") + set(CMAKE_INSTALL_PREFIX "/Applications") + set(CMAKE_INSTALL_MANDIR "/usr/local/share/man") endif() if(MINGW) - set(CLI_INSTALL_DIR ".") - set(PROXY_INSTALL_DIR ".") - set(BIN_INSTALL_DIR ".") - set(PLUGIN_INSTALL_DIR ".") - set(DATA_INSTALL_DIR "share") + set(CLI_INSTALL_DIR ".") + set(PROXY_INSTALL_DIR ".") + set(BIN_INSTALL_DIR ".") + set(PLUGIN_INSTALL_DIR ".") + set(DATA_INSTALL_DIR "share") elseif(APPLE AND WITH_APP_BUNDLE) - set(CLI_INSTALL_DIR "/usr/local/bin") - set(PROXY_INSTALL_DIR "/usr/local/bin") - set(BIN_INSTALL_DIR ".") - set(PLUGIN_INSTALL_DIR "${PROGNAME}.app/Contents/PlugIns") - set(DATA_INSTALL_DIR "${PROGNAME}.app/Contents/Resources") + set(CLI_INSTALL_DIR "/usr/local/bin") + set(PROXY_INSTALL_DIR "/usr/local/bin") + set(BIN_INSTALL_DIR ".") + set(PLUGIN_INSTALL_DIR "${PROGNAME}.app/Contents/PlugIns") + set(DATA_INSTALL_DIR "${PROGNAME}.app/Contents/Resources") else() - include(GNUInstallDirs) + include(GNUInstallDirs) - set(CLI_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") - set(PROXY_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") - set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") - set(PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/keepassxc") - set(DATA_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/keepassxc") + set(CLI_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") + set(PROXY_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") + set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") + set(PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/keepassxc") + set(DATA_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/keepassxc") endif() if(WITH_TESTS) - enable_testing() + enable_testing() endif(WITH_TESTS) if(WITH_COVERAGE) - # Include code coverage, use with -DCMAKE_BUILD_TYPE=Debug - include(CodeCoverage) - set(COVERAGE_GCOVR_EXCLUDES - "\\(.+/\\)?tests/.\\*" - ".\\*/moc_\\[^/\\]+\\.cpp" - ".\\*/ui_\\[^/\\]+\\.h" - "\\(.+/\\)?zxcvbn/.\\*") - append_coverage_compiler_flags() - setup_target_for_coverage_gcovr_html( - NAME coverage - EXECUTABLE $(MAKE) && $(MAKE) test - ) + # Include code coverage, use with -DCMAKE_BUILD_TYPE=Debug + include(CodeCoverage) + set(COVERAGE_GCOVR_EXCLUDES + "\\(.+/\\)?tests/.\\*" + ".\\*/moc_\\[^/\\]+\\.cpp" + ".\\*/ui_\\[^/\\]+\\.h" + "\\(.+/\\)?zxcvbn/.\\*") + append_coverage_compiler_flags() + setup_target_for_coverage_gcovr_html( + NAME coverage + EXECUTABLE $(MAKE) && $(MAKE) test + ) endif() include(CLangFormat) +set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools) if(UNIX AND NOT APPLE) - find_package(Qt5 COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools DBus REQUIRED) + find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus REQUIRED) elseif(APPLE) - find_package(Qt5 COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools REQUIRED - HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH - ) - find_package(Qt5 COMPONENTS MacExtras - HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH - ) + find_package(Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH) + find_package(Qt5 COMPONENTS MacExtras HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH) else() - find_package(Qt5 COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools REQUIRED) + find_package(Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED) endif() if(Qt5Core_VERSION VERSION_LESS "5.2.0") - message(FATAL_ERROR "Qt version 5.2.0 or higher is required") + message(FATAL_ERROR "Qt version 5.2.0 or higher is required") endif() get_filename_component(Qt5_PREFIX ${Qt5_DIR}/../../.. REALPATH) @@ -329,13 +326,13 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) if(APPLE) - set(CMAKE_MACOSX_RPATH TRUE) - find_program(MACDEPLOYQT_EXE macdeployqt HINTS ${Qt5_PREFIX}/bin ENV PATH) - if(NOT MACDEPLOYQT_EXE) - message(FATAL_ERROR "macdeployqt is required to build in macOS") - else() - message(STATUS "Using macdeployqt: ${MACDEPLOYQT_EXE}") - endif() + set(CMAKE_MACOSX_RPATH TRUE) + find_program(MACDEPLOYQT_EXE macdeployqt HINTS ${Qt5_PREFIX}/bin ENV PATH) + if(NOT MACDEPLOYQT_EXE) + message(FATAL_ERROR "macdeployqt is required to build in macOS") + else() + message(STATUS "Using macdeployqt: ${MACDEPLOYQT_EXE}") + endif() endif() # Debian sets the the build type to None for package builds. @@ -351,24 +348,24 @@ find_package(QREncode REQUIRED) set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR}) if(ZLIB_VERSION_STRING VERSION_LESS "1.2.0") - message(FATAL_ERROR "zlib 1.2.0 or higher is required to use the gzip format") + message(FATAL_ERROR "zlib 1.2.0 or higher is required to use the gzip format") endif() include_directories(SYSTEM ${ARGON2_INCLUDE_DIR}) # Optional if(WITH_XC_YUBIKEY) - find_package(YubiKey REQUIRED) + find_package(YubiKey REQUIRED) - include_directories(SYSTEM ${YUBIKEY_INCLUDE_DIRS}) + include_directories(SYSTEM ${YUBIKEY_INCLUDE_DIRS}) endif() if(UNIX) - check_cxx_source_compiles("#include + check_cxx_source_compiles("#include int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }" - HAVE_PR_SET_DUMPABLE) + HAVE_PR_SET_DUMPABLE) - check_cxx_source_compiles("#include + check_cxx_source_compiles("#include int main() { struct rlimit limit; limit.rlim_cur = 0; @@ -377,12 +374,12 @@ if(UNIX) return 0; }" HAVE_RLIMIT_CORE) - if(APPLE) - check_cxx_source_compiles("#include + if(APPLE) + check_cxx_source_compiles("#include #include int main() { ptrace(PT_DENY_ATTACH, 0, 0, 0); return 0; }" - HAVE_PT_DENY_ATTACH) - endif() + HAVE_PT_DENY_ATTACH) + endif() endif() include_directories(SYSTEM ${GCRYPT_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) @@ -392,14 +389,14 @@ include(FeatureSummary) add_subdirectory(src) add_subdirectory(share) if(WITH_TESTS) - add_subdirectory(tests) + add_subdirectory(tests) endif(WITH_TESTS) if(PRINT_SUMMARY) - # This will print ENABLED, REQUIRED and DISABLED - feature_summary(WHAT ALL) + # This will print ENABLED, REQUIRED and DISABLED + feature_summary(WHAT ALL) else() - # This will only print ENABLED and DISABLED feature - feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:") - feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:") + # This will only print ENABLED and DISABLED feature + feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:") + feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:") endif() diff --git a/cmake/CLangFormat.cmake b/cmake/CLangFormat.cmake index 8c26db93b..481bba27b 100644 --- a/cmake/CLangFormat.cmake +++ b/cmake/CLangFormat.cmake @@ -14,45 +14,43 @@ # along with this program. If not, see . set(EXCLUDED_DIRS - # third-party directories - zxcvbn/ - streams/QtIOCompressor - # objective-c directories - autotype/mac -) + # third-party directories + zxcvbn/ + streams/QtIOCompressor + # objective-c directories + autotype/mac) set(EXCLUDED_FILES - # third-party files - streams/qtiocompressor.cpp - streams/qtiocompressor.h - gui/KMessageWidget.h - gui/KMessageWidget.cpp - gui/MainWindowAdaptor.h - gui/MainWindowAdaptor.cpp - sshagent/bcrypt_pbkdf.cpp - sshagent/blf.h - sshagent/blowfish.c - tests/modeltest.cpp - tests/modeltest.h - # objective-c files - core/ScreenLockListenerMac.h - core/ScreenLockListenerMac.cpp -) + # third-party files + streams/qtiocompressor.cpp + streams/qtiocompressor.h + gui/KMessageWidget.h + gui/KMessageWidget.cpp + gui/MainWindowAdaptor.h + gui/MainWindowAdaptor.cpp + sshagent/bcrypt_pbkdf.cpp + sshagent/blf.h + sshagent/blowfish.c + tests/modeltest.cpp + tests/modeltest.h + # objective-c files + core/ScreenLockListenerMac.h + core/ScreenLockListenerMac.cpp) file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h) -foreach (SOURCE_FILE ${ALL_SOURCE_FILES}) - foreach (EXCLUDED_DIR ${EXCLUDED_DIRS}) +foreach(SOURCE_FILE ${ALL_SOURCE_FILES}) + foreach(EXCLUDED_DIR ${EXCLUDED_DIRS}) string(FIND ${SOURCE_FILE} ${EXCLUDED_DIR} SOURCE_FILE_EXCLUDED) - if (NOT ${SOURCE_FILE_EXCLUDED} EQUAL -1) + if(NOT ${SOURCE_FILE_EXCLUDED} EQUAL -1) list(REMOVE_ITEM ALL_SOURCE_FILES ${SOURCE_FILE}) - endif () - endforeach () - foreach (EXCLUDED_FILE ${EXCLUDED_FILES}) - if (${SOURCE_FILE} MATCHES ".*${EXCLUDED_FILE}$") + endif() + endforeach() + foreach(EXCLUDED_FILE ${EXCLUDED_FILES}) + if(${SOURCE_FILE} MATCHES ".*${EXCLUDED_FILE}$") list(REMOVE_ITEM ALL_SOURCE_FILES ${SOURCE_FILE}) - endif () - endforeach () -endforeach () + endif() + endforeach() +endforeach() add_custom_target( format diff --git a/cmake/FindArgon2.cmake b/cmake/FindArgon2.cmake index bb2f5811d..766e659b3 100644 --- a/cmake/FindArgon2.cmake +++ b/cmake/FindArgon2.cmake @@ -14,21 +14,21 @@ # along with this program. If not, see . find_path(ARGON2_INCLUDE_DIR argon2.h) -if (MINGW) - # find static library on Windows, and redefine used symbols to - # avoid definition name conflicts with libsodium - find_library(ARGON2_SYS_LIBRARIES libargon2.a) - message(STATUS "Patching libargon2...\n") - execute_process(COMMAND objcopy - --redefine-sym argon2_hash=libargon2_argon2_hash - --redefine-sym _argon2_hash=_libargon2_argon2_hash - --redefine-sym argon2_error_message=libargon2_argon2_error_message - --redefine-sym _argon2_error_message=_libargon2_argon2_error_message - ${ARGON2_SYS_LIBRARIES} ${CMAKE_BINARY_DIR}/libargon2_patched.a - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) - find_library(ARGON2_LIBRARIES libargon2_patched.a PATHS ${CMAKE_BINARY_DIR} NO_DEFAULT_PATH) +if(MINGW) + # find static library on Windows, and redefine used symbols to + # avoid definition name conflicts with libsodium + find_library(ARGON2_SYS_LIBRARIES libargon2.a) + message(STATUS "Patching libargon2...\n") + execute_process(COMMAND objcopy + --redefine-sym argon2_hash=libargon2_argon2_hash + --redefine-sym _argon2_hash=_libargon2_argon2_hash + --redefine-sym argon2_error_message=libargon2_argon2_error_message + --redefine-sym _argon2_error_message=_libargon2_argon2_error_message + ${ARGON2_SYS_LIBRARIES} ${CMAKE_BINARY_DIR}/libargon2_patched.a + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + find_library(ARGON2_LIBRARIES libargon2_patched.a PATHS ${CMAKE_BINARY_DIR} NO_DEFAULT_PATH) else() - find_library(ARGON2_LIBRARIES argon2) + find_library(ARGON2_LIBRARIES argon2) endif() mark_as_advanced(ARGON2_LIBRARIES ARGON2_INCLUDE_DIR) diff --git a/cmake/FindGcrypt.cmake b/cmake/FindGcrypt.cmake index 077570462..59c6f473a 100644 --- a/cmake/FindGcrypt.cmake +++ b/cmake/FindGcrypt.cmake @@ -22,7 +22,7 @@ mark_as_advanced(GCRYPT_LIBRARIES GCRYPT_INCLUDE_DIR) if(GCRYPT_INCLUDE_DIR AND EXISTS "${GCRYPT_INCLUDE_DIR}/gcrypt.h") file(STRINGS "${GCRYPT_INCLUDE_DIR}/gcrypt.h" GCRYPT_H REGEX "^#define GCRYPT_VERSION \"[^\"]*\"$") string(REGEX REPLACE "^.*GCRYPT_VERSION \"([0-9]+).*$" "\\1" GCRYPT_VERSION_MAJOR "${GCRYPT_H}") - string(REGEX REPLACE "^.*GCRYPT_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" GCRYPT_VERSION_MINOR "${GCRYPT_H}") + string(REGEX REPLACE "^.*GCRYPT_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" GCRYPT_VERSION_MINOR "${GCRYPT_H}") string(REGEX REPLACE "^.*GCRYPT_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" GCRYPT_VERSION_PATCH "${GCRYPT_H}") set(GCRYPT_VERSION_STRING "${GCRYPT_VERSION_MAJOR}.${GCRYPT_VERSION_MINOR}.${GCRYPT_VERSION_PATCH}") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b41b3878..56726e43d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2017 KeePassXC Team +# Copyright (C) 2018 KeePassXC Team # Copyright (C) 2010 Felix Geyer # # This program is free software: you can redistribute it and/or modify @@ -22,193 +22,191 @@ include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_HEAD) git_describe(GIT_DESCRIBE --long) -if (NOT GIT_HEAD OR NOT GIT_DESCRIBE) +if(NOT GIT_HEAD OR NOT GIT_DESCRIBE) set(GIT_HEAD "") set(GIT_DESCRIBE "") endif() find_library(ZXCVBN_LIBRARIES zxcvbn) if(NOT ZXCVBN_LIBRARIES) - add_library(zxcvbn STATIC zxcvbn/zxcvbn.c) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/zxcvbn) - set(ZXCVBN_LIBRARIES zxcvbn) + add_library(zxcvbn STATIC zxcvbn/zxcvbn.c) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/zxcvbn) + set(ZXCVBN_LIBRARIES zxcvbn) endif(NOT ZXCVBN_LIBRARIES) configure_file(version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version.h @ONLY) set(keepassx_SOURCES - core/AutoTypeAssociations.cpp - core/AsyncTask.h - core/AutoTypeMatch.cpp - core/Compare.cpp - core/Config.cpp - core/CsvParser.cpp - core/CustomData.cpp - core/Database.cpp - core/DatabaseIcons.cpp - core/Entry.cpp - core/EntryAttachments.cpp - core/EntryAttributes.cpp - core/EntrySearcher.cpp - core/FilePath.cpp - core/Bootstrap.cpp - core/Global.h - core/Group.cpp - core/InactivityTimer.cpp - core/ListDeleter.h - core/Merger.cpp - core/Metadata.cpp - core/PasswordGenerator.cpp - core/PassphraseGenerator.cpp - core/SignalMultiplexer.cpp - core/ScreenLockListener.cpp - core/ScreenLockListener.h - core/ScreenLockListenerPrivate.h - core/ScreenLockListenerPrivate.cpp - core/TimeDelta.cpp - core/TimeInfo.cpp - core/Clock.cpp - core/Tools.cpp - core/Translator.cpp - core/Base32.h - core/Base32.cpp - cli/Utils.cpp - cli/Utils.h - crypto/Crypto.cpp - crypto/CryptoHash.cpp - crypto/Random.cpp - crypto/SymmetricCipher.cpp - crypto/SymmetricCipherBackend.h - crypto/SymmetricCipherGcrypt.cpp - crypto/kdf/Kdf.cpp - crypto/kdf/Kdf_p.h - crypto/kdf/AesKdf.cpp - crypto/kdf/Argon2Kdf.cpp - format/CsvExporter.cpp - format/KeePass1.h - format/KeePass1Reader.cpp - format/KeePass2.cpp - format/KeePass2RandomStream.cpp - format/KdbxReader.cpp - format/KdbxWriter.cpp - format/KdbxXmlReader.cpp - format/KeePass2Reader.cpp - format/KeePass2Writer.cpp - format/Kdbx3Reader.cpp - format/Kdbx3Writer.cpp - format/Kdbx4Reader.cpp - format/Kdbx4Writer.cpp - format/KdbxXmlWriter.cpp - gui/AboutDialog.cpp - gui/Application.cpp - gui/CategoryListWidget.cpp - gui/Clipboard.cpp - gui/CloneDialog.cpp - gui/DatabaseOpenWidget.cpp - gui/DatabaseTabWidget.cpp - gui/DatabaseWidget.cpp - gui/DatabaseWidgetStateSync.cpp - gui/EntryPreviewWidget.cpp - gui/DialogyWidget.cpp - gui/DragTabBar.cpp - gui/EditWidget.cpp - gui/EditWidgetIcons.cpp - gui/EditWidgetProperties.cpp - gui/FileDialog.cpp - gui/Font.cpp - gui/IconModels.cpp - gui/KeePass1OpenWidget.cpp - gui/KMessageWidget.cpp - gui/LineEdit.cpp - gui/MainWindow.cpp - gui/MessageBox.cpp - gui/MessageWidget.cpp - gui/PasswordEdit.cpp - gui/PasswordGeneratorWidget.cpp - gui/ApplicationSettingsWidget.cpp - gui/SearchWidget.cpp - gui/SortFilterHideProxyModel.cpp - gui/SquareSvgWidget.cpp - gui/TotpSetupDialog.cpp - gui/TotpDialog.cpp - gui/TotpExportSettingsDialog.cpp - gui/UnlockDatabaseWidget.cpp - gui/UnlockDatabaseDialog.cpp - gui/WelcomeWidget.cpp - gui/widgets/ElidedLabel.cpp - gui/csvImport/CsvImportWidget.cpp - gui/csvImport/CsvImportWizard.cpp - gui/csvImport/CsvParserModel.cpp - gui/entry/AutoTypeAssociationsModel.cpp - gui/entry/AutoTypeMatchModel.cpp - gui/entry/AutoTypeMatchView.cpp - gui/entry/EditEntryWidget.cpp - gui/entry/EditEntryWidget_p.h - gui/entry/EntryAttachmentsModel.cpp - gui/entry/EntryAttachmentsWidget.cpp - gui/entry/EntryAttributesModel.cpp - gui/entry/EntryHistoryModel.cpp - gui/entry/EntryModel.cpp - gui/entry/EntryView.cpp - gui/group/EditGroupWidget.cpp - gui/group/GroupModel.cpp - gui/group/GroupView.cpp - gui/masterkey/KeyComponentWidget.cpp - gui/masterkey/PasswordEditWidget.cpp - gui/masterkey/YubiKeyEditWidget.cpp - gui/masterkey/KeyFileEditWidget.cpp - gui/dbsettings/DatabaseSettingsWidget.cpp - gui/dbsettings/DatabaseSettingsDialog.cpp - gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp - gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp - gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp - gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp - gui/settings/SettingsWidget.cpp - gui/wizard/NewDatabaseWizard.cpp - gui/wizard/NewDatabaseWizardPage.cpp - gui/wizard/NewDatabaseWizardPageMetaData.cpp - gui/wizard/NewDatabaseWizardPageEncryption.cpp - gui/wizard/NewDatabaseWizardPageMasterKey.cpp - keys/ChallengeResponseKey.h - keys/CompositeKey.cpp - keys/drivers/YubiKey.h - keys/FileKey.cpp - keys/Key.h - keys/PasswordKey.cpp - keys/YkChallengeResponseKey.cpp - streams/HashedBlockStream.cpp - streams/HmacBlockStream.cpp - streams/LayeredStream.cpp - streams/qtiocompressor.cpp - streams/StoreDataStream.cpp - streams/SymmetricCipherStream.cpp - totp/totp.h - totp/totp.cpp) + core/AutoTypeAssociations.cpp + core/AsyncTask.h + core/AutoTypeMatch.cpp + core/Compare.cpp + core/Config.cpp + core/CsvParser.cpp + core/CustomData.cpp + core/Database.cpp + core/DatabaseIcons.cpp + core/Entry.cpp + core/EntryAttachments.cpp + core/EntryAttributes.cpp + core/EntrySearcher.cpp + core/FilePath.cpp + core/Bootstrap.cpp + core/Global.h + core/Group.cpp + core/InactivityTimer.cpp + core/ListDeleter.h + core/Merger.cpp + core/Metadata.cpp + core/PasswordGenerator.cpp + core/PassphraseGenerator.cpp + core/SignalMultiplexer.cpp + core/ScreenLockListener.cpp + core/ScreenLockListener.h + core/ScreenLockListenerPrivate.h + core/ScreenLockListenerPrivate.cpp + core/TimeDelta.cpp + core/TimeInfo.cpp + core/Clock.cpp + core/Tools.cpp + core/Translator.cpp + core/Base32.h + core/Base32.cpp + cli/Utils.cpp + cli/Utils.h + crypto/Crypto.cpp + crypto/CryptoHash.cpp + crypto/Random.cpp + crypto/SymmetricCipher.cpp + crypto/SymmetricCipherBackend.h + crypto/SymmetricCipherGcrypt.cpp + crypto/kdf/Kdf.cpp + crypto/kdf/Kdf_p.h + crypto/kdf/AesKdf.cpp + crypto/kdf/Argon2Kdf.cpp + format/CsvExporter.cpp + format/KeePass1.h + format/KeePass1Reader.cpp + format/KeePass2.cpp + format/KeePass2RandomStream.cpp + format/KdbxReader.cpp + format/KdbxWriter.cpp + format/KdbxXmlReader.cpp + format/KeePass2Reader.cpp + format/KeePass2Writer.cpp + format/Kdbx3Reader.cpp + format/Kdbx3Writer.cpp + format/Kdbx4Reader.cpp + format/Kdbx4Writer.cpp + format/KdbxXmlWriter.cpp + gui/AboutDialog.cpp + gui/Application.cpp + gui/CategoryListWidget.cpp + gui/Clipboard.cpp + gui/CloneDialog.cpp + gui/DatabaseOpenWidget.cpp + gui/DatabaseTabWidget.cpp + gui/DatabaseWidget.cpp + gui/DatabaseWidgetStateSync.cpp + gui/EntryPreviewWidget.cpp + gui/DialogyWidget.cpp + gui/DragTabBar.cpp + gui/EditWidget.cpp + gui/EditWidgetIcons.cpp + gui/EditWidgetProperties.cpp + gui/FileDialog.cpp + gui/Font.cpp + gui/IconModels.cpp + gui/KeePass1OpenWidget.cpp + gui/KMessageWidget.cpp + gui/LineEdit.cpp + gui/MainWindow.cpp + gui/MessageBox.cpp + gui/MessageWidget.cpp + gui/PasswordEdit.cpp + gui/PasswordGeneratorWidget.cpp + gui/ApplicationSettingsWidget.cpp + gui/SearchWidget.cpp + gui/SortFilterHideProxyModel.cpp + gui/SquareSvgWidget.cpp + gui/TotpSetupDialog.cpp + gui/TotpDialog.cpp + gui/TotpExportSettingsDialog.cpp + gui/UnlockDatabaseWidget.cpp + gui/UnlockDatabaseDialog.cpp + gui/WelcomeWidget.cpp + gui/widgets/ElidedLabel.cpp + gui/csvImport/CsvImportWidget.cpp + gui/csvImport/CsvImportWizard.cpp + gui/csvImport/CsvParserModel.cpp + gui/entry/AutoTypeAssociationsModel.cpp + gui/entry/AutoTypeMatchModel.cpp + gui/entry/AutoTypeMatchView.cpp + gui/entry/EditEntryWidget.cpp + gui/entry/EditEntryWidget_p.h + gui/entry/EntryAttachmentsModel.cpp + gui/entry/EntryAttachmentsWidget.cpp + gui/entry/EntryAttributesModel.cpp + gui/entry/EntryHistoryModel.cpp + gui/entry/EntryModel.cpp + gui/entry/EntryView.cpp + gui/group/EditGroupWidget.cpp + gui/group/GroupModel.cpp + gui/group/GroupView.cpp + gui/masterkey/KeyComponentWidget.cpp + gui/masterkey/PasswordEditWidget.cpp + gui/masterkey/YubiKeyEditWidget.cpp + gui/masterkey/KeyFileEditWidget.cpp + gui/dbsettings/DatabaseSettingsWidget.cpp + gui/dbsettings/DatabaseSettingsDialog.cpp + gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp + gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp + gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp + gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp + gui/settings/SettingsWidget.cpp + gui/wizard/NewDatabaseWizard.cpp + gui/wizard/NewDatabaseWizardPage.cpp + gui/wizard/NewDatabaseWizardPageMetaData.cpp + gui/wizard/NewDatabaseWizardPageEncryption.cpp + gui/wizard/NewDatabaseWizardPageMasterKey.cpp + keys/ChallengeResponseKey.h + keys/CompositeKey.cpp + keys/drivers/YubiKey.h + keys/FileKey.cpp + keys/Key.h + keys/PasswordKey.cpp + keys/YkChallengeResponseKey.cpp + streams/HashedBlockStream.cpp + streams/HmacBlockStream.cpp + streams/LayeredStream.cpp + streams/qtiocompressor.cpp + streams/StoreDataStream.cpp + streams/SymmetricCipherStream.cpp + totp/totp.h + totp/totp.cpp) if(APPLE) - set(keepassx_SOURCES ${keepassx_SOURCES} - core/ScreenLockListenerMac.h - core/ScreenLockListenerMac.cpp - core/MacPasteboard.h - core/MacPasteboard.cpp - ) + set(keepassx_SOURCES + ${keepassx_SOURCES} + core/ScreenLockListenerMac.h + core/ScreenLockListenerMac.cpp + core/MacPasteboard.h + core/MacPasteboard.cpp) endif() if(UNIX AND NOT APPLE) - set(keepassx_SOURCES ${keepassx_SOURCES} - core/ScreenLockListenerDBus.h - core/ScreenLockListenerDBus.cpp - gui/MainWindowAdaptor.cpp - ) + set(keepassx_SOURCES + ${keepassx_SOURCES} + core/ScreenLockListenerDBus.h + core/ScreenLockListenerDBus.cpp + gui/MainWindowAdaptor.cpp) endif() if(MINGW) - set(keepassx_SOURCES ${keepassx_SOURCES} - core/ScreenLockListenerWin.h - core/ScreenLockListenerWin.cpp - ) + set(keepassx_SOURCES + ${keepassx_SOURCES} + core/ScreenLockListenerWin.h + core/ScreenLockListenerWin.cpp) endif() -set(keepassx_SOURCES_MAINEXE - main.cpp -) +set(keepassx_SOURCES_MAINEXE main.cpp) add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing") add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)") @@ -237,32 +235,29 @@ if(WITH_XC_SSHAGENT) endif() set(autotype_SOURCES - core/Tools.cpp - autotype/AutoType.cpp - autotype/AutoTypeAction.cpp - autotype/AutoTypePlatformPlugin.h - autotype/AutoTypeSelectDialog.cpp - autotype/AutoTypeSelectView.cpp - autotype/ShortcutWidget.cpp - autotype/WildcardMatcher.cpp - autotype/WindowSelectComboBox.cpp - autotype/test/AutoTypeTestInterface.h -) + core/Tools.cpp + autotype/AutoType.cpp + autotype/AutoTypeAction.cpp + autotype/AutoTypePlatformPlugin.h + autotype/AutoTypeSelectDialog.cpp + autotype/AutoTypeSelectView.cpp + autotype/ShortcutWidget.cpp + autotype/WildcardMatcher.cpp + autotype/WindowSelectComboBox.cpp + autotype/test/AutoTypeTestInterface.h) if(MINGW) - set(keepassx_SOURCES_MAINEXE - ${keepassx_SOURCES_MAINEXE} - ${CMAKE_SOURCE_DIR}/share/windows/icon.rc) + set(keepassx_SOURCES_MAINEXE ${keepassx_SOURCES_MAINEXE} ${CMAKE_SOURCE_DIR}/share/windows/icon.rc) endif() if(WITH_XC_YUBIKEY) - list(APPEND keepassx_SOURCES keys/drivers/YubiKey.cpp) + list(APPEND keepassx_SOURCES keys/drivers/YubiKey.cpp) else() - list(APPEND keepassx_SOURCES keys/drivers/YubiKeyStub.cpp) + list(APPEND keepassx_SOURCES keys/drivers/YubiKeyStub.cpp) endif() if(WITH_XC_TOUCHID) - list(APPEND keepassx_SOURCES touchid/TouchID.mm) + list(APPEND keepassx_SOURCES touchid/TouchID.mm) endif() add_library(autotype STATIC ${autotype_SOURCES}) @@ -272,35 +267,35 @@ add_library(keepassx_core STATIC ${keepassx_SOURCES}) set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE) target_link_libraries(keepassx_core - autotype - ${keepassxcbrowser_LIB} - ${sshagent_LIB} - ${qrcode_LIB} - Qt5::Core - Qt5::Concurrent - Qt5::Network - Qt5::Widgets - ${CURL_LIBRARIES} - ${YUBIKEY_LIBRARIES} - ${ZXCVBN_LIBRARIES} - ${ARGON2_LIBRARIES} - ${GCRYPT_LIBRARIES} - ${GPGERROR_LIBRARIES} - ${YUBIKEY_LIBRARIES} - ${ZLIB_LIBRARIES} - ${ZXCVBN_LIBRARIES}) + autotype + ${keepassxcbrowser_LIB} + ${sshagent_LIB} + ${qrcode_LIB} + Qt5::Core + Qt5::Concurrent + Qt5::Network + Qt5::Widgets + ${CURL_LIBRARIES} + ${YUBIKEY_LIBRARIES} + ${ZXCVBN_LIBRARIES} + ${ARGON2_LIBRARIES} + ${GCRYPT_LIBRARIES} + ${GPGERROR_LIBRARIES} + ${YUBIKEY_LIBRARIES} + ${ZLIB_LIBRARIES} + ${ZXCVBN_LIBRARIES}) if(APPLE) target_link_libraries(keepassx_core "-framework Foundation") if(Qt5MacExtras_FOUND) - target_link_libraries(keepassx_core Qt5::MacExtras) + target_link_libraries(keepassx_core Qt5::MacExtras) endif() if(WITH_XC_TOUCHID) - target_link_libraries(keepassx_core "-framework Security") - target_link_libraries(keepassx_core "-framework LocalAuthentication") + target_link_libraries(keepassx_core "-framework Security") + target_link_libraries(keepassx_core "-framework LocalAuthentication") endif() endif() -if (UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE) target_link_libraries(keepassx_core Qt5::DBus) endif() if(MINGW) @@ -308,15 +303,15 @@ if(MINGW) endif() if(MINGW) - include(GenerateProductVersion) - generate_product_version( - WIN32_ProductVersionFiles - NAME "KeePassXC" - COMPANY_NAME "KeePassXC Team" - VERSION_MAJOR ${KEEPASSXC_VERSION_MAJOR} - VERSION_MINOR ${KEEPASSXC_VERSION_MINOR} - VERSION_PATCH ${KEEPASSXC_VERSION_PATCH} - ) + include(GenerateProductVersion) + generate_product_version( + WIN32_ProductVersionFiles + NAME "KeePassXC" + COMPANY_NAME "KeePassXC Team" + VERSION_MAJOR ${KEEPASSXC_VERSION_MAJOR} + VERSION_MINOR ${KEEPASSXC_VERSION_MINOR} + VERSION_PATCH ${KEEPASSXC_VERSION_PATCH} + ) endif() add_executable(${PROGNAME} WIN32 ${keepassx_SOURCES_MAINEXE} ${WIN32_ProductVersionFiles}) @@ -325,116 +320,116 @@ target_link_libraries(${PROGNAME} keepassx_core) set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON) if(APPLE AND WITH_APP_BUNDLE) - configure_file(${CMAKE_SOURCE_DIR}/share/macosx/Info.plist.cmake ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) - set_target_properties(${PROGNAME} PROPERTIES - MACOSX_BUNDLE ON - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) + configure_file(${CMAKE_SOURCE_DIR}/share/macosx/Info.plist.cmake ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) + set_target_properties(${PROGNAME} PROPERTIES + MACOSX_BUNDLE ON + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) - if(WITH_XC_TOUCHID) - set_target_properties(${PROGNAME} PROPERTIES - CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/share/macosx/keepassxc.entitlements" ) - endif() + if(WITH_XC_TOUCHID) + set_target_properties(${PROGNAME} PROPERTIES + CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/share/macosx/keepassxc.entitlements") + endif() - if(QT_MAC_USE_COCOA AND EXISTS "${QT_LIBRARY_DIR}/Resources/qt_menu.nib") - install(DIRECTORY "${QT_LIBRARY_DIR}/Resources/qt_menu.nib" - DESTINATION "${DATA_INSTALL_DIR}") - endif() + if(QT_MAC_USE_COCOA AND EXISTS "${QT_LIBRARY_DIR}/Resources/qt_menu.nib") + install(DIRECTORY "${QT_LIBRARY_DIR}/Resources/qt_menu.nib" + DESTINATION "${DATA_INSTALL_DIR}") + endif() - set(CPACK_GENERATOR "DragNDrop") - set(CPACK_DMG_FORMAT "UDBZ") - set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/share/macosx/DS_Store.in") - set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/share/macosx/background.tiff") - set(CPACK_DMG_VOLUME_NAME "${PROGNAME}") - set(CPACK_SYSTEM_NAME "OSX") - set(CPACK_STRIP_FILES ON) - set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION}") - include(CPack) + set(CPACK_GENERATOR "DragNDrop") + set(CPACK_DMG_FORMAT "UDBZ") + set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/share/macosx/DS_Store.in") + set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/share/macosx/background.tiff") + set(CPACK_DMG_VOLUME_NAME "${PROGNAME}") + set(CPACK_SYSTEM_NAME "OSX") + set(CPACK_STRIP_FILES ON) + set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION}") + include(CPack) - add_custom_command(TARGET ${PROGNAME} - POST_BUILD - COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src - COMMENT "Deploying app bundle") + add_custom_command(TARGET ${PROGNAME} + POST_BUILD + COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src + COMMENT "Deploying app bundle") endif() install(TARGETS ${PROGNAME} - BUNDLE DESTINATION . COMPONENT Runtime - RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT Runtime) + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT Runtime) if(MINGW) - if(${CMAKE_SIZEOF_VOID_P} EQUAL "8") - set(OUTPUT_FILE_POSTFIX "Win64") - else() - set(OUTPUT_FILE_POSTFIX "Win32") - endif() + if(${CMAKE_SIZEOF_VOID_P} EQUAL "8") + set(OUTPUT_FILE_POSTFIX "Win64") + else() + set(OUTPUT_FILE_POSTFIX "Win32") + endif() - # We have to copy the license file in the configuration phase. - # CMake checks that CPACK_RESOURCE_FILE_LICENSE actually exists and - # we have to copy it because WiX needs it to have a .txt extension. - execute_process(COMMAND ${CMAKE_COMMAND} -E copy - "${CMAKE_SOURCE_DIR}/LICENSE.GPL-2" - "${CMAKE_CURRENT_BINARY_DIR}/INSTALLER_LICENSE.txt") + # We have to copy the license file in the configuration phase. + # CMake checks that CPACK_RESOURCE_FILE_LICENSE actually exists and + # we have to copy it because WiX needs it to have a .txt extension. + execute_process(COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_SOURCE_DIR}/LICENSE.GPL-2" + "${CMAKE_CURRENT_BINARY_DIR}/INSTALLER_LICENSE.txt") - string(REGEX REPLACE "-snapshot$" "" KEEPASSXC_VERSION_CLEAN ${KEEPASSXC_VERSION}) + string(REGEX REPLACE "-snapshot$" "" KEEPASSXC_VERSION_CLEAN ${KEEPASSXC_VERSION}) - set(CPACK_GENERATOR "ZIP;NSIS") - set(CPACK_STRIP_FILES OFF) - set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION}-${OUTPUT_FILE_POSTFIX}") - set(CPACK_PACKAGE_INSTALL_DIRECTORY ${PROGNAME}) - set(CPACK_PACKAGE_VERSION ${KEEPASSXC_VERSION_CLEAN}) - set(CPACK_PACKAGE_VENDOR "${PROGNAME} Team") - string(REGEX REPLACE "/" "\\\\\\\\" CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/share/windows/installer-header.bmp") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_BINARY_DIR}/INSTALLER_LICENSE.txt") - set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) - set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/share/windows/keepassxc.ico") - set(CPACK_NSIS_MUI_UNIICON "${CPACK_NSIS_MUI_ICON}") - set(CPACK_NSIS_INSTALLED_ICON_NAME "\\\\${PROGNAME}.exe") - string(REGEX REPLACE "/" "\\\\\\\\" CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP "${CMAKE_SOURCE_DIR}/share/windows/installer-wizard.bmp") - set(CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP "${CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP}") - set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${PROGNAME}.lnk' '$INSTDIR\\\\${PROGNAME}.exe'") - set(CPACK_NSIS_DELETE_ICONS_EXTRA "Delete '$SMPROGRAMS\\\\$START_MENU\\\\${PROGNAME}.lnk'") - set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "ExecWait 'Taskkill /IM KeePassXC.exe'") - set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS}\nExecWait 'Taskkill /IM keepassxc-proxy.exe /F'") - set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "ExecWait 'Taskkill /IM KeePassXC.exe'") - set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}\nExecWait 'Taskkill /IM keepassxc-proxy.exe /F'") - set(CPACK_NSIS_URL_INFO_ABOUT "https://keepassxc.org") - set(CPACK_NSIS_DISPLAY_NAME ${PROGNAME}) - set(CPACK_NSIS_PACKAGE_NAME "${PROGNAME} v${KEEPASSXC_VERSION}") - set(CPACK_NSIS_MUI_FINISHPAGE_RUN "../${PROGNAME}.exe") - set(CPACK_WIX_UPGRADE_GUID 88785A72-3EAE-4F29-89E3-BC6B19BA9A5B) - set(CPACK_WIX_PRODUCT_ICON "${CMAKE_SOURCE_DIR}/share/windows/keepassxc.ico") - set(CPACK_WIX_UI_BANNER "${CMAKE_SOURCE_DIR}/share/windows/wix-banner.bmp") - set(CPACK_WIX_UI_DIALOG "${CMAKE_SOURCE_DIR}/share/windows/wix-dialog.bmp") - set(CPACK_WIX_TEMPLATE "${CMAKE_SOURCE_DIR}/share/windows/wix-template.xml") - set(CPACK_WIX_PATCH_FILE "${CMAKE_SOURCE_DIR}/share/windows/wix-patch.xml") - set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "https://keepassxc.org") - set(CPACK_WIX_EXTENSIONS "WixUtilExtension.dll") - include(CPack) + set(CPACK_GENERATOR "ZIP;NSIS") + set(CPACK_STRIP_FILES OFF) + set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION}-${OUTPUT_FILE_POSTFIX}") + set(CPACK_PACKAGE_INSTALL_DIRECTORY ${PROGNAME}) + set(CPACK_PACKAGE_VERSION ${KEEPASSXC_VERSION_CLEAN}) + set(CPACK_PACKAGE_VENDOR "${PROGNAME} Team") + string(REGEX REPLACE "/" "\\\\\\\\" CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/share/windows/installer-header.bmp") + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_BINARY_DIR}/INSTALLER_LICENSE.txt") + set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) + set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/share/windows/keepassxc.ico") + set(CPACK_NSIS_MUI_UNIICON "${CPACK_NSIS_MUI_ICON}") + set(CPACK_NSIS_INSTALLED_ICON_NAME "\\\\${PROGNAME}.exe") + string(REGEX REPLACE "/" "\\\\\\\\" CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP "${CMAKE_SOURCE_DIR}/share/windows/installer-wizard.bmp") + set(CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP "${CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP}") + set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${PROGNAME}.lnk' '$INSTDIR\\\\${PROGNAME}.exe'") + set(CPACK_NSIS_DELETE_ICONS_EXTRA "Delete '$SMPROGRAMS\\\\$START_MENU\\\\${PROGNAME}.lnk'") + set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "ExecWait 'Taskkill /IM KeePassXC.exe'") + set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS}\nExecWait 'Taskkill /IM keepassxc-proxy.exe /F'") + set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "ExecWait 'Taskkill /IM KeePassXC.exe'") + set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}\nExecWait 'Taskkill /IM keepassxc-proxy.exe /F'") + set(CPACK_NSIS_URL_INFO_ABOUT "https://keepassxc.org") + set(CPACK_NSIS_DISPLAY_NAME ${PROGNAME}) + set(CPACK_NSIS_PACKAGE_NAME "${PROGNAME} v${KEEPASSXC_VERSION}") + set(CPACK_NSIS_MUI_FINISHPAGE_RUN "../${PROGNAME}.exe") + set(CPACK_WIX_UPGRADE_GUID 88785A72-3EAE-4F29-89E3-BC6B19BA9A5B) + set(CPACK_WIX_PRODUCT_ICON "${CMAKE_SOURCE_DIR}/share/windows/keepassxc.ico") + set(CPACK_WIX_UI_BANNER "${CMAKE_SOURCE_DIR}/share/windows/wix-banner.bmp") + set(CPACK_WIX_UI_DIALOG "${CMAKE_SOURCE_DIR}/share/windows/wix-dialog.bmp") + set(CPACK_WIX_TEMPLATE "${CMAKE_SOURCE_DIR}/share/windows/wix-template.xml") + set(CPACK_WIX_PATCH_FILE "${CMAKE_SOURCE_DIR}/share/windows/wix-patch.xml") + set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "https://keepassxc.org") + set(CPACK_WIX_EXTENSIONS "WixUtilExtension.dll") + include(CPack) - install(CODE " - set(gp_tool \"objdump\") - " COMPONENT Runtime) + install(CODE " + set(gp_tool \"objdump\") + " COMPONENT Runtime) - include(DeployQt4) - install_qt4_executable(${PROGNAME}.exe) - - # install Qt5 plugins - set(PLUGINS_DIR ${Qt5_PREFIX}/share/qt5/plugins) - install(FILES - ${PLUGINS_DIR}/platforms/qwindows$<$:d>.dll - ${PLUGINS_DIR}/platforms/qdirect2d$<$:d>.dll - DESTINATION "platforms") - install(FILES ${PLUGINS_DIR}/styles/qwindowsvistastyle$<$:d>.dll DESTINATION "styles") - install(FILES ${PLUGINS_DIR}/platforminputcontexts/qtvirtualkeyboardplugin$<$:d>.dll DESTINATION "platforminputcontexts") - install(FILES ${PLUGINS_DIR}/iconengines/qsvgicon$<$:d>.dll DESTINATION "iconengines") - install(FILES - ${PLUGINS_DIR}/imageformats/qgif$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qicns$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qico$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qjpeg$<$:d>.dll - ${PLUGINS_DIR}/imageformats/qwebp$<$:d>.dll - DESTINATION "imageformats") + include(DeployQt4) + install_qt4_executable(${PROGNAME}.exe) - # install CA cert chains - install(FILES ${Qt5_PREFIX}/ssl/certs/ca-bundle.crt DESTINATION "ssl/certs") + # install Qt5 plugins + set(PLUGINS_DIR ${Qt5_PREFIX}/share/qt5/plugins) + install(FILES + ${PLUGINS_DIR}/platforms/qwindows$<$:d>.dll + ${PLUGINS_DIR}/platforms/qdirect2d$<$:d>.dll + DESTINATION "platforms") + install(FILES ${PLUGINS_DIR}/styles/qwindowsvistastyle$<$:d>.dll DESTINATION "styles") + install(FILES ${PLUGINS_DIR}/platforminputcontexts/qtvirtualkeyboardplugin$<$:d>.dll DESTINATION "platforminputcontexts") + install(FILES ${PLUGINS_DIR}/iconengines/qsvgicon$<$:d>.dll DESTINATION "iconengines") + install(FILES + ${PLUGINS_DIR}/imageformats/qgif$<$:d>.dll + ${PLUGINS_DIR}/imageformats/qicns$<$:d>.dll + ${PLUGINS_DIR}/imageformats/qico$<$:d>.dll + ${PLUGINS_DIR}/imageformats/qjpeg$<$:d>.dll + ${PLUGINS_DIR}/imageformats/qwebp$<$:d>.dll + DESTINATION "imageformats") + + # install CA cert chains + install(FILES ${Qt5_PREFIX}/ssl/certs/ca-bundle.crt DESTINATION "ssl/certs") endif() diff --git a/src/autotype/CMakeLists.txt b/src/autotype/CMakeLists.txt index 4b3610538..df0483a08 100644 --- a/src/autotype/CMakeLists.txt +++ b/src/autotype/CMakeLists.txt @@ -1,23 +1,23 @@ if(WITH_XC_AUTOTYPE) - if(UNIX AND NOT APPLE) - find_package(X11) - find_package(Qt5X11Extras 5.2) - if(PRINT_SUMMARY) - add_feature_info(libXi X11_Xi_FOUND "The X11 Xi Protocol library is required for auto-type") - add_feature_info(libXtst X11_XTest_FOUND "The X11 XTEST Protocol library is required for auto-type") - add_feature_info(Qt5X11Extras Qt5X11Extras_FOUND "The Qt5X11Extras library is required for auto-type") + if(UNIX AND NOT APPLE) + find_package(X11) + find_package(Qt5X11Extras 5.2) + if(PRINT_SUMMARY) + add_feature_info(libXi X11_Xi_FOUND "The X11 Xi Protocol library is required for auto-type") + add_feature_info(libXtst X11_XTest_FOUND "The X11 XTEST Protocol library is required for auto-type") + add_feature_info(Qt5X11Extras Qt5X11Extras_FOUND "The Qt5X11Extras library is required for auto-type") + endif() + + if(X11_FOUND AND X11_Xi_FOUND AND X11_XTest_FOUND AND Qt5X11Extras_FOUND) + add_subdirectory(xcb) + endif() + elseif(APPLE) + add_subdirectory(mac) + elseif(WIN32) + add_subdirectory(windows) endif() - if(X11_FOUND AND X11_Xi_FOUND AND X11_XTest_FOUND AND Qt5X11Extras_FOUND) - add_subdirectory(xcb) + if(WITH_TESTS) + add_subdirectory(test) endif() - elseif(APPLE) - add_subdirectory(mac) - elseif(WIN32) - add_subdirectory(windows) - endif() - - if(WITH_TESTS) - add_subdirectory(test) - endif() endif() diff --git a/src/autotype/mac/CMakeLists.txt b/src/autotype/mac/CMakeLists.txt index 08c532784..f2915bc0a 100644 --- a/src/autotype/mac/CMakeLists.txt +++ b/src/autotype/mac/CMakeLists.txt @@ -1,24 +1,20 @@ -set(autotype_mac_SOURCES - AutoTypeMac.cpp -) +set(autotype_mac_SOURCES AutoTypeMac.cpp) -set(autotype_mac_mm_SOURCES - AppKitImpl.mm -) +set(autotype_mac_mm_SOURCES AppKitImpl.mm) add_library(keepassx-autotype-cocoa MODULE ${autotype_mac_SOURCES} ${autotype_mac_mm_SOURCES}) set_target_properties(keepassx-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon") target_link_libraries(keepassx-autotype-cocoa ${PROGNAME} Qt5::Core Qt5::Widgets) if(WITH_APP_BUNDLE) - add_custom_command(TARGET keepassx-autotype-cocoa - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libkeepassx-autotype-cocoa.so ${PLUGIN_INSTALL_DIR} - COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app -executable=${PLUGIN_INSTALL_DIR}/libkeepassx-autotype-cocoa.so -no-plugins - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src - COMMENT "Deploying autotype plugin") + add_custom_command(TARGET keepassx-autotype-cocoa + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libkeepassx-autotype-cocoa.so ${PLUGIN_INSTALL_DIR} + COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app -executable=${PLUGIN_INSTALL_DIR}/libkeepassx-autotype-cocoa.so -no-plugins + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src + COMMENT "Deploying autotype plugin") else() - install(TARGETS keepassx-autotype-cocoa - BUNDLE DESTINATION . COMPONENT Runtime - LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime) + install(TARGETS keepassx-autotype-cocoa + BUNDLE DESTINATION . COMPONENT Runtime + LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime) endif() diff --git a/src/autotype/test/CMakeLists.txt b/src/autotype/test/CMakeLists.txt index f41fa1b50..a9cf998df 100644 --- a/src/autotype/test/CMakeLists.txt +++ b/src/autotype/test/CMakeLists.txt @@ -1,6 +1,4 @@ -set(autotype_test_SOURCES - AutoTypeTest.cpp -) +set(autotype_test_SOURCES AutoTypeTest.cpp) add_library(keepassx-autotype-test MODULE ${autotype_test_SOURCES}) target_link_libraries(keepassx-autotype-test keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets) diff --git a/src/autotype/windows/CMakeLists.txt b/src/autotype/windows/CMakeLists.txt index cc3ad4b44..be74b7aa7 100644 --- a/src/autotype/windows/CMakeLists.txt +++ b/src/autotype/windows/CMakeLists.txt @@ -1,9 +1,7 @@ -set(autotype_win_SOURCES - AutoTypeWindows.cpp -) +set(autotype_win_SOURCES AutoTypeWindows.cpp) add_library(keepassx-autotype-windows MODULE ${autotype_win_SOURCES}) target_link_libraries(keepassx-autotype-windows keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets) install(TARGETS keepassx-autotype-windows BUNDLE DESTINATION . COMPONENT Runtime -LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime) + LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime) diff --git a/src/autotype/xcb/CMakeLists.txt b/src/autotype/xcb/CMakeLists.txt index 7e7f252d7..e41d4a099 100644 --- a/src/autotype/xcb/CMakeLists.txt +++ b/src/autotype/xcb/CMakeLists.txt @@ -1,8 +1,6 @@ include_directories(SYSTEM ${X11_X11_INCLUDE_PATH}) -set(autotype_XCB_SOURCES - AutoTypeXCB.cpp -) +set(autotype_XCB_SOURCES AutoTypeXCB.cpp) add_library(keepassx-autotype-xcb MODULE ${autotype_XCB_SOURCES}) target_link_libraries(keepassx-autotype-xcb keepassx_core Qt5::Core Qt5::Widgets Qt5::X11Extras ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_XTest_LIB}) diff --git a/src/browser/CMakeLists.txt b/src/browser/CMakeLists.txt index bb5a01908..10189d931 100755 --- a/src/browser/CMakeLists.txt +++ b/src/browser/CMakeLists.txt @@ -19,19 +19,18 @@ if(WITH_XC_BROWSER) find_package(sodium 1.0.12 REQUIRED) set(keepassxcbrowser_SOURCES - BrowserAccessControlDialog.cpp - BrowserAction.cpp - BrowserClients.cpp - BrowserEntryConfig.cpp - BrowserEntrySaveDialog.cpp - BrowserOptionDialog.cpp - BrowserService.cpp - BrowserSettings.cpp - HostInstaller.cpp - NativeMessagingBase.cpp - NativeMessagingHost.cpp - Variant.cpp - ) + BrowserAccessControlDialog.cpp + BrowserAction.cpp + BrowserClients.cpp + BrowserEntryConfig.cpp + BrowserEntrySaveDialog.cpp + BrowserOptionDialog.cpp + BrowserService.cpp + BrowserSettings.cpp + HostInstaller.cpp + NativeMessagingBase.cpp + NativeMessagingHost.cpp + Variant.cpp) add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES}) target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network sodium) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index a5126f999..f6377aa07 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -14,46 +14,46 @@ # along with this program. If not, see . set(cli_SOURCES - Add.cpp - Add.h - Clip.cpp - Clip.h - Command.cpp - Command.h - Diceware.cpp - Diceware.h - Edit.cpp - Edit.h - Estimate.cpp - Estimate.h - Extract.cpp - Extract.h - Generate.cpp - Generate.h - List.cpp - List.h - Locate.cpp - Locate.h - Merge.cpp - Merge.h - Remove.cpp - Remove.h - Show.cpp - Show.h) + Add.cpp + Add.h + Clip.cpp + Clip.h + Command.cpp + Command.h + Diceware.cpp + Diceware.h + Edit.cpp + Edit.h + Estimate.cpp + Estimate.h + Extract.cpp + Extract.h + Generate.cpp + Generate.h + List.cpp + List.h + Locate.cpp + Locate.h + Merge.cpp + Merge.h + Remove.cpp + Remove.h + Show.cpp + Show.h) add_library(cli STATIC ${cli_SOURCES}) target_link_libraries(cli Qt5::Core Qt5::Widgets) add_executable(keepassxc-cli keepassxc-cli.cpp) target_link_libraries(keepassxc-cli - cli - keepassx_core - Qt5::Core - ${GCRYPT_LIBRARIES} - ${ARGON2_LIBRARIES} - ${GPGERROR_LIBRARIES} - ${ZLIB_LIBRARIES} - ${ZXCVBN_LIBRARIES}) + cli + keepassx_core + Qt5::Core + ${GCRYPT_LIBRARIES} + ${ARGON2_LIBRARIES} + ${GPGERROR_LIBRARIES} + ${ZLIB_LIBRARIES} + ${ZXCVBN_LIBRARIES}) install(TARGETS keepassxc-cli BUNDLE DESTINATION . COMPONENT Runtime diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 96f684171..3839b58e4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,4 @@ +# Copyright (C) 2018 KeePassXC Team # Copyright (C) 2010 Felix Geyer # # This program is free software: you can redistribute it and/or modify @@ -13,7 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/../src) +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR}/../src) add_definitions(-DQT_TEST_LIB) @@ -21,99 +26,98 @@ set(KEEPASSX_TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) configure_file(config-keepassx-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx-tests.h) macro(parse_arguments prefix arg_names option_names) - set(DEFAULT_ARGS) - foreach(arg_name ${arg_names}) - set(${prefix}_${arg_name}) - endforeach(arg_name) - foreach(option ${option_names}) - set(${prefix}_${option} FALSE) - endforeach(option) + set(DEFAULT_ARGS) + foreach(arg_name ${arg_names}) + set(${prefix}_${arg_name}) + endforeach(arg_name) + foreach(option ${option_names}) + set(${prefix}_${option} FALSE) + endforeach(option) - set(current_arg_name DEFAULT_ARGS) - set(current_arg_list) - foreach(arg ${ARGN}) - set(larg_names ${arg_names}) - list(FIND larg_names "${arg}" is_arg_name) - if(is_arg_name GREATER -1) - set(${prefix}_${current_arg_name} ${current_arg_list}) - set(current_arg_name ${arg}) - set(current_arg_list) - else() - set(loption_names ${option_names}) - list(FIND loption_names "${arg}" is_option) - if(is_option GREATER -1) - set(${prefix}_${arg} TRUE) - else(is_option GREATER -1) - set(current_arg_list ${current_arg_list} ${arg}) - endif() - endif() - endforeach(arg) - set(${prefix}_${current_arg_name} ${current_arg_list}) + set(current_arg_name DEFAULT_ARGS) + set(current_arg_list) + foreach(arg ${ARGN}) + set(larg_names ${arg_names}) + list(FIND larg_names "${arg}" is_arg_name) + if(is_arg_name GREATER -1) + set(${prefix}_${current_arg_name} ${current_arg_list}) + set(current_arg_name ${arg}) + set(current_arg_list) + else() + set(loption_names ${option_names}) + list(FIND loption_names "${arg}" is_option) + if(is_option GREATER -1) + set(${prefix}_${arg} TRUE) + else(is_option GREATER -1) + set(current_arg_list ${current_arg_list} ${arg}) + endif() + endif() + endforeach(arg) + set(${prefix}_${current_arg_name} ${current_arg_list}) endmacro(parse_arguments) macro(add_unit_test) - parse_arguments(TEST "NAME;SOURCES;LIBS" "" ${ARGN}) - set(_test_NAME ${TEST_NAME}) - set(_srcList ${TEST_SOURCES}) - add_executable(${_test_NAME} ${_srcList}) - target_link_libraries(${_test_NAME} ${TEST_LIBS}) + parse_arguments(TEST "NAME;SOURCES;LIBS" "" ${ARGN}) + set(_test_NAME ${TEST_NAME}) + set(_srcList ${TEST_SOURCES}) + add_executable(${_test_NAME} ${_srcList}) + target_link_libraries(${_test_NAME} ${TEST_LIBS}) - if(NOT TEST_OUTPUT) - set(TEST_OUTPUT plaintext) - endif(NOT TEST_OUTPUT) - set(TEST_OUTPUT ${TEST_OUTPUT} CACHE STRING "The output to generate when running the QTest unit tests") + if(NOT TEST_OUTPUT) + set(TEST_OUTPUT plaintext) + endif(NOT TEST_OUTPUT) + set(TEST_OUTPUT ${TEST_OUTPUT} CACHE STRING "The output to generate when running the QTest unit tests") - if(KDE4_TEST_OUTPUT STREQUAL "xml") - add_test(${_test_NAME} ${_test_NAME} -xml -o ${_test_NAME}.tml) - else(KDE4_TEST_OUTPUT STREQUAL "xml") - add_test(${_test_NAME} ${_test_NAME}) - endif(KDE4_TEST_OUTPUT STREQUAL "xml") + if(KDE4_TEST_OUTPUT STREQUAL "xml") + add_test(${_test_NAME} ${_test_NAME} -xml -o ${_test_NAME}.tml) + else(KDE4_TEST_OUTPUT STREQUAL "xml") + add_test(${_test_NAME} ${_test_NAME}) + endif(KDE4_TEST_OUTPUT STREQUAL "xml") - if(NOT MSVC_IDE) #not needed for the ide - # if the tests are EXCLUDE_FROM_ALL, add a target "buildtests" to build all tests - if(NOT WITH_TESTS) - get_directory_property(_buildtestsAdded BUILDTESTS_ADDED) - if(NOT _buildtestsAdded) - add_custom_target(buildtests) - set_directory_properties(PROPERTIES BUILDTESTS_ADDED TRUE) - endif() - add_dependencies(buildtests ${_test_NAME}) + if(NOT MSVC_IDE) #not needed for the ide + # if the tests are EXCLUDE_FROM_ALL, add a target "buildtests" to build all tests + if(NOT WITH_TESTS) + get_directory_property(_buildtestsAdded BUILDTESTS_ADDED) + if(NOT _buildtestsAdded) + add_custom_target(buildtests) + set_directory_properties(PROPERTIES BUILDTESTS_ADDED TRUE) + endif() + add_dependencies(buildtests ${_test_NAME}) + endif() endif() - endif() endmacro(add_unit_test) set(TEST_LIBRARIES - keepassx_core - ${keepasshttp_LIB} - ${autotype_LIB} - Qt5::Core - Qt5::Concurrent - Qt5::Widgets - Qt5::Test - ${GCRYPT_LIBRARIES} - ${GPGERROR_LIBRARIES} - ${ZLIB_LIBRARIES} -) + keepassx_core + ${keepasshttp_LIB} + ${autotype_LIB} + Qt5::Core + Qt5::Concurrent + Qt5::Widgets + Qt5::Test + ${GCRYPT_LIBRARIES} + ${GPGERROR_LIBRARIES} + ${ZLIB_LIBRARIES}) set(testsupport_SOURCES TestGlobal.h modeltest.cpp FailDevice.cpp mock/MockClock.cpp util/TemporaryFile.cpp) add_library(testsupport STATIC ${testsupport_SOURCES}) target_link_libraries(testsupport Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Test) if(YUBIKEY_FOUND) - set(TEST_LIBRARIES ${TEST_LIBRARIES} ${YUBIKEY_LIBRARIES}) + set(TEST_LIBRARIES ${TEST_LIBRARIES} ${YUBIKEY_LIBRARIES}) endif() add_unit_test(NAME testgroup SOURCES TestGroup.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) add_unit_test(NAME testkdbx2 SOURCES TestKdbx2.cpp LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testkdbx3 SOURCES TestKeePass2Format.cpp FailDevice.cpp TestKdbx3.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) add_unit_test(NAME testkdbx4 SOURCES TestKeePass2Format.cpp FailDevice.cpp mock/MockChallengeResponseKey.cpp TestKdbx4.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) add_unit_test(NAME testkeys SOURCES TestKeys.cpp mock/MockChallengeResponseKey.cpp LIBS ${TEST_LIBRARIES}) @@ -137,7 +141,7 @@ add_unit_test(NAME testkeepass2randomstream SOURCES TestKeePass2RandomStream.cpp LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testmodified SOURCES TestModified.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) add_unit_test(NAME testdeletedobjects SOURCES TestDeletedObjects.cpp LIBS ${TEST_LIBRARIES}) @@ -149,14 +153,14 @@ add_unit_test(NAME testwildcardmatcher SOURCES TestWildcardMatcher.cpp LIBS ${TEST_LIBRARIES}) if(WITH_XC_AUTOTYPE) - add_unit_test(NAME testautotype SOURCES TestAutoType.cpp - LIBS ${TEST_LIBRARIES}) - set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON) + add_unit_test(NAME testautotype SOURCES TestAutoType.cpp + LIBS ${TEST_LIBRARIES}) + set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON) endif() if(WITH_XC_SSHAGENT) - add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp - LIBS sshagent ${TEST_LIBRARIES}) + add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp + LIBS sshagent ${TEST_LIBRARIES}) endif() add_unit_test(NAME testentry SOURCES TestEntry.cpp @@ -183,7 +187,7 @@ add_unit_test(NAME testrandom SOURCES TestRandom.cpp add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp LIBS ${TEST_LIBRARIES}) -add_unit_test(NAME testcsveporter SOURCES TestCsvExporter.cpp +add_unit_test(NAME testcsvexporter SOURCES TestCsvExporter.cpp LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testykchallengeresponsekey @@ -198,9 +202,9 @@ add_unit_test(NAME testtools SOURCES TestTools.cpp if(WITH_GUI_TESTS) - # CLI clip tests need X environment on Linux - add_unit_test(NAME testcli SOURCES TestCli.cpp - LIBS testsupport cli ${TEST_LIBRARIES}) + # CLI clip tests need X environment on Linux + add_unit_test(NAME testcli SOURCES TestCli.cpp + LIBS testsupport cli ${TEST_LIBRARIES}) - add_subdirectory(gui) + add_subdirectory(gui) endif(WITH_GUI_TESTS) From a44138dd5c4b1c0f28f127140de0836d4999f144 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Fri, 14 Sep 2018 17:41:30 -0400 Subject: [PATCH 15/84] Multiple fixes to MainWindow and some cleanup * Fix MainWindow startup when minimize to tray was enabled * Reduce duplicate code in DatabaseWidget.cpp * Fix snapcraft build dependencies * Add support for CTRL+TAB, CTRL+PGUP, CTRL+SHIFT+TAB, CTRL+PGDN to control database tabs from any focus location * Add CTRL+SHIFT+M shortcut to minimize to tray * Allow minimize instead of app exit without tray icon --- snapcraft.yaml | 3 +- src/core/Bootstrap.cpp | 18 +-- src/gui/ApplicationSettingsWidget.cpp | 5 +- src/gui/ApplicationSettingsWidgetGeneral.ui | 115 ++++++++------------ src/gui/DatabaseWidget.cpp | 35 ++---- src/gui/EntryPreviewWidget.ui | 24 ++++ src/gui/MainWindow.cpp | 46 ++++++-- src/gui/MainWindow.h | 6 +- 8 files changed, 125 insertions(+), 127 deletions(-) diff --git a/snapcraft.yaml b/snapcraft.yaml index d9c08ec9c..f7bc1434b 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -38,8 +38,8 @@ parts: - g++ - libgcrypt20-dev - libqt5x11extras5-dev + - libqt5svg5-dev - qtbase5-dev - - qtsvg5-dev - qttools5-dev - qttools5-dev-tools - zlib1g-dev @@ -59,6 +59,7 @@ parts: - libsodium23 - libxtst6 - libqt5x11extras5 + - libqt5svg5 - libusb-1.0-0 - qtwayland5 override-build: | diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp index 2c25b2505..70ca7eec4 100644 --- a/src/core/Bootstrap.cpp +++ b/src/core/Bootstrap.cpp @@ -71,20 +71,10 @@ void bootstrapApplication() void restoreMainWindowState(MainWindow& mainWindow) { // start minimized if configured - bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool(); - bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool(); -#ifndef Q_OS_LINUX - if (minimizeOnStartup) { -#else - // On some Linux systems, the window should NOT be minimized and hidden (i.e. not shown), at - // the same time (which would happen if both minimize on startup and minimize to tray are set) - // since otherwise it causes problems on restore as seen on issue #1595. Hiding it is enough. - if (minimizeOnStartup && !minimizeToTray) { -#endif - mainWindow.setWindowState(Qt::WindowMinimized); - } - if (!(minimizeOnStartup && minimizeToTray)) { - mainWindow.show(); + if (config()->get("GUI/MinimizeOnStartup").toBool()) { + mainWindow.showMinimized(); + } else { + mainWindow.bringToFront(); } if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) { diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 0e80f2fc5..c23e2bfa5 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -159,7 +159,7 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->systrayShowCheckBox->setChecked(config()->get("GUI/ShowTrayIcon").toBool()); m_generalUi->systrayDarkIconCheckBox->setChecked(config()->get("GUI/DarkTrayIcon").toBool()); m_generalUi->systrayMinimizeToTrayCheckBox->setChecked(config()->get("GUI/MinimizeToTray").toBool()); - m_generalUi->systrayMinimizeOnCloseCheckBox->setChecked(config()->get("GUI/MinimizeOnClose").toBool()); + m_generalUi->minimizeOnCloseCheckBox->setChecked(config()->get("GUI/MinimizeOnClose").toBool()); m_generalUi->systrayMinimizeOnStartup->setChecked(config()->get("GUI/MinimizeOnStartup").toBool()); m_generalUi->autoTypeAskCheckBox->setChecked(config()->get("security/autotypeask").toBool()); @@ -235,7 +235,7 @@ void ApplicationSettingsWidget::saveSettings() config()->set("GUI/ShowTrayIcon", m_generalUi->systrayShowCheckBox->isChecked()); config()->set("GUI/DarkTrayIcon", m_generalUi->systrayDarkIconCheckBox->isChecked()); config()->set("GUI/MinimizeToTray", m_generalUi->systrayMinimizeToTrayCheckBox->isChecked()); - config()->set("GUI/MinimizeOnClose", m_generalUi->systrayMinimizeOnCloseCheckBox->isChecked()); + config()->set("GUI/MinimizeOnClose", m_generalUi->minimizeOnCloseCheckBox->isChecked()); config()->set("GUI/MinimizeOnStartup", m_generalUi->systrayMinimizeOnStartup->isChecked()); config()->set("security/autotypeask", m_generalUi->autoTypeAskCheckBox->isChecked()); @@ -299,5 +299,4 @@ void ApplicationSettingsWidget::enableSystray(bool checked) { m_generalUi->systrayDarkIconCheckBox->setEnabled(checked); m_generalUi->systrayMinimizeToTrayCheckBox->setEnabled(checked); - m_generalUi->systrayMinimizeOnCloseCheckBox->setEnabled(checked); } diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index 157e52299..f8e304cca 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -186,6 +186,13 @@ + + + + Minimize instead of app exit + + + @@ -193,6 +200,42 @@ + + + + 0 + + + QLayout::SetMaximumSize + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + false + + + Dark system tray icon + + + + + @@ -250,78 +293,6 @@ - - - - 0 - - - QLayout::SetMaximumSize - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - false - - - Hide window to system tray instead of app exit - - - - - - - - - 0 - - - QLayout::SetMaximumSize - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - false - - - Dark system tray icon - - - - - diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index effae45c1..e34a363ba 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -116,10 +116,8 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_previewView->hide(); 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(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); @@ -138,8 +136,6 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) rightHandSideWidget->setLayout(vLayout); - setTabOrder(m_entryView, m_groupView); - m_mainSplitter->addWidget(m_groupView); m_mainSplitter->addWidget(rightHandSideWidget); @@ -1342,29 +1338,16 @@ QStringList DatabaseWidget::customEntryAttributes() const } /* - * Restores the focus on the group and entry that was focused - * before the database was locked or reloaded. + * Restores the focus on the group and entry provided */ void DatabaseWidget::restoreGroupEntryFocus(const QUuid& groupUuid, const QUuid& entryUuid) { - Group* restoredGroup = nullptr; - const QList groups = m_db->rootGroup()->groupsRecursive(true); - for (Group* group : groups) { - if (group->uuid() == groupUuid) { - restoredGroup = group; - break; - } - } - - if (restoredGroup != nullptr) { - m_groupView->setCurrentGroup(restoredGroup); - - const QList entries = restoredGroup->entries(); - for (Entry* entry : entries) { - if (entry->uuid() == entryUuid) { - m_entryView->setCurrentEntry(entry); - break; - } + auto group = m_db->resolveGroup(groupUuid); + if (group) { + m_groupView->setCurrentGroup(group); + auto entry = group->findEntryByUuid(entryUuid); + if (entry) { + m_entryView->setCurrentEntry(entry); } } } diff --git a/src/gui/EntryPreviewWidget.ui b/src/gui/EntryPreviewWidget.ui index b7b8445a8..65b779d43 100644 --- a/src/gui/EntryPreviewWidget.ui +++ b/src/gui/EntryPreviewWidget.ui @@ -2,6 +2,14 @@ EntryPreviewWidget + + + 0 + 0 + 280 + 267 + + 0 @@ -107,6 +115,9 @@ + + Qt::ClickFocus + Generate TOTP Token @@ -120,6 +131,9 @@ + + Qt::ClickFocus + Close @@ -135,6 +149,9 @@ + + Qt::ClickFocus + 0 @@ -732,6 +749,13 @@
gui/widgets/ElidedLabel.h
+ + entryTotpButton + entryAutotypeTree + entryTabWidget + groupCloseButton + groupTabWidget +
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index a729f588e..bee57516e 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -207,6 +207,11 @@ MainWindow::MainWindow() m_ui->actionEntryCopyURL->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_U); new QShortcut(Qt::CTRL + Qt::Key_M, this, SLOT(showMinimized())); + new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_M, this, SLOT(hideWindow())); + new QShortcut(Qt::CTRL + Qt::Key_Tab, this, SLOT(selectNextDatabaseTab())); + new QShortcut(Qt::CTRL + Qt::Key_PageUp, this, SLOT(selectNextDatabaseTab())); + new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab, this, SLOT(selectPreviousDatabaseTab())); + new QShortcut(Qt::CTRL + Qt::Key_PageDown, this, SLOT(selectPreviousDatabaseTab())); m_ui->actionDatabaseNew->setIcon(filePath()->icon("actions", "document-new")); m_ui->actionDatabaseOpen->setIcon(filePath()->icon("actions", "document-open")); @@ -697,6 +702,30 @@ void MainWindow::databaseStatusChanged(DatabaseWidget*) updateTrayIcon(); } +void MainWindow::selectNextDatabaseTab() +{ + if (m_ui->stackedWidget->currentIndex() == DatabaseTabScreen) { + int index = m_ui->tabWidget->currentIndex() + 1; + if (index >= m_ui->tabWidget->count()) { + m_ui->tabWidget->setCurrentIndex(0); + } else { + m_ui->tabWidget->setCurrentIndex(index); + } + } +} + +void MainWindow::selectPreviousDatabaseTab() +{ + if (m_ui->stackedWidget->currentIndex() == DatabaseTabScreen) { + int index = m_ui->tabWidget->currentIndex() - 1; + if (index < 0) { + m_ui->tabWidget->setCurrentIndex(m_ui->tabWidget->count() - 1); + } else { + m_ui->tabWidget->setCurrentIndex(index); + } + } +} + void MainWindow::databaseTabChanged(int tabIndex) { if (tabIndex != -1 && m_ui->stackedWidget->currentIndex() == WelcomeScreen) { @@ -716,15 +745,9 @@ void MainWindow::closeEvent(QCloseEvent* event) return; } - bool minimizeOnClose = isTrayIconEnabled() && config()->get("GUI/MinimizeOnClose").toBool(); - if (minimizeOnClose && !m_appExitCalled) { - event->accept(); + if (config()->get("GUI/MinimizeOnClose").toBool() && !m_appExitCalled) { + event->ignore(); hideWindow(); - - if (config()->get("security/lockdatabaseminimize").toBool()) { - m_ui->tabWidget->lockDatabases(); - } - return; } @@ -908,7 +931,12 @@ void MainWindow::hideWindow() // TODO: Add an explanation for why this is also not done on Mac (or remove the check) setWindowState(windowState() | Qt::WindowMinimized); #endif - QTimer::singleShot(0, this, SLOT(hide())); + // Only hide if tray icon is active, otherwise window will be gone forever + if (isTrayIconEnabled()) { + hide(); + } else { + showMinimized(); + } if (config()->get("security/lockdatabaseminimize").toBool()) { m_ui->tabWidget->lockDatabases(); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 566034c89..9c2f41cef 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -69,6 +69,8 @@ public slots: void hideGlobalMessage(); void showYubiKeyPopup(); void hideYubiKeyPopup(); + void hideWindow(); + void toggleWindow(); void bringToFront(); void closeAllDatabases(); void lockAllDatabases(); @@ -103,13 +105,13 @@ private slots: void rememberOpenDatabases(const QString& filePath); void applySettingsChanges(); void trayIconTriggered(QSystemTrayIcon::ActivationReason reason); - void hideWindow(); - void toggleWindow(); void lockDatabasesAfterInactivity(); void forgetTouchIDAfterInactivity(); void hideTabMessage(); void handleScreenLock(); void showErrorMessage(const QString& message); + void selectNextDatabaseTab(); + void selectPreviousDatabaseTab(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); From b45f0e3e466deb0e5e1c64d2baa444216c22c883 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 20 Oct 2018 12:18:33 +0200 Subject: [PATCH 16/84] Enable high-DPI scaling to support 4k screens. This enables coordinate system scaling for high-DPI displays, which enforces correct proportions even on small 4k displays. The icons are scaled up without interpolation, which makes them crisp, but a bit pixelated. A new scalable icon set will solve this problem, but is not scope of this patch. Resolves #548, #1381, #1710, #1888 In addition, this patch enforces the KeePassXC icon theme for the KMessageBox close icon, since using the system theme produces very ugly icons on some Linux systems. --- src/gui/KMessageWidget.cpp | 2 +- src/main.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gui/KMessageWidget.cpp b/src/gui/KMessageWidget.cpp index acd6509bb..910f0c91b 100644 --- a/src/gui/KMessageWidget.cpp +++ b/src/gui/KMessageWidget.cpp @@ -94,7 +94,7 @@ void KMessageWidgetPrivate::init(KMessageWidget *q_ptr) QAction *closeAction = new QAction(q); closeAction->setText(KMessageWidget::tr("&Close")); closeAction->setToolTip(KMessageWidget::tr("Close message")); - closeAction->setIcon(FilePath::instance()->icon("actions", "message-close")); + closeAction->setIcon(FilePath::instance()->icon("actions", "message-close", false)); QObject::connect(closeAction, SIGNAL(triggered(bool)), q, SLOT(animatedHide())); diff --git a/src/main.cpp b/src/main.cpp index a37648ec1..2a1fa324c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,10 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) int main(int argc, char** argv) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + Application app(argc, argv); Application::setApplicationName("keepassxc"); Application::setApplicationVersion(KEEPASSXC_VERSION); From 5c92082f7c169024e8894d0a8f2bdf6df325f1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20V=C3=A4nttinen?= Date: Tue, 23 Oct 2018 16:03:18 +0300 Subject: [PATCH 17/84] Add warning message to browser integration settings when keepassxc-proxy is not found (#2396) --- src/browser/BrowserOptionDialog.cpp | 18 ++++++ src/browser/BrowserOptionDialog.ui | 10 +++ src/browser/BrowserSettings.cpp | 5 ++ src/browser/BrowserSettings.h | 1 + src/browser/HostInstaller.cpp | 96 ++++++++++++++++++++++++++++- src/browser/HostInstaller.h | 2 + 6 files changed, 131 insertions(+), 1 deletion(-) diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index e2ed8420b..a52f53e10 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -39,6 +39,13 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) "Firefox", "Google Chrome / Chromium / Vivaldi")); + m_ui->scriptWarningWidget->setVisible(false); + m_ui->scriptWarningWidget->setAutoHideTimeout(-1); + m_ui->scriptWarningWidget->showMessage(tr("Warning, the keepassxc-proxy application was not found!" + "
Please check the KeePassXC installation directory or confirm the custom path in advanced options." + "
Browser integration WILL NOT WORK without the proxy application." + "
Expected Path: "), MessageWidget::Warning); + m_ui->warningWidget->showMessage(tr("Warning: The following options can be dangerous!"), MessageWidget::Warning); m_ui->warningWidget->setCloseButtonVisible(false); m_ui->warningWidget->setAutoHideTimeout(-1); @@ -109,6 +116,17 @@ void BrowserOptionDialog::loadSettings() m_ui->browserGlobalWarningWidget->setCloseButtonVisible(false); m_ui->browserGlobalWarningWidget->setAutoHideTimeout(-1); #endif + + // Check for native messaging host location errors + QString path; + if (!settings->checkIfProxyExists(path)) { + QString text = m_ui->scriptWarningWidget->text(); + text.append(path); + m_ui->scriptWarningWidget->setText(text); + m_ui->scriptWarningWidget->setVisible(true); + } else { + m_ui->scriptWarningWidget->setVisible(false); + } } void BrowserOptionDialog::saveSettings() diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index 24589c147..fa5cda367 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -49,6 +49,16 @@ General + + + + + 0 + 0 + + + + diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index 96d8f98d7..dc92f5dc8 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -490,3 +490,8 @@ void BrowserSettings::updateBinaryPaths(QString customProxyLocation) bool isProxy = supportBrowserProxy(); m_hostInstaller.updateBinaryPaths(isProxy, customProxyLocation); } + +bool BrowserSettings::checkIfProxyExists(QString& path) +{ + return m_hostInstaller.checkIfProxyExists(supportBrowserProxy(), customProxyLocation(), path); +} diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index 5dc28593a..e501a1201 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -113,6 +113,7 @@ public: PasswordGenerator::GeneratorFlags passwordGeneratorFlags(); QString generatePassword(); void updateBinaryPaths(QString customProxyLocation = QString()); + bool checkIfProxyExists(QString& path); private: static BrowserSettings* m_instance; diff --git a/src/browser/HostInstaller.cpp b/src/browser/HostInstaller.cpp index 99d09f4f7..a2f4c74b8 100644 --- a/src/browser/HostInstaller.cpp +++ b/src/browser/HostInstaller.cpp @@ -52,6 +52,12 @@ HostInstaller::HostInstaller() { } +/** + * Checks if the selected browser has native messaging host properly installed + * + * @param browser Selected browser + * @return bool Script is installed correctly + */ bool HostInstaller::checkIfInstalled(SupportedBrowsers browser) { QString fileName = getPath(browser); @@ -63,6 +69,29 @@ bool HostInstaller::checkIfInstalled(SupportedBrowsers browser) #endif } +/** + * Checks if keepassxc-proxy location is found + * + * @param proxy Is keepassxc-proxy enabled + * @param location Custom proxy location + * @param path The path is set here and returned to the caller + * @return bool + */ +bool HostInstaller::checkIfProxyExists(const bool& proxy, const QString& location, QString& path) const +{ + QString fileName = getProxyPath(proxy, location); + path = fileName; + return QFile::exists(fileName); +} + +/** + * Installs native messaging JSON script for the selected browser + * + * @param browser Selected browser + * @param enabled Is browser integration enabled + * @param proxy Is keepassxc-proxy enabled + * @param location Custom proxy location + */ void HostInstaller::installBrowser(SupportedBrowsers browser, const bool& enabled, const bool& proxy, @@ -98,6 +127,12 @@ void HostInstaller::installBrowser(SupportedBrowsers browser, } } +/** + * Updates the paths to native messaging host for each browser that has been enabled + * + * @param proxy Is keepassxc-proxy enabled + * @param location Custom proxy location + */ void HostInstaller::updateBinaryPaths(const bool& proxy, const QString& location) { for (int i = 0; i < 4; ++i) { @@ -107,6 +142,12 @@ void HostInstaller::updateBinaryPaths(const bool& proxy, const QString& location } } +/** + * Returns the target path for each browser. Windows uses a registry path instead of a file path + * + * @param browser Selected browser + * @return QString Current target path for the selected browser + */ QString HostInstaller::getTargetPath(SupportedBrowsers browser) const { switch (browser) { @@ -123,6 +164,13 @@ QString HostInstaller::getTargetPath(SupportedBrowsers browser) const } } +/** + * Returns the browser name + * Needed for Windows to separate Chromium- or Firefox-based scripts + * + * @param browser Selected browser + * @return QString Name of the selected browser + */ QString HostInstaller::getBrowserName(SupportedBrowsers browser) const { switch (browser) { @@ -139,6 +187,12 @@ QString HostInstaller::getBrowserName(SupportedBrowsers browser) const } } +/** + * Returns the path of native messaging JSON script for the selected browser + * + * @param browser Selected browser + * @return QString JSON script path for the selected browser + */ QString HostInstaller::getPath(SupportedBrowsers browser) const { #ifdef Q_OS_WIN @@ -160,6 +214,12 @@ QString HostInstaller::getPath(SupportedBrowsers browser) const #endif } +/** + * Gets the installation directory for JSON script file (application install path) + * + * @param browser Selected browser + * @return QString Install path + */ QString HostInstaller::getInstallDir(SupportedBrowsers browser) const { QString path = getTargetPath(browser); @@ -170,7 +230,14 @@ QString HostInstaller::getInstallDir(SupportedBrowsers browser) const #endif } -QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location) +/** + * Gets the path to keepassxc-proxy binary + * + * @param proxy Is keepassxc-proxy used with KeePassXC + * @param location Custom proxy path + * @return path Path to keepassxc-proxy + */ +QString HostInstaller::getProxyPath(const bool& proxy, const QString& location) const { QString path; #ifdef KEEPASSXC_DIST_APPIMAGE @@ -198,6 +265,20 @@ QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& #endif #endif // #ifdef KEEPASSXC_DIST_APPIMAGE + return path; +} + +/** + * Constructs the JSON script file used with native messaging + * + * @param browser Browser (Chromium- and Firefox-based browsers need a different parameters for the script) + * @param proxy Is keepassxc-proxy used with KeePassXC + * @param location Custom proxy location + * @return script The JSON script file + */ +QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location) +{ + QString path = getProxyPath(proxy, location); QJsonObject script; script["name"] = HOST_NAME; @@ -221,11 +302,24 @@ QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& return script; } +/** + * Checks if a registry setting is found with default value + * + * @param settings Registry path + * @return bool Is the registry value found + */ bool HostInstaller::registryEntryFound(const QSettings& settings) { return !settings.value("Default").isNull(); } +/** + * Saves a JSON script file + * + * @param browser Selected browser + * @param script JSON native messaging script object + * @return bool Write succeeds + */ bool HostInstaller::saveFile(SupportedBrowsers browser, const QJsonObject& script) { QString path = getPath(browser); diff --git a/src/browser/HostInstaller.h b/src/browser/HostInstaller.h index 3b985c300..204c3d982 100644 --- a/src/browser/HostInstaller.h +++ b/src/browser/HostInstaller.h @@ -39,6 +39,7 @@ public: public: HostInstaller(); bool checkIfInstalled(SupportedBrowsers browser); + bool checkIfProxyExists(const bool& proxy, const QString& location, QString& path) const; void installBrowser(SupportedBrowsers browser, const bool& enabled, const bool& proxy = false, @@ -50,6 +51,7 @@ private: QString getBrowserName(SupportedBrowsers browser) const; QString getPath(SupportedBrowsers browser) const; QString getInstallDir(SupportedBrowsers browser) const; + QString getProxyPath(const bool& proxy, const QString& location) const; QJsonObject constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location); bool registryEntryFound(const QSettings& settings); bool saveFile(SupportedBrowsers browser, const QJsonObject& script); From 6ea869bb184a1d1b11241164d6c43a1acae4bf54 Mon Sep 17 00:00:00 2001 From: Chih-Hsuan Yen Date: Wed, 24 Oct 2018 20:08:37 +0800 Subject: [PATCH 18/84] Replace all .svgz in paths to .svg (#2405) --- COPYING | 12 ++++---- share/CMakeLists.txt | 72 +++++++++++++++++++++---------------------- src/core/FilePath.cpp | 4 +-- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/COPYING b/COPYING index 8095ef067..b91037658 100644 --- a/COPYING +++ b/COPYING @@ -56,15 +56,15 @@ Copyright: 2015 halex2005 License: MIT Files: share/icons/application/*/apps/keepassxc.png - share/icons/application/scalable/apps/keepassxc.svgz + share/icons/application/scalable/apps/keepassxc.svg share/icons/application/*/apps/keepassxc-dark.png - share/icons/application/scalable/apps/keepassxc-dark.svgz + share/icons/application/scalable/apps/keepassxc-dark.svg share/icons/application/*/apps/keepassxc-locked.png - share/icons/application/scalable/apps/keepassxc-locked.svgz + share/icons/application/scalable/apps/keepassxc-locked.svg share/icons/application/*/apps/keepassxc-unlocked.png - share/icons/application/scalable/apps/keepassxc-unlocked.svgz + share/icons/application/scalable/apps/keepassxc-unlocked.svg share/icons/application/*/mimetypes/application-x-keepassxc.png - share/icons/application/scalable/mimetypes/application-x-keepassxc.svgz + share/icons/application/scalable/mimetypes/application-x-keepassxc.svg Copyright: 2016, Lorenzo Stella License: LGPL-2 @@ -181,7 +181,7 @@ Files: share/icons/application/*/actions/application-exit.png share/icons/application/*/status/dialog-information.png share/icons/application/*/status/dialog-warning.png share/icons/application/*/status/security-high.png - share/icons/svg/*.svgz + share/icons/svg/*.svg Copyright: 2007, Nuno Pinheiro 2007, David Vignoni 2007, David Miller diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index 816966036..06b718130 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -25,10 +25,10 @@ install(FILES ${DATABASE_ICONS} DESTINATION ${DATA_INSTALL_DIR}/icons/database) if(UNIX AND NOT APPLE) install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor - FILES_MATCHING PATTERN "keepassx*.png" PATTERN "keepassx*.svgz" + FILES_MATCHING PATTERN "keepassx*.png" PATTERN "keepassx*.svg" PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE PATTERN "categories" EXCLUDE) install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor - FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svgz" + FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svg" PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE PATTERN "categories" EXCLUDE) install(FILES linux/org.keepassxc.KeePassXC.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) install(FILES linux/org.keepassxc.KeePassXC.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) @@ -42,84 +42,84 @@ endif() install(DIRECTORY wizard/ DESTINATION ${DATA_INSTALL_DIR}/wizard FILES_MATCHING PATTERN "*.png") install(DIRECTORY icons/application/ DESTINATION ${DATA_INSTALL_DIR}/icons/application - FILES_MATCHING PATTERN "*.png" PATTERN "*.svgz") + FILES_MATCHING PATTERN "*.png" PATTERN "*.svg") add_custom_target(icons # SVGZ to PNGs for KeePassXC COMMAND inkscape -z -w 16 -h 16 - icons/application/scalable/apps/keepassxc.svgz -e icons/application/16x16/apps/keepassxc.png + icons/application/scalable/apps/keepassxc.svg -e icons/application/16x16/apps/keepassxc.png COMMAND inkscape -z -w 24 -h 24 - icons/application/scalable/apps/keepassxc.svgz -e icons/application/24x24/apps/keepassxc.png + icons/application/scalable/apps/keepassxc.svg -e icons/application/24x24/apps/keepassxc.png COMMAND inkscape -z -w 32 -h 32 - icons/application/scalable/apps/keepassxc.svgz -e icons/application/32x32/apps/keepassxc.png + icons/application/scalable/apps/keepassxc.svg -e icons/application/32x32/apps/keepassxc.png COMMAND inkscape -z -w 48 -h 48 - icons/application/scalable/apps/keepassxc.svgz -e icons/application/48x48/apps/keepassxc.png + icons/application/scalable/apps/keepassxc.svg -e icons/application/48x48/apps/keepassxc.png COMMAND inkscape -z -w 64 -h 64 - icons/application/scalable/apps/keepassxc.svgz -e icons/application/64x64/apps/keepassxc.png + icons/application/scalable/apps/keepassxc.svg -e icons/application/64x64/apps/keepassxc.png COMMAND inkscape -z -w 128 -h 128 - icons/application/scalable/apps/keepassxc.svgz -e icons/application/128x128/apps/keepassxc.png + icons/application/scalable/apps/keepassxc.svg -e icons/application/128x128/apps/keepassxc.png COMMAND inkscape -z -w 256 -h 256 - icons/application/scalable/apps/keepassxc.svgz -e icons/application/256x256/apps/keepassxc.png + icons/application/scalable/apps/keepassxc.svg -e icons/application/256x256/apps/keepassxc.png # SVGZ to PNGs for KeePassXC COMMAND inkscape -z -w 16 -h 16 - icons/application/scalable/apps/keepassxc-dark.svgz -e icons/application/16x16/apps/keepassxc-dark.png + icons/application/scalable/apps/keepassxc-dark.svg -e icons/application/16x16/apps/keepassxc-dark.png COMMAND inkscape -z -w 24 -h 24 - icons/application/scalable/apps/keepassxc-dark.svgz -e icons/application/24x24/apps/keepassxc-dark.png + icons/application/scalable/apps/keepassxc-dark.svg -e icons/application/24x24/apps/keepassxc-dark.png COMMAND inkscape -z -w 32 -h 32 - icons/application/scalable/apps/keepassxc-dark.svgz -e icons/application/32x32/apps/keepassxc-dark.png + icons/application/scalable/apps/keepassxc-dark.svg -e icons/application/32x32/apps/keepassxc-dark.png COMMAND inkscape -z -w 48 -h 48 - icons/application/scalable/apps/keepassxc-dark.svgz -e icons/application/48x48/apps/keepassxc-dark.png + icons/application/scalable/apps/keepassxc-dark.svg -e icons/application/48x48/apps/keepassxc-dark.png COMMAND inkscape -z -w 64 -h 64 - icons/application/scalable/apps/keepassxc-dark.svgz -e icons/application/64x64/apps/keepassxc-dark.png + icons/application/scalable/apps/keepassxc-dark.svg -e icons/application/64x64/apps/keepassxc-dark.png COMMAND inkscape -z -w 128 -h 128 - icons/application/scalable/apps/keepassxc-dark.svgz -e icons/application/128x128/apps/keepassxc-dark.png + icons/application/scalable/apps/keepassxc-dark.svg -e icons/application/128x128/apps/keepassxc-dark.png COMMAND inkscape -z -w 256 -h 256 - icons/application/scalable/apps/keepassxc-dark.svgz -e icons/application/256x256/apps/keepassxc-dark.png + icons/application/scalable/apps/keepassxc-dark.svg -e icons/application/256x256/apps/keepassxc-dark.png # SVGZ to PNGs for KeePassXC COMMAND inkscape -z -w 16 -h 16 - icons/application/scalable/apps/keepassxc-locked.svgz -e icons/application/16x16/apps/keepassxc-locked.png + icons/application/scalable/apps/keepassxc-locked.svg -e icons/application/16x16/apps/keepassxc-locked.png COMMAND inkscape -z -w 24 -h 24 - icons/application/scalable/apps/keepassxc-locked.svgz -e icons/application/24x24/apps/keepassxc-locked.png + icons/application/scalable/apps/keepassxc-locked.svg -e icons/application/24x24/apps/keepassxc-locked.png COMMAND inkscape -z -w 32 -h 32 - icons/application/scalable/apps/keepassxc-locked.svgz -e icons/application/32x32/apps/keepassxc-locked.png + icons/application/scalable/apps/keepassxc-locked.svg -e icons/application/32x32/apps/keepassxc-locked.png COMMAND inkscape -z -w 48 -h 48 - icons/application/scalable/apps/keepassxc-locked.svgz -e icons/application/48x48/apps/keepassxc-locked.png + icons/application/scalable/apps/keepassxc-locked.svg -e icons/application/48x48/apps/keepassxc-locked.png COMMAND inkscape -z -w 64 -h 64 - icons/application/scalable/apps/keepassxc-locked.svgz -e icons/application/64x64/apps/keepassxc-locked.png + icons/application/scalable/apps/keepassxc-locked.svg -e icons/application/64x64/apps/keepassxc-locked.png COMMAND inkscape -z -w 128 -h 128 - icons/application/scalable/apps/keepassxc-locked.svgz -e icons/application/128x128/apps/keepassxc-locked.png + icons/application/scalable/apps/keepassxc-locked.svg -e icons/application/128x128/apps/keepassxc-locked.png COMMAND inkscape -z -w 256 -h 256 - icons/application/scalable/apps/keepassxc-locked.svgz -e icons/application/256x256/apps/keepassxc-locked.png + icons/application/scalable/apps/keepassxc-locked.svg -e icons/application/256x256/apps/keepassxc-locked.png # SVGZ to PNGs for KeePassXC COMMAND inkscape -z -w 16 -h 16 - icons/application/scalable/apps/keepassxc-unlocked.svgz -e icons/application/16x16/apps/keepassxc-unlocked.png + icons/application/scalable/apps/keepassxc-unlocked.svg -e icons/application/16x16/apps/keepassxc-unlocked.png COMMAND inkscape -z -w 24 -h 24 - icons/application/scalable/apps/keepassxc-unlocked.svgz -e icons/application/24x24/apps/keepassxc-unlocked.png + icons/application/scalable/apps/keepassxc-unlocked.svg -e icons/application/24x24/apps/keepassxc-unlocked.png COMMAND inkscape -z -w 32 -h 32 - icons/application/scalable/apps/keepassxc-unlocked.svgz -e icons/application/32x32/apps/keepassxc-unlocked.png + icons/application/scalable/apps/keepassxc-unlocked.svg -e icons/application/32x32/apps/keepassxc-unlocked.png COMMAND inkscape -z -w 48 -h 48 - icons/application/scalable/apps/keepassxc-unlocked.svgz -e icons/application/48x48/apps/keepassxc-unlocked.png + icons/application/scalable/apps/keepassxc-unlocked.svg -e icons/application/48x48/apps/keepassxc-unlocked.png COMMAND inkscape -z -w 64 -h 64 - icons/application/scalable/apps/keepassxc-unlocked.svgz -e icons/application/64x64/apps/keepassxc-unlocked.png + icons/application/scalable/apps/keepassxc-unlocked.svg -e icons/application/64x64/apps/keepassxc-unlocked.png COMMAND inkscape -z -w 128 -h 128 - icons/application/scalable/apps/keepassxc-unlocked.svgz -e icons/application/128x128/apps/keepassxc-unlocked.png + icons/application/scalable/apps/keepassxc-unlocked.svg -e icons/application/128x128/apps/keepassxc-unlocked.png COMMAND inkscape -z -w 256 -h 256 - icons/application/scalable/apps/keepassxc-unlocked.svgz -e icons/application/256x256/apps/keepassxc-unlocked.png + icons/application/scalable/apps/keepassxc-unlocked.svg -e icons/application/256x256/apps/keepassxc-unlocked.png # SVGZ to PNGs for KeePassXC MIME-Type COMMAND inkscape -z -w 16 -h 16 - icons/application/scalable/mimetypes/application-x-keepassxc.svgz -e icons/application/16x16/mimetypes/application-x-keepassxc.png + icons/application/scalable/mimetypes/application-x-keepassxc.svg -e icons/application/16x16/mimetypes/application-x-keepassxc.png COMMAND inkscape -z -w 22 -h 22 - icons/application/scalable/mimetypes/application-x-keepassxc.svgz -e icons/application/22x22/mimetypes/application-x-keepassxc.png + icons/application/scalable/mimetypes/application-x-keepassxc.svg -e icons/application/22x22/mimetypes/application-x-keepassxc.png COMMAND inkscape -z -w 32 -h 32 - icons/application/scalable/mimetypes/application-x-keepassxc.svgz -e icons/application/32x32/mimetypes/application-x-keepassxc.png + icons/application/scalable/mimetypes/application-x-keepassxc.svg -e icons/application/32x32/mimetypes/application-x-keepassxc.png COMMAND inkscape -z -w 64 -h 64 - icons/application/scalable/mimetypes/application-x-keepassxc.svgz -e icons/application/64x64/mimetypes/application-x-keepassxc.png + icons/application/scalable/mimetypes/application-x-keepassxc.svg -e icons/application/64x64/mimetypes/application-x-keepassxc.png COMMAND inkscape -z -w 128 -h 128 - icons/application/scalable/mimetypes/application-x-keepassxc.svgz -e icons/application/128x128/mimetypes/application-x-keepassxc.png + icons/application/scalable/mimetypes/application-x-keepassxc.svg -e icons/application/128x128/mimetypes/application-x-keepassxc.png # ICNS for MacOS COMMAND png2icns macosx/keepassxc.icns diff --git a/src/core/FilePath.cpp b/src/core/FilePath.cpp index 80de1f0a7..55b4724a9 100644 --- a/src/core/FilePath.cpp +++ b/src/core/FilePath.cpp @@ -157,7 +157,7 @@ QIcon FilePath::icon(const QString& category, const QString& name, bool fromThem icon.addFile(filename, QSize(size, size)); } } - filename = QString("%1/icons/application/scalable/%2.svgz").arg(m_dataPath, combinedName); + filename = QString("%1/icons/application/scalable/%2.svg").arg(m_dataPath, combinedName); if (QFile::exists(filename)) { icon.addFile(filename); } @@ -200,7 +200,7 @@ QIcon FilePath::onOffIcon(const QString& category, const QString& name) icon.addFile(filename, QSize(size, size), QIcon::Normal, state); } } - filename = QString("%1/icons/application/scalable/%2-%3.svgz").arg(m_dataPath, combinedName, stateName); + filename = QString("%1/icons/application/scalable/%2-%3.svg").arg(m_dataPath, combinedName, stateName); if (QFile::exists(filename)) { icon.addFile(filename, QSize(), QIcon::Normal, state); } From 1d9f46ebc5689ff6895871981b84b00275539025 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Wed, 24 Oct 2018 14:09:12 +0200 Subject: [PATCH 19/84] Fix the position of the Q_REQUIRED_RESULT macro (#2411) The Q_REQUIRED_RESULT macro was appended to a few method declarations. It should've been prepended instead. --- src/crypto/kdf/AesKdf.h | 4 ++-- src/crypto/kdf/Argon2Kdf.h | 4 ++-- src/keys/CompositeKey.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/crypto/kdf/AesKdf.h b/src/crypto/kdf/AesKdf.h index 20ef5c476..84156e6fb 100644 --- a/src/crypto/kdf/AesKdf.h +++ b/src/crypto/kdf/AesKdf.h @@ -35,8 +35,8 @@ protected: int benchmarkImpl(int msec) const override; private: - static bool - transformKeyRaw(const QByteArray& key, const QByteArray& seed, int rounds, QByteArray* result) Q_REQUIRED_RESULT; + Q_REQUIRED_RESULT static bool + transformKeyRaw(const QByteArray& key, const QByteArray& seed, int rounds, QByteArray* result); }; #endif // KEEPASSX_AESKDF_H diff --git a/src/crypto/kdf/Argon2Kdf.h b/src/crypto/kdf/Argon2Kdf.h index 9449f3455..2f029a570 100644 --- a/src/crypto/kdf/Argon2Kdf.h +++ b/src/crypto/kdf/Argon2Kdf.h @@ -45,13 +45,13 @@ protected: quint32 m_parallelism; private: - static bool transformKeyRaw(const QByteArray& key, + Q_REQUIRED_RESULT static bool transformKeyRaw(const QByteArray& key, const QByteArray& seed, quint32 version, quint32 rounds, quint64 memory, quint32 parallelism, - QByteArray& result) Q_REQUIRED_RESULT; + QByteArray& result); }; #endif // KEEPASSX_ARGON2KDF_H diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h index d81c46986..43c624acb 100644 --- a/src/keys/CompositeKey.h +++ b/src/keys/CompositeKey.h @@ -39,7 +39,7 @@ public: QByteArray rawKey() const override; QByteArray rawKey(const QByteArray* transformSeed, bool* ok = nullptr) const; - bool transform(const Kdf& kdf, QByteArray& result) const Q_REQUIRED_RESULT; + Q_REQUIRED_RESULT bool transform(const Kdf& kdf, QByteArray& result) const; bool challenge(const QByteArray& seed, QByteArray& result) const; void addKey(QSharedPointer key); From d990f12f5673ae0b1d9565c1252548c8c5773384 Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Wed, 24 Oct 2018 05:11:18 -0700 Subject: [PATCH 20/84] Convert preview panel close button from checkbox to momentary (#2384) The {group,entry}CloseButton QToolButton, had the "checkable" property set. This caused it to act like a toggle flip flop instead of a momentary push button. After removing that property, the signal it was changed to use was clicked() instead of toggled(bool). Trigger upon click is consistent with the rest of the UI's momentary buttons. --- src/gui/EntryPreviewWidget.cpp | 4 ++-- src/gui/EntryPreviewWidget.ui | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index c80e4ddcf..55107a86f 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -52,13 +52,13 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent) m_ui->entryAttachmentsWidget->setButtonsVisible(false); connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool))); - connect(m_ui->entryCloseButton, SIGNAL(toggled(bool)), SLOT(hide())); + connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide())); connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection); connect(&m_totpTimer, SIGNAL(timeout()), this, SLOT(updateTotpLabel())); // Group m_ui->groupCloseButton->setIcon(filePath()->icon("actions", "dialog-close")); - connect(m_ui->groupCloseButton, SIGNAL(toggled(bool)), SLOT(hide())); + connect(m_ui->groupCloseButton, SIGNAL(clicked()), SLOT(hide())); connect(m_ui->groupTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection); } diff --git a/src/gui/EntryPreviewWidget.ui b/src/gui/EntryPreviewWidget.ui index 65b779d43..1fde8aa3c 100644 --- a/src/gui/EntryPreviewWidget.ui +++ b/src/gui/EntryPreviewWidget.ui @@ -140,9 +140,6 @@ - - true - @@ -536,9 +533,6 @@ - - true - From cfa1eca24900089a369a43c3fb871f344c19395a Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Wed, 24 Oct 2018 05:11:58 -0700 Subject: [PATCH 21/84] Make preview panel expiration date use local time (#2382) Adds call to toLocalTime() to convert expiration date string to correct timezone instead of UTC, which matches the date string in the database view. --- src/gui/EntryPreviewWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index 55107a86f..af6ffac33 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -179,7 +179,7 @@ void EntryPreviewWidget::updateEntryGeneralTab() const TimeInfo entryTime = m_currentEntry->timeInfo(); const QString expires = - entryTime.expires() ? entryTime.expiryTime().toString(Qt::DefaultLocaleShortDate) : tr("Never"); + entryTime.expires() ? entryTime.expiryTime().toLocalTime().toString(Qt::DefaultLocaleShortDate) : tr("Never"); m_ui->entryExpirationLabel->setText(expires); } From efdb43dc53ad701a84fcc0beef700e81a8953734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20V=C3=A4nttinen?= Date: Wed, 24 Oct 2018 17:49:53 +0300 Subject: [PATCH 22/84] Browser connection keys and rules are stored in custom data instead of attributes (#1497) --- src/CMakeLists.txt | 1 + src/browser/BrowserAction.cpp | 12 - src/browser/BrowserAction.h | 4 - src/browser/BrowserEntryConfig.cpp | 6 +- src/browser/BrowserOptionDialog.cpp | 3 - src/browser/BrowserOptionDialog.h | 5 +- src/browser/BrowserOptionDialog.ui | 36 +-- src/browser/BrowserService.cpp | 271 +++++++++--------- src/browser/BrowserService.h | 11 +- src/browser/NativeMessagingHost.cpp | 12 - src/browser/NativeMessagingHost.h | 4 - src/gui/MainWindow.cpp | 10 +- src/gui/dbsettings/DatabaseSettingsDialog.cpp | 14 + src/gui/dbsettings/DatabaseSettingsDialog.h | 7 + .../DatabaseSettingsWidgetBrowser.cpp | 245 ++++++++++++++++ .../DatabaseSettingsWidgetBrowser.h | 78 +++++ .../DatabaseSettingsWidgetBrowser.ui | 174 +++++++++++ 17 files changed, 680 insertions(+), 213 deletions(-) create mode 100644 src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp create mode 100644 src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h create mode 100644 src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56726e43d..37fe0b553 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -222,6 +222,7 @@ add_subdirectory(browser) add_subdirectory(proxy) if(WITH_XC_BROWSER) set(keepassxcbrowser_LIB keepassxcbrowser) + set(keepassx_SOURCES ${keepassx_SOURCES} gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp) endif() add_subdirectory(autotype) diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index e0c3a8501..fcbc318ee 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -566,15 +566,3 @@ QString BrowserAction::incrementNonce(const QString& nonce) sodium_increment(n.data(), n.size()); return getQByteArray(n.data(), n.size()).toBase64(); } - -void BrowserAction::removeSharedEncryptionKeys() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeSharedEncryptionKeys(); -} - -void BrowserAction::removeStoredPermissions() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeStoredPermissions(); -} diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index 46fb9d04f..5a7c83bf8 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -54,10 +54,6 @@ public: QJsonObject readResponse(const QJsonObject& json); -public slots: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - private: QJsonObject handleAction(const QJsonObject& json); QJsonObject handleChangePublicKeys(const QJsonObject& json, const QString& action); diff --git a/src/browser/BrowserEntryConfig.cpp b/src/browser/BrowserEntryConfig.cpp index 90d9f107c..47e8762a8 100644 --- a/src/browser/BrowserEntryConfig.cpp +++ b/src/browser/BrowserEntryConfig.cpp @@ -21,7 +21,7 @@ #include "core/EntryAttributes.h" #include -static const char KEEPASSBROWSER_NAME[] = "KeePassXC-Browser Settings"; +static const char KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; BrowserEntryConfig::BrowserEntryConfig(QObject* parent) : QObject(parent) @@ -82,7 +82,7 @@ void BrowserEntryConfig::setRealm(const QString& realm) bool BrowserEntryConfig::load(const Entry* entry) { - QString s = entry->attributes()->value(KEEPASSBROWSER_NAME); + QString s = entry->customData()->value(KEEPASSXCBROWSER_NAME); if (s.isEmpty()) { return false; } @@ -104,5 +104,5 @@ void BrowserEntryConfig::save(Entry* entry) QVariantMap v = qo2qv(this); QJsonObject o = QJsonObject::fromVariantMap(v); QByteArray json = QJsonDocument(o).toJson(QJsonDocument::Compact); - entry->attributes()->set(KEEPASSBROWSER_NAME, json); + entry->customData()->set(KEEPASSXCBROWSER_NAME, json); } diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index a52f53e10..41194e9b6 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -24,15 +24,12 @@ #include "ui_BrowserOptionDialog.h" #include -#include BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) : QWidget(parent) , m_ui(new Ui::BrowserOptionDialog()) { m_ui->setupUi(this); - connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SIGNAL(removeSharedEncryptionKeys())); - connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SIGNAL(removeStoredPermissions())); m_ui->extensionLabel->setOpenExternalLinks(true); m_ui->extensionLabel->setText(tr("KeePassXC-Browser is needed for the browser integration to work.
Download it for %1 and %2.").arg( diff --git a/src/browser/BrowserOptionDialog.h b/src/browser/BrowserOptionDialog.h index a48504dfd..5423f010f 100644 --- a/src/browser/BrowserOptionDialog.h +++ b/src/browser/BrowserOptionDialog.h @@ -21,6 +21,7 @@ #define BROWSEROPTIONDIALOG_H #include +#include #include namespace Ui @@ -40,10 +41,6 @@ public slots: void loadSettings(); void saveSettings(); -signals: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - private slots: void showProxyLocationFileDialog(); diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index fa5cda367..9a951b33d 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -145,7 +145,7 @@ - + Qt::Vertical @@ -215,7 +215,7 @@ - + Qt::Vertical @@ -230,36 +230,6 @@ - - - - - - - 0 - 0 - - - - &Disconnect all browsers - - - - - - - - 0 - 0 - - - - Forget all remembered &permissions - - - - - @@ -376,7 +346,7 @@ - + Qt::Vertical diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 9a0f07f28..d66637ff5 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -35,10 +35,14 @@ #include "core/PasswordGenerator.h" #include "gui/MainWindow.h" -static const char KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; -static const char ASSOCIATE_KEY_PREFIX[] = "Public Key: "; +const char BrowserService::KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; +const char BrowserService::ASSOCIATE_KEY_PREFIX[] = "KPXC_BROWSER_"; static const char KEEPASSXCBROWSER_GROUP_NAME[] = "KeePassXC-Browser Passwords"; static int KEEPASSXCBROWSER_DEFAULT_ICON = 1; +// These are for the settings and password conversion +const char BrowserService::LEGACY_ASSOCIATE_KEY_PREFIX[] = "Public Key: "; +static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings"; +static const char KEEPASSHTTP_GROUP_NAME[] = "KeePassHttp Passwords"; BrowserService::BrowserService(DatabaseTabWidget* parent) : m_dbTabWidget(parent) @@ -46,12 +50,15 @@ BrowserService::BrowserService(DatabaseTabWidget* parent) , m_bringToFrontRequested(false) , m_keepassBrowserUUID(QUuid::fromRfc4122(QByteArray::fromHex("de887cc3036343b8974b5911b8816224"))) { - connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*))); - connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*))); - connect(m_dbTabWidget, - SIGNAL(activateDatabaseChanged(DatabaseWidget*)), - this, - SLOT(activateDatabaseChanged(DatabaseWidget*))); + // Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr) + if (m_dbTabWidget) { + connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*))); + connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*))); + connect(m_dbTabWidget, + SIGNAL(activateDatabaseChanged(DatabaseWidget*)), + this, + SLOT(activateDatabaseChanged(DatabaseWidget*))); + } } bool BrowserService::isDatabaseOpened() const @@ -107,12 +114,12 @@ QString BrowserService::getDatabaseRootUuid() { Database* db = getDatabase(); if (!db) { - return QString(); + return {}; } Group* rootGroup = db->rootGroup(); if (!rootGroup) { - return QString(); + return {}; } return rootGroup->uuidToHex(); @@ -122,46 +129,16 @@ QString BrowserService::getDatabaseRecycleBinUuid() { Database* db = getDatabase(); if (!db) { - return QString(); + return {}; } Group* recycleBin = db->metadata()->recycleBin(); if (!recycleBin) { - return QString(); + return {}; } return recycleBin->uuidToHex(); } -Entry* BrowserService::getConfigEntry(bool create) -{ - Entry* entry = nullptr; - Database* db = getDatabase(); - if (!db) { - return nullptr; - } - - entry = db->resolveEntry(m_keepassBrowserUUID); - if (!entry && create) { - entry = new Entry(); - entry->setTitle(QLatin1String(KEEPASSXCBROWSER_NAME)); - entry->setUuid(m_keepassBrowserUUID); - entry->setAutoTypeEnabled(false); - entry->setGroup(db->rootGroup()); - return entry; - } - - if (entry && entry->group() == db->metadata()->recycleBin()) { - if (!create) { - return nullptr; - } else { - entry->setGroup(db->rootGroup()); - return entry; - } - } - - return entry; -} - QString BrowserService::storeKey(const QString& key) { QString id; @@ -172,8 +149,8 @@ QString BrowserService::storeKey(const QString& key) return id; } - Entry* config = getConfigEntry(true); - if (!config) { + Database* db = getDatabase(); + if (!db) { return {}; } @@ -199,7 +176,7 @@ QString BrowserService::storeKey(const QString& key) return {}; } - contains = config->attributes()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); + contains = db->metadata()->customData()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); if (contains) { dialogResult = QMessageBox::warning(nullptr, tr("KeePassXC: Overwrite existing key?"), @@ -210,18 +187,18 @@ QString BrowserService::storeKey(const QString& key) } } while (contains && dialogResult == QMessageBox::No); - config->attributes()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key, true); + db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key); return id; } QString BrowserService::getKey(const QString& id) { - Entry* config = getConfigEntry(); - if (!config) { - return QString(); + Database* db = getDatabase(); + if (!db) { + return {}; } - return config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); + return db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); } QJsonArray BrowserService::findMatchingEntries(const QString& id, @@ -445,12 +422,9 @@ QList BrowserService::searchEntries(const QString& url, const StringPair if (Database* db = dbWidget->database()) { // Check if database is connected with KeePassXC-Browser for (const StringPair keyPair : keyList) { - Entry* entry = db->resolveEntry(m_keepassBrowserUUID); - if (entry) { - QString key = entry->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first); - if (!key.isEmpty() && keyPair.second == key) { - databases << db; - } + QString key = db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first); + if (!key.isEmpty() && keyPair.second == key) { + databases << db; } } } @@ -472,102 +446,70 @@ QList BrowserService::searchEntries(const QString& url, const StringPair return entries; } -void BrowserService::removeSharedEncryptionKeys() +void BrowserService::convertAttributesToCustomData(Database *currentDb) { - if (!isDatabaseOpened()) { - QMessageBox::critical(0, - tr("KeePassXC: Database locked!"), - tr("The active database is locked!\n" - "Please unlock the selected database or choose another one which is unlocked."), - QMessageBox::Ok); - return; - } - - Entry* entry = getConfigEntry(); - if (!entry) { - QMessageBox::information(0, - tr("KeePassXC: Settings not available!"), - tr("The active database does not contain a settings entry."), - QMessageBox::Ok); - return; - } - - QStringList keysToRemove; - for (const QString& key : entry->attributes()->keys()) { - if (key.startsWith(ASSOCIATE_KEY_PREFIX)) { - keysToRemove << key; - } - } - - if (keysToRemove.isEmpty()) { - QMessageBox::information(0, - tr("KeePassXC: No keys found"), - tr("No shared encryption keys found in KeePassXC settings."), - QMessageBox::Ok); - return; - } - - entry->beginUpdate(); - for (const QString& key : keysToRemove) { - entry->attributes()->remove(key); - } - entry->endUpdate(); - - const int count = keysToRemove.count(); - QMessageBox::information(0, - tr("KeePassXC: Removed keys from database"), - tr("Successfully removed %n encryption key(s) from KeePassXC settings.", "", count), - QMessageBox::Ok); -} - -void BrowserService::removeStoredPermissions() -{ - if (!isDatabaseOpened()) { - QMessageBox::critical(0, - tr("KeePassXC: Database locked!"), - tr("The active database is locked!\n" - "Please unlock the selected database or choose another one which is unlocked."), - QMessageBox::Ok); - return; - } - - Database* db = m_dbTabWidget->currentDatabaseWidget()->database(); + Database* db = currentDb ? currentDb : getDatabase(); if (!db) { return; } QList entries = db->rootGroup()->entriesRecursive(); - - QProgressDialog progress(tr("Removing stored permissions…"), tr("Abort"), 0, entries.count()); + QProgressDialog progress(tr("Converting attributes to custom data…"), tr("Abort"), 0, entries.count()); progress.setWindowModality(Qt::WindowModal); - uint counter = 0; + int counter = 0; + int keyCounter = 0; for (Entry* entry : entries) { if (progress.wasCanceled()) { return; } - if (entry->attributes()->contains(KEEPASSXCBROWSER_NAME)) { - entry->beginUpdate(); - entry->attributes()->remove(KEEPASSXCBROWSER_NAME); - entry->endUpdate(); + if (moveSettingsToCustomData(entry, KEEPASSHTTP_NAME)) { ++counter; } + if (moveSettingsToCustomData(entry, KEEPASSXCBROWSER_NAME)) { + ++counter; + } + + if (entry->title() == KEEPASSHTTP_NAME || entry->title() == KEEPASSXCBROWSER_NAME) { + keyCounter += moveKeysToCustomData(entry, db); + delete entry; + } + progress.setValue(progress.value() + 1); } progress.reset(); if (counter > 0) { - QMessageBox::information(0, - tr("KeePassXC: Removed permissions"), - tr("Successfully removed permissions from %n entry(s).", "", counter), + QMessageBox::information(0, tr("KeePassXC: Converted KeePassHTTP attributes"), + tr("Successfully converted attributes from %1 entry(s).\n" + "Moved %2 keys to custom data.", "").arg(counter).arg(keyCounter), + QMessageBox::Ok); + } else if (counter == 0 && keyCounter > 0) { + QMessageBox::information(0, tr("KeePassXC: Converted KeePassHTTP attributes"), + tr("Successfully moved %n keys to custom data.", "", keyCounter), QMessageBox::Ok); } else { - QMessageBox::information(0, - tr("KeePassXC: No entry with permissions found!"), - tr("The active database does not contain an entry with permissions."), + QMessageBox::information(0, tr("KeePassXC: No entry with KeePassHTTP attributes found!"), + tr("The active database does not contain an entry with KeePassHTTP attributes."), QMessageBox::Ok); } + + // Rename password groupName + Group* rootGroup = db->rootGroup(); + if (!rootGroup) { + return; + } + + const QString keePassBrowserGroupName = QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); + const QString keePassHttpGroupName = QLatin1String(KEEPASSHTTP_GROUP_NAME); + + for (Group* g : rootGroup->groupsRecursive(true)) { + if (g->name() == keePassHttpGroupName) { + g->setName(keePassBrowserGroupName); + break; + } + } } QList BrowserService::sortEntries(QList& pwEntries, const QString& host, const QString& entryUrl) @@ -873,6 +815,75 @@ Database* BrowserService::selectedDatabase() return getDatabase(); } +bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name) const +{ + if (entry->attributes()->contains(name)) { + QString attr = entry->attributes()->value(name); + entry->beginUpdate(); + if (!attr.isEmpty()) { + entry->customData()->set(KEEPASSXCBROWSER_NAME, attr); + } + entry->attributes()->remove(name); + entry->endUpdate(); + return true; + } + return false; +} + +int BrowserService::moveKeysToCustomData(Entry* entry, Database* db) const +{ + int keyCounter = 0; + for (const auto& key : entry->attributes()->keys()) { + if (key.contains(LEGACY_ASSOCIATE_KEY_PREFIX)) { + QString publicKey = key; + publicKey.remove(LEGACY_ASSOCIATE_KEY_PREFIX); + + // Add key to database custom data + if (db && !db->metadata()->customData()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey)) { + db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey, entry->attributes()->value(key)); + ++keyCounter; + } + } + } + + return keyCounter; +} + +bool BrowserService::checkLegacySettings() +{ + Database* db = getDatabase(); + if (!db) { + return false; + } + + bool legacySettingsFound = false; + QList entries = db->rootGroup()->entriesRecursive(); + for (const auto& e : entries) { + if ((e->attributes()->contains(KEEPASSHTTP_NAME) || e->attributes()->contains(KEEPASSXCBROWSER_NAME)) || + (e->title() == KEEPASSHTTP_NAME || e->title() == KEEPASSXCBROWSER_NAME)) { + legacySettingsFound = true; + break; + } + } + + if (!legacySettingsFound) { + return false; + } + + auto dialogResult = QMessageBox::warning(nullptr, + tr("KeePassXC: Legacy browser integration settings detected"), + tr("Legacy browser integration settings have been detected.\n" + "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; +} + void BrowserService::databaseLocked(DatabaseWidget* dbWidget) { if (dbWidget) { @@ -888,6 +899,10 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget) m_bringToFrontRequested = false; } emit databaseUnlocked(); + + if (checkLegacySettings()) { + convertAttributesToCustomData(); + } } } diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 165901a87..482bf99cb 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -55,8 +55,12 @@ public: Database* selectedDb = nullptr); QList searchEntries(Database* db, const QString& hostname, const QString& url); QList searchEntries(const QString& url, const StringPairList& keyList); - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); + void convertAttributesToCustomData(Database *currentDb = nullptr); + +public: + static const char KEEPASSXCBROWSER_NAME[]; + static const char ASSOCIATE_KEY_PREFIX[]; + static const char LEGACY_ASSOCIATE_KEY_PREFIX[]; public slots: QJsonArray findMatchingEntries(const QString& id, @@ -106,6 +110,9 @@ private: QString baseDomain(const QString& url) const; Database* getDatabase(); Database* selectedDatabase(); + bool moveSettingsToCustomData(Entry* entry, const QString& name) const; + int moveKeysToCustomData(Entry* entry, Database* db) const; + bool checkLegacySettings(); private: DatabaseTabWidget* const m_dbTabWidget; diff --git a/src/browser/NativeMessagingHost.cpp b/src/browser/NativeMessagingHost.cpp index fc35bbec5..03f742020 100644 --- a/src/browser/NativeMessagingHost.cpp +++ b/src/browser/NativeMessagingHost.cpp @@ -207,18 +207,6 @@ void NativeMessagingHost::disconnectSocket() } } -void NativeMessagingHost::removeSharedEncryptionKeys() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeSharedEncryptionKeys(); -} - -void NativeMessagingHost::removeStoredPermissions() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeStoredPermissions(); -} - void NativeMessagingHost::databaseLocked() { QJsonObject response; diff --git a/src/browser/NativeMessagingHost.h b/src/browser/NativeMessagingHost.h index fde8f051c..869af9e24 100644 --- a/src/browser/NativeMessagingHost.h +++ b/src/browser/NativeMessagingHost.h @@ -37,10 +37,6 @@ public: void run(); void stop(); -public slots: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - signals: void quit(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index bee57516e..08068202d 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -84,12 +84,6 @@ public: QWidget* createWidget() override { BrowserOptionDialog* dlg = new BrowserOptionDialog(); - QObject::connect(dlg, - SIGNAL(removeSharedEncryptionKeys()), - m_nativeMessagingHost.data(), - SLOT(removeSharedEncryptionKeys())); - QObject::connect( - dlg, SIGNAL(removeStoredPermissions()), m_nativeMessagingHost.data(), SLOT(removeStoredPermissions())); return dlg; } @@ -108,8 +102,8 @@ public: } } -private: - QSharedPointer m_nativeMessagingHost; + private: + QSharedPointer m_nativeMessagingHost; }; #endif diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp index 8b28ced04..7dd96030c 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp +++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp @@ -21,6 +21,9 @@ #include "DatabaseSettingsWidgetGeneral.h" #include "DatabaseSettingsWidgetEncryption.h" #include "DatabaseSettingsWidgetMasterKey.h" +#ifdef WITH_XC_BROWSER +#include "DatabaseSettingsWidgetBrowser.h" +#endif #include "core/Config.h" #include "core/FilePath.h" @@ -34,6 +37,9 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent) , m_securityTabWidget(new QTabWidget(this)) , m_masterKeyWidget(new DatabaseSettingsWidgetMasterKey(this)) , m_encryptionWidget(new DatabaseSettingsWidgetEncryption(this)) +#ifdef WITH_XC_BROWSER + , m_browserWidget(new DatabaseSettingsWidgetBrowser(this)) +#endif { m_ui->setupUi(this); @@ -55,6 +61,11 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent) connect(m_ui->categoryList, SIGNAL(categoryChanged(int)), m_ui->stackedWidget, SLOT(setCurrentIndex(int))); connect(m_ui->advancedSettingsToggle, SIGNAL(toggled(bool)), SLOT(toggleAdvancedMode(bool))); +#ifdef WITH_XC_BROWSER + m_ui->categoryList->addCategory(tr("Browser Integration"), FilePath::instance()->icon("apps", "internet-web-browser")); + m_ui->stackedWidget->addWidget(m_browserWidget); +#endif + pageChanged(); } @@ -68,6 +79,9 @@ void DatabaseSettingsDialog::load(Database* db) m_generalWidget->load(db); m_masterKeyWidget->load(db); m_encryptionWidget->load(db); +#ifdef WITH_XC_BROWSER + m_browserWidget->load(db); +#endif m_ui->advancedSettingsToggle->setChecked(config()->get("GUI/AdvancedSettings", false).toBool()); m_db = db; } diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.h b/src/gui/dbsettings/DatabaseSettingsDialog.h index 50fec32d6..2dd457cd0 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.h +++ b/src/gui/dbsettings/DatabaseSettingsDialog.h @@ -19,6 +19,7 @@ #define KEEPASSX_DATABASESETTINGSWIDGET_H #include "gui/DialogyWidget.h" +#include "config-keepassx.h" #include #include @@ -27,6 +28,9 @@ class Database; class DatabaseSettingsWidgetGeneral; class DatabaseSettingsWidgetEncryption; class DatabaseSettingsWidgetMasterKey; +#ifdef WITH_XC_BROWSER +class DatabaseSettingsWidgetBrowser; +#endif class QTabWidget; namespace Ui @@ -68,6 +72,9 @@ private: QPointer m_securityTabWidget; QPointer m_masterKeyWidget; QPointer m_encryptionWidget; +#ifdef WITH_XC_BROWSER + QPointer m_browserWidget; +#endif }; #endif // KEEPASSX_DATABASESETTINGSWIDGET_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp new file mode 100644 index 000000000..0dae1486d --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2018 Sami Vänttinen + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "DatabaseSettingsWidgetBrowser.h" +#include +#include "core/Clock.h" +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "core/Metadata.h" +#include "gui/MessageBox.h" +#include "browser/BrowserSettings.h" +#include "ui_DatabaseSettingsWidgetBrowser.h" + +DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent) + : DatabaseSettingsWidget(parent), m_ui(new Ui::DatabaseSettingsWidgetBrowser()) + , m_customData(new CustomData(this)) + , m_customDataModel(new QStandardItemModel(this)) + , m_browserService(nullptr) +{ + m_ui->setupUi(this); + m_ui->removeCustomDataButton->setEnabled(false); + m_ui->customDataTable->setModel(m_customDataModel); + + settingsWarning(); + connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SLOT(toggleRemoveButton(QItemSelection))); + connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedKey())); + connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SLOT(convertAttributesToCustomData())); + connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SLOT(updateSharedKeyList())); + connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SLOT(removeSharedEncryptionKeys())); + connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SLOT(updateSharedKeyList())); + connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SLOT(removeStoredPermissions())); +} + +DatabaseSettingsWidgetBrowser::~DatabaseSettingsWidgetBrowser() +{ +} + +CustomData* DatabaseSettingsWidgetBrowser::customData() const +{ + // Returns the current database customData from metadata. Otherwise return an empty customData member. + if (m_db) { + return m_db->metadata()->customData(); + } + return m_customData; +} + +void DatabaseSettingsWidgetBrowser::initialize() +{ + updateModel(); + settingsWarning(); +} + +void DatabaseSettingsWidgetBrowser::uninitialize() +{ +} + +void DatabaseSettingsWidgetBrowser::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); +} + +bool DatabaseSettingsWidgetBrowser::save() +{ + return true; +} + +void DatabaseSettingsWidgetBrowser::removeSelectedKey() +{ + if (QMessageBox::Yes != MessageBox::question(this, + tr("Delete the selected key?"), + tr("Do you really want to delete the selected key?\n" + "This may prevent connection to the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel(); + if (itemSelectionModel) { + for (const QModelIndex& index : itemSelectionModel->selectedRows(0)) { + QString key = index.data().toString(); + key.insert(0, BrowserService::ASSOCIATE_KEY_PREFIX); + customData()->remove(key); + } + updateModel(); + } +} + +void DatabaseSettingsWidgetBrowser::toggleRemoveButton(const QItemSelection& selected) +{ + m_ui->removeCustomDataButton->setEnabled(!selected.isEmpty()); +} + +void DatabaseSettingsWidgetBrowser::updateModel() +{ + m_customDataModel->clear(); + m_customDataModel->setHorizontalHeaderLabels({tr("Key"), tr("Value")}); + + for (const QString& key : customData()->keys()) { + if (key.startsWith(BrowserService::ASSOCIATE_KEY_PREFIX)) { + QString strippedKey = key; + strippedKey.remove(BrowserService::ASSOCIATE_KEY_PREFIX); + m_customDataModel->appendRow(QList() + << new QStandardItem(strippedKey) + << new QStandardItem(customData()->value(key))); + } + } + + m_ui->removeCustomDataButton->setEnabled(false); +} + +void DatabaseSettingsWidgetBrowser::settingsWarning() +{ + if (!browserSettings()->isEnabled()) { + m_ui->convertToCustomData->setEnabled(false); + m_ui->removeSharedEncryptionKeys->setEnabled(false); + m_ui->removeStoredPermissions->setEnabled(false); + m_ui->customDataTable->setEnabled(false); + m_ui->warningWidget->showMessage(tr("Enable Browser Integration to access these settings."), MessageWidget::Warning); + m_ui->warningWidget->setCloseButtonVisible(false); + m_ui->warningWidget->setAutoHideTimeout(-1); + } else { + m_ui->convertToCustomData->setEnabled(true); + m_ui->removeSharedEncryptionKeys->setEnabled(true); + m_ui->removeStoredPermissions->setEnabled(true); + m_ui->customDataTable->setEnabled(true); + m_ui->warningWidget->hideMessage(); + } +} + +void DatabaseSettingsWidgetBrowser::removeSharedEncryptionKeys() +{ + if (QMessageBox::Yes != MessageBox::question(this, + tr("Disconnect all browsers"), + tr("Do you really want to disconnect all browsers?\n" + "This may prevent connection to the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + QStringList keysToRemove; + for (const QString& key : m_db->metadata()->customData()->keys()) { + if (key.startsWith(BrowserService::ASSOCIATE_KEY_PREFIX)) { + keysToRemove << key; + } + } + + if (keysToRemove.isEmpty()) { + QMessageBox::information(this, + tr("KeePassXC: No keys found"), + tr("No shared encryption keys found in KeePassXC settings."), + QMessageBox::Ok); + return; + } + + for (const QString& key : keysToRemove) { + m_db->metadata()->customData()->remove(key); + } + + const int count = keysToRemove.count(); + QMessageBox::information(this, + tr("KeePassXC: Removed keys from database"), + tr("Successfully removed %n encryption key(s) from KeePassXC settings.", "", count), + QMessageBox::Ok); +} + +void DatabaseSettingsWidgetBrowser::removeStoredPermissions() +{ + if (QMessageBox::Yes != MessageBox::question(this, + tr("Forget all site-specific settings on entries"), + tr("Do you really want forget all site-specific settings on every entry?\n" + "Permissions to access entries will be revoked."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + QList entries = m_db->rootGroup()->entriesRecursive(); + + QProgressDialog progress(tr("Removing stored permissions…"), tr("Abort"), 0, entries.count()); + progress.setWindowModality(Qt::WindowModal); + + uint counter = 0; + for (Entry* entry : entries) { + if (progress.wasCanceled()) { + return; + } + + if (entry->customData()->contains(BrowserService::KEEPASSXCBROWSER_NAME)) { + entry->beginUpdate(); + entry->customData()->remove(BrowserService::KEEPASSXCBROWSER_NAME); + entry->endUpdate(); + ++counter; + } + progress.setValue(progress.value() + 1); + } + progress.reset(); + + if (counter > 0) { + QMessageBox::information(this, + tr("KeePassXC: Removed permissions"), + tr("Successfully removed permissions from %n entry(s).", "", counter), + QMessageBox::Ok); + } else { + QMessageBox::information(this, + tr("KeePassXC: No entry with permissions found!"), + tr("The active database does not contain an entry with permissions."), + QMessageBox::Ok); + } +} + +void DatabaseSettingsWidgetBrowser::convertAttributesToCustomData() +{ + if (QMessageBox::Yes != MessageBox::question(this, + tr("Move KeePassHTTP attributes to custom data"), + tr("Do you really want to move all legacy browser integration data to the latest standard?\n" + "This is necessary to maintain compatibility with the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + m_browserService.convertAttributesToCustomData(m_db); +} + +// Updates the shared key list after the list is cleared +void DatabaseSettingsWidgetBrowser::updateSharedKeyList() +{ + updateModel(); +} + diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h new file mode 100644 index 000000000..0c56ede57 --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2018 Sami Vänttinen + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H +#define KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H + +#include "DatabaseSettingsWidget.h" + +#include +#include +#include +#include +#include "core/CustomData.h" +#include "gui/DatabaseTabWidget.h" +#include "browser/BrowserService.h" + +class Database; +namespace Ui +{ +class DatabaseSettingsWidgetBrowser; +} + +class DatabaseSettingsWidgetBrowser : public DatabaseSettingsWidget +{ + Q_OBJECT + +public: + explicit DatabaseSettingsWidgetBrowser(QWidget* parent = nullptr); + Q_DISABLE_COPY(DatabaseSettingsWidgetBrowser); + ~DatabaseSettingsWidgetBrowser() override; + + CustomData* customData() const; + inline bool hasAdvancedMode() const override { return false; } + +public slots: + void initialize() override; + void uninitialize() override; + bool save() override; + +private slots: + void removeSelectedKey(); + void toggleRemoveButton(const QItemSelection& selected); + void updateSharedKeyList(); + void removeSharedEncryptionKeys(); + void removeStoredPermissions(); + void convertAttributesToCustomData(); + +private: + void updateModel(); + void settingsWarning(); + +protected: + void showEvent(QShowEvent* event) override; + + const QScopedPointer m_ui; + +private: + QPointer m_customData; + QPointer m_customDataModel; + BrowserService m_browserService; +}; + +#endif //KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui new file mode 100644 index 000000000..dfd814bdc --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui @@ -0,0 +1,174 @@ + + + DatabaseSettingsWidgetBrowser + + + + 0 + 0 + 453 + 374 + + + + + 0 + 0 + + + + + 450 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + KeePassXC-Browser settings + + + + + + + + + 0 + 0 + + + + &Disconnect all browsers + + + + + + + + 0 + 0 + + + + Forg&et all site-specific settings on entries + + + + + + + + + + + + 0 + 0 + + + + Move KeePassHTTP attributes to KeePassXC-Browser &custom data + + + + + + + + + + + + Stored keys + + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + 200 + + + true + + + false + + + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + MessageWidget + QWidget +
gui/MessageWidget.h
+ 1 +
+
+ + +
From f31d65bdafcfef491654298db532fee2f11a7877 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Fri, 26 Oct 2018 15:19:04 +0200 Subject: [PATCH 23/84] Replace deprecated parts of the code (#2419) Performing a dev build against the latest version of Qt failed because of some deprecated members. They have been replaced according to the Qt documentation. Further, Q_OS_MACOS is now the only macro available to identify a machine running macOS, the others are now deprecated. See https://doc.qt.io/qt-5/qtglobal.html#Q_OS_OSX and https://doc.qt.io/qt-5/qtglobal.html#Q_OS_MAC. --- src/autotype/AutoType.cpp | 6 +++--- src/autotype/AutoTypePlatformPlugin.h | 2 +- src/autotype/AutoTypeSelectDialog.cpp | 8 ++++++++ src/autotype/test/AutoTypeTest.cpp | 2 +- src/autotype/test/AutoTypeTest.h | 2 +- src/browser/HostInstaller.cpp | 2 +- src/browser/NativeMessagingBase.cpp | 4 ++-- src/core/Bootstrap.cpp | 2 +- src/core/Config.cpp | 2 +- src/core/FilePath.cpp | 6 +++--- src/core/Metadata.cpp | 4 ++++ src/core/ScreenLockListenerPrivate.cpp | 4 ++-- src/gui/Application.cpp | 6 +++--- src/gui/Clipboard.cpp | 6 +++--- src/gui/Clipboard.h | 4 ++-- src/gui/DatabaseWidget.cpp | 2 +- src/gui/DialogyWidget.cpp | 2 +- src/gui/FileDialog.cpp | 2 +- src/gui/KMessageWidget.cpp | 2 +- src/gui/MainWindow.cpp | 12 ++++++------ src/gui/MainWindow.h | 2 +- src/gui/csvImport/CsvImportWidget.cpp | 12 ++++++------ src/gui/csvImport/CsvImportWidget.h | 4 +--- src/gui/entry/AutoTypeMatchView.cpp | 2 +- src/gui/entry/EntryView.cpp | 2 +- src/main.cpp | 2 +- 26 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 89c24e55e..c2c16bc75 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -142,7 +142,7 @@ QStringList AutoType::windowTitles() void AutoType::raiseWindow() { -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) m_plugin->raiseOwnWindow(); #endif } @@ -213,7 +213,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c } if (hideWindow) { -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) m_plugin->raiseLastActiveWindow(); #else hideWindow->showMinimized(); @@ -327,7 +327,7 @@ void AutoType::performGlobalAutoType(const QList& dbList) connect(selectDialog, SIGNAL(rejected()), SLOT(autoTypeRejectedFromGlobal())); selectDialog->setMatchList(matchList); -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) m_plugin->raiseOwnWindow(); Tools::wait(500); #endif diff --git a/src/autotype/AutoTypePlatformPlugin.h b/src/autotype/AutoTypePlatformPlugin.h index 8ec48a96e..68cf99be2 100644 --- a/src/autotype/AutoTypePlatformPlugin.h +++ b/src/autotype/AutoTypePlatformPlugin.h @@ -42,7 +42,7 @@ public: virtual AutoTypeExecutor* createExecutor() = 0; -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) virtual bool raiseLastActiveWindow() = 0; virtual bool raiseOwnWindow() = 0; #endif diff --git a/src/autotype/AutoTypeSelectDialog.cpp b/src/autotype/AutoTypeSelectDialog.cpp index a4fd98dec..7178c70f9 100644 --- a/src/autotype/AutoTypeSelectDialog.cpp +++ b/src/autotype/AutoTypeSelectDialog.cpp @@ -19,7 +19,11 @@ #include "AutoTypeSelectDialog.h" #include +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#else #include +#endif #include #include #include @@ -44,7 +48,11 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent) setWindowTitle(tr("Auto-Type - KeePassXC")); setWindowIcon(filePath()->applicationIcon()); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + QRect screenGeometry = QApplication::screenAt(QCursor::pos())->availableGeometry(); +#else QRect screenGeometry = QApplication::desktop()->availableGeometry(QCursor::pos()); +#endif QSize size = config()->get("GUI/AutoTypeSelectDialogSize", QSize(600, 250)).toSize(); size.setWidth(qMin(size.width(), screenGeometry.width())); size.setHeight(qMin(size.height(), screenGeometry.height())); diff --git a/src/autotype/test/AutoTypeTest.cpp b/src/autotype/test/AutoTypeTest.cpp index e2ae7c692..f8754ef3b 100644 --- a/src/autotype/test/AutoTypeTest.cpp +++ b/src/autotype/test/AutoTypeTest.cpp @@ -110,7 +110,7 @@ bool AutoTypePlatformTest::raiseWindow(WId window) return false; } -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) bool AutoTypePlatformTest::raiseLastActiveWindow() { return false; diff --git a/src/autotype/test/AutoTypeTest.h b/src/autotype/test/AutoTypeTest.h index 767c3c729..a17028b51 100644 --- a/src/autotype/test/AutoTypeTest.h +++ b/src/autotype/test/AutoTypeTest.h @@ -43,7 +43,7 @@ public: bool raiseWindow(WId window) override; AutoTypeExecutor* createExecutor() override; -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) bool raiseLastActiveWindow() override; bool raiseOwnWindow() override; #endif diff --git a/src/browser/HostInstaller.cpp b/src/browser/HostInstaller.cpp index a2f4c74b8..5d7eb3b3b 100644 --- a/src/browser/HostInstaller.cpp +++ b/src/browser/HostInstaller.cpp @@ -33,7 +33,7 @@ HostInstaller::HostInstaller() , ALLOWED_EXTENSIONS(QStringList() << "keepassxc-browser@keepassxc.org") , ALLOWED_ORIGINS(QStringList() << "chrome-extension://iopaggbpplllidnfmcghoonnokmjoicf/" << "chrome-extension://oboonakemofpalcgghocfoadofidjkkk/") -#if defined(Q_OS_OSX) +#if defined(Q_OS_MACOS) , TARGET_DIR_CHROME("/Library/Application Support/Google/Chrome/NativeMessagingHosts") , TARGET_DIR_CHROMIUM("/Library/Application Support/Chromium/NativeMessagingHosts") , TARGET_DIR_FIREFOX("/Library/Application Support/Mozilla/NativeMessagingHosts") diff --git a/src/browser/NativeMessagingBase.cpp b/src/browser/NativeMessagingBase.cpp index ef35705aa..87c44b42c 100644 --- a/src/browser/NativeMessagingBase.cpp +++ b/src/browser/NativeMessagingBase.cpp @@ -135,11 +135,11 @@ void NativeMessagingBase::sendReply(const QString& reply) QString NativeMessagingBase::getLocalServerPath() const { const QString serverPath = "/kpxc_server"; -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) // Use XDG_RUNTIME_DIR instead of /tmp if it's available QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); return path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverPath : path + serverPath; -#else // Q_OS_MAC, Q_OS_WIN and others +#else // Q_OS_MACOS, Q_OS_WIN and others return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverPath; #endif } diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp index 70ca7eec4..0610def8b 100644 --- a/src/core/Bootstrap.cpp +++ b/src/core/Bootstrap.cpp @@ -57,7 +57,7 @@ void bootstrapApplication() applyEarlyQNetworkAccessManagerWorkaround(); Translator::installTranslators(); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // Don't show menu icons on OSX QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); #endif diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 2c92d47a0..13d375e7d 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -110,7 +110,7 @@ Config::Config(QObject* parent) QString userPath; QString homePath = QDir::homePath(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) // we can't use QStandardPaths on X11 as it uses XDG_DATA_HOME instead of XDG_CONFIG_HOME QByteArray env = qgetenv("XDG_CONFIG_HOME"); if (env.isEmpty()) { diff --git a/src/core/FilePath.cpp b/src/core/FilePath.cpp index 55b4724a9..5b0322707 100644 --- a/src/core/FilePath.cpp +++ b/src/core/FilePath.cpp @@ -50,7 +50,7 @@ QString FilePath::pluginPath(const QString& name) // for TestAutoType pluginPaths << QCoreApplication::applicationDirPath() + "/../src/autotype/test"; -#if defined(Q_OS_MAC) && defined(WITH_APP_BUNDLE) +#if defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE) pluginPaths << QCoreApplication::applicationDirPath() + "/../PlugIns"; #endif @@ -223,13 +223,13 @@ FilePath::FilePath() else if (testSetDir(QString(KEEPASSX_SOURCE_DIR) + "/share")) { } #endif -#if defined(Q_OS_UNIX) && !(defined(Q_OS_MAC) && defined(WITH_APP_BUNDLE)) +#if defined(Q_OS_UNIX) && !(defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE)) else if (isDataDirAbsolute && testSetDir(KEEPASSX_DATA_DIR)) { } else if (!isDataDirAbsolute && testSetDir(QString("%1/../%2").arg(appDirPath, KEEPASSX_DATA_DIR))) { } else if (!isDataDirAbsolute && testSetDir(QString("%1/%2").arg(KEEPASSX_PREFIX_DIR, KEEPASSX_DATA_DIR))) { } #endif -#if defined(Q_OS_MAC) && defined(WITH_APP_BUNDLE) +#if defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE) else if (testSetDir(appDirPath + "/../Resources")) { } #endif diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp index ac9d38fda..fb247b891 100644 --- a/src/core/Metadata.cpp +++ b/src/core/Metadata.cpp @@ -450,7 +450,11 @@ void Metadata::copyCustomIcons(const QSet& iconList, const Metadata* othe QByteArray Metadata::hashImage(const QImage& image) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + auto data = QByteArray(reinterpret_cast(image.bits()), static_cast(image.sizeInBytes())); +#else auto data = QByteArray(reinterpret_cast(image.bits()), image.byteCount()); +#endif return QCryptographicHash::hash(data, QCryptographicHash::Md5); } diff --git a/src/core/ScreenLockListenerPrivate.cpp b/src/core/ScreenLockListenerPrivate.cpp index 1371a0c92..9dce17d4d 100644 --- a/src/core/ScreenLockListenerPrivate.cpp +++ b/src/core/ScreenLockListenerPrivate.cpp @@ -16,7 +16,7 @@ */ #include "ScreenLockListenerPrivate.h" -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) #include "ScreenLockListenerMac.h" #elif defined(Q_OS_UNIX) #include "ScreenLockListenerDBus.h" @@ -31,7 +31,7 @@ ScreenLockListenerPrivate::ScreenLockListenerPrivate(QWidget* parent) ScreenLockListenerPrivate* ScreenLockListenerPrivate::instance(QWidget* parent) { -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) Q_UNUSED(parent); return ScreenLockListenerMac::instance(); #elif defined(Q_OS_UNIX) diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index 02209b142..cdd4df03f 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -44,7 +44,7 @@ namespace const char BlockSizeProperty[] = "blockSize"; } -#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) class XcbEventFilter : public QAbstractNativeEventFilter { public: @@ -92,7 +92,7 @@ Application::Application(int& argc, char** argv) , m_alreadyRunning(false) , m_lockFile(nullptr) { -#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) installNativeEventFilter(new XcbEventFilter()); #elif defined(Q_OS_WIN) installNativeEventFilter(new WinEventFilter()); @@ -195,7 +195,7 @@ bool Application::event(QEvent* event) emit openFile(static_cast(event)->file()); return true; } -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // restore main window when clicking on the docker icon else if (event->type() == QEvent::ApplicationActivate) { emit applicationActivated(); diff --git a/src/gui/Clipboard.cpp b/src/gui/Clipboard.cpp index 9f4d7ed62..a0978bfd4 100644 --- a/src/gui/Clipboard.cpp +++ b/src/gui/Clipboard.cpp @@ -26,7 +26,7 @@ #include "core/Config.h" Clipboard* Clipboard::m_instance(nullptr); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS QPointer Clipboard::m_pasteboard(nullptr); #endif @@ -34,7 +34,7 @@ Clipboard::Clipboard(QObject* parent) : QObject(parent) , m_timer(new QTimer(this)) { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS if (!m_pasteboard) { m_pasteboard = new MacPasteboard(); } @@ -49,7 +49,7 @@ void Clipboard::setText(const QString& text) QClipboard* clipboard = QApplication::clipboard(); QMimeData* mime = new QMimeData; -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS mime->setText(text); mime->setData("application/x-nspasteboard-concealed-type", text.toUtf8()); clipboard->setMimeData(mime, QClipboard::Clipboard); diff --git a/src/gui/Clipboard.h b/src/gui/Clipboard.h index 062d360f0..2748ba6da 100644 --- a/src/gui/Clipboard.h +++ b/src/gui/Clipboard.h @@ -20,7 +20,7 @@ #define KEEPASSX_CLIPBOARD_H #include -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS #include "core/MacPasteboard.h" #include #endif @@ -48,7 +48,7 @@ private: static Clipboard* m_instance; QTimer* m_timer; -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // This object lives for the whole program lifetime and we cannot delete it on exit, // so ignore leak warnings. See https://bugreports.qt.io/browse/QTBUG-54832 static QPointer m_pasteboard; diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index e34a363ba..7b36f4668 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1432,7 +1432,7 @@ void DatabaseWidget::showUnlockDialog() m_unlockDatabaseDialog->clearForms(); m_unlockDatabaseDialog->setFilePath(m_filePath); -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) autoType()->raiseWindow(); Tools::wait(500); #endif diff --git a/src/gui/DialogyWidget.cpp b/src/gui/DialogyWidget.cpp index 2c0bf35e3..89e114f10 100644 --- a/src/gui/DialogyWidget.cpp +++ b/src/gui/DialogyWidget.cpp @@ -28,7 +28,7 @@ DialogyWidget::DialogyWidget(QWidget* parent) void DialogyWidget::keyPressEvent(QKeyEvent* e) { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS if (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { if (!clickButton(QDialogButtonBox::Cancel)) { e->ignore(); diff --git a/src/gui/FileDialog.cpp b/src/gui/FileDialog.cpp index d58f52928..5bccc4af3 100644 --- a/src/gui/FileDialog.cpp +++ b/src/gui/FileDialog.cpp @@ -97,7 +97,7 @@ QString FileDialog::getSaveFileName(QWidget* parent, } QString result; -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) +#if defined(Q_OS_MACOS) || defined(Q_OS_WIN) Q_UNUSED(defaultExtension); // the native dialogs on these platforms already append the file extension result = QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options); diff --git a/src/gui/KMessageWidget.cpp b/src/gui/KMessageWidget.cpp index 910f0c91b..3d0500708 100644 --- a/src/gui/KMessageWidget.cpp +++ b/src/gui/KMessageWidget.cpp @@ -102,7 +102,7 @@ void KMessageWidgetPrivate::init(KMessageWidget *q_ptr) closeButton->setAutoRaise(true); closeButton->setDefaultAction(closeAction); closeButtonPixmap = QPixmap(closeButton->icon().pixmap(closeButton->icon().actualSize(QSize(16, 16)))); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS closeButton->setStyleSheet("QToolButton { background: transparent;" "border-radius: 2px; padding: 3px; }" "QToolButton::hover, QToolButton::focus {" diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 08068202d..9efe0c273 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -47,7 +47,7 @@ #include "browser/NativeMessagingHost.h" #endif -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS) #include "gui/MainWindowAdaptor.h" #include #include @@ -117,7 +117,7 @@ MainWindow::MainWindow() { m_ui->setupUi(this); -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS) new MainWindowAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject("/keepassxc", this); @@ -314,7 +314,7 @@ MainWindow::MainWindow() connect(m_ui->actionDonate, SIGNAL(triggered()), SLOT(openDonateUrl())); connect(m_ui->actionBugReport, SIGNAL(triggered()), SLOT(openBugReportUrl())); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS setUnifiedTitleAndToolBarOnMac(true); #endif @@ -817,7 +817,7 @@ void MainWindow::updateTrayIcon() QAction* actionToggle = new QAction(tr("Toggle window"), menu); menu->addAction(actionToggle); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS QAction* actionQuit = new QAction(tr("Quit KeePassXC"), menu); menu->addAction(actionQuit); @@ -918,7 +918,7 @@ void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason) void MainWindow::hideWindow() { saveWindowInformation(); -#if !defined(Q_OS_LINUX) && !defined(Q_OS_MAC) +#if !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) // On some Linux systems, the window should NOT be minimized and hidden (i.e. not shown), at // the same time (which would happen if both minimize on startup and minimize to tray are set) // since otherwise it causes problems on restore as seen on issue #1595. Hiding it is enough. @@ -944,7 +944,7 @@ void MainWindow::toggleWindow() } else { bringToFront(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS) && (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS) && (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) // re-register global D-Bus menu (needed on Ubuntu with Unity) // see https://github.com/keepassxreboot/keepassxc/issues/271 // and https://bugreports.qt.io/browse/QTBUG-58723 diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 9c2f41cef..d83b4a1ff 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -39,7 +39,7 @@ class MainWindow : public QMainWindow { Q_OBJECT -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS) Q_CLASSINFO("D-Bus Interface", "org.keepassxc.KeePassXC.MainWindow") #endif diff --git a/src/gui/csvImport/CsvImportWidget.cpp b/src/gui/csvImport/CsvImportWidget.cpp index 662a9744e..4cb219a77 100644 --- a/src/gui/csvImport/CsvImportWidget.cpp +++ b/src/gui/csvImport/CsvImportWidget.cpp @@ -37,7 +37,6 @@ CsvImportWidget::CsvImportWidget(QWidget* parent) , m_ui(new Ui::CsvImportWidget()) , m_parserModel(new CsvParserModel(this)) , m_comboModel(new QStringListModel(this)) - , m_comboMapper(new QSignalMapper(this)) , m_columnHeader(QStringList() << QObject::tr("Group") << QObject::tr("Title") << QObject::tr("Username") << QObject::tr("Password") << QObject::tr("URL") << QObject::tr("Notes") << QObject::tr("Last Modified") << QObject::tr("Created") @@ -87,8 +86,11 @@ CsvImportWidget::CsvImportWidget(QWidget* parent) combo->setFont(font); m_combos.append(combo); combo->setModel(m_comboModel); - m_comboMapper->setMapping(combo, i); - connect(combo, SIGNAL(currentIndexChanged(int)), m_comboMapper, SLOT(map())); +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + connect(combo, QOverload::of(&QComboBox::currentIndexChanged), [=]{ comboChanged(combo, i); }); +#else + connect(combo, static_cast(&QComboBox::currentIndexChanged), [=]{ comboChanged(combo, i); }); +#endif // layout labels and combo fields in column-first order int combo_rows = 1 + (m_columnHeader.count() - 1) / 2; @@ -110,15 +112,13 @@ CsvImportWidget::CsvImportWidget(QWidget* parent) connect(m_ui->comboBoxFieldSeparator, SIGNAL(currentIndexChanged(int)), SLOT(parse())); connect(m_ui->checkBoxBackslash, SIGNAL(toggled(bool)), SLOT(parse())); connect(m_ui->checkBoxFieldNames, SIGNAL(toggled(bool)), SLOT(updatePreview())); - connect(m_comboMapper, SIGNAL(mapped(int)), this, SLOT(comboChanged(int))); connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(writeDatabase())); connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } -void CsvImportWidget::comboChanged(int comboId) +void CsvImportWidget::comboChanged(QComboBox* currentSender, int comboId) { - QComboBox* currentSender = qobject_cast(m_comboMapper->mapping(comboId)); if (currentSender->currentIndex() != -1) { // this line is the one that actually updates GUI table m_parserModel->mapColumns(currentSender->currentIndex(), comboId); diff --git a/src/gui/csvImport/CsvImportWidget.h b/src/gui/csvImport/CsvImportWidget.h index 2a68c95e9..cd13836f0 100644 --- a/src/gui/csvImport/CsvImportWidget.h +++ b/src/gui/csvImport/CsvImportWidget.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -50,7 +49,7 @@ signals: private slots: void parse(); - void comboChanged(int comboId); + void comboChanged(QComboBox* currentSender, int comboId); void skippedChanged(int rows); void writeDatabase(); void updatePreview(); @@ -62,7 +61,6 @@ private: const QScopedPointer m_ui; CsvParserModel* const m_parserModel; QStringListModel* const m_comboModel; - QSignalMapper* m_comboMapper; QList m_combos; Database* m_db; diff --git a/src/gui/entry/AutoTypeMatchView.cpp b/src/gui/entry/AutoTypeMatchView.cpp index a0667b310..2750082d8 100644 --- a/src/gui/entry/AutoTypeMatchView.cpp +++ b/src/gui/entry/AutoTypeMatchView.cpp @@ -51,7 +51,7 @@ void AutoTypeMatchView::keyPressEvent(QKeyEvent* event) { if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) { emitMatchActivated(currentIndex()); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // Pressing return does not emit the QTreeView::activated signal on mac os emit activated(currentIndex()); #endif diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 8c573e9ea..05c55ad82 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -123,7 +123,7 @@ void EntryView::keyPressEvent(QKeyEvent* event) { if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) { emitEntryActivated(currentIndex()); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // Pressing return does not emit the QTreeView::activated signal on mac os emit activated(currentIndex()); #endif diff --git a/src/main.cpp b/src/main.cpp index 2a1fa324c..0b5e0b1cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,7 +39,7 @@ #if defined(Q_OS_WIN) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) -#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC) +#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) #endif #endif From c9cab250c7f35b0a277eb2626b50e580b8a410a0 Mon Sep 17 00:00:00 2001 From: Nathan Merritt Date: Sun, 28 Oct 2018 13:51:20 -0400 Subject: [PATCH 24/84] Only check for scheme matches when an entry has a scheme (#2426) --- src/browser/BrowserService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index d66637ff5..9917ded3c 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -397,7 +397,7 @@ QList BrowserService::searchEntries(Database* db, const QString& hostnam // Ignore entry if port or scheme defined in the URL doesn't match if ((entryQUrl.port() > 0 && entryQUrl.port() != qUrl.port()) || - (browserSettings()->matchUrlScheme() && entryScheme.compare(qUrl.scheme()) != 0)) { + (browserSettings()->matchUrlScheme() && !entryScheme.isEmpty() && entryScheme.compare(qUrl.scheme()) != 0)) { continue; } From 7263dcddfe52552747c478da941f1f078d8d91e0 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sun, 28 Oct 2018 19:55:00 +0100 Subject: [PATCH 25/84] Fix stdin/stdout encoding on Windows. (#2425) QTextStream uses the system default locale, but this breaks in various situations: (1) It does not work on the native Windows shell (cmd.exe, Powershell), since the default Windows locale is Windows-1252, but the shell uses Windows-850. (2) It also breaks on *nix systems where the locale is Latin1 or C, which is the case for most CI systems or build servers. We allow overriding the detected codec by setting the ENCODING_OVERRIDE environment variable, but otherwise prefer Windows-850 on Windows and UTF-8 on any other system, even if LANG is set to something else. This resolves #2413 --- src/CMakeLists.txt | 3 +- src/cli/Add.cpp | 8 ++--- src/cli/Clip.cpp | 8 ++--- src/cli/Diceware.cpp | 6 ++-- src/cli/Edit.cpp | 8 ++--- src/cli/Estimate.cpp | 8 ++--- src/cli/Extract.cpp | 6 ++-- src/cli/Generate.cpp | 10 +++--- src/cli/List.cpp | 8 ++--- src/cli/Locate.cpp | 10 +++--- src/cli/Merge.cpp | 6 ++-- src/cli/Remove.cpp | 8 ++--- src/cli/Show.cpp | 10 +++--- src/cli/TextStream.cpp | 73 +++++++++++++++++++++++++++++++++++++++ src/cli/TextStream.h | 50 +++++++++++++++++++++++++++ src/cli/Utils.cpp | 6 ++-- src/cli/Utils.h | 2 +- src/cli/keepassxc-cli.cpp | 4 +-- tests/TestCli.cpp | 7 ++-- 19 files changed, 180 insertions(+), 61 deletions(-) create mode 100644 src/cli/TextStream.cpp create mode 100644 src/cli/TextStream.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37fe0b553..c6f614568 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,10 +70,9 @@ set(keepassx_SOURCES core/Clock.cpp core/Tools.cpp core/Translator.cpp - core/Base32.h core/Base32.cpp cli/Utils.cpp - cli/Utils.h + cli/TextStream.cpp crypto/Crypto.cpp crypto/CryptoHash.cpp crypto/Random.cpp diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index 81a5cad13..09a161071 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -21,8 +21,8 @@ #include "Add.h" #include -#include +#include "cli/TextStream.h" #include "cli/Utils.h" #include "core/Database.h" #include "core/Entry.h" @@ -41,9 +41,9 @@ Add::~Add() int Add::execute(const QStringList& arguments) { - QTextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly); - QTextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); + TextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 0b78a24b4..f04c8b5e9 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -23,8 +23,8 @@ #include "Clip.h" #include -#include +#include "cli/TextStream.h" #include "cli/Utils.h" #include "core/Database.h" #include "core/Entry.h" @@ -42,7 +42,7 @@ Clip::~Clip() int Clip::execute(const QStringList& arguments) { - QTextStream out(Utils::STDOUT); + TextStream out(Utils::STDOUT); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -73,7 +73,7 @@ int Clip::execute(const QStringList& arguments) int Clip::clipEntry(Database* database, QString entryPath, QString timeout) { - QTextStream err(Utils::STDERR); + TextStream err(Utils::STDERR); int timeoutSeconds = 0; if (!timeout.isEmpty() && !timeout.toInt()) { @@ -83,7 +83,7 @@ int Clip::clipEntry(Database* database, QString entryPath, QString timeout) timeoutSeconds = timeout.toInt(); } - QTextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntry(entryPath); if (!entry) { err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; diff --git a/src/cli/Diceware.cpp b/src/cli/Diceware.cpp index 72cbd1960..d2821e7cc 100644 --- a/src/cli/Diceware.cpp +++ b/src/cli/Diceware.cpp @@ -21,8 +21,8 @@ #include "Diceware.h" #include -#include +#include "cli/TextStream.h" #include "core/PassphraseGenerator.h" #include "Utils.h" @@ -38,8 +38,8 @@ Diceware::~Diceware() int Diceware::execute(const QStringList& arguments) { - QTextStream in(Utils::STDIN, QIODevice::ReadOnly); - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream in(Utils::STDIN, QIODevice::ReadOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index c2f067794..91a76b195 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -21,8 +21,8 @@ #include "Edit.h" #include -#include +#include "cli/TextStream.h" #include "cli/Utils.h" #include "core/Database.h" #include "core/Entry.h" @@ -41,9 +41,9 @@ Edit::~Edit() int Edit::execute(const QStringList& arguments) { - QTextStream in(Utils::STDIN, QIODevice::ReadOnly); - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream in(Utils::STDIN, QIODevice::ReadOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream err(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); diff --git a/src/cli/Estimate.cpp b/src/cli/Estimate.cpp index c249d7b1f..db32f8dc7 100644 --- a/src/cli/Estimate.cpp +++ b/src/cli/Estimate.cpp @@ -19,8 +19,8 @@ #include "cli/Utils.h" #include -#include +#include "cli/TextStream.h" #include #include #include @@ -45,7 +45,7 @@ Estimate::~Estimate() static void estimate(const char* pwd, bool advanced) { - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); double e = 0.0; int len = static_cast(strlen(pwd)); @@ -150,8 +150,8 @@ static void estimate(const char* pwd, bool advanced) int Estimate::execute(const QStringList& arguments) { - QTextStream in(Utils::STDIN, QIODevice::ReadOnly); - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream in(Utils::STDIN, QIODevice::ReadOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index cc39c469a..32b1bc028 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -22,8 +22,8 @@ #include #include -#include +#include "cli/TextStream.h" #include "cli/Utils.h" #include "core/Database.h" #include "format/KeePass2Reader.h" @@ -43,8 +43,8 @@ Extract::~Extract() int Extract::execute(const QStringList& arguments) { - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream err(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); diff --git a/src/cli/Generate.cpp b/src/cli/Generate.cpp index 7780aa829..da2deb8d8 100644 --- a/src/cli/Generate.cpp +++ b/src/cli/Generate.cpp @@ -22,8 +22,8 @@ #include "cli/Utils.h" #include -#include +#include "cli/TextStream.h" #include "core/PasswordGenerator.h" Generate::Generate() @@ -38,9 +38,8 @@ Generate::~Generate() int Generate::execute(const QStringList& arguments) { - QTextStream in(Utils::STDIN, QIODevice::ReadOnly); - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - out.setCodec("UTF-8"); // force UTF-8 to prevent ??? characters in extended-ASCII passwords + TextStream in(Utils::STDIN, QIODevice::ReadOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -87,8 +86,7 @@ int Generate::execute(const QStringList& arguments) if (parser.value(len).isEmpty()) { passwordGenerator.setLength(PasswordGenerator::DefaultLength); } else { - int length = parser.value(len).toInt(); - passwordGenerator.setLength(static_cast(length)); + passwordGenerator.setLength(parser.value(len).toInt()); } PasswordGenerator::CharClasses classes = 0x0; diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 4d1ebcfc5..6d85404ff 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -22,8 +22,8 @@ #include "cli/Utils.h" #include -#include +#include "cli/TextStream.h" #include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" @@ -40,7 +40,7 @@ List::~List() int List::execute(const QStringList& arguments) { - QTextStream out(Utils::STDOUT); + TextStream out(Utils::STDOUT); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -77,8 +77,8 @@ int List::execute(const QStringList& arguments) int List::listGroup(Database* database, bool recursive, const QString& groupPath) { - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream err(Utils::STDERR, QIODevice::WriteOnly); if (groupPath.isEmpty()) { out << database->rootGroup()->print(recursive) << flush; diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index 3bca8ae1d..8ab8f4c61 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -1,5 +1,3 @@ -#include - /* * Copyright (C) 2017 KeePassXC Team * @@ -24,8 +22,8 @@ #include #include -#include +#include "cli/TextStream.h" #include "cli/Utils.h" #include "core/Global.h" #include "core/Database.h" @@ -44,7 +42,7 @@ Locate::~Locate() int Locate::execute(const QStringList& arguments) { - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -73,8 +71,8 @@ int Locate::execute(const QStringList& arguments) int Locate::locateEntry(Database* database, const QString& searchTerm) { - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream err(Utils::STDERR, QIODevice::WriteOnly); QStringList results = database->rootGroup()->locate(searchTerm); if (results.isEmpty()) { diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index a5b4a2cb7..e81b7aa99 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -18,8 +18,8 @@ #include "Merge.h" #include -#include +#include "cli/TextStream.h" #include "core/Database.h" #include "core/Merger.h" #include "cli/Utils.h" @@ -38,8 +38,8 @@ Merge::~Merge() int Merge::execute(const QStringList& arguments) { - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream err(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 6523cff97..4800b5c94 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -23,8 +23,8 @@ #include #include #include -#include +#include "cli/TextStream.h" #include "cli/Utils.h" #include "core/Database.h" #include "core/Entry.h" @@ -44,7 +44,7 @@ Remove::~Remove() int Remove::execute(const QStringList& arguments) { - QTextStream out(Utils::STDERR, QIODevice::WriteOnly); + TextStream out(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::tr("main", "Remove an entry from the database.")); @@ -73,8 +73,8 @@ int Remove::execute(const QStringList& arguments) int Remove::removeEntry(Database* database, const QString& databasePath, const QString& entryPath) { - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream err(Utils::STDERR, QIODevice::WriteOnly); QPointer entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index 5e2ec14b4..032f1fcad 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -21,8 +21,8 @@ #include #include -#include +#include "cli/TextStream.h" #include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" @@ -41,7 +41,7 @@ Show::~Show() int Show::execute(const QStringList& arguments) { - QTextStream out(Utils::STDOUT); + TextStream out(Utils::STDOUT); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -78,9 +78,9 @@ int Show::execute(const QStringList& arguments) int Show::showEntry(Database* database, QStringList attributes, const QString& entryPath) { - QTextStream in(Utils::STDIN, QIODevice::ReadOnly); - QTextStream out(Utils::STDOUT, QIODevice::WriteOnly); - QTextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream in(Utils::STDIN, QIODevice::ReadOnly); + TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream err(Utils::STDERR, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntry(entryPath); if (!entry) { diff --git a/src/cli/TextStream.cpp b/src/cli/TextStream.cpp new file mode 100644 index 000000000..d75cb74a9 --- /dev/null +++ b/src/cli/TextStream.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TextStream.h" + +#include +#include + +TextStream::TextStream() +{ + detectCodec(); +} + +TextStream::TextStream(QIODevice* device) + : QTextStream(device) +{ + detectCodec(); +} + +TextStream::TextStream(FILE* fileHandle, QIODevice::OpenMode openMode) + : QTextStream(fileHandle, openMode) +{ + detectCodec(); +} + +TextStream::TextStream(QString* string, QIODevice::OpenMode openMode) + : QTextStream(string, openMode) +{ + detectCodec(); +} + +TextStream::TextStream(QByteArray* array, QIODevice::OpenMode openMode) + : QTextStream(array, openMode) +{ + detectCodec(); +} + +TextStream::TextStream(const QByteArray& array, QIODevice::OpenMode openMode) + : QTextStream(array, openMode) +{ + detectCodec(); +} + +void TextStream::detectCodec() +{ + QString codecName = "UTF-8"; + auto env = QProcessEnvironment::systemEnvironment(); +#ifdef Q_OS_WIN + if (!env.contains("SHELL")) { + // native shell (no Msys or cygwin) + codecName = "Windows-850"; + } +#endif + codecName = env.value("ENCODING_OVERRIDE", codecName); + auto* codec = QTextCodec::codecForName(codecName.toLatin1()); + if (codec) { + setCodec(codec); + } +} diff --git a/src/cli/TextStream.h b/src/cli/TextStream.h new file mode 100644 index 000000000..971f8651d --- /dev/null +++ b/src/cli/TextStream.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_TEXTSTREAM_H +#define KEEPASSXC_TEXTSTREAM_H + +#include + +/** + * QTextStream with codec fixes for the Windows command line. + * + * QTextStream uses the system default locale, but this breaks in various + * situations: (1) It does not work on the native Windows shell (cmd.exe, Powershell), + * since the default Windows locale is Windows-1252, but the shell uses Windows-850. + * (2) It also breaks on *nix systems where the locale is Latin1 or C, which + * is the case for most CI systems or build servers. + * + * We allow overriding the detected codec by setting the ENCODING_OVERRIDE + * environment variable, but otherwise prefer Windows-850 on Windows and UTF-8 + * on any other system, even if LANG is set to something else. + */ +class TextStream : public QTextStream +{ +public: + TextStream(); + explicit TextStream(QIODevice* device); + explicit TextStream(FILE* fileHandle, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit TextStream(QString* string, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit TextStream(QByteArray* array, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit TextStream(const QByteArray& array, QIODevice::OpenMode openMode = QIODevice::ReadOnly); + +private: + void detectCodec(); +}; + +#endif //KEEPASSXC_TEXTSTREAM_H diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index a0f75bc8e..a7bf83c13 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -97,7 +97,7 @@ void setNextPassword(const QString& password) */ QString getPassword() { - QTextStream out(STDOUT, QIODevice::WriteOnly); + TextStream out(STDOUT, QIODevice::WriteOnly); // return preset password if one is set if (!Test::nextPasswords.isEmpty()) { @@ -107,7 +107,7 @@ QString getPassword() return password; } - QTextStream in(STDIN, QIODevice::ReadOnly); + TextStream in(STDIN, QIODevice::ReadOnly); setStdinEcho(false); QString line = in.readLine(); @@ -123,7 +123,7 @@ QString getPassword() */ int clipText(const QString& text) { - QTextStream err(Utils::STDERR); + TextStream err(Utils::STDERR); QString programName = ""; QStringList arguments; diff --git a/src/cli/Utils.h b/src/cli/Utils.h index 1d5e0f356..a5c995f10 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -19,7 +19,7 @@ #define KEEPASSXC_UTILS_H #include -#include +#include "cli/TextStream.h" namespace Utils { diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 041908663..a2399e741 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -20,8 +20,8 @@ #include #include #include -#include +#include "cli/TextStream.h" #include #include "config-keepassx.h" @@ -46,7 +46,7 @@ int main(int argc, char** argv) Bootstrap::bootstrapApplication(); #endif - QTextStream out(stdout); + TextStream out(stdout); QStringList arguments; for (int i = 0; i < argc; ++i) { arguments << QString(argv[i]); diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index e10a651d7..c95b1f32b 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -455,12 +455,13 @@ void TestCli::testGenerate() qint64 pos = 0; // run multiple times to make accidental passes unlikely + TextStream stream(m_stdoutFile.data()); for (int i = 0; i < 10; ++i) { generateCmd.execute(parameters); - m_stdoutFile->seek(pos); + stream.seek(pos); QRegularExpression regex(pattern); - QString password = QString::fromUtf8(m_stdoutFile->readLine()); - pos = m_stdoutFile->pos(); + QString password = stream.readLine(); + pos = stream.pos(); QVERIFY2(regex.match(password).hasMatch(), qPrintable("Password " + password + " does not match pattern " + pattern)); } } From fa687f246ea056338c2d2b5c736aebc8af99474f Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Tue, 30 Oct 2018 08:42:35 -0400 Subject: [PATCH 26/84] Fix issues with group functions (#2410) --- src/cli/Clip.cpp | 2 +- src/cli/Show.cpp | 2 +- src/core/Group.cpp | 108 ++++++++++++++++++++------------------------ src/core/Group.h | 24 +++------- tests/TestGroup.cpp | 99 +++++++++++++++++++++------------------- tests/TestMerge.cpp | 72 ++++++++++++++--------------- 6 files changed, 147 insertions(+), 160 deletions(-) diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index f04c8b5e9..2268c6624 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -84,7 +84,7 @@ int Clip::clipEntry(Database* database, QString entryPath, QString timeout) } TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); - Entry* entry = database->rootGroup()->findEntry(entryPath); + Entry* entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; return EXIT_FAILURE; diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index 032f1fcad..e474e2489 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -82,7 +82,7 @@ int Show::showEntry(Database* database, QStringList attributes, const QString& e TextStream out(Utils::STDOUT, QIODevice::WriteOnly); TextStream err(Utils::STDERR, QIODevice::WriteOnly); - Entry* entry = database->rootGroup()->findEntry(entryPath); + Entry* entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; return EXIT_FAILURE; diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 4ff6e5b68..dab48ebd1 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -321,9 +321,7 @@ void Group::setNotes(const QString& notes) void Group::setIcon(int iconNumber) { - Q_ASSERT(iconNumber >= 0); - - if (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull()) { + if (iconNumber >= 0 && (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull())) { m_data.iconNumber = iconNumber; m_data.customIcon = QUuid(); emit modified(); @@ -333,9 +331,7 @@ void Group::setIcon(int iconNumber) void Group::setIcon(const QUuid& uuid) { - Q_ASSERT(!uuid.isNull()); - - if (m_data.customIcon != uuid) { + if (!uuid.isNull() && m_data.customIcon != uuid) { m_data.customIcon = uuid; m_data.iconNumber = 0; emit modified(); @@ -552,36 +548,12 @@ QList Group::entriesRecursive(bool includeHistoryItems) const return entryList; } -Entry* Group::findEntry(QString entryId) -{ - Q_ASSERT(!entryId.isNull()); - - Entry* entry; - QUuid entryUuid = QUuid::fromRfc4122(QByteArray::fromHex(entryId.toLatin1())); - if (!entryUuid.isNull()) { - entry = findEntryByUuid(entryUuid); - if (entry) { - return entry; - } - } - - entry = findEntryByPath(entryId); - if (entry) { - return entry; - } - - for (Entry* entry : entriesRecursive(false)) { - if (entry->title() == entryId) { - return entry; - } - } - - return nullptr; -} - Entry* Group::findEntryByUuid(const QUuid& uuid) const { - Q_ASSERT(!uuid.isNull()); + if (uuid.isNull()) { + return nullptr; + } + for (Entry* entry : entriesRecursive(false)) { if (entry->uuid() == uuid) { return entry; @@ -591,20 +563,34 @@ Entry* Group::findEntryByUuid(const QUuid& uuid) const return nullptr; } -Entry* Group::findEntryByPath(QString entryPath, QString basePath) +Entry* Group::findEntryByPath(QString entryPath) { + if (entryPath.isEmpty()) { + return nullptr; + } - Q_ASSERT(!entryPath.isNull()); + // Add a beginning slash if the search string contains a slash + // We don't add a slash by default to allow searching by entry title + QString normalizedEntryPath = entryPath; + if (!normalizedEntryPath.startsWith("/") && normalizedEntryPath.contains("/")) { + normalizedEntryPath = "/" + normalizedEntryPath; + } + return findEntryByPathRecursive(normalizedEntryPath, "/"); +} - for (Entry* entry : asConst(m_entries)) { - QString currentEntryPath = basePath + entry->title(); - if (entryPath == currentEntryPath || entryPath == QString("/" + currentEntryPath)) { +Entry* Group::findEntryByPathRecursive(QString entryPath, QString basePath) +{ + // Return the first entry that matches the full path OR if there is no leading + // slash, return the first entry title that matches + for (Entry* entry : entries()) { + if (entryPath == (basePath + entry->title()) + || (!entryPath.startsWith("/") && entry->title() == entryPath)) { return entry; } } - for (Group* group : asConst(m_children)) { - Entry* entry = group->findEntryByPath(entryPath, basePath + group->name() + QString("/")); + for (Group* group : children()) { + Entry* entry = group->findEntryByPathRecursive(entryPath, basePath + group->name() + "/"); if (entry != nullptr) { return entry; } @@ -615,22 +601,20 @@ Entry* Group::findEntryByPath(QString entryPath, QString basePath) Group* Group::findGroupByPath(QString groupPath) { - Q_ASSERT(!groupPath.isNull()); - // normalize the groupPath by adding missing front and rear slashes. once. QString normalizedGroupPath; - if (groupPath == "") { + if (groupPath.isEmpty()) { normalizedGroupPath = QString("/"); // root group } else { - normalizedGroupPath = ((groupPath.startsWith("/"))? "" : "/") + normalizedGroupPath = (groupPath.startsWith("/") ? "" : "/") + groupPath - + ((groupPath.endsWith("/") )? "" : "/"); + + (groupPath.endsWith("/") ? "" : "/"); } - return findGroupByPathRecursion(normalizedGroupPath, "/"); + return findGroupByPathRecursive(normalizedGroupPath, "/"); } -Group* Group::findGroupByPathRecursion(QString groupPath, QString basePath) +Group* Group::findGroupByPathRecursive(QString groupPath, QString basePath) { // paths must be normalized Q_ASSERT(groupPath.startsWith("/") && groupPath.endsWith("/")); @@ -642,7 +626,7 @@ Group* Group::findGroupByPathRecursion(QString groupPath, QString basePath) for (Group* innerGroup : children()) { QString innerBasePath = basePath + innerGroup->name() + "/"; - Group* group = innerGroup->findGroupByPathRecursion(groupPath, innerBasePath); + Group* group = innerGroup->findGroupByPathRecursive(groupPath, innerBasePath); if (group != nullptr) { return group; } @@ -683,7 +667,7 @@ QList Group::groupsRecursive(bool includeSelf) const groupList.append(this); } - for (const Group* group : m_children) { + for (const Group* group : asConst(m_children)) { groupList.append(group->groupsRecursive(true)); } @@ -728,7 +712,10 @@ QSet Group::customIconsRecursive() const Group* Group::findGroupByUuid(const QUuid& uuid) { - Q_ASSERT(!uuid.isNull()); + if (uuid.isNull()) { + return nullptr; + } + for (Group* group : groupsRecursive(true)) { if (group->uuid() == uuid) { return group; @@ -749,6 +736,11 @@ Group* Group::findChildByName(const QString& name) return nullptr; } +/** + * Creates a duplicate of this group. + * Note that you need to copy the custom icons manually when inserting the + * new group into another database. + */ Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags) const { Group* clonedGroup = new Group(); @@ -936,8 +928,11 @@ bool Group::resolveAutoTypeEnabled() const QStringList Group::locate(QString locateTerm, QString currentPath) { - Q_ASSERT(!locateTerm.isNull()); + // TODO: Replace with EntrySearcher QStringList response; + if (locateTerm.isEmpty()) { + return response; + } for (Entry* entry : asConst(m_entries)) { QString entryPath = currentPath + entry->title(); @@ -957,20 +952,15 @@ QStringList Group::locate(QString locateTerm, QString currentPath) Entry* Group::addEntryWithPath(QString entryPath) { - Q_ASSERT(!entryPath.isNull()); - if (this->findEntryByPath(entryPath)) { + if (entryPath.isEmpty() || findEntryByPath(entryPath)) { return nullptr; } QStringList groups = entryPath.split("/"); QString entryTitle = groups.takeLast(); QString groupPath = groups.join("/"); - if (groupPath.isNull()) { - groupPath = QString(""); - } - Q_ASSERT(!groupPath.isNull()); - Group* group = this->findGroupByPath(groupPath); + Group* group = findGroupByPath(groupPath); if (!group) { return nullptr; } diff --git a/src/core/Group.h b/src/core/Group.h index 89343e829..e2b55cbd4 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -114,12 +114,11 @@ public: static const QString RootAutoTypeSequence; Group* findChildByName(const QString& name); - Entry* findEntry(QString entryId); Entry* findEntryByUuid(const QUuid& uuid) const; - Entry* findEntryByPath(QString entryPath, QString basePath = QString("")); + Entry* findEntryByPath(QString entryPath); Group* findGroupByUuid(const QUuid& uuid); Group* findGroupByPath(QString groupPath); - QStringList locate(QString locateTerm, QString currentPath = QString("/")); + QStringList locate(QString locateTerm, QString currentPath = {"/"}); Entry* addEntryWithPath(QString entryPath); void setUuid(const QUuid& uuid); void setName(const QString& name); @@ -154,11 +153,7 @@ public: QList groupsRecursive(bool includeSelf) const; QList groupsRecursive(bool includeSelf); QSet customIconsRecursive() const; - /** - * Creates a duplicate of this group. - * Note that you need to copy the custom icons manually when inserting the - * new group into another database. - */ + Group* clone(Entry::CloneFlags entryFlags = DefaultEntryCloneFlags, CloneFlags groupFlags = DefaultCloneFlags) const; @@ -167,28 +162,22 @@ public: void addEntry(Entry* entry); void removeEntry(Entry* entry); + signals: void dataChanged(Group* group); - void aboutToAdd(Group* group, int index); void added(); void aboutToRemove(Group* group); void removed(); - /** - * Group moved within the database. - */ void aboutToMove(Group* group, Group* toGroup, int index); void moved(); - + void modified(); void entryAboutToAdd(Entry* entry); void entryAdded(Entry* entry); void entryAboutToRemove(Entry* entry); void entryRemoved(Entry* entry); - void entryDataChanged(Entry* entry); - void modified(); - private slots: void updateTimeinfo(); @@ -201,7 +190,8 @@ private: void cleanupParent(); void recCreateDelObjects(); - Group* findGroupByPathRecursion(QString groupPath, QString basePath); + Entry* findEntryByPathRecursive(QString entryPath, QString basePath); + Group* findGroupByPathRecursive(QString groupPath, QString basePath); QPointer m_db; QUuid m_uuid; diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 9ee9389c0..7abb808d1 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -493,57 +493,64 @@ void TestGroup::testFindEntry() Entry* entry; - entry = db->rootGroup()->findEntry(entry1->uuidToHex()); - QVERIFY(entry != nullptr); + entry = db->rootGroup()->findEntryByUuid(entry1->uuid()); + QVERIFY(entry); QCOMPARE(entry->title(), QString("entry1")); - entry = db->rootGroup()->findEntry(QString("entry1")); - QVERIFY(entry != nullptr); + entry = db->rootGroup()->findEntryByPath(QString("entry1")); + QVERIFY(entry); QCOMPARE(entry->title(), QString("entry1")); // We also can find the entry with the leading slash. - entry = db->rootGroup()->findEntry(QString("/entry1")); - QVERIFY(entry != nullptr); + entry = db->rootGroup()->findEntryByPath(QString("/entry1")); + QVERIFY(entry); QCOMPARE(entry->title(), QString("entry1")); // But two slashes should not be accepted. - entry = db->rootGroup()->findEntry(QString("//entry1")); - QVERIFY(entry == nullptr); + entry = db->rootGroup()->findEntryByPath(QString("//entry1")); + QVERIFY(!entry); - entry = db->rootGroup()->findEntry(entry2->uuidToHex()); - QVERIFY(entry != nullptr); + entry = db->rootGroup()->findEntryByUuid(entry2->uuid()); + QVERIFY(entry); QCOMPARE(entry->title(), QString("entry2")); - entry = db->rootGroup()->findEntry(QString("group1/entry2")); - QVERIFY(entry != nullptr); + entry = db->rootGroup()->findEntryByPath(QString("group1/entry2")); + QVERIFY(entry); QCOMPARE(entry->title(), QString("entry2")); - entry = db->rootGroup()->findEntry(QString("/entry2")); - QVERIFY(entry == nullptr); + entry = db->rootGroup()->findEntryByPath(QString("/entry2")); + QVERIFY(!entry); // We also can find the entry with the leading slash. - entry = db->rootGroup()->findEntry(QString("/group1/entry2")); - QVERIFY(entry != nullptr); + entry = db->rootGroup()->findEntryByPath(QString("/group1/entry2")); + QVERIFY(entry); QCOMPARE(entry->title(), QString("entry2")); // Should also find the entry only by title. - entry = db->rootGroup()->findEntry(QString("entry2")); - QVERIFY(entry != nullptr); + entry = db->rootGroup()->findEntryByPath(QString("entry2")); + QVERIFY(entry); QCOMPARE(entry->title(), QString("entry2")); - entry = db->rootGroup()->findEntry(QString("invalid/path/to/entry2")); - QVERIFY(entry == nullptr); + entry = db->rootGroup()->findEntryByPath(QString("invalid/path/to/entry2")); + QVERIFY(!entry); - entry = db->rootGroup()->findEntry(QString("entry27")); - QVERIFY(entry == nullptr); + entry = db->rootGroup()->findEntryByPath(QString("entry27")); + QVERIFY(!entry); // A valid UUID that does not exist in this database. - entry = db->rootGroup()->findEntry(QString("febfb01ebcdf9dbd90a3f1579dc75281")); - QVERIFY(entry == nullptr); + entry = db->rootGroup()->findEntryByUuid(QUuid("febfb01ebcdf9dbd90a3f1579dc75281")); + QVERIFY(!entry); // An invalid UUID. - entry = db->rootGroup()->findEntry(QString("febfb01ebcdf9dbd90a3f1579dc")); - QVERIFY(entry == nullptr); + entry = db->rootGroup()->findEntryByUuid(QUuid("febfb01ebcdf9dbd90a3f1579dc")); + QVERIFY(!entry); + + // Empty strings + entry = db->rootGroup()->findEntryByUuid({}); + QVERIFY(!entry); + + entry = db->rootGroup()->findEntryByPath({}); + QVERIFY(!entry); } void TestGroup::testFindGroupByPath() @@ -561,51 +568,51 @@ void TestGroup::testFindGroupByPath() Group* group; group = db->rootGroup()->findGroupByPath("/"); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), db->rootGroup()->uuid()); // We also accept it if the leading slash is missing. group = db->rootGroup()->findGroupByPath(""); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), db->rootGroup()->uuid()); group = db->rootGroup()->findGroupByPath("/group1/"); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), group1->uuid()); // We also accept it if the leading slash is missing. group = db->rootGroup()->findGroupByPath("group1/"); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), group1->uuid()); // Too many slashes at the end group = db->rootGroup()->findGroupByPath("group1//"); - QVERIFY(group == nullptr); + QVERIFY(!group); // Missing a slash at the end. group = db->rootGroup()->findGroupByPath("/group1"); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), group1->uuid()); // Too many slashes at the start group = db->rootGroup()->findGroupByPath("//group1"); - QVERIFY(group == nullptr); + QVERIFY(!group); group = db->rootGroup()->findGroupByPath("/group1/group2/"); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), group2->uuid()); // We also accept it if the leading slash is missing. group = db->rootGroup()->findGroupByPath("group1/group2/"); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), group2->uuid()); group = db->rootGroup()->findGroupByPath("group1/group2"); - QVERIFY(group != nullptr); + QVERIFY(group); QCOMPARE(group->uuid(), group2->uuid()); group = db->rootGroup()->findGroupByPath("invalid"); - QVERIFY(group == nullptr); + QVERIFY(!group); } void TestGroup::testPrint() @@ -697,7 +704,7 @@ void TestGroup::testLocate() QVERIFY(results.contains("/entry1")); results = db->rootGroup()->locate("invalid"); - QVERIFY(results.size() == 0); + QVERIFY(results.isEmpty()); results = db->rootGroup()->locate("google"); QVERIFY(results.size() == 1); @@ -725,37 +732,37 @@ void TestGroup::testAddEntryWithPath() group2->setParent(group1); Entry* entry = db->rootGroup()->addEntryWithPath("entry1"); - QVERIFY(entry != nullptr); + QVERIFY(entry); QVERIFY(!entry->uuid().isNull()); entry = db->rootGroup()->addEntryWithPath("entry1"); - QVERIFY(entry == nullptr); + QVERIFY(!entry); entry = db->rootGroup()->addEntryWithPath("/entry1"); - QVERIFY(entry == nullptr); + QVERIFY(!entry); entry = db->rootGroup()->addEntryWithPath("entry2"); - QVERIFY(entry != nullptr); + QVERIFY(entry); QVERIFY(entry->title() == "entry2"); QVERIFY(!entry->uuid().isNull()); entry = db->rootGroup()->addEntryWithPath("/entry3"); - QVERIFY(entry != nullptr); + QVERIFY(entry); QVERIFY(entry->title() == "entry3"); QVERIFY(!entry->uuid().isNull()); entry = db->rootGroup()->addEntryWithPath("/group1/entry4"); - QVERIFY(entry != nullptr); + QVERIFY(entry); QVERIFY(entry->title() == "entry4"); QVERIFY(!entry->uuid().isNull()); entry = db->rootGroup()->addEntryWithPath("/group1/group2/entry5"); - QVERIFY(entry != nullptr); + QVERIFY(entry); QVERIFY(entry->title() == "entry5"); QVERIFY(!entry->uuid().isNull()); entry = db->rootGroup()->addEntryWithPath("/group1/invalid_group/entry6"); - QVERIFY(entry == nullptr); + QVERIFY(!entry); delete db; } diff --git a/tests/TestMerge.cpp b/tests/TestMerge.cpp index 4a5081777..728b0a751 100644 --- a/tests/TestMerge.cpp +++ b/tests/TestMerge.cpp @@ -125,7 +125,7 @@ void TestMerge::testResolveConflictNewer() QVERIFY(groupDestinationInitial != nullptr); QCOMPARE(groupDestinationInitial->entries().size(), 2); - QPointer entrySourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entrySourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entrySourceInitial != nullptr); QVERIFY(entrySourceInitial->group() == groupSourceInitial); @@ -159,7 +159,7 @@ void TestMerge::testResolveConflictNewer() QCOMPARE(groupDestinationMerged->entries().size(), 2); QCOMPARE(groupDestinationMerged->timeInfo(), groupDestinationInitialTimeInfo); - QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationMerged != nullptr); QVERIFY(entryDestinationMerged->group() != nullptr); QCOMPARE(entryDestinationMerged->password(), QString("password")); @@ -192,7 +192,7 @@ void TestMerge::testResolveConflictExisting() QVERIFY(groupDestinationInitial != nullptr); QCOMPARE(groupSourceInitial->entries().size(), 2); - QPointer entrySourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entrySourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entrySourceInitial != nullptr); QVERIFY(entrySourceInitial->group() == groupSourceInitial); @@ -213,7 +213,7 @@ void TestMerge::testResolveConflictExisting() QPointer groupDestinationUpdated = dbDestination->rootGroup()->findChildByName("group1"); QVERIFY(groupDestinationUpdated != nullptr); QCOMPARE(groupDestinationUpdated->entries().size(), 2); - QPointer entryDestinationUpdated = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationUpdated = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationUpdated != nullptr); QVERIFY(entryDestinationUpdated->group() == groupDestinationUpdated); @@ -244,7 +244,7 @@ void TestMerge::testResolveConflictExisting() QCOMPARE(groupDestinationMerged->entries().size(), 2); QCOMPARE(groupDestinationMerged->timeInfo(), groupDestinationUpdatedNewerTimeInfo); - QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationMerged != nullptr); QCOMPARE(entryDestinationMerged->password(), QString("password2")); QCOMPARE(entryDestinationMerged->timeInfo(), entryDestinationUpdatedNewerTimeInfo); @@ -382,9 +382,9 @@ void TestMerge::testResolveConflictTemplate(int mergeMode, std::functionadvanceMinute(1); - QPointer deletedEntryDestination = dbDestination->rootGroup()->findEntry("deletedDestination"); + QPointer deletedEntryDestination = dbDestination->rootGroup()->findEntryByPath("deletedDestination"); dbDestination->recycleEntry(deletedEntryDestination); - QPointer deletedEntrySource = dbSource->rootGroup()->findEntry("deletedSource"); + QPointer deletedEntrySource = dbSource->rootGroup()->findEntryByPath("deletedSource"); dbSource->recycleEntry(deletedEntrySource); m_clock->advanceMinute(1); @@ -428,8 +428,8 @@ void TestMerge::testResolveConflictTemplate(int mergeMode, std::functionrootGroup()->findEntry("entryDestination")); - QVERIFY(dbDestination->rootGroup()->findEntry("entrySource")); + QVERIFY(dbDestination->rootGroup()->findEntryByPath("entryDestination")); + QVERIFY(dbDestination->rootGroup()->findEntryByPath("entrySource")); } void TestMerge::testDeletionConflictTemplate(int mergeMode, std::function&)> verification) @@ -790,7 +790,7 @@ void TestMerge::testMoveEntry() QScopedPointer dbSource( createTestDatabaseStructureClone(dbDestination.data(), Entry::CloneNoFlags, Group::CloneIncludeEntries)); - QPointer entrySourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entrySourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entrySourceInitial != nullptr); QPointer groupSourceInitial = dbSource->rootGroup()->findChildByName("group2"); @@ -807,7 +807,7 @@ void TestMerge::testMoveEntry() Merger merger(dbSource.data(), dbDestination.data()); merger.merge(); - QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationMerged != nullptr); QCOMPARE(entryDestinationMerged->group()->name(), QString("group2")); QCOMPARE(dbDestination->rootGroup()->entriesRecursive().size(), 2); @@ -824,7 +824,7 @@ void TestMerge::testMoveEntryPreserveChanges() QScopedPointer dbSource( createTestDatabaseStructureClone(dbDestination.data(), Entry::CloneNoFlags, Group::CloneIncludeEntries)); - QPointer entrySourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entrySourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entrySourceInitial != nullptr); QPointer group2Source = dbSource->rootGroup()->findChildByName("group2"); @@ -835,7 +835,7 @@ void TestMerge::testMoveEntryPreserveChanges() entrySourceInitial->setGroup(group2Source); QCOMPARE(entrySourceInitial->group()->name(), QString("group2")); - QPointer entryDestinationInitial = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationInitial = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationInitial != nullptr); m_clock->advanceSecond(1); @@ -849,7 +849,7 @@ void TestMerge::testMoveEntryPreserveChanges() Merger merger(dbSource.data(), dbDestination.data()); merger.merge(); - QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationMerged != nullptr); QCOMPARE(entryDestinationMerged->group()->name(), QString("group2")); QCOMPARE(dbDestination->rootGroup()->entriesRecursive().size(), 2); @@ -892,7 +892,7 @@ void TestMerge::testMoveEntryIntoNewGroup() groupSourceCreated->setUuid(QUuid::createUuid()); groupSourceCreated->setParent(dbSource->rootGroup()); - QPointer entrySourceMoved = dbSource->rootGroup()->findEntry("entry1"); + QPointer entrySourceMoved = dbSource->rootGroup()->findEntryByPath("entry1"); entrySourceMoved->setGroup(groupSourceCreated); m_clock->advanceSecond(1); @@ -907,7 +907,7 @@ void TestMerge::testMoveEntryIntoNewGroup() QCOMPARE(groupDestinationMerged->name(), QString("group3")); QCOMPARE(groupDestinationMerged->entries().size(), 1); - QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationMerged != nullptr); QCOMPARE(entryDestinationMerged->group()->name(), QString("group3")); } @@ -929,7 +929,7 @@ void TestMerge::testUpdateEntryDifferentLocation() m_clock->advanceSecond(1); - QPointer entryDestinationMoved = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationMoved = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationMoved != nullptr); entryDestinationMoved->setGroup(groupDestinationCreated); QUuid uuidBeforeSyncing = entryDestinationMoved->uuid(); @@ -938,7 +938,7 @@ void TestMerge::testUpdateEntryDifferentLocation() // Change the entry in the source db. m_clock->advanceSecond(1); - QPointer entrySourceMoved = dbSource->rootGroup()->findEntry("entry1"); + QPointer entrySourceMoved = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entrySourceMoved != nullptr); entrySourceMoved->beginUpdate(); entrySourceMoved->setUsername("username"); @@ -954,7 +954,7 @@ void TestMerge::testUpdateEntryDifferentLocation() QCOMPARE(dbDestination->rootGroup()->entriesRecursive().size(), 2); - QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entryDestinationMerged != nullptr); QVERIFY(entryDestinationMerged->group() != nullptr); QCOMPARE(entryDestinationMerged->username(), QString("username")); @@ -983,7 +983,7 @@ void TestMerge::testUpdateGroup() dbSource->metadata()->addCustomIcon(customIconId, customIcon); groupSourceInitial->setIcon(customIconId); - QPointer entrySourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entrySourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entrySourceInitial != nullptr); entrySourceInitial->setGroup(groupSourceInitial); entrySourceInitial->setTitle("entry1 renamed"); @@ -996,7 +996,7 @@ void TestMerge::testUpdateGroup() QCOMPARE(dbDestination->rootGroup()->entriesRecursive().size(), 2); - QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntry("entry1 renamed"); + QPointer entryDestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1 renamed"); QVERIFY(entryDestinationMerged != nullptr); QVERIFY(entryDestinationMerged->group() != nullptr); QCOMPARE(entryDestinationMerged->group()->name(), QString("group2 renamed")); @@ -1125,7 +1125,7 @@ void TestMerge::testDeletedEntry() m_clock->advanceSecond(1); - QPointer entry1SourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entry1SourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entry1SourceInitial != nullptr); QUuid entry1Uuid = entry1SourceInitial->uuid(); delete entry1SourceInitial; @@ -1133,7 +1133,7 @@ void TestMerge::testDeletedEntry() m_clock->advanceSecond(1); - QPointer entry2DestinationInitial = dbDestination->rootGroup()->findEntry("entry2"); + QPointer entry2DestinationInitial = dbDestination->rootGroup()->findEntryByPath("entry2"); QVERIFY(entry2DestinationInitial != nullptr); QUuid entry2Uuid = entry2DestinationInitial->uuid(); delete entry2DestinationInitial; @@ -1144,10 +1144,10 @@ void TestMerge::testDeletedEntry() Merger merger(dbSource.data(), dbDestination.data()); merger.merge(); - QPointer entry1DestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entry1DestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entry1DestinationMerged); QVERIFY(!dbDestination->containsDeletedObject(entry1Uuid)); - QPointer entry2DestinationMerged = dbDestination->rootGroup()->findEntry("entry2"); + QPointer entry2DestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry2"); QVERIFY(entry2DestinationMerged); // Uuid in db and deletedObjects is intended according to KeePass #1752 QVERIFY(dbDestination->containsDeletedObject(entry2Uuid)); @@ -1176,9 +1176,9 @@ void TestMerge::testDeletedGroup() QPointer group1SourceInitial = dbSource->rootGroup()->findChildByName("group1"); QVERIFY(group1SourceInitial != nullptr); - QPointer entry1SourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entry1SourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entry1SourceInitial != nullptr); - QPointer entry2SourceInitial = dbSource->rootGroup()->findEntry("entry2"); + QPointer entry2SourceInitial = dbSource->rootGroup()->findEntryByPath("entry2"); QVERIFY(entry2SourceInitial != nullptr); QUuid group1Uuid = group1SourceInitial->uuid(); QUuid entry1Uuid = entry1SourceInitial->uuid(); @@ -1206,11 +1206,11 @@ void TestMerge::testDeletedGroup() QVERIFY(!dbDestination->containsDeletedObject(entry2Uuid)); QVERIFY(!dbDestination->containsDeletedObject(group2Uuid)); - QPointer entry1DestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entry1DestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entry1DestinationMerged); - QPointer entry2DestinationMerged = dbDestination->rootGroup()->findEntry("entry2"); + QPointer entry2DestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry2"); QVERIFY(entry2DestinationMerged); - QPointer entry3DestinationMerged = dbDestination->rootGroup()->findEntry("entry3"); + QPointer entry3DestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry3"); QVERIFY(entry3DestinationMerged); QPointer group1DestinationMerged = dbDestination->rootGroup()->findChildByName("group1"); QVERIFY(group1DestinationMerged); @@ -1228,7 +1228,7 @@ void TestMerge::testDeletedRevertedEntry() m_clock->advanceSecond(1); - QPointer entry1DestinationInitial = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entry1DestinationInitial = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entry1DestinationInitial != nullptr); QUuid entry1Uuid = entry1DestinationInitial->uuid(); delete entry1DestinationInitial; @@ -1236,7 +1236,7 @@ void TestMerge::testDeletedRevertedEntry() m_clock->advanceSecond(1); - QPointer entry2SourceInitial = dbSource->rootGroup()->findEntry("entry2"); + QPointer entry2SourceInitial = dbSource->rootGroup()->findEntryByPath("entry2"); QVERIFY(entry2SourceInitial != nullptr); QUuid entry2Uuid = entry2SourceInitial->uuid(); delete entry2SourceInitial; @@ -1244,11 +1244,11 @@ void TestMerge::testDeletedRevertedEntry() m_clock->advanceSecond(1); - QPointer entry1SourceInitial = dbSource->rootGroup()->findEntry("entry1"); + QPointer entry1SourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QVERIFY(entry1SourceInitial != nullptr); entry1SourceInitial->setNotes("Updated"); - QPointer entry2DestinationInitial = dbDestination->rootGroup()->findEntry("entry2"); + QPointer entry2DestinationInitial = dbDestination->rootGroup()->findEntryByPath("entry2"); QVERIFY(entry2DestinationInitial != nullptr); entry2DestinationInitial->setNotes("Updated"); @@ -1259,10 +1259,10 @@ void TestMerge::testDeletedRevertedEntry() QVERIFY(dbDestination->containsDeletedObject(entry1Uuid)); QVERIFY(!dbDestination->containsDeletedObject(entry2Uuid)); - QPointer entry1DestinationMerged = dbDestination->rootGroup()->findEntry("entry1"); + QPointer entry1DestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry1"); QVERIFY(entry1DestinationMerged); QVERIFY(entry1DestinationMerged->notes() == "Updated"); - QPointer entry2DestinationMerged = dbDestination->rootGroup()->findEntry("entry2"); + QPointer entry2DestinationMerged = dbDestination->rootGroup()->findEntryByPath("entry2"); QVERIFY(entry2DestinationMerged); QVERIFY(entry2DestinationMerged->notes() == "Updated"); } From 4e1d3bfd73587ce6472b7cfdbed3f3410fce016a Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Tue, 30 Oct 2018 13:46:12 +0100 Subject: [PATCH 27/84] Extract the OS event filter implementation (#2422) --- src/CMakeLists.txt | 5 +++ src/core/OSEventFilter.cpp | 27 +++++++++++++++ src/core/OSEventFilter.h | 16 +++++++++ src/core/ScreenLockListenerDBus.h | 2 +- src/gui/Application.cpp | 55 ++++++------------------------- src/gui/Application.h | 10 +++++- 6 files changed, 68 insertions(+), 47 deletions(-) create mode 100644 src/core/OSEventFilter.cpp create mode 100644 src/core/OSEventFilter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c6f614568..1e7d64a1f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -204,6 +204,11 @@ if(MINGW) core/ScreenLockListenerWin.h core/ScreenLockListenerWin.cpp) endif() +if(MINGW OR (UNIX AND NOT APPLE)) + set(keepassx_SOURCES + ${keepassx_SOURCES} + core/OSEventFilter.cpp) +endif() set(keepassx_SOURCES_MAINEXE main.cpp) diff --git a/src/core/OSEventFilter.cpp b/src/core/OSEventFilter.cpp new file mode 100644 index 000000000..f6ad6c76a --- /dev/null +++ b/src/core/OSEventFilter.cpp @@ -0,0 +1,27 @@ +#include "OSEventFilter.h" + +#include + +#include "autotype/AutoType.h" + +OSEventFilter::OSEventFilter() +{ +} + +bool OSEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long* result) +{ + Q_UNUSED(result) + +#if defined(Q_OS_UNIX) + if (eventType == QByteArrayLiteral("xcb_generic_event_t")) { +#elif defined(Q_OS_WIN) + if (eventType == QByteArrayLiteral("windows_generic_MSG") + || eventType == QByteArrayLiteral("windows_dispatcher_MSG")) { +#endif + int retCode = autoType()->callEventFilter(message); + + return retCode == 1; + } + + return false; +} diff --git a/src/core/OSEventFilter.h b/src/core/OSEventFilter.h new file mode 100644 index 000000000..1dca5392e --- /dev/null +++ b/src/core/OSEventFilter.h @@ -0,0 +1,16 @@ +#ifndef OSEVENTFILTER_H +#define OSEVENTFILTER_H +#include + +class QByteArray; + +class OSEventFilter : public QAbstractNativeEventFilter +{ +public: + OSEventFilter(); + bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; +private: + Q_DISABLE_COPY(OSEventFilter) +}; + +#endif // OSEVENTFILTER_H diff --git a/src/core/ScreenLockListenerDBus.h b/src/core/ScreenLockListenerDBus.h index dd6f5ea86..ab73a8cf3 100644 --- a/src/core/ScreenLockListenerDBus.h +++ b/src/core/ScreenLockListenerDBus.h @@ -25,7 +25,7 @@ class ScreenLockListenerDBus : public ScreenLockListenerPrivate { Q_OBJECT public: - explicit ScreenLockListenerDBus(QWidget* parent = 0); + explicit ScreenLockListenerDBus(QWidget* parent = nullptr); private slots: void gnomeSessionStatusChanged(uint status); diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index cdd4df03f..b67f542c6 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -21,7 +21,6 @@ #include "MainWindow.h" #include "core/Config.h" -#include #include #include #include @@ -32,6 +31,10 @@ #include "autotype/AutoType.h" #include "core/Global.h" +#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) +#include "core/OSEventFilter.h" +#endif + #if defined(Q_OS_UNIX) #include #include @@ -42,46 +45,7 @@ namespace { constexpr int WaitTimeoutMSec = 150; const char BlockSizeProperty[] = "blockSize"; -} - -#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) -class XcbEventFilter : public QAbstractNativeEventFilter -{ -public: - bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override - { - Q_UNUSED(result) - - if (eventType == QByteArrayLiteral("xcb_generic_event_t")) { - int retCode = autoType()->callEventFilter(message); - if (retCode == 1) { - return true; - } - } - - return false; - } -}; -#elif defined(Q_OS_WIN) -class WinEventFilter : public QAbstractNativeEventFilter -{ -public: - bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override - { - Q_UNUSED(result); - - if (eventType == QByteArrayLiteral("windows_generic_MSG") - || eventType == QByteArrayLiteral("windows_dispatcher_MSG")) { - int retCode = autoType()->callEventFilter(message); - if (retCode == 1) { - return true; - } - } - - return false; - } -}; -#endif +} // namespace Application::Application(int& argc, char** argv) : QApplication(argc, argv) @@ -91,11 +55,12 @@ Application::Application(int& argc, char** argv) #endif , m_alreadyRunning(false) , m_lockFile(nullptr) +#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + , m_osEventFilter(new OSEventFilter()) +{ + installNativeEventFilter(m_osEventFilter.data()); +#else { -#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) - installNativeEventFilter(new XcbEventFilter()); -#elif defined(Q_OS_WIN) - installNativeEventFilter(new WinEventFilter()); #endif #if defined(Q_OS_UNIX) registerUnixSignals(); diff --git a/src/gui/Application.h b/src/gui/Application.h index 3fdd8af90..7b1a77f60 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -23,6 +23,11 @@ #include #include +#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) +#include + +class OSEventFilter; +#endif class QLockFile; class QSocketNotifier; @@ -33,7 +38,7 @@ class Application : public QApplication public: Application(int& argc, char** argv); QWidget* mainWindow() const; - ~Application(); + ~Application() override; void setMainWindow(QWidget* mainWindow); bool event(QEvent* event) override; @@ -70,6 +75,9 @@ private: QLockFile* m_lockFile; QLocalServer m_lockServer; QString m_socketName; +#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + QScopedPointer m_osEventFilter; +#endif }; #endif // KEEPASSX_APPLICATION_H From 5bf4f51389bb0741058d40beb19356d7e2927dd4 Mon Sep 17 00:00:00 2001 From: Darwin Shameran Date: Tue, 30 Oct 2018 15:16:49 +0200 Subject: [PATCH 28/84] Don't obscure password field after invalid password attempt if setting is off (#2353) --- src/gui/DatabaseOpenWidget.cpp | 4 +++- src/gui/PasswordEdit.cpp | 4 ++++ src/gui/PasswordEdit.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index d1ae52780..67af3ff13 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -184,7 +184,9 @@ void DatabaseOpenWidget::openDatabase() return; } - m_ui->editPassword->setShowPassword(false); + if (!m_ui->editPassword->isPasswordVisible()) { + m_ui->editPassword->setShowPassword(false); + } QCoreApplication::processEvents(); QFile file(m_filename); diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 94125acfe..33d022176 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -71,6 +71,10 @@ void PasswordEdit::setShowPassword(bool show) emit showPasswordChanged(show); } +bool PasswordEdit::isPasswordVisible() const { + return isEnabled(); +} + bool PasswordEdit::passwordsEqual() const { return text() == m_basePasswordEdit->text(); diff --git a/src/gui/PasswordEdit.h b/src/gui/PasswordEdit.h index d5439f1a0..5976347e6 100644 --- a/src/gui/PasswordEdit.h +++ b/src/gui/PasswordEdit.h @@ -31,6 +31,7 @@ public: explicit PasswordEdit(QWidget* parent = nullptr); void enableVerifyMode(PasswordEdit* baseEdit); + bool isPasswordVisible() const; public slots: void setShowPassword(bool show); From 2ad803682367c1ec689d8d564805502689e7e34f Mon Sep 17 00:00:00 2001 From: hidden by cloudflare Date: Tue, 30 Oct 2018 08:07:59 -0700 Subject: [PATCH 29/84] Added Linux, MacOS, and Windows support for Tor Browser. (#2387) Add path for native-messaging-hosts on Linux, Windows, and macOS for Tor Browser --- docs/QUICKSTART.md | 2 +- src/browser/BrowserOptionDialog.cpp | 9 +++++ src/browser/BrowserOptionDialog.ui | 55 +++++++++++++---------------- src/browser/BrowserSettings.cpp | 11 ++++++ src/browser/BrowserSettings.h | 4 ++- src/browser/HostInstaller.cpp | 11 ++++-- src/browser/HostInstaller.h | 4 ++- 7 files changed, 60 insertions(+), 36 deletions(-) diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 8b694888e..4b69ddf21 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -27,7 +27,7 @@ for all your websites, programs, etc. * In **Browser Integration**, check **Enable KeePassXC browser integration** * Right below that, click the checkbox for the browser(s) you use Leave the other options at their defaults. -* *In your default web browser,* install the KeePassXC Browser extension/add-on. Instructions for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome](https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) +* *In your default web browser,* install the KeePassXC Browser extension/add-on. Instructions for [Firefox or Tor Browser](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome](https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) * Click the KeePassXC icon in the upper-right corner. You'll see the dialog below. * Click the blue Connect button to make the browser extension connect to the KeePassXC application. KeePassXC Connect dialog diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index 41194e9b6..6dcbd3263 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -60,6 +60,9 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) // Vivaldi uses Chrome's registry settings m_ui->vivaldiSupport->setHidden(true); m_ui->chromeSupport->setText("Chrome and Vivaldi"); + // Tor Browser uses Firefox's registry settings + m_ui->torBrowserSupport->setHidden(true); + m_ui->firefoxSupport->setText("Firefox and Tor Browser"); #endif m_ui->browserGlobalWarningWidget->setVisible(false); } @@ -99,7 +102,10 @@ void BrowserOptionDialog::loadSettings() m_ui->chromeSupport->setChecked(settings->chromeSupport()); m_ui->chromiumSupport->setChecked(settings->chromiumSupport()); m_ui->firefoxSupport->setChecked(settings->firefoxSupport()); +#ifndef Q_OS_WIN m_ui->vivaldiSupport->setChecked(settings->vivaldiSupport()); + m_ui->torBrowserSupport->setChecked(settings->torBrowserSupport()); +#endif #if defined(KEEPASSXC_DIST_APPIMAGE) m_ui->supportBrowserProxy->setChecked(true); @@ -149,7 +155,10 @@ void BrowserOptionDialog::saveSettings() settings->setChromeSupport(m_ui->chromeSupport->isChecked()); settings->setChromiumSupport(m_ui->chromiumSupport->isChecked()); settings->setFirefoxSupport(m_ui->firefoxSupport->isChecked()); +#ifndef Q_OS_WIN settings->setVivaldiSupport(m_ui->vivaldiSupport->isChecked()); + settings->setTorBrowserSupport(m_ui->torBrowserSupport->isChecked()); +#endif } void BrowserOptionDialog::showProxyLocationFileDialog() diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index 9a951b33d..c01be920b 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -60,8 +60,7 @@
- - + @@ -88,6 +87,19 @@ 40 + + + + Qt::Horizontal + + + + 179 + 20 + + + + @@ -108,19 +120,6 @@ - - - - Qt::Horizontal - - - - 179 - 20 - - - - @@ -141,6 +140,16 @@ + + + + &Tor Browser + + + false + + + @@ -214,22 +223,6 @@ - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index dc92f5dc8..630d0ff18 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -228,6 +228,17 @@ void BrowserSettings::setVivaldiSupport(bool enabled) HostInstaller::SupportedBrowsers::VIVALDI, enabled, supportBrowserProxy(), customProxyLocation()); } +bool BrowserSettings::torBrowserSupport() +{ + return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::TOR_BROWSER); +} + +void BrowserSettings::setTorBrowserSupport(bool enabled) +{ + m_hostInstaller.installBrowser( + HostInstaller::SupportedBrowsers::TOR_BROWSER, enabled, supportBrowserProxy(), customProxyLocation()); +} + bool BrowserSettings::passwordUseNumbers() { return config()->get("generator/Numbers", PasswordGenerator::DefaultNumbers).toBool(); diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index e501a1201..2e7c55ec6 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -70,7 +70,9 @@ public: void setFirefoxSupport(bool enabled); bool vivaldiSupport(); void setVivaldiSupport(bool enabled); - + bool torBrowserSupport(); + void setTorBrowserSupport(bool enabled); + bool passwordUseNumbers(); void setPasswordUseNumbers(bool useNumbers); bool passwordUseLowercase(); diff --git a/src/browser/HostInstaller.cpp b/src/browser/HostInstaller.cpp index 5d7eb3b3b..a3ad608d4 100644 --- a/src/browser/HostInstaller.cpp +++ b/src/browser/HostInstaller.cpp @@ -38,16 +38,19 @@ HostInstaller::HostInstaller() , TARGET_DIR_CHROMIUM("/Library/Application Support/Chromium/NativeMessagingHosts") , TARGET_DIR_FIREFOX("/Library/Application Support/Mozilla/NativeMessagingHosts") , TARGET_DIR_VIVALDI("/Library/Application Support/Vivaldi/NativeMessagingHosts") + , TARGET_DIR_TOR_BROWSER("/Library/Application Support/TorBrowser-Data/Browser/Mozilla/NativeMessagingHosts") #elif defined(Q_OS_LINUX) , TARGET_DIR_CHROME("/.config/google-chrome/NativeMessagingHosts") , TARGET_DIR_CHROMIUM("/.config/chromium/NativeMessagingHosts") , TARGET_DIR_FIREFOX("/.mozilla/native-messaging-hosts") , TARGET_DIR_VIVALDI("/.config/vivaldi/NativeMessagingHosts") + , TARGET_DIR_TOR_BROWSER("/.tor-browser/app/Browser/TorBrowser/Data/Browser/.mozilla/native-messaging-hosts") #elif defined(Q_OS_WIN) , TARGET_DIR_CHROME("HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser") , TARGET_DIR_CHROMIUM("HKEY_CURRENT_USER\\Software\\Chromium\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser") , TARGET_DIR_FIREFOX("HKEY_CURRENT_USER\\Software\\Mozilla\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser") - , TARGET_DIR_VIVALDI("HKEY_CURRENT_USER\\Software\\Vivaldi\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser") + , TARGET_DIR_VIVALDI(TARGET_DIR_CHROME) + , TARGET_DIR_TOR_BROWSER(TARGET_DIR_FIREFOX) #endif { } @@ -159,6 +162,8 @@ QString HostInstaller::getTargetPath(SupportedBrowsers browser) const return TARGET_DIR_FIREFOX; case SupportedBrowsers::VIVALDI: return TARGET_DIR_VIVALDI; + case SupportedBrowsers::TOR_BROWSER: + return TARGET_DIR_TOR_BROWSER; default: return QString(); } @@ -182,6 +187,8 @@ QString HostInstaller::getBrowserName(SupportedBrowsers browser) const return "firefox"; case SupportedBrowsers::VIVALDI: return "vivaldi"; + case SupportedBrowsers::TOR_BROWSER: + return "tor-browser"; default: return QString(); } @@ -287,7 +294,7 @@ QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& script["type"] = "stdio"; QJsonArray arr; - if (browser == SupportedBrowsers::FIREFOX) { + if (browser == SupportedBrowsers::FIREFOX || browser == SupportedBrowsers::TOR_BROWSER) { for (const QString& extension : ALLOWED_EXTENSIONS) { arr.append(extension); } diff --git a/src/browser/HostInstaller.h b/src/browser/HostInstaller.h index 204c3d982..77d1ffca5 100644 --- a/src/browser/HostInstaller.h +++ b/src/browser/HostInstaller.h @@ -33,7 +33,8 @@ public: CHROME = 0, CHROMIUM = 1, FIREFOX = 2, - VIVALDI = 3 + VIVALDI = 3, + TOR_BROWSER = 4 }; public: @@ -64,6 +65,7 @@ private: const QString TARGET_DIR_CHROMIUM; const QString TARGET_DIR_FIREFOX; const QString TARGET_DIR_VIVALDI; + const QString TARGET_DIR_TOR_BROWSER; }; #endif // HOSTINSTALLER_H From 4ae5ddf80f1383431e09b1c4c466950a7a658af4 Mon Sep 17 00:00:00 2001 From: st-sloth Date: Mon, 29 Oct 2018 09:24:14 +0500 Subject: [PATCH 30/84] Add more expiration presets --- src/gui/entry/EditEntryWidget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index f15ca5328..e8a450026 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -1277,7 +1277,9 @@ QMenu* EditEntryWidget::createPresetsMenu() expirePresetsMenu->addAction(tr("%n month(s)", 0, 3))->setData(QVariant::fromValue(TimeDelta::fromMonths(3))); expirePresetsMenu->addAction(tr("%n month(s)", 0, 6))->setData(QVariant::fromValue(TimeDelta::fromMonths(6))); expirePresetsMenu->addSeparator(); - expirePresetsMenu->addAction(tr("1 year"))->setData(QVariant::fromValue(TimeDelta::fromYears(1))); + 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))); return expirePresetsMenu; } From 09fbb6d35a404b659e3adc4561178678eb1527f3 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sat, 27 Oct 2018 21:39:50 +0200 Subject: [PATCH 31/84] Remove redundant headers from the build system Headers are not to be placed amongst the source files in the CMake script. The preprocessor and the linker will take care of glue all the files together. Also, the "include_directories()" statement at the top of the file already tells CMake where to look for all the needed header files. --- src/CMakeLists.txt | 21 +-------------------- src/cli/CMakeLists.txt | 15 +-------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e7d64a1f..4c7620063 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,7 +38,6 @@ configure_file(version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version.h @ONLY) set(keepassx_SOURCES core/AutoTypeAssociations.cpp - core/AsyncTask.h core/AutoTypeMatch.cpp core/Compare.cpp core/Config.cpp @@ -52,18 +51,14 @@ set(keepassx_SOURCES core/EntrySearcher.cpp core/FilePath.cpp core/Bootstrap.cpp - core/Global.h core/Group.cpp core/InactivityTimer.cpp - core/ListDeleter.h core/Merger.cpp core/Metadata.cpp core/PasswordGenerator.cpp core/PassphraseGenerator.cpp core/SignalMultiplexer.cpp core/ScreenLockListener.cpp - core/ScreenLockListener.h - core/ScreenLockListenerPrivate.h core/ScreenLockListenerPrivate.cpp core/TimeDelta.cpp core/TimeInfo.cpp @@ -77,14 +72,11 @@ set(keepassx_SOURCES crypto/CryptoHash.cpp crypto/Random.cpp crypto/SymmetricCipher.cpp - crypto/SymmetricCipherBackend.h crypto/SymmetricCipherGcrypt.cpp crypto/kdf/Kdf.cpp - crypto/kdf/Kdf_p.h crypto/kdf/AesKdf.cpp crypto/kdf/Argon2Kdf.cpp format/CsvExporter.cpp - format/KeePass1.h format/KeePass1Reader.cpp format/KeePass2.cpp format/KeePass2RandomStream.cpp @@ -142,7 +134,6 @@ set(keepassx_SOURCES gui/entry/AutoTypeMatchModel.cpp gui/entry/AutoTypeMatchView.cpp gui/entry/EditEntryWidget.cpp - gui/entry/EditEntryWidget_p.h gui/entry/EntryAttachmentsModel.cpp gui/entry/EntryAttachmentsWidget.cpp gui/entry/EntryAttributesModel.cpp @@ -168,11 +159,8 @@ set(keepassx_SOURCES gui/wizard/NewDatabaseWizardPageMetaData.cpp gui/wizard/NewDatabaseWizardPageEncryption.cpp gui/wizard/NewDatabaseWizardPageMasterKey.cpp - keys/ChallengeResponseKey.h keys/CompositeKey.cpp - keys/drivers/YubiKey.h keys/FileKey.cpp - keys/Key.h keys/PasswordKey.cpp keys/YkChallengeResponseKey.cpp streams/HashedBlockStream.cpp @@ -181,27 +169,22 @@ set(keepassx_SOURCES streams/qtiocompressor.cpp streams/StoreDataStream.cpp streams/SymmetricCipherStream.cpp - totp/totp.h totp/totp.cpp) if(APPLE) set(keepassx_SOURCES ${keepassx_SOURCES} - core/ScreenLockListenerMac.h core/ScreenLockListenerMac.cpp - core/MacPasteboard.h core/MacPasteboard.cpp) endif() if(UNIX AND NOT APPLE) set(keepassx_SOURCES ${keepassx_SOURCES} - core/ScreenLockListenerDBus.h core/ScreenLockListenerDBus.cpp gui/MainWindowAdaptor.cpp) endif() if(MINGW) set(keepassx_SOURCES ${keepassx_SOURCES} - core/ScreenLockListenerWin.h core/ScreenLockListenerWin.cpp) endif() if(MINGW OR (UNIX AND NOT APPLE)) @@ -243,13 +226,11 @@ set(autotype_SOURCES core/Tools.cpp autotype/AutoType.cpp autotype/AutoTypeAction.cpp - autotype/AutoTypePlatformPlugin.h autotype/AutoTypeSelectDialog.cpp autotype/AutoTypeSelectView.cpp autotype/ShortcutWidget.cpp autotype/WildcardMatcher.cpp - autotype/WindowSelectComboBox.cpp - autotype/test/AutoTypeTestInterface.h) + autotype/WindowSelectComboBox.cpp) if(MINGW) set(keepassx_SOURCES_MAINEXE ${keepassx_SOURCES_MAINEXE} ${CMAKE_SOURCE_DIR}/share/windows/icon.rc) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index f6377aa07..37a085ad3 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -15,31 +15,18 @@ set(cli_SOURCES Add.cpp - Add.h Clip.cpp - Clip.h Command.cpp - Command.h Diceware.cpp - Diceware.h Edit.cpp - Edit.h Estimate.cpp - Estimate.h Extract.cpp - Extract.h Generate.cpp - Generate.h List.cpp - List.h Locate.cpp - Locate.h Merge.cpp - Merge.h Remove.cpp - Remove.h - Show.cpp - Show.h) + Show.cpp) add_library(cli STATIC ${cli_SOURCES}) target_link_libraries(cli Qt5::Core Qt5::Widgets) From 7a823e8dc7d2f149b51036dd918e66deef9affd9 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sat, 27 Oct 2018 04:38:10 +0200 Subject: [PATCH 32/84] Pin AutoTypeAction's vtable to a translation unit AutoTypeAction class had no out-of-line definitions and because of this the compiler would place its vtable everywhere the class is used. By simply defining the virtual destructor in the .cpp file, the issue goes away. Also, a few classes derived from AutoTypeAction had missing 'override' qualifiers, which have now been added. --- src/autotype/AutoTypeAction.cpp | 6 ++++++ src/autotype/AutoTypeAction.h | 20 +++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/autotype/AutoTypeAction.cpp b/src/autotype/AutoTypeAction.cpp index 0beb19b39..f9d928f0d 100644 --- a/src/autotype/AutoTypeAction.cpp +++ b/src/autotype/AutoTypeAction.cpp @@ -87,3 +87,9 @@ void AutoTypeExecutor::execClearField(AutoTypeClearField* action) { Q_UNUSED(action); } + +AutoTypeAction::~AutoTypeAction() +{ + // This makes sure that AutoTypeAction's vtable is placed + // in this translation unit. +} diff --git a/src/autotype/AutoTypeAction.h b/src/autotype/AutoTypeAction.h index 263566dd8..e598b1dcc 100644 --- a/src/autotype/AutoTypeAction.h +++ b/src/autotype/AutoTypeAction.h @@ -29,19 +29,17 @@ class AutoTypeExecutor; class KEEPASSX_EXPORT AutoTypeAction { public: - virtual ~AutoTypeAction() - { - } virtual AutoTypeAction* clone() = 0; virtual void accept(AutoTypeExecutor* executor) = 0; + virtual ~AutoTypeAction(); }; class KEEPASSX_EXPORT AutoTypeChar : public AutoTypeAction { public: explicit AutoTypeChar(const QChar& character); - AutoTypeAction* clone(); - void accept(AutoTypeExecutor* executor); + AutoTypeAction* clone() override; + void accept(AutoTypeExecutor* executor) override; const QChar character; }; @@ -50,8 +48,8 @@ class KEEPASSX_EXPORT AutoTypeKey : public AutoTypeAction { public: explicit AutoTypeKey(Qt::Key key); - AutoTypeAction* clone(); - void accept(AutoTypeExecutor* executor); + AutoTypeAction* clone() override; + void accept(AutoTypeExecutor* executor) override; const Qt::Key key; }; @@ -60,8 +58,8 @@ class KEEPASSX_EXPORT AutoTypeDelay : public AutoTypeAction { public: explicit AutoTypeDelay(int delayMs); - AutoTypeAction* clone(); - void accept(AutoTypeExecutor* executor); + AutoTypeAction* clone() override; + void accept(AutoTypeExecutor* executor) override; const int delayMs; }; @@ -70,8 +68,8 @@ class KEEPASSX_EXPORT AutoTypeClearField : public AutoTypeAction { public: AutoTypeClearField(); - AutoTypeAction* clone(); - void accept(AutoTypeExecutor* executor); + AutoTypeAction* clone() override; + void accept(AutoTypeExecutor* executor) override; }; class KEEPASSX_EXPORT AutoTypeExecutor From e2ee82169c3b2edc75bb424e7ab3d292411cd67e Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sat, 27 Oct 2018 15:58:50 +0200 Subject: [PATCH 33/84] Remove redundant check for the version flag Just a couple lines above, the application would already exit if the version option is set. This extra check is not needed. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 0b5e0b1cb..9764c52d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -88,7 +88,7 @@ int main(int argc, char** argv) const QStringList fileNames = parser.positionalArguments(); - if (app.isAlreadyRunning() && !parser.isSet(versionOption)) { + if (app.isAlreadyRunning()) { if (!fileNames.isEmpty()) { app.sendFileNamesToRunningInstance(fileNames); } From 4876beabedb4f7d40a71820428d44e612805d65e Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sat, 27 Oct 2018 23:23:34 +0200 Subject: [PATCH 34/84] Improve performance of a few for-loops Some for-loops needlessly copied the collection they were looping over. --- src/browser/BrowserService.cpp | 2 +- src/core/Group.cpp | 8 ++++---- src/core/Group.h | 2 +- src/sshagent/OpenSSHKey.cpp | 8 ++++---- src/sshagent/SSHAgent.cpp | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 9917ded3c..456028632 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -421,7 +421,7 @@ QList BrowserService::searchEntries(const QString& url, const StringPair if (DatabaseWidget* dbWidget = qobject_cast(m_dbTabWidget->widget(i))) { if (Database* db = dbWidget->database()) { // Check if database is connected with KeePassXC-Browser - for (const StringPair keyPair : keyList) { + for (const StringPair& keyPair : keyList) { QString key = db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first); if (!key.isEmpty() && keyPair.second == key) { databases << db; diff --git a/src/core/Group.cpp b/src/core/Group.cpp index dab48ebd1..05cac41e9 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -926,7 +926,7 @@ bool Group::resolveAutoTypeEnabled() const } } -QStringList Group::locate(QString locateTerm, QString currentPath) +QStringList Group::locate(QString locateTerm, QString currentPath) const { // TODO: Replace with EntrySearcher QStringList response; @@ -934,15 +934,15 @@ QStringList Group::locate(QString locateTerm, QString currentPath) return response; } - for (Entry* entry : asConst(m_entries)) { + for (const Entry* entry : asConst(m_entries)) { QString entryPath = currentPath + entry->title(); if (entryPath.toLower().contains(locateTerm.toLower())) { response << entryPath; } } - for (Group* group : asConst(m_children)) { - for (QString path : group->locate(locateTerm, currentPath + group->name() + QString("/"))) { + for (const Group* group : asConst(m_children)) { + for (const QString& path : group->locate(locateTerm, currentPath + group->name() + QString("/"))) { response << path; } } diff --git a/src/core/Group.h b/src/core/Group.h index e2b55cbd4..3a9332c8e 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -118,7 +118,7 @@ public: Entry* findEntryByPath(QString entryPath); Group* findGroupByUuid(const QUuid& uuid); Group* findGroupByPath(QString groupPath); - QStringList locate(QString locateTerm, QString currentPath = {"/"}); + QStringList locate(QString locateTerm, QString currentPath = {"/"}) const; Entry* addEntryWithPath(QString entryPath); void setUuid(const QUuid& uuid); void setName(const QString& name); diff --git a/src/sshagent/OpenSSHKey.cpp b/src/sshagent/OpenSSHKey.cpp index 44684d620..9d1301a05 100644 --- a/src/sshagent/OpenSSHKey.cpp +++ b/src/sshagent/OpenSSHKey.cpp @@ -101,7 +101,7 @@ const QString OpenSSHKey::fingerprint(QCryptographicHash::Algorithm algo) const stream.writeString(m_type); - for (QByteArray ba : m_publicData) { + for (const QByteArray& ba : m_publicData) { stream.writeString(ba); } @@ -137,7 +137,7 @@ const QString OpenSSHKey::publicKey() const stream.writeString(m_type); - for (QByteArray ba : m_publicData) { + for (const QByteArray& ba : m_publicData) { stream.writeString(ba); } @@ -544,7 +544,7 @@ bool OpenSSHKey::writePublic(BinaryStream& stream) return false; } - for (QByteArray t : m_publicData) { + for (const QByteArray& t : m_publicData) { if (!stream.writeString(t)) { m_error = tr("Unexpected EOF when writing public key"); return false; @@ -566,7 +566,7 @@ bool OpenSSHKey::writePrivate(BinaryStream& stream) return false; } - for (QByteArray t : m_privateData) { + for (const QByteArray& t : m_privateData) { if (!stream.writeString(t)) { m_error = tr("Unexpected EOF when writing private key"); return false; diff --git a/src/sshagent/SSHAgent.cpp b/src/sshagent/SSHAgent.cpp index 758c86851..487398238 100644 --- a/src/sshagent/SSHAgent.cpp +++ b/src/sshagent/SSHAgent.cpp @@ -38,7 +38,7 @@ SSHAgent::SSHAgent(QObject* parent) SSHAgent::~SSHAgent() { - for (QSet keys : m_keys.values()) { + for (const QSet& keys : m_keys.values()) { for (OpenSSHKey key : keys) { removeIdentity(key); } From 4ac1601696577361b0309797d23cc0208891abe4 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sat, 27 Oct 2018 23:54:57 +0200 Subject: [PATCH 35/84] Replace old for-loops with range-based for-loops --- src/core/Tools.cpp | 4 ++-- src/gui/ApplicationSettingsWidget.cpp | 4 ++-- src/gui/DialogyWidget.cpp | 3 +-- src/gui/entry/EntryModel.cpp | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 8467fb416..ded3a1651 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -121,8 +121,8 @@ QString imageReaderFilter() QStringList formatsStringList; for (const QByteArray& format : formats) { - for (int i = 0; i < format.size(); i++) { - if (!QChar(format.at(i)).isLetterOrNumber()) { + for (char codePoint : format) { + if (!QChar(codePoint).isLetterOrNumber()) { continue; } } diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index c23e2bfa5..a0293ce8d 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -146,8 +146,8 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->languageComboBox->clear(); QList> languages = Translator::availableLanguages(); - for (int i = 0; i < languages.size(); i++) { - m_generalUi->languageComboBox->addItem(languages[i].second, languages[i].first); + for (const auto& language : languages) { + m_generalUi->languageComboBox->addItem(language.second, language.first); } int defaultIndex = m_generalUi->languageComboBox->findData(config()->get("GUI/Language")); if (defaultIndex > 0) { diff --git a/src/gui/DialogyWidget.cpp b/src/gui/DialogyWidget.cpp index 89e114f10..858d2949b 100644 --- a/src/gui/DialogyWidget.cpp +++ b/src/gui/DialogyWidget.cpp @@ -71,8 +71,7 @@ bool DialogyWidget::clickButton(QDialogButtonBox::StandardButton standardButton) } QList buttonBoxes = findChildren(); - for (int i = 0; i < buttonBoxes.size(); ++i) { - QDialogButtonBox* buttonBox = buttonBoxes.at(i); + for (auto buttonBox : buttonBoxes) { pb = buttonBox->button(standardButton); if (pb && pb->isVisible() && pb->isEnabled()) { pb->click(); diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 0616374ac..fa3db177b 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -208,12 +208,12 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const case Attachments: { // Display comma-separated list of attachments QList attachments = entry->attachments()->keys(); - for (int i = 0; i < attachments.size(); ++i) { + for (const auto& attachment : attachments) { if (result.isEmpty()) { - result.append(attachments.at(i)); + result.append(attachment); continue; } - result.append(QString(", ") + attachments.at(i)); + result.append(QString(", ") + attachment); } return result; } From 18fd20f898681126e6014c092047ca2ed1aa5f0a Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sun, 28 Oct 2018 11:39:15 +0100 Subject: [PATCH 36/84] Remove redundant null-checks for pointer deletion Deleting a null pointer is defined behavior and results in a no-op at the assembly level, so it's perfectly safe. --- src/gui/DatabaseOpenWidget.cpp | 10 +++++----- src/gui/KeePass1OpenWidget.cpp | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 67af3ff13..892c41d3e 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -143,7 +143,7 @@ void DatabaseOpenWidget::load(const QString& filename) QHash useTouchID = config()->get("UseTouchID").toHash(); m_ui->checkTouchID->setChecked(useTouchID.value(m_filename, false).toBool()); - + m_ui->editPassword->setFocus(); } @@ -195,9 +195,9 @@ void DatabaseOpenWidget::openDatabase() MessageWidget::Error); return; } - if (m_db) { - delete m_db; - } + + delete m_db; + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_db = reader.readDatabase(&file, masterKey); QApplication::restoreOverrideCursor(); @@ -254,7 +254,7 @@ QSharedPointer DatabaseOpenWidget::databaseKey() // check if the user cancelled the operation if (passwordKey.isNull()) return QSharedPointer(); - + masterKey->addKey(PasswordKey::fromRawKey(*passwordKey)); } } diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp index 4a54aaf3a..8123d239f 100644 --- a/src/gui/KeePass1OpenWidget.cpp +++ b/src/gui/KeePass1OpenWidget.cpp @@ -53,9 +53,9 @@ void KeePass1OpenWidget::openDatabase() MessageWidget::Error); return; } - if (m_db) { - delete m_db; - } + + delete m_db; + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_db = reader.readDatabase(&file, password, keyFileName); QApplication::restoreOverrideCursor(); From a67a574b89ae1e12de158d6fdc5b51fdec6f13f3 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sun, 28 Oct 2018 12:23:06 +0100 Subject: [PATCH 37/84] Reduce function call overhead The arg() function of the QString class has a variable length argument which allows to reduce the number of chained calls to the same function. With proper formatting, readability is not affected. --- src/browser/BrowserService.cpp | 2 +- src/cli/Extract.cpp | 2 +- src/core/Group.cpp | 2 +- src/core/Merger.cpp | 38 ++++++++++------------------ src/gui/AboutDialog.cpp | 6 ++--- src/gui/KMessageWidget.cpp | 8 +++--- src/gui/csvImport/CsvParserModel.cpp | 6 ++--- src/totp/totp.cpp | 6 ++--- 8 files changed, 30 insertions(+), 40 deletions(-) diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 456028632..824cc94b6 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -362,7 +362,7 @@ void BrowserService::updateEntry(const QString& id, if (!browserSettings()->alwaysAllowUpdate()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("KeePassXC: Update Entry")); - msgBox.setText(tr("Do you want to update the information in %1 - %2?").arg(QUrl(url).host()).arg(username)); + msgBox.setText(tr("Do you want to update the information in %1 - %2?").arg(QUrl(url).host(), username)); msgBox.setStandardButtons(QMessageBox::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 32b1bc028..c0b1b1119 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -77,7 +77,7 @@ int Extract::execute(const QStringList& arguments) auto fileKey = QSharedPointer::create(); QString errorMsg; if (!fileKey->load(keyFilePath, &errorMsg)) { - err << QObject::tr("Failed to load key file %1: %2").arg(keyFilePath).arg(errorMsg) << endl; + err << QObject::tr("Failed to load key file %1: %2").arg(keyFilePath, errorMsg) << endl; return EXIT_FAILURE; } diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 05cac41e9..72f2490a0 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -811,7 +811,7 @@ void Group::removeEntry(Entry* entry) { Q_ASSERT_X(m_entries.contains(entry), Q_FUNC_INFO, - QString("Group %1 does not contain %2").arg(this->name()).arg(entry->title()).toLatin1()); + QString("Group %1 does not contain %2").arg(this->name(), entry->title()).toLatin1()); emit entryAboutToRemove(entry); diff --git a/src/core/Merger.cpp b/src/core/Merger.cpp index 9b87a6ac3..2bdff7377 100644 --- a/src/core/Merger.cpp +++ b/src/core/Merger.cpp @@ -95,7 +95,7 @@ Merger::ChangeList Merger::mergeGroup(const MergeContext& context) // Entry is already present in the database. Update it. const bool locationChanged = targetEntry->timeInfo().locationChanged() < sourceEntry->timeInfo().locationChanged(); if (locationChanged && targetEntry->group() != context.m_targetGroup) { - changes << tr("Relocating %1 [%2]").arg(sourceEntry->title()).arg(sourceEntry->uuidToHex()); + changes << tr("Relocating %1 [%2]").arg(sourceEntry->title(), sourceEntry->uuidToHex()); moveEntry(targetEntry, context.m_targetGroup); } changes << resolveEntryConflict(context, sourceEntry, targetEntry); @@ -107,7 +107,7 @@ Merger::ChangeList Merger::mergeGroup(const MergeContext& context) for (Group* sourceChildGroup : sourceChildGroups) { Group* targetChildGroup = context.m_targetRootGroup->findGroupByUuid(sourceChildGroup->uuid()); if (!targetChildGroup) { - changes << tr("Creating missing %1 [%2]").arg(sourceChildGroup->name()).arg(sourceChildGroup->uuidToHex()); + changes << tr("Creating missing %1 [%2]").arg(sourceChildGroup->name(), sourceChildGroup->uuidToHex()); targetChildGroup = sourceChildGroup->clone(Entry::CloneNoFlags, Group::CloneNoFlags); moveGroup(targetChildGroup, context.m_targetGroup); TimeInfo timeinfo = targetChildGroup->timeInfo(); @@ -117,7 +117,7 @@ Merger::ChangeList Merger::mergeGroup(const MergeContext& context) bool locationChanged = targetChildGroup->timeInfo().locationChanged() < sourceChildGroup->timeInfo().locationChanged(); if (locationChanged && targetChildGroup->parent() != context.m_targetGroup) { - changes << tr("Relocating %1 [%2]").arg(sourceChildGroup->name()).arg(sourceChildGroup->uuidToHex()); + changes << tr("Relocating %1 [%2]").arg(sourceChildGroup->name(), sourceChildGroup->uuidToHex()); moveGroup(targetChildGroup, context.m_targetGroup); TimeInfo timeinfo = targetChildGroup->timeInfo(); timeinfo.setLocationChanged(sourceChildGroup->timeInfo().locationChanged()); @@ -146,7 +146,7 @@ Merger::ChangeList Merger::resolveGroupConflict(const MergeContext& context, con // only if the other group is newer, update the existing one. if (timeExisting < timeOther) { - changes << tr("Overwriting %1 [%2]").arg(sourceChildGroup->name()).arg(sourceChildGroup->uuidToHex()); + changes << tr("Overwriting %1 [%2]").arg(sourceChildGroup->name(), sourceChildGroup->uuidToHex()); targetChildGroup->setName(sourceChildGroup->name()); targetChildGroup->setNotes(sourceChildGroup->notes()); if (sourceChildGroup->iconNumber() == 0) { @@ -270,16 +270,12 @@ Merger::ChangeList Merger::resolveEntryConflict_Duplicate(const MergeContext& co Entry* clonedEntry = sourceEntry->clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory); moveEntry(clonedEntry, context.m_targetGroup); markOlderEntry(targetEntry); - changes << tr("Adding backup for older target %1 [%2]") - .arg(targetEntry->title()) - .arg(targetEntry->uuidToHex()); + changes << tr("Adding backup for older target %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex()); } else if (comparison > 0) { Entry* clonedEntry = sourceEntry->clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory); moveEntry(clonedEntry, context.m_targetGroup); markOlderEntry(clonedEntry); - changes << tr("Adding backup for older source %1 [%2]") - .arg(sourceEntry->title()) - .arg(sourceEntry->uuidToHex()); + changes << tr("Adding backup for older source %1 [%2]").arg(sourceEntry->title(), sourceEntry->uuidToHex()); } return changes; } @@ -297,8 +293,7 @@ Merger::ChangeList Merger::resolveEntryConflict_KeepLocal(const MergeContext& co // this type of merge changes the database timestamp since reapplying the // old entry is an active change of the database! changes << tr("Reapplying older target entry on top of newer source %1 [%2]") - .arg(targetEntry->title()) - .arg(targetEntry->uuidToHex()); + .arg(targetEntry->title(), targetEntry->uuidToHex()); Entry* agedTargetEntry = targetEntry->clone(Entry::CloneNoFlags); targetEntry->addHistoryItem(agedTargetEntry); } @@ -318,8 +313,7 @@ Merger::ChangeList Merger::resolveEntryConflict_KeepRemote(const MergeContext& c // this type of merge changes the database timestamp since reapplying the // old entry is an active change of the database! changes << tr("Reapplying older source entry on top of newer target %1 [%2]") - .arg(targetEntry->title()) - .arg(targetEntry->uuidToHex()); + .arg(targetEntry->title(), targetEntry->uuidToHex()); targetEntry->beginUpdate(); targetEntry->copyDataFrom(sourceEntry); targetEntry->endUpdate(); @@ -342,9 +336,7 @@ Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContex qPrintable(targetEntry->title()), qPrintable(sourceEntry->title()), qPrintable(currentGroup->name())); - changes << tr("Synchronizing from newer source %1 [%2]") - .arg(targetEntry->title()) - .arg(targetEntry->uuidToHex()); + changes << tr("Synchronizing from newer source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex()); moveEntry(clonedEntry, currentGroup); mergeHistory(targetEntry, clonedEntry, mergeMethod); eraseEntry(targetEntry); @@ -355,9 +347,7 @@ Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContex qPrintable(targetEntry->group()->name())); const bool changed = mergeHistory(sourceEntry, targetEntry, mergeMethod); if (changed) { - changes << tr("Synchronizing from older source %1 [%2]") - .arg(targetEntry->title()) - .arg(targetEntry->uuidToHex()); + changes << tr("Synchronizing from older source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex()); } } return changes; @@ -550,9 +540,9 @@ Merger::ChangeList Merger::mergeDeletions(const MergeContext& context) } deletions << object; if (entry->group()) { - changes << tr("Deleting child %1 [%2]").arg(entry->title()).arg(entry->uuidToHex()); + changes << tr("Deleting child %1 [%2]").arg(entry->title(), entry->uuidToHex()); } else { - changes << tr("Deleting orphan %1 [%2]").arg(entry->title()).arg(entry->uuidToHex()); + changes << tr("Deleting orphan %1 [%2]").arg(entry->title(), entry->uuidToHex()); } // Entry is inserted into deletedObjects after deletions are processed eraseEntry(entry); @@ -576,9 +566,9 @@ Merger::ChangeList Merger::mergeDeletions(const MergeContext& context) } deletions << object; if (group->parentGroup()) { - changes << tr("Deleting child %1 [%2]").arg(group->name()).arg(group->uuidToHex()); + changes << tr("Deleting child %1 [%2]").arg(group->name(), group->uuidToHex()); } else { - changes << tr("Deleting orphan %1 [%2]").arg(group->name()).arg(group->uuidToHex()); + changes << tr("Deleting orphan %1 [%2]").arg(group->name(), group->uuidToHex()); } eraseGroup(group); } diff --git a/src/gui/AboutDialog.cpp b/src/gui/AboutDialog.cpp index f6a9d15f9..483e4dd2b 100644 --- a/src/gui/AboutDialog.cpp +++ b/src/gui/AboutDialog.cpp @@ -65,9 +65,9 @@ AboutDialog::AboutDialog(QWidget* parent) #endif debugInfo.append("\n").append(QString("%1\n- Qt %2\n- %3\n\n") - .arg(tr("Libraries:")) - .arg(QString::fromLocal8Bit(qVersion())) - .arg(Crypto::backendVersion())); + .arg(tr("Libraries:"), + QString::fromLocal8Bit(qVersion()), + Crypto::backendVersion())); #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) debugInfo.append(tr("Operating system: %1\nCPU architecture: %2\nKernel: %3 %4") diff --git a/src/gui/KMessageWidget.cpp b/src/gui/KMessageWidget.cpp index 3d0500708..2e68975e3 100644 --- a/src/gui/KMessageWidget.cpp +++ b/src/gui/KMessageWidget.cpp @@ -309,10 +309,10 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type) "}" ".QLabel { color: %6; }" )) - .arg(bg0.name()) - .arg(bg1.name()) - .arg(bg2.name()) - .arg(border.name()) + .arg(bg0.name(), + bg1.name(), + bg2.name(), + border.name()) // DefaultFrameWidth returns the size of the external margin + border width. We know our border is 1px, // so we subtract this from the frame normal QStyle FrameWidth to get our margin .arg(style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this) - 1) diff --git a/src/gui/csvImport/CsvParserModel.cpp b/src/gui/csvImport/CsvParserModel.cpp index e69ea1853..8f7d98ec4 100644 --- a/src/gui/csvImport/CsvParserModel.cpp +++ b/src/gui/csvImport/CsvParserModel.cpp @@ -36,9 +36,9 @@ void CsvParserModel::setFilename(const QString& filename) QString CsvParserModel::getFileInfo() { QString a(tr("%1, %2, %3", "file info: bytes, rows, columns") - .arg(tr("%n byte(s)", nullptr, getFileSize())) - .arg(tr("%n row(s)", nullptr, getCsvRows())) - .arg(tr("%n column(s)", nullptr, qMax(0, getCsvCols() - 1)))); + .arg(tr("%n byte(s)", nullptr, getFileSize()), + tr("%n row(s)", nullptr, getCsvRows()), + tr("%n column(s)", nullptr, qMax(0, getCsvCols() - 1)))); return a; } diff --git a/src/totp/totp.cpp b/src/totp/totp.cpp index 4140993c7..bc66fffa4 100644 --- a/src/totp/totp.cpp +++ b/src/totp/totp.cpp @@ -106,9 +106,9 @@ QString Totp::writeSettings(const QSharedPointer settings, const // OTP Url output if (settings->otpUrl || forceOtp) { auto urlstring = QString("otpauth://totp/%1:%2?secret=%3&period=%4&digits=%5&issuer=%1") - .arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title))) - .arg(username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username))) - .arg(QString(Base32::sanitizeInput(settings->key.toLatin1()))) + .arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title)), + username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username)), + QString(Base32::sanitizeInput(settings->key.toLatin1()))) .arg(settings->step) .arg(settings->digits); From da9afd3f6fcb7b7c3c5c02112ffe49c1ee34ba43 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sun, 28 Oct 2018 12:49:32 +0100 Subject: [PATCH 38/84] Reduce number of unneeded copies This patch aims at reducing the number of copies for obejcts that could be referenced rather than copied, because they're not modified during the computation. --- src/browser/BrowserAction.cpp | 8 ++++---- src/browser/BrowserAction.h | 8 ++++---- src/browser/BrowserSettings.cpp | 8 ++++---- src/browser/BrowserSettings.h | 8 ++++---- src/cli/Clip.cpp | 2 +- src/cli/Clip.h | 2 +- src/cli/Command.cpp | 2 +- src/cli/Command.h | 2 +- src/core/CsvParser.cpp | 6 +++--- src/core/CsvParser.h | 4 ++-- src/core/Database.cpp | 12 ++++++------ src/core/Database.h | 12 ++++++------ src/core/EntrySearcher.cpp | 4 ++-- src/core/Group.cpp | 12 ++++++------ src/core/Group.h | 12 ++++++------ src/core/PasswordGenerator.cpp | 2 +- src/core/PasswordGenerator.h | 2 +- src/format/CsvExporter.cpp | 4 ++-- src/format/KeePass2.cpp | 4 ++-- src/format/KeePass2.h | 4 ++-- src/gui/EditWidgetIcons.cpp | 4 ++-- src/gui/FileDialog.cpp | 2 +- src/gui/FileDialog.h | 2 +- src/gui/MainWindow.cpp | 2 +- src/gui/MainWindow.h | 2 +- src/gui/PasswordEdit.cpp | 2 +- src/gui/PasswordEdit.h | 2 +- src/gui/csvImport/CsvImportWidget.cpp | 4 ++-- src/gui/csvImport/CsvImportWidget.h | 4 ++-- src/gui/entry/AutoTypeMatchModel.cpp | 2 +- src/gui/entry/AutoTypeMatchModel.h | 2 +- src/gui/entry/AutoTypeMatchView.cpp | 2 +- src/gui/entry/AutoTypeMatchView.h | 2 +- src/gui/entry/EntryHistoryModel.cpp | 2 +- src/keys/CompositeKey.cpp | 4 ++-- src/keys/CompositeKey.h | 4 ++-- src/streams/HmacBlockStream.cpp | 2 +- src/streams/HmacBlockStream.h | 2 +- src/totp/totp.cpp | 8 ++++---- src/totp/totp.h | 8 ++++---- 40 files changed, 90 insertions(+), 90 deletions(-) diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index fcbc318ee..ccd17c5fd 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -468,7 +468,7 @@ QJsonObject BrowserAction::decryptMessage(const QString& message, const QString& return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE); } -QString BrowserAction::encrypt(const QString plaintext, const QString nonce) +QString BrowserAction::encrypt(const QString& plaintext, const QString& nonce) { QMutexLocker locker(&m_mutex); const QByteArray ma = plaintext.toUtf8(); @@ -496,7 +496,7 @@ QString BrowserAction::encrypt(const QString plaintext, const QString nonce) return QString(); } -QByteArray BrowserAction::decrypt(const QString encrypted, const QString nonce) +QByteArray BrowserAction::decrypt(const QString& encrypted, const QString& nonce) { QMutexLocker locker(&m_mutex); const QByteArray ma = base64Decode(encrypted); @@ -546,14 +546,14 @@ QJsonObject BrowserAction::getJsonObject(const uchar* pArray, const uint len) co return doc.object(); } -QJsonObject BrowserAction::getJsonObject(const QByteArray ba) const +QJsonObject BrowserAction::getJsonObject(const QByteArray& ba) const { QJsonParseError err; QJsonDocument doc(QJsonDocument::fromJson(ba, &err)); return doc.object(); } -QByteArray BrowserAction::base64Decode(const QString str) +QByteArray BrowserAction::base64Decode(const QString& str) { return QByteArray::fromBase64(str.toUtf8()); } diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index 5a7c83bf8..b7a60938f 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -73,14 +73,14 @@ private: QString encryptMessage(const QJsonObject& message, const QString& nonce); QJsonObject decryptMessage(const QString& message, const QString& nonce, const QString& action = QString()); - QString encrypt(const QString plaintext, const QString nonce); - QByteArray decrypt(const QString encrypted, const QString nonce); + QString encrypt(const QString& plaintext, const QString& nonce); + QByteArray decrypt(const QString& encrypted, const QString& nonce); QString getBase64FromKey(const uchar* array, const uint len); QByteArray getQByteArray(const uchar* array, const uint len) const; QJsonObject getJsonObject(const uchar* pArray, const uint len) const; - QJsonObject getJsonObject(const QByteArray ba) const; - QByteArray base64Decode(const QString str); + QJsonObject getJsonObject(const QByteArray& ba) const; + QByteArray base64Decode(const QString& str); QString incrementNonce(const QString& nonce); private: diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index 630d0ff18..646c6c4d3 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -169,7 +169,7 @@ QString BrowserSettings::customProxyLocation() return config()->get("Browser/CustomProxyLocation", "").toString(); } -void BrowserSettings::setCustomProxyLocation(QString location) +void BrowserSettings::setCustomProxyLocation(const QString& location) { config()->set("Browser/CustomProxyLocation", location); } @@ -364,7 +364,7 @@ QString BrowserSettings::passwordExcludedChars() return config()->get("generator/ExcludedChars", PasswordGenerator::DefaultExcludedChars).toString(); } -void BrowserSettings::setPasswordExcludedChars(QString chars) +void BrowserSettings::setPasswordExcludedChars(const QString& chars) { config()->set("generator/ExcludedChars", chars); } @@ -384,7 +384,7 @@ QString BrowserSettings::passPhraseWordSeparator() return config()->get("generator/WordSeparator", PassphraseGenerator::DefaultSeparator).toString(); } -void BrowserSettings::setPassPhraseWordSeparator(QString separator) +void BrowserSettings::setPassPhraseWordSeparator(const QString& separator) { config()->set("generator/WordSeparator", separator); } @@ -496,7 +496,7 @@ QString BrowserSettings::generatePassword() } } -void BrowserSettings::updateBinaryPaths(QString customProxyLocation) +void BrowserSettings::updateBinaryPaths(const QString& customProxyLocation) { bool isProxy = supportBrowserProxy(); m_hostInstaller.updateBinaryPaths(isProxy, customProxyLocation); diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index 2e7c55ec6..92cdcd16d 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -59,7 +59,7 @@ public: bool useCustomProxy(); void setUseCustomProxy(bool enabled); QString customProxyLocation(); - void setCustomProxyLocation(QString location); + void setCustomProxyLocation(const QString& location); bool updateBinaryPath(); void setUpdateBinaryPath(bool enabled); bool chromeSupport(); @@ -98,11 +98,11 @@ public: bool advancedMode(); void setAdvancedMode(bool advancedMode); QString passwordExcludedChars(); - void setPasswordExcludedChars(QString chars); + void setPasswordExcludedChars(const QString& chars); int passPhraseWordCount(); void setPassPhraseWordCount(int wordCount); QString passPhraseWordSeparator(); - void setPassPhraseWordSeparator(QString separator); + void setPassPhraseWordSeparator(const QString& separator); int generatorType(); void setGeneratorType(int type); bool passwordEveryGroup(); @@ -114,7 +114,7 @@ public: PasswordGenerator::CharClasses passwordCharClasses(); PasswordGenerator::GeneratorFlags passwordGeneratorFlags(); QString generatePassword(); - void updateBinaryPaths(QString customProxyLocation = QString()); + void updateBinaryPaths(const QString& customProxyLocation = QString()); bool checkIfProxyExists(QString& path); private: diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 2268c6624..a9135eff4 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -71,7 +71,7 @@ int Clip::execute(const QStringList& arguments) return clipEntry(db, args.at(1), args.value(2)); } -int Clip::clipEntry(Database* database, QString entryPath, QString timeout) +int Clip::clipEntry(Database* database, const QString& entryPath, const QString& timeout) { TextStream err(Utils::STDERR); diff --git a/src/cli/Clip.h b/src/cli/Clip.h index e94231236..929ddf678 100644 --- a/src/cli/Clip.h +++ b/src/cli/Clip.h @@ -26,7 +26,7 @@ public: Clip(); ~Clip(); int execute(const QStringList& arguments); - int clipEntry(Database* database, QString entryPath, QString timeout); + int clipEntry(Database* database, const QString& entryPath, const QString& timeout); }; #endif // KEEPASSXC_CLIP_H diff --git a/src/cli/Command.cpp b/src/cli/Command.cpp index c85e5d95d..a95676ff0 100644 --- a/src/cli/Command.cpp +++ b/src/cli/Command.cpp @@ -71,7 +71,7 @@ void populateCommands() } } -Command* Command::getCommand(QString commandName) +Command* Command::getCommand(const QString& commandName) { populateCommands(); if (commands.contains(commandName)) { diff --git a/src/cli/Command.h b/src/cli/Command.h index 7ad49440a..4e4e076de 100644 --- a/src/cli/Command.h +++ b/src/cli/Command.h @@ -35,7 +35,7 @@ public: QString getDescriptionLine(); static QList getCommands(); - static Command* getCommand(QString commandName); + static Command* getCommand(const QString& commandName); }; #endif // KEEPASSXC_COMMAND_H diff --git a/src/core/CsvParser.cpp b/src/core/CsvParser.cpp index a66c919b2..e545d1db4 100644 --- a/src/core/CsvParser.cpp +++ b/src/core/CsvParser.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2016 Enrico Mariotti * Copyright (C) 2017 KeePassXC Team * @@ -327,7 +327,7 @@ bool CsvParser::isText(QChar c) const return !((isCRLF(c)) || (isSeparator(c))); } -bool CsvParser::isEmptyRow(CsvRow row) const +bool CsvParser::isEmptyRow(const CsvRow& row) const { CsvRow::const_iterator it = row.constBegin(); for (; it != row.constEnd(); ++it) @@ -414,7 +414,7 @@ int CsvParser::getCsvRows() const return m_table.size(); } -void CsvParser::appendStatusMsg(QString s, bool isCritical) +void CsvParser::appendStatusMsg(const QString& s, bool isCritical) { m_statusMsg += QObject::tr("%1: (row, col) %2,%3").arg(s, m_currRow, m_currCol).append("\n"); m_isGood = !isCritical; diff --git a/src/core/CsvParser.h b/src/core/CsvParser.h index 323023114..d90e8300a 100644 --- a/src/core/CsvParser.h +++ b/src/core/CsvParser.h @@ -83,7 +83,7 @@ private: bool isCRLF(const QChar& c) const; bool isSpace(const QChar& c) const; bool isTab(const QChar& c) const; - bool isEmptyRow(CsvRow row) const; + bool isEmptyRow(const CsvRow& row) const; bool parseFile(); void parseRecord(); void parseField(CsvRow& row); @@ -96,7 +96,7 @@ private: void clear(); bool skipEndline(); void skipLine(); - void appendStatusMsg(QString s, bool isCritical = false); + void appendStatusMsg(const QString& s, bool isCritical = false); }; #endif // CSVPARSER_H diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 5116fd199..693b9c549 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -315,7 +315,7 @@ void Database::setCompressionAlgo(Database::CompressionAlgorithm algo) * @param updateTransformSalt true to update the transform salt * @return true on success */ -bool Database::setKey(QSharedPointer key, bool updateChangedTime, bool updateTransformSalt) +bool Database::setKey(const QSharedPointer& key, bool updateChangedTime, bool updateTransformSalt) { if (!key) { m_data.key.reset(); @@ -354,7 +354,7 @@ bool Database::hasKey() const return m_data.hasKey; } -bool Database::verifyKey(QSharedPointer key) const +bool Database::verifyKey(const QSharedPointer& key) const { Q_ASSERT(hasKey()); @@ -501,7 +501,7 @@ Database* Database::openDatabaseFile(const QString& fileName, QSharedPointer::create(); QTextStream out(outputDescriptor); @@ -553,7 +553,7 @@ Database* Database::unlockFromStdin(QString databaseFilename, QString keyFilenam * @param backup Backup the existing database file, if exists * @return error string, if any */ -QString Database::saveToFile(QString filePath, bool atomic, bool backup) +QString Database::saveToFile(const QString& filePath, bool atomic, bool backup) { QString error; if (atomic) { @@ -633,7 +633,7 @@ QString Database::writeDatabase(QIODevice* device) * @param filePath Path to the file to backup * @return */ -bool Database::backupDatabase(QString filePath) +bool Database::backupDatabase(const QString& filePath) { QString backupFilePath = filePath; auto re = QRegularExpression("\\.kdbx$|(? kdf) m_data.kdf = std::move(kdf); } -bool Database::changeKdf(QSharedPointer kdf) +bool Database::changeKdf(const QSharedPointer& kdf) { kdf->randomizeSeed(); QByteArray transformedMasterKey; diff --git a/src/core/Database.h b/src/core/Database.h index 7108ded31..692444f4d 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -110,9 +110,9 @@ public: void setCipher(const QUuid& cipher); void setCompressionAlgo(Database::CompressionAlgorithm algo); void setKdf(QSharedPointer kdf); - bool setKey(QSharedPointer key, bool updateChangedTime = true, bool updateTransformSalt = false); + bool setKey(const QSharedPointer& key, bool updateChangedTime = true, bool updateTransformSalt = false); bool hasKey() const; - bool verifyKey(QSharedPointer key) const; + bool verifyKey(const QSharedPointer& key) const; QVariantMap& publicCustomData(); const QVariantMap& publicCustomData() const; void setPublicCustomData(const QVariantMap& customData); @@ -121,17 +121,17 @@ public: void emptyRecycleBin(); void setEmitModified(bool value); void markAsModified(); - QString saveToFile(QString filePath, bool atomic = true, bool backup = false); + QString saveToFile(const QString& filePath, bool atomic = true, bool backup = false); /** * Returns a unique id that is only valid as long as the Database exists. */ const QUuid& uuid(); - bool changeKdf(QSharedPointer kdf); + bool changeKdf(const QSharedPointer& kdf); static Database* databaseByUuid(const QUuid& uuid); static Database* openDatabaseFile(const QString& fileName, QSharedPointer key); - static Database* unlockFromStdin(QString databaseFilename, QString keyFilename = {}, + static Database* unlockFromStdin(const QString& databaseFilename, const QString& keyFilename = {}, FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr); signals: @@ -156,7 +156,7 @@ private: void createRecycleBin(); QString writeDatabase(QIODevice* device); - bool backupDatabase(QString filePath); + bool backupDatabase(const QString& filePath); Metadata* const m_metadata; Group* m_rootGroup; diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index 820646a98..3413f1cd0 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -34,12 +34,12 @@ EntrySearcher::searchEntries(const QString& searchTerm, const Group* group, Qt:: { QList searchResult; - const QList entryList = group->entries(); + const QList& entryList = group->entries(); for (Entry* entry : entryList) { searchResult.append(matchEntry(searchTerm, entry, caseSensitivity)); } - const QList children = group->children(); + const QList& children = group->children(); for (Group* childGroup : children) { if (childGroup->searchingEnabled() != Group::Disable) { if (matchGroup(searchTerm, childGroup, caseSensitivity)) { diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 72f2490a0..4b1d11ab0 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -563,7 +563,7 @@ Entry* Group::findEntryByUuid(const QUuid& uuid) const return nullptr; } -Entry* Group::findEntryByPath(QString entryPath) +Entry* Group::findEntryByPath(const QString& entryPath) { if (entryPath.isEmpty()) { return nullptr; @@ -578,7 +578,7 @@ Entry* Group::findEntryByPath(QString entryPath) return findEntryByPathRecursive(normalizedEntryPath, "/"); } -Entry* Group::findEntryByPathRecursive(QString entryPath, QString basePath) +Entry* Group::findEntryByPathRecursive(const QString& entryPath, const QString& basePath) { // Return the first entry that matches the full path OR if there is no leading // slash, return the first entry title that matches @@ -599,7 +599,7 @@ Entry* Group::findEntryByPathRecursive(QString entryPath, QString basePath) return nullptr; } -Group* Group::findGroupByPath(QString groupPath) +Group* Group::findGroupByPath(const QString& groupPath) { // normalize the groupPath by adding missing front and rear slashes. once. QString normalizedGroupPath; @@ -614,7 +614,7 @@ Group* Group::findGroupByPath(QString groupPath) return findGroupByPathRecursive(normalizedGroupPath, "/"); } -Group* Group::findGroupByPathRecursive(QString groupPath, QString basePath) +Group* Group::findGroupByPathRecursive(const QString& groupPath, const QString& basePath) { // paths must be normalized Q_ASSERT(groupPath.startsWith("/") && groupPath.endsWith("/")); @@ -926,7 +926,7 @@ bool Group::resolveAutoTypeEnabled() const } } -QStringList Group::locate(QString locateTerm, QString currentPath) const +QStringList Group::locate(const QString& locateTerm, const QString& currentPath) const { // TODO: Replace with EntrySearcher QStringList response; @@ -950,7 +950,7 @@ QStringList Group::locate(QString locateTerm, QString currentPath) const return response; } -Entry* Group::addEntryWithPath(QString entryPath) +Entry* Group::addEntryWithPath(const QString& entryPath) { if (entryPath.isEmpty() || findEntryByPath(entryPath)) { return nullptr; diff --git a/src/core/Group.h b/src/core/Group.h index 3a9332c8e..da6994d2a 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -115,11 +115,11 @@ public: Group* findChildByName(const QString& name); Entry* findEntryByUuid(const QUuid& uuid) const; - Entry* findEntryByPath(QString entryPath); + Entry* findEntryByPath(const QString& entryPath); Group* findGroupByUuid(const QUuid& uuid); - Group* findGroupByPath(QString groupPath); - QStringList locate(QString locateTerm, QString currentPath = {"/"}) const; - Entry* addEntryWithPath(QString entryPath); + Group* findGroupByPath(const QString& groupPath); + QStringList locate(const QString& locateTerm, const QString& currentPath = {"/"}) const; + Entry* addEntryWithPath(const QString& entryPath); void setUuid(const QUuid& uuid); void setName(const QString& name); void setNotes(const QString& notes); @@ -190,8 +190,8 @@ private: void cleanupParent(); void recCreateDelObjects(); - Entry* findEntryByPathRecursive(QString entryPath, QString basePath); - Group* findGroupByPathRecursive(QString groupPath, QString basePath); + Entry* findEntryByPathRecursive(const QString& entryPath, const QString& basePath); + Group* findGroupByPathRecursive(const QString& groupPath, const QString& basePath); QPointer m_db; QUuid m_uuid; diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp index 3dbcdaad8..124b99896 100644 --- a/src/core/PasswordGenerator.cpp +++ b/src/core/PasswordGenerator.cpp @@ -31,7 +31,7 @@ PasswordGenerator::PasswordGenerator() { } -double PasswordGenerator::calculateEntropy(QString password) +double PasswordGenerator::calculateEntropy(const QString& password) { return ZxcvbnMatch(password.toLatin1(), 0, 0); } diff --git a/src/core/PasswordGenerator.h b/src/core/PasswordGenerator.h index cb6402d0f..7bfdddd69 100644 --- a/src/core/PasswordGenerator.h +++ b/src/core/PasswordGenerator.h @@ -57,7 +57,7 @@ public: public: PasswordGenerator(); - double calculateEntropy(QString password); + double calculateEntropy(const QString& password); void setLength(int length); void setCharClasses(const CharClasses& classes); void setFlags(const GeneratorFlags& flags); diff --git a/src/format/CsvExporter.cpp b/src/format/CsvExporter.cpp index c444afe23..67e6a44fc 100644 --- a/src/format/CsvExporter.cpp +++ b/src/format/CsvExporter.cpp @@ -64,7 +64,7 @@ bool CsvExporter::writeGroup(QIODevice* device, const Group* group, QString grou } groupPath.append(group->name()); - const QList entryList = group->entries(); + const QList& entryList = group->entries(); for (const Entry* entry : entryList) { QString line; @@ -83,7 +83,7 @@ bool CsvExporter::writeGroup(QIODevice* device, const Group* group, QString grou } } - const QList children = group->children(); + const QList& children = group->children(); for (const Group* child : children) { if (!writeGroup(device, child, groupPath)) { return false; diff --git a/src/format/KeePass2.cpp b/src/format/KeePass2.cpp index 9c0714484..639255d27 100644 --- a/src/format/KeePass2.cpp +++ b/src/format/KeePass2.cpp @@ -59,7 +59,7 @@ const QList> KeePass2::KDFS{ qMakePair(KeePass2::KDF_AES_KDBX3, QObject::tr("AES-KDF (KDBX 3.1)")) }; -QByteArray KeePass2::hmacKey(QByteArray masterSeed, QByteArray transformedMasterKey) +QByteArray KeePass2::hmacKey(const QByteArray& masterSeed, const QByteArray& transformedMasterKey) { CryptoHash hmacKeyHash(CryptoHash::Sha512); hmacKeyHash.addData(masterSeed); @@ -98,7 +98,7 @@ QSharedPointer KeePass2::kdfFromParameters(const QVariantMap& p) return kdf; } -QVariantMap KeePass2::kdfToParameters(QSharedPointer kdf) +QVariantMap KeePass2::kdfToParameters(const QSharedPointer& kdf) { return kdf->writeParameters(); } diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h index 02fe635ca..195ce8c2b 100644 --- a/src/format/KeePass2.h +++ b/src/format/KeePass2.h @@ -126,9 +126,9 @@ extern const QList> KDFS; ByteArray = 0x42 }; -QByteArray hmacKey(QByteArray masterSeed, QByteArray transformedMasterKey); +QByteArray hmacKey(const QByteArray& masterSeed, const QByteArray& transformedMasterKey); QSharedPointer kdfFromParameters(const QVariantMap& p); -QVariantMap kdfToParameters(QSharedPointer kdf); +QVariantMap kdfToParameters(const QSharedPointer& kdf); QSharedPointer uuidToKdf(const QUuid& uuid); ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id); diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 0555359d8..f2c320dc9 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -176,7 +176,7 @@ namespace { // Try to get the 2nd level domain of the host part of a QUrl. For example, // "foo.bar.example.com" would become "example.com", and "foo.bar.example.co.uk" // would become "example.co.uk". - QString getSecondLevelDomain(QUrl url) + QString getSecondLevelDomain(const QUrl& url) { QString fqdn = url.host(); fqdn.truncate(fqdn.length() - url.topLevelDomain().length()); @@ -185,7 +185,7 @@ namespace { return newdom; } - QUrl convertVariantToUrl(QVariant var) + QUrl convertVariantToUrl(const QVariant& var) { QUrl url; if (var.canConvert()) diff --git a/src/gui/FileDialog.cpp b/src/gui/FileDialog.cpp index 5bccc4af3..b063d62f3 100644 --- a/src/gui/FileDialog.cpp +++ b/src/gui/FileDialog.cpp @@ -178,7 +178,7 @@ FileDialog::FileDialog() { } -void FileDialog::saveLastDir(QString dir) +void FileDialog::saveLastDir(const QString& dir) { if (!dir.isEmpty() && !m_forgetLastDir) { config()->set("LastDir", QFileInfo(dir).absolutePath()); diff --git a/src/gui/FileDialog.h b/src/gui/FileDialog.h index 4862dcfda..83b151ec9 100644 --- a/src/gui/FileDialog.h +++ b/src/gui/FileDialog.h @@ -65,7 +65,7 @@ private: QString m_nextDirName; bool m_forgetLastDir = false; - void saveLastDir(QString); + void saveLastDir(const QString&); static FileDialog* m_instance; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 9efe0c273..8f0c23044 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -673,7 +673,7 @@ void MainWindow::switchToOpenDatabase() switchToDatabases(); } -void MainWindow::switchToDatabaseFile(QString file) +void MainWindow::switchToDatabaseFile(const QString& file) { m_ui->tabWidget->openDatabase(file); switchToDatabases(); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index d83b4a1ff..174e47564 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -90,7 +90,7 @@ private slots: void switchToPasswordGen(bool enabled); void switchToNewDatabase(); void switchToOpenDatabase(); - void switchToDatabaseFile(QString file); + void switchToDatabaseFile(const QString& file); void switchToKeePass1Database(); void switchToCsvImport(); void closePasswordGen(); diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 33d022176..02067ccc4 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -98,7 +98,7 @@ void PasswordEdit::updateStylesheet() setStyleSheet(stylesheet); } -void PasswordEdit::autocompletePassword(QString password) +void PasswordEdit::autocompletePassword(const QString& password) { if (config()->get("security/passwordsrepeat").toBool() && echoMode() == QLineEdit::Normal) { setText(password); diff --git a/src/gui/PasswordEdit.h b/src/gui/PasswordEdit.h index 5976347e6..222376d09 100644 --- a/src/gui/PasswordEdit.h +++ b/src/gui/PasswordEdit.h @@ -41,7 +41,7 @@ signals: private slots: void updateStylesheet(); - void autocompletePassword(QString password); + void autocompletePassword(const QString& password); private: bool passwordsEqual() const; diff --git a/src/gui/csvImport/CsvImportWidget.cpp b/src/gui/csvImport/CsvImportWidget.cpp index 4cb219a77..3b623f73d 100644 --- a/src/gui/csvImport/CsvImportWidget.cpp +++ b/src/gui/csvImport/CsvImportWidget.cpp @@ -315,7 +315,7 @@ void CsvImportWidget::setRootGroup() m_db->rootGroup()->setName("Root"); } -Group* CsvImportWidget::splitGroups(QString label) +Group* CsvImportWidget::splitGroups(const QString& label) { // extract group names from nested path provided in "label" Group* current = m_db->rootGroup(); @@ -345,7 +345,7 @@ Group* CsvImportWidget::splitGroups(QString label) return current; } -Group* CsvImportWidget::hasChildren(Group* current, QString groupName) +Group* CsvImportWidget::hasChildren(Group* current, const QString& groupName) { // returns the group whose name is "groupName" and is child of "current" group for (Group* group : current->children()) { diff --git a/src/gui/csvImport/CsvImportWidget.h b/src/gui/csvImport/CsvImportWidget.h index cd13836f0..a5807eefd 100644 --- a/src/gui/csvImport/CsvImportWidget.h +++ b/src/gui/csvImport/CsvImportWidget.h @@ -68,8 +68,8 @@ private: QStringList m_fieldSeparatorList; void configParser(); void updateTableview(); - Group* splitGroups(QString label); - Group* hasChildren(Group* current, QString groupName); + Group* splitGroups(const QString& label); + Group* hasChildren(Group* current, const QString& groupName); QString formatStatusText() const; }; diff --git a/src/gui/entry/AutoTypeMatchModel.cpp b/src/gui/entry/AutoTypeMatchModel.cpp index 1a6a6ba3b..197b3cd96 100644 --- a/src/gui/entry/AutoTypeMatchModel.cpp +++ b/src/gui/entry/AutoTypeMatchModel.cpp @@ -37,7 +37,7 @@ AutoTypeMatch AutoTypeMatchModel::matchFromIndex(const QModelIndex& index) const return m_matches.at(index.row()); } -QModelIndex AutoTypeMatchModel::indexFromMatch(AutoTypeMatch match) const +QModelIndex AutoTypeMatchModel::indexFromMatch(const AutoTypeMatch& match) const { int row = m_matches.indexOf(match); Q_ASSERT(row != -1); diff --git a/src/gui/entry/AutoTypeMatchModel.h b/src/gui/entry/AutoTypeMatchModel.h index 791dbc3df..58b89465b 100644 --- a/src/gui/entry/AutoTypeMatchModel.h +++ b/src/gui/entry/AutoTypeMatchModel.h @@ -41,7 +41,7 @@ public: explicit AutoTypeMatchModel(QObject* parent = nullptr); AutoTypeMatch matchFromIndex(const QModelIndex& index) const; - QModelIndex indexFromMatch(AutoTypeMatch match) const; + QModelIndex indexFromMatch(const AutoTypeMatch& match) const; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; diff --git a/src/gui/entry/AutoTypeMatchView.cpp b/src/gui/entry/AutoTypeMatchView.cpp index 2750082d8..0eb257237 100644 --- a/src/gui/entry/AutoTypeMatchView.cpp +++ b/src/gui/entry/AutoTypeMatchView.cpp @@ -98,7 +98,7 @@ AutoTypeMatch AutoTypeMatchView::currentMatch() return AutoTypeMatch(); } -void AutoTypeMatchView::setCurrentMatch(AutoTypeMatch match) +void AutoTypeMatchView::setCurrentMatch(const AutoTypeMatch& match) { selectionModel()->setCurrentIndex(m_sortModel->mapFromSource(m_model->indexFromMatch(match)), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); diff --git a/src/gui/entry/AutoTypeMatchView.h b/src/gui/entry/AutoTypeMatchView.h index 14ad9ea2a..b9f3f3aa5 100644 --- a/src/gui/entry/AutoTypeMatchView.h +++ b/src/gui/entry/AutoTypeMatchView.h @@ -34,7 +34,7 @@ class AutoTypeMatchView : public QTreeView public: explicit AutoTypeMatchView(QWidget* parent = nullptr); AutoTypeMatch currentMatch(); - void setCurrentMatch(AutoTypeMatch match); + void setCurrentMatch(const AutoTypeMatch& match); AutoTypeMatch matchFromIndex(const QModelIndex& index); void setMatchList(const QList& matches); void setFirstMatchActive(); diff --git a/src/gui/entry/EntryHistoryModel.cpp b/src/gui/entry/EntryHistoryModel.cpp index aaec3de62..2506e06d7 100644 --- a/src/gui/entry/EntryHistoryModel.cpp +++ b/src/gui/entry/EntryHistoryModel.cpp @@ -54,7 +54,7 @@ QVariant EntryHistoryModel::data(const QModelIndex& index, int role) const if (role == Qt::DisplayRole || role == Qt::UserRole) { Entry* entry = entryFromIndex(index); - TimeInfo timeInfo = entry->timeInfo(); + const TimeInfo& timeInfo = entry->timeInfo(); QDateTime lastModificationLocalTime = timeInfo.lastModificationTime().toLocalTime(); switch (index.column()) { case 0: diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index 4fca7d320..10e86318b 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -153,7 +153,7 @@ bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const * * @param key the key */ -void CompositeKey::addKey(QSharedPointer key) +void CompositeKey::addKey(const QSharedPointer& key) { m_keys.append(key); } @@ -173,7 +173,7 @@ const QList>& CompositeKey::keys() const * * @param key the key */ -void CompositeKey::addChallengeResponseKey(QSharedPointer key) +void CompositeKey::addChallengeResponseKey(const QSharedPointer& key) { m_challengeResponseKeys.append(key); } diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h index 43c624acb..f32f3a1a1 100644 --- a/src/keys/CompositeKey.h +++ b/src/keys/CompositeKey.h @@ -42,10 +42,10 @@ public: Q_REQUIRED_RESULT bool transform(const Kdf& kdf, QByteArray& result) const; bool challenge(const QByteArray& seed, QByteArray& result) const; - void addKey(QSharedPointer key); + void addKey(const QSharedPointer& key); const QList>& keys() const; - void addChallengeResponseKey(QSharedPointer key);\ + void addChallengeResponseKey(const QSharedPointer& key);\ const QList>& challengeResponseKeys() const; private: diff --git a/src/streams/HmacBlockStream.cpp b/src/streams/HmacBlockStream.cpp index 780db98c1..01d9ba7cd 100644 --- a/src/streams/HmacBlockStream.cpp +++ b/src/streams/HmacBlockStream.cpp @@ -245,7 +245,7 @@ QByteArray HmacBlockStream::getCurrentHmacKey() const return getHmacKey(m_blockIndex, m_key); } -QByteArray HmacBlockStream::getHmacKey(quint64 blockIndex, QByteArray key) +QByteArray HmacBlockStream::getHmacKey(quint64 blockIndex, const QByteArray& key) { Q_ASSERT(key.size() == 64); QByteArray indexBytes = Endian::sizedIntToBytes(blockIndex, ByteOrder); diff --git a/src/streams/HmacBlockStream.h b/src/streams/HmacBlockStream.h index c10c96746..a2ad062e3 100644 --- a/src/streams/HmacBlockStream.h +++ b/src/streams/HmacBlockStream.h @@ -34,7 +34,7 @@ public: bool reset() override; void close() override; - static QByteArray getHmacKey(quint64 blockIndex, QByteArray key); + static QByteArray getHmacKey(quint64 blockIndex, const QByteArray& key); bool atEnd() const override; diff --git a/src/totp/totp.cpp b/src/totp/totp.cpp index bc66fffa4..f1146441a 100644 --- a/src/totp/totp.cpp +++ b/src/totp/totp.cpp @@ -97,7 +97,7 @@ QSharedPointer Totp::createSettings(const QString& key, const ui }); } -QString Totp::writeSettings(const QSharedPointer settings, const QString& title, const QString& username, bool forceOtp) +QString Totp::writeSettings(const QSharedPointer& settings, const QString& title, const QString& username, bool forceOtp) { if (settings.isNull()) { return {}; @@ -127,7 +127,7 @@ QString Totp::writeSettings(const QSharedPointer settings, const return QString("%1;%2").arg(settings->step).arg(settings->digits); } -QString Totp::generateTotp(const QSharedPointer settings, const quint64 time) +QString Totp::generateTotp(const QSharedPointer& settings, const quint64 time) { Q_ASSERT(!settings.isNull()); if (settings.isNull()) { @@ -194,7 +194,7 @@ Totp::Encoder& Totp::steamEncoder() return getEncoderByShortName("S"); } -Totp::Encoder& Totp::getEncoderByShortName(QString shortName) +Totp::Encoder& Totp::getEncoderByShortName(const QString& shortName) { for (auto& encoder : encoders) { if (encoder.shortName == shortName) { @@ -204,7 +204,7 @@ Totp::Encoder& Totp::getEncoderByShortName(QString shortName) return defaultEncoder(); } -Totp::Encoder& Totp::getEncoderByName(QString name) +Totp::Encoder& Totp::getEncoderByName(const QString& name) { for (auto& encoder : encoders) { if (encoder.name == name) { diff --git a/src/totp/totp.h b/src/totp/totp.h index ba11ba2b0..0697281bf 100644 --- a/src/totp/totp.h +++ b/src/totp/totp.h @@ -60,15 +60,15 @@ static const QString ATTRIBUTE_SETTINGS = "TOTP Settings"; QSharedPointer parseSettings(const QString& rawSettings, const QString& key = {}); QSharedPointer createSettings(const QString& key, const uint digits, const uint step, const QString& encoderShortName = {}); -QString writeSettings(const QSharedPointer settings, const QString& title = {}, +QString writeSettings(const QSharedPointer& settings, const QString& title = {}, const QString& username = {}, bool forceOtp = false); -QString generateTotp(const QSharedPointer settings, const quint64 time = 0ull); +QString generateTotp(const QSharedPointer& settings, const quint64 time = 0ull); Encoder& defaultEncoder(); Encoder& steamEncoder(); -Encoder& getEncoderByShortName(QString shortName); -Encoder& getEncoderByName(QString name); +Encoder& getEncoderByShortName(const QString& shortName); +Encoder& getEncoderByName(const QString& name); } #endif // QTOTP_H From 7208635502a21e565b1b613504709e1d161a39a0 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sun, 28 Oct 2018 14:29:57 +0100 Subject: [PATCH 39/84] Enhance readability when accessing static members The sole purpose of a few objects was calling a static member of the class they belonged to. This is not needed, as you can access a static member with the :: notation. --- src/autotype/AutoType.cpp | 3 +-- src/gui/entry/EditEntryWidget.cpp | 3 +-- src/sshagent/BinaryStream.cpp | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index c2c16bc75..eee93c52c 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -731,8 +731,7 @@ bool AutoType::checkHighRepetition(const QString& string) bool AutoType::verifyAutoTypeSyntax(const QString& sequence) { if (!AutoType::checkSyntax(sequence)) { - QMessageBox messageBox; - messageBox.critical(nullptr, tr("Auto-Type"), tr("The Syntax of your Auto-Type statement is incorrect!")); + QMessageBox::critical(nullptr, tr("Auto-Type"), tr("The Syntax of your Auto-Type statement is incorrect!")); return false; } else if (AutoType::checkHighDelay(sequence)) { QMessageBox::StandardButton reply; diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index e8a450026..26bb419ce 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -1311,8 +1311,7 @@ void EditEntryWidget::pickColor() oldColor = QColor(m_advancedUi->bgColorButton->property("color").toString()); } - QColorDialog colorDialog(this); - QColor newColor = colorDialog.getColor(oldColor); + QColor newColor = QColorDialog::getColor(oldColor); if (newColor.isValid()) { setupColorButton(isForeground, newColor); setUnsavedChanges(true); diff --git a/src/sshagent/BinaryStream.cpp b/src/sshagent/BinaryStream.cpp index 2aa8ac1c7..2af04cee5 100644 --- a/src/sshagent/BinaryStream.cpp +++ b/src/sshagent/BinaryStream.cpp @@ -151,7 +151,7 @@ bool BinaryStream::readString(QString& str) return false; } - str = str.fromLatin1(ba); + str = QString::fromLatin1(ba); return true; } From 896a66e6d815e9122d5f26831a193226076feb9e Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sun, 28 Oct 2018 15:47:24 +0100 Subject: [PATCH 40/84] Improve readability and type-safety Use nullptr instead of 0 or NULL to initialize a null pointer. In some cases, readability was enhanced by replacing 0 with more meaningful values according to the type of the pointer being initialized. --- src/autotype/AutoType.cpp | 2 +- src/autotype/ShortcutWidget.cpp | 4 ++-- src/autotype/xcb/AutoTypeXCB.cpp | 6 +++--- src/browser/BrowserEntryConfig.h | 2 +- src/browser/BrowserService.cpp | 6 +++--- src/browser/HostInstaller.cpp | 2 +- src/browser/NativeMessagingHost.h | 2 +- src/core/PasswordGenerator.cpp | 6 +++--- src/core/ScreenLockListenerPrivate.h | 4 ++-- src/core/ScreenLockListenerWin.h | 2 +- src/crypto/Crypto.cpp | 2 +- src/gui/CategoryListWidget.h | 4 ++-- src/gui/DatabaseOpenWidget.cpp | 2 +- src/gui/DatabaseTabWidget.cpp | 4 ++-- src/gui/KMessageWidget.cpp | 4 ++-- src/gui/MessageWidget.h | 2 +- src/gui/SearchWidget.h | 4 ++-- src/gui/entry/EditEntryWidget.cpp | 12 ++++++------ src/gui/entry/EditEntryWidget_p.h | 2 +- src/gui/entry/EntryModel.cpp | 2 +- src/keys/drivers/YubiKey.cpp | 16 ++++++++-------- src/streams/qtiocompressor.cpp | 4 ++-- 22 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index eee93c52c..9d6b75557 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -43,7 +43,7 @@ AutoType::AutoType(QObject* parent, bool test) : QObject(parent) , m_autoTypeDelay(0) , m_currentGlobalKey(static_cast(0)) - , m_currentGlobalModifiers(0) + , m_currentGlobalModifiers(nullptr) , m_pluginLoader(new QPluginLoader(this)) , m_plugin(nullptr) , m_executor(nullptr) diff --git a/src/autotype/ShortcutWidget.cpp b/src/autotype/ShortcutWidget.cpp index 95174e430..3dcc669d9 100644 --- a/src/autotype/ShortcutWidget.cpp +++ b/src/autotype/ShortcutWidget.cpp @@ -24,7 +24,7 @@ ShortcutWidget::ShortcutWidget(QWidget* parent) : QLineEdit(parent) , m_key(static_cast(0)) - , m_modifiers(0) + , m_modifiers(nullptr) , m_locked(false) { setReadOnly(true); @@ -58,7 +58,7 @@ void ShortcutWidget::setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) void ShortcutWidget::resetShortcut() { m_key = static_cast(0); - m_modifiers = 0; + m_modifiers = nullptr; m_locked = false; autoType()->unregisterGlobalShortcut(); } diff --git a/src/autotype/xcb/AutoTypeXCB.cpp b/src/autotype/xcb/AutoTypeXCB.cpp index d2030b771..5198468cf 100644 --- a/src/autotype/xcb/AutoTypeXCB.cpp +++ b/src/autotype/xcb/AutoTypeXCB.cpp @@ -50,7 +50,7 @@ AutoTypePlatformX11::AutoTypePlatformX11() << "xfce4-panel"; // Xfce 4 m_currentGlobalKey = static_cast(0); - m_currentGlobalModifiers = 0; + m_currentGlobalModifiers = nullptr; m_keysymTable = nullptr; m_xkb = nullptr; @@ -197,7 +197,7 @@ void AutoTypePlatformX11::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModi XUngrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask | LockMask, m_rootWindow); m_currentGlobalKey = static_cast(0); - m_currentGlobalModifiers = 0; + m_currentGlobalModifiers = nullptr; m_currentGlobalKeycode = 0; m_currentGlobalNativeModifiers = 0; } @@ -496,7 +496,7 @@ void AutoTypePlatformX11::updateKeymap() m_xkb = getKeyboard(); XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode); - if (m_keysymTable != NULL) + if (m_keysymTable != nullptr) XFree(m_keysymTable); m_keysymTable = XGetKeyboardMapping(m_dpy, m_minKeycode, m_maxKeycode - m_minKeycode + 1, &m_keysymPerKeycode); diff --git a/src/browser/BrowserEntryConfig.h b/src/browser/BrowserEntryConfig.h index 3b808b7e0..f1363f421 100644 --- a/src/browser/BrowserEntryConfig.h +++ b/src/browser/BrowserEntryConfig.h @@ -35,7 +35,7 @@ class BrowserEntryConfig : public QObject Q_PROPERTY(QString Realm READ realm WRITE setRealm) public: - BrowserEntryConfig(QObject* object = 0); + BrowserEntryConfig(QObject* object = nullptr); bool load(const Entry* entry); void save(Entry* entry); diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 824cc94b6..8128ee14f 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -481,16 +481,16 @@ void BrowserService::convertAttributesToCustomData(Database *currentDb) progress.reset(); if (counter > 0) { - QMessageBox::information(0, tr("KeePassXC: Converted KeePassHTTP attributes"), + QMessageBox::information(nullptr, tr("KeePassXC: Converted KeePassHTTP attributes"), tr("Successfully converted attributes from %1 entry(s).\n" "Moved %2 keys to custom data.", "").arg(counter).arg(keyCounter), QMessageBox::Ok); } else if (counter == 0 && keyCounter > 0) { - QMessageBox::information(0, tr("KeePassXC: Converted KeePassHTTP attributes"), + QMessageBox::information(nullptr, tr("KeePassXC: Converted KeePassHTTP attributes"), tr("Successfully moved %n keys to custom data.", "", keyCounter), QMessageBox::Ok); } else { - QMessageBox::information(0, tr("KeePassXC: No entry with KeePassHTTP attributes found!"), + QMessageBox::information(nullptr, tr("KeePassXC: No entry with KeePassHTTP attributes found!"), tr("The active database does not contain an entry with KeePassHTTP attributes."), QMessageBox::Ok); } diff --git a/src/browser/HostInstaller.cpp b/src/browser/HostInstaller.cpp index a3ad608d4..f4585febe 100644 --- a/src/browser/HostInstaller.cpp +++ b/src/browser/HostInstaller.cpp @@ -111,7 +111,7 @@ void HostInstaller::installBrowser(SupportedBrowsers browser, // Always create the script file QJsonObject script = constructFile(browser, proxy, location); if (!saveFile(browser, script)) { - QMessageBox::critical(0, + QMessageBox::critical(nullptr, tr("KeePassXC: Cannot save file!"), tr("Cannot save the native messaging script file."), QMessageBox::Ok); diff --git a/src/browser/NativeMessagingHost.h b/src/browser/NativeMessagingHost.h index 869af9e24..da9ac5346 100644 --- a/src/browser/NativeMessagingHost.h +++ b/src/browser/NativeMessagingHost.h @@ -31,7 +31,7 @@ class NativeMessagingHost : public NativeMessagingBase typedef QList SocketList; public: - explicit NativeMessagingHost(DatabaseTabWidget* parent = 0, const bool enabled = false); + explicit NativeMessagingHost(DatabaseTabWidget* parent = nullptr, const bool enabled = false); ~NativeMessagingHost(); int init(); void run(); diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp index 124b99896..69a0dfb3e 100644 --- a/src/core/PasswordGenerator.cpp +++ b/src/core/PasswordGenerator.cpp @@ -25,15 +25,15 @@ const char* PasswordGenerator::DefaultExcludedChars = ""; PasswordGenerator::PasswordGenerator() : m_length(0) - , m_classes(0) - , m_flags(0) + , m_classes(nullptr) + , m_flags(nullptr) , m_excluded(PasswordGenerator::DefaultExcludedChars) { } double PasswordGenerator::calculateEntropy(const QString& password) { - return ZxcvbnMatch(password.toLatin1(), 0, 0); + return ZxcvbnMatch(password.toLatin1(), nullptr, nullptr); } void PasswordGenerator::setLength(int length) diff --git a/src/core/ScreenLockListenerPrivate.h b/src/core/ScreenLockListenerPrivate.h index 8ecad17d8..a7c080687 100644 --- a/src/core/ScreenLockListenerPrivate.h +++ b/src/core/ScreenLockListenerPrivate.h @@ -24,10 +24,10 @@ class ScreenLockListenerPrivate : public QObject { Q_OBJECT public: - static ScreenLockListenerPrivate* instance(QWidget* parent = 0); + static ScreenLockListenerPrivate* instance(QWidget* parent = nullptr); protected: - ScreenLockListenerPrivate(QWidget* parent = 0); + ScreenLockListenerPrivate(QWidget* parent = nullptr); signals: void screenLocked(); diff --git a/src/core/ScreenLockListenerWin.h b/src/core/ScreenLockListenerWin.h index 0778c99d8..523ae5d0b 100644 --- a/src/core/ScreenLockListenerWin.h +++ b/src/core/ScreenLockListenerWin.h @@ -27,7 +27,7 @@ class ScreenLockListenerWin : public ScreenLockListenerPrivate, public QAbstract { Q_OBJECT public: - explicit ScreenLockListenerWin(QWidget* parent = 0); + explicit ScreenLockListenerWin(QWidget* parent = nullptr); ~ScreenLockListenerWin(); virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override; diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index 0ed6f003a..fffcddfbd 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -336,4 +336,4 @@ bool Crypto::testChaCha20() } return true; -} \ No newline at end of file +} diff --git a/src/gui/CategoryListWidget.h b/src/gui/CategoryListWidget.h index 3f08fe384..7873a3d3e 100644 --- a/src/gui/CategoryListWidget.h +++ b/src/gui/CategoryListWidget.h @@ -35,7 +35,7 @@ class CategoryListWidget : public QWidget Q_OBJECT public: - CategoryListWidget(QWidget* parent = 0); + CategoryListWidget(QWidget* parent = nullptr); ~CategoryListWidget(); int currentCategory(); @@ -90,4 +90,4 @@ private: Q_DISABLE_COPY(CategoryListWidgetDelegate) }; -#endif \ No newline at end of file +#endif diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 892c41d3e..4d906db35 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -122,7 +122,7 @@ void DatabaseOpenWidget::hideEvent(QHideEvent* event) #ifdef WITH_XC_YUBIKEY // Don't listen to any Yubikey events if we are hidden - disconnect(YubiKey::instance(), 0, this, 0); + disconnect(YubiKey::instance(), nullptr, this, nullptr); m_yubiKeyBeingPolled = false; #endif } diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index dd36ddae4..b502c116b 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -419,7 +419,7 @@ bool DatabaseTabWidget::saveDatabaseAs(Database* db) oldFilePath, tr("KeePass 2 Database").append(" (*.kdbx)"), nullptr, - 0, + nullptr, "kdbx"); if (!newFilePath.isEmpty()) { // Ensure we don't recurse back into this function @@ -488,7 +488,7 @@ void DatabaseTabWidget::exportToCsv() } QString fileName = fileDialog()->getSaveFileName( - this, tr("Export database to CSV file"), QString(), tr("CSV file").append(" (*.csv)"), nullptr, 0, "csv"); + this, tr("Export database to CSV file"), QString(), tr("CSV file").append(" (*.csv)"), nullptr, nullptr, "csv"); if (fileName.isEmpty()) { return; } diff --git a/src/gui/KMessageWidget.cpp b/src/gui/KMessageWidget.cpp index 2e68975e3..3de1a15b6 100644 --- a/src/gui/KMessageWidget.cpp +++ b/src/gui/KMessageWidget.cpp @@ -411,7 +411,7 @@ void KMessageWidget::removeAction(QAction *action) void KMessageWidget::animatedShow() { - if (!style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) { + if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) { show(); emit showAnimationFinished(); return; @@ -436,7 +436,7 @@ void KMessageWidget::animatedShow() void KMessageWidget::animatedHide() { - if (!style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) { + if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) { hide(); emit hideAnimationFinished(); return; diff --git a/src/gui/MessageWidget.h b/src/gui/MessageWidget.h index 73f0b2108..eac506014 100644 --- a/src/gui/MessageWidget.h +++ b/src/gui/MessageWidget.h @@ -28,7 +28,7 @@ class MessageWidget : public KMessageWidget Q_OBJECT public: - explicit MessageWidget(QWidget* parent = 0); + explicit MessageWidget(QWidget* parent = nullptr); int autoHideTimeout() const; diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h index 1d95c664b..0ec3287c1 100644 --- a/src/gui/SearchWidget.h +++ b/src/gui/SearchWidget.h @@ -35,7 +35,7 @@ class SearchWidget : public QWidget Q_OBJECT public: - explicit SearchWidget(QWidget* parent = 0); + explicit SearchWidget(QWidget* parent = nullptr); ~SearchWidget(); void connectSignals(SignalMultiplexer& mx); @@ -55,7 +55,7 @@ signals: void enterPressed(); public slots: - void databaseChanged(DatabaseWidget* dbWidget = 0); + void databaseChanged(DatabaseWidget* dbWidget = nullptr); private slots: void startSearchTimer(); diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 26bb419ce..49c19280a 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -1269,13 +1269,13 @@ QMenu* EditEntryWidget::createPresetsMenu() QMenu* expirePresetsMenu = new QMenu(this); expirePresetsMenu->addAction(tr("Tomorrow"))->setData(QVariant::fromValue(TimeDelta::fromDays(1))); expirePresetsMenu->addSeparator(); - expirePresetsMenu->addAction(tr("%n week(s)", 0, 1))->setData(QVariant::fromValue(TimeDelta::fromDays(7))); - expirePresetsMenu->addAction(tr("%n week(s)", 0, 2))->setData(QVariant::fromValue(TimeDelta::fromDays(14))); - expirePresetsMenu->addAction(tr("%n week(s)", 0, 3))->setData(QVariant::fromValue(TimeDelta::fromDays(21))); + expirePresetsMenu->addAction(tr("%n week(s)", nullptr, 1))->setData(QVariant::fromValue(TimeDelta::fromDays(7))); + expirePresetsMenu->addAction(tr("%n week(s)", nullptr, 2))->setData(QVariant::fromValue(TimeDelta::fromDays(14))); + expirePresetsMenu->addAction(tr("%n week(s)", nullptr, 3))->setData(QVariant::fromValue(TimeDelta::fromDays(21))); expirePresetsMenu->addSeparator(); - expirePresetsMenu->addAction(tr("%n month(s)", 0, 1))->setData(QVariant::fromValue(TimeDelta::fromMonths(1))); - expirePresetsMenu->addAction(tr("%n month(s)", 0, 3))->setData(QVariant::fromValue(TimeDelta::fromMonths(3))); - expirePresetsMenu->addAction(tr("%n month(s)", 0, 6))->setData(QVariant::fromValue(TimeDelta::fromMonths(6))); + expirePresetsMenu->addAction(tr("%n month(s)", nullptr, 1))->setData(QVariant::fromValue(TimeDelta::fromMonths(1))); + 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))); diff --git a/src/gui/entry/EditEntryWidget_p.h b/src/gui/entry/EditEntryWidget_p.h index 0ba01cd51..dd0bac8b8 100644 --- a/src/gui/entry/EditEntryWidget_p.h +++ b/src/gui/entry/EditEntryWidget_p.h @@ -24,7 +24,7 @@ class AttributesListView : public QListView { public: - explicit AttributesListView(QWidget* parent = 0) + explicit AttributesListView(QWidget* parent = nullptr) : QListView(parent) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index fa3db177b..8cbf4bfe4 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -331,7 +331,7 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro Qt::DropActions EntryModel::supportedDropActions() const { - return 0; + return Qt::IgnoreAction; } Qt::DropActions EntryModel::supportedDragActions() const diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index b94916197..b6905fc38 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -37,8 +37,8 @@ #define m_ykds (static_cast(m_ykds_void)) YubiKey::YubiKey() - : m_yk_void(NULL) - , m_ykds_void(NULL) + : m_yk_void(nullptr) + , m_ykds_void(nullptr) , m_mutex(QMutex::Recursive) { } @@ -59,7 +59,7 @@ bool YubiKey::init() m_mutex.lock(); // previously initialized - if (m_yk != NULL && m_ykds != NULL) { + if (m_yk != nullptr && m_ykds != nullptr) { if (yk_get_status(m_yk, m_ykds)) { // Still connected @@ -78,15 +78,15 @@ bool YubiKey::init() // TODO: handle multiple attached hardware devices m_yk_void = static_cast(yk_open_first_key()); - if (m_yk == NULL) { + if (m_yk == nullptr) { m_mutex.unlock(); return false; } m_ykds_void = static_cast(ykds_alloc()); - if (m_ykds == NULL) { + if (m_ykds == nullptr) { yk_close_key(m_yk); - m_yk_void = NULL; + m_yk_void = nullptr; m_mutex.unlock(); return false; } @@ -101,12 +101,12 @@ bool YubiKey::deinit() if (m_yk) { yk_close_key(m_yk); - m_yk_void = NULL; + m_yk_void = nullptr; } if (m_ykds) { ykds_free(m_ykds); - m_ykds_void = NULL; + m_ykds_void = nullptr; } m_mutex.unlock(); diff --git a/src/streams/qtiocompressor.cpp b/src/streams/qtiocompressor.cpp index 97955e472..6e3c69ffc 100644 --- a/src/streams/qtiocompressor.cpp +++ b/src/streams/qtiocompressor.cpp @@ -116,7 +116,7 @@ QtIOCompressorPrivate::~QtIOCompressorPrivate() void QtIOCompressorPrivate::flushZlib(int flushMode) { // No input. - zlibStream.next_in = 0; + zlibStream.next_in = nullptr; zlibStream.avail_in = 0; int status; do { @@ -387,7 +387,7 @@ bool QtIOCompressor::open(OpenMode mode) if (read) { d->state = QtIOCompressorPrivate::NotReadFirstByte; d->zlibStream.avail_in = 0; - d->zlibStream.next_in = 0; + d->zlibStream.next_in = nullptr; if (d->streamFormat == QtIOCompressor::ZlibFormat) { status = inflateInit(&d->zlibStream); } else { From 379c41d20c1cb25bfa599efc0ea89ae47d197173 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sun, 28 Oct 2018 16:13:58 +0100 Subject: [PATCH 41/84] Reduce unnecessary copies using move semantics --- src/autotype/WildcardMatcher.cpp | 5 +++-- src/autotype/WildcardMatcher.h | 2 +- src/core/AutoTypeMatch.cpp | 4 +++- src/core/Database.cpp | 3 ++- src/core/Entry.cpp | 3 ++- src/format/KdbxXmlReader.cpp | 5 +++-- src/format/KdbxXmlReader.h | 2 +- src/format/KeePass2Reader.cpp | 3 ++- src/gui/csvImport/CsvParserModel.cpp | 4 +++- src/streams/HmacBlockStream.cpp | 6 ++++-- 10 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/autotype/WildcardMatcher.cpp b/src/autotype/WildcardMatcher.cpp index bac785897..6afd10cee 100644 --- a/src/autotype/WildcardMatcher.cpp +++ b/src/autotype/WildcardMatcher.cpp @@ -18,12 +18,13 @@ #include "WildcardMatcher.h" #include +#include const QChar WildcardMatcher::Wildcard = '*'; const Qt::CaseSensitivity WildcardMatcher::Sensitivity = Qt::CaseInsensitive; -WildcardMatcher::WildcardMatcher(const QString& text) - : m_text(text) +WildcardMatcher::WildcardMatcher(QString text) + : m_text(std::move(text)) { } diff --git a/src/autotype/WildcardMatcher.h b/src/autotype/WildcardMatcher.h index 6ef48743b..5be6f5e40 100644 --- a/src/autotype/WildcardMatcher.h +++ b/src/autotype/WildcardMatcher.h @@ -23,7 +23,7 @@ class WildcardMatcher { public: - explicit WildcardMatcher(const QString& text); + explicit WildcardMatcher(QString text); bool match(const QString& pattern); static const QChar Wildcard; diff --git a/src/core/AutoTypeMatch.cpp b/src/core/AutoTypeMatch.cpp index 9b7940f4d..13b037bb6 100644 --- a/src/core/AutoTypeMatch.cpp +++ b/src/core/AutoTypeMatch.cpp @@ -18,6 +18,8 @@ #include "AutoTypeMatch.h" +#include + AutoTypeMatch::AutoTypeMatch() : entry(nullptr) , sequence() @@ -26,7 +28,7 @@ AutoTypeMatch::AutoTypeMatch() AutoTypeMatch::AutoTypeMatch(Entry* entry, QString sequence) : entry(entry) - , sequence(sequence) + , sequence(std::move(sequence)) { } diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 693b9c549..fe9ef600b 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "cli/Utils.h" #include "core/Clock.h" @@ -492,7 +493,7 @@ Database* Database::openDatabaseFile(const QString& fileName, QSharedPointer #include #include +#include const int Entry::DefaultIconNumber = 0; const int Entry::ResolveMaximumDepth = 10; @@ -367,7 +368,7 @@ QString Entry::totp() const void Entry::setTotp(QSharedPointer settings) { beginUpdate(); - m_data.totpSettings = settings; + m_data.totpSettings = std::move(settings); auto text = Totp::writeSettings(m_data.totpSettings, title(), username()); if (m_attributes->hasKey(Totp::ATTRIBUTE_OTP)) { diff --git a/src/format/KdbxXmlReader.cpp b/src/format/KdbxXmlReader.cpp index c9e5c31af..ec556dd85 100644 --- a/src/format/KdbxXmlReader.cpp +++ b/src/format/KdbxXmlReader.cpp @@ -28,6 +28,7 @@ #include #include +#include #define UUID_LENGTH 16 @@ -43,9 +44,9 @@ KdbxXmlReader::KdbxXmlReader(quint32 version) * @param version KDBX version * @param binaryPool binary pool */ -KdbxXmlReader::KdbxXmlReader(quint32 version, const QHash& binaryPool) +KdbxXmlReader::KdbxXmlReader(quint32 version, QHash binaryPool) : m_kdbxVersion(version) - , m_binaryPool(binaryPool) + , m_binaryPool(std::move(binaryPool)) { } diff --git a/src/format/KdbxXmlReader.h b/src/format/KdbxXmlReader.h index 566fbfc7e..4ab82cc0e 100644 --- a/src/format/KdbxXmlReader.h +++ b/src/format/KdbxXmlReader.h @@ -42,7 +42,7 @@ class KdbxXmlReader public: explicit KdbxXmlReader(quint32 version); - explicit KdbxXmlReader(quint32 version, const QHash& binaryPool); + explicit KdbxXmlReader(quint32 version, QHash binaryPool); virtual ~KdbxXmlReader() = default; virtual Database* readDatabase(const QString& filename); diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 8cdb8ff43..f727f274d 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -21,6 +21,7 @@ #include "format/KeePass1.h" #include +#include /** * Read database from file and detect correct file format. @@ -37,7 +38,7 @@ Database* KeePass2Reader::readDatabase(const QString& filename, QSharedPointer db(readDatabase(&file, key)); + QScopedPointer db(readDatabase(&file, std::move(key))); if (file.error() != QFile::NoError) { raiseError(file.errorString()); diff --git a/src/gui/csvImport/CsvParserModel.cpp b/src/gui/csvImport/CsvParserModel.cpp index 8f7d98ec4..269fbdd62 100644 --- a/src/gui/csvImport/CsvParserModel.cpp +++ b/src/gui/csvImport/CsvParserModel.cpp @@ -18,6 +18,8 @@ #include "CsvParserModel.h" +#include + CsvParserModel::CsvParserModel(QObject* parent) : QAbstractTableModel(parent) , m_skipped(0) @@ -92,7 +94,7 @@ void CsvParserModel::setSkippedRows(int skipped) void CsvParserModel::setHeaderLabels(QStringList l) { - m_columnHeader = l; + m_columnHeader = std::move(l); } int CsvParserModel::rowCount(const QModelIndex& parent) const diff --git a/src/streams/HmacBlockStream.cpp b/src/streams/HmacBlockStream.cpp index 01d9ba7cd..30c7bac5e 100644 --- a/src/streams/HmacBlockStream.cpp +++ b/src/streams/HmacBlockStream.cpp @@ -17,6 +17,8 @@ #include "HmacBlockStream.h" +#include + #include "core/Endian.h" #include "crypto/CryptoHash.h" @@ -25,7 +27,7 @@ const QSysInfo::Endian HmacBlockStream::ByteOrder = QSysInfo::LittleEndian; HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key) : LayeredStream(baseDevice) , m_blockSize(1024 * 1024) - , m_key(key) + , m_key(std::move(key)) { init(); } @@ -33,7 +35,7 @@ HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key) HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key, qint32 blockSize) : LayeredStream(baseDevice) , m_blockSize(blockSize) - , m_key(key) + , m_key(std::move(key)) { init(); } From 0f604aa8c7ce47ce504f5a9d72acd9871802bebd Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Sun, 28 Oct 2018 23:06:27 +0100 Subject: [PATCH 42/84] Normalize signature of SIGNAL() and SLOT() See https://stackoverflow.com/q/18091058/6335279 --- src/autotype/AutoTypeSelectDialog.cpp | 2 +- src/browser/BrowserService.cpp | 36 +++++++++---------- src/core/Group.cpp | 4 +-- src/gui/CategoryListWidget.cpp | 2 +- src/gui/DatabaseOpenWidget.cpp | 2 +- src/gui/DatabaseTabWidget.cpp | 2 +- src/gui/DatabaseWidget.cpp | 8 ++--- src/gui/EditWidgetIcons.cpp | 4 +-- src/gui/EditWidgetProperties.cpp | 2 +- src/gui/MainWindow.cpp | 8 ++--- .../DatabaseSettingsWidgetBrowser.cpp | 2 +- src/gui/entry/AutoTypeMatchView.cpp | 2 +- src/gui/entry/EditEntryWidget.cpp | 36 +++++++++---------- src/gui/entry/EntryAttachmentsWidget.cpp | 2 +- src/gui/entry/EntryAttributesModel.cpp | 4 +-- src/gui/entry/EntryView.cpp | 10 +++--- src/gui/group/EditGroupWidget.cpp | 4 +-- src/gui/group/GroupModel.cpp | 4 +-- src/gui/group/GroupView.cpp | 4 +-- src/gui/masterkey/KeyComponentWidget.cpp | 4 +-- src/gui/masterkey/PasswordEditWidget.cpp | 2 +- src/gui/masterkey/YubiKeyEditWidget.cpp | 2 +- 22 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/autotype/AutoTypeSelectDialog.cpp b/src/autotype/AutoTypeSelectDialog.cpp index 7178c70f9..fe0ba811e 100644 --- a/src/autotype/AutoTypeSelectDialog.cpp +++ b/src/autotype/AutoTypeSelectDialog.cpp @@ -69,7 +69,7 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent) connect(m_view, SIGNAL(activated(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); connect(m_view, SIGNAL(clicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); - connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(matchRemoved())); + connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(matchRemoved())); connect(m_view, SIGNAL(rejected()), SLOT(reject())); layout->addWidget(m_view); diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 8128ee14f..cca76c15f 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -145,7 +145,7 @@ QString BrowserService::storeKey(const QString& key) if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod( - this, "storeKey", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, id), Q_ARG(const QString&, key)); + this, "storeKey", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, id), Q_ARG(QString, key)); return id; } @@ -213,11 +213,11 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, "findMatchingEntries", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QJsonArray, result), - Q_ARG(const QString&, id), - Q_ARG(const QString&, url), - Q_ARG(const QString&, submitUrl), - Q_ARG(const QString&, realm), - Q_ARG(const StringPairList&, keyList)); + Q_ARG(QString, id), + Q_ARG(QString, url), + Q_ARG(QString, submitUrl), + Q_ARG(QString, realm), + Q_ARG(StringPairList, keyList)); return result; } @@ -279,12 +279,12 @@ void BrowserService::addEntry(const QString& id, QMetaObject::invokeMethod(this, "addEntry", Qt::BlockingQueuedConnection, - Q_ARG(const QString&, id), - Q_ARG(const QString&, login), - Q_ARG(const QString&, password), - Q_ARG(const QString&, url), - Q_ARG(const QString&, submitUrl), - Q_ARG(const QString&, realm), + Q_ARG(QString, id), + Q_ARG(QString, login), + Q_ARG(QString, password), + Q_ARG(QString, url), + Q_ARG(QString, submitUrl), + Q_ARG(QString, realm), Q_ARG(Database*, selectedDb)); } @@ -332,12 +332,12 @@ void BrowserService::updateEntry(const QString& id, QMetaObject::invokeMethod(this, "updateEntry", Qt::BlockingQueuedConnection, - Q_ARG(const QString&, id), - Q_ARG(const QString&, uuid), - Q_ARG(const QString&, login), - Q_ARG(const QString&, password), - Q_ARG(const QString&, url), - Q_ARG(const QString&, submitUrl)); + Q_ARG(QString, id), + Q_ARG(QString, uuid), + Q_ARG(QString, login), + Q_ARG(QString, password), + Q_ARG(QString, url), + Q_ARG(QString, submitUrl)); } Database* db = selectedDatabase(); diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 4b1d11ab0..fcb8fe8a8 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -850,9 +850,9 @@ void Group::recSetDatabase(Database* 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(aboutToAdd(Group*,int)), db, SIGNAL(groupAboutToAdd(Group*,int))); connect(this, SIGNAL(added()), db, SIGNAL(groupAdded())); - connect(this, SIGNAL(aboutToMove(Group*, Group*, int)), db, SIGNAL(groupAboutToMove(Group*, Group*, int))); + 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())); } diff --git a/src/gui/CategoryListWidget.cpp b/src/gui/CategoryListWidget.cpp index 14429083b..50b02da81 100644 --- a/src/gui/CategoryListWidget.cpp +++ b/src/gui/CategoryListWidget.cpp @@ -39,7 +39,7 @@ CategoryListWidget::CategoryListWidget(QWidget* parent) connect(m_ui->scrollDown, SIGNAL(clicked()), SLOT(scrollCategoriesDown())); connect(m_ui->categoryList->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(updateCategoryScrollButtons())); connect( - m_ui->categoryList->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), SLOT(updateCategoryScrollButtons())); + m_ui->categoryList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), SLOT(updateCategoryScrollButtons())); } CategoryListWidget::~CategoryListWidget() diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 4d906db35..d872ca68d 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -106,7 +106,7 @@ void DatabaseOpenWidget::showEvent(QShowEvent* event) // 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); + 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); diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index b502c116b..fcd4f954b 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -693,7 +693,7 @@ void DatabaseTabWidget::insertDatabase(Database* db, const DatabaseManagerStruct 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(databaseChanged(Database*,bool)), SLOT(changeDatabase(Database*,bool))); connect(dbStruct.dbWidget, SIGNAL(unlockedDatabase()), SLOT(updateTabNameFromDbWidgetSender())); connect(dbStruct.dbWidget, SIGNAL(unlockedDatabase()), SLOT(emitDatabaseUnlockedFromDbWidgetSender())); } diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 7b36f4668..388919952 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -175,14 +175,14 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) addWidget(m_keepass1OpenWidget); addWidget(m_unlockDatabaseWidget); - connect(m_mainSplitter, SIGNAL(splitterMoved(int, int)), SIGNAL(mainSplitterSizesChanged())); - connect(m_previewSplitter, SIGNAL(splitterMoved(int, int)), SIGNAL(previewSplitterSizesChanged())); + connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged())); + connect(m_previewSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(previewSplitterSizesChanged())); 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))); + 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*))); diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index f2c320dc9..e26966df6 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -84,11 +84,11 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent) connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated())); connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated())); connect(m_ui->defaultIconsView->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(widgetUpdated())); connect(m_ui->customIconsView->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(widgetUpdated())); diff --git a/src/gui/EditWidgetProperties.cpp b/src/gui/EditWidgetProperties.cpp index 93e3b0ae8..b2de9d778 100644 --- a/src/gui/EditWidgetProperties.cpp +++ b/src/gui/EditWidgetProperties.cpp @@ -33,7 +33,7 @@ EditWidgetProperties::EditWidgetProperties(QWidget* parent) m_ui->customDataTable->setModel(m_customDataModel); connect(m_ui->customDataTable->selectionModel(), - SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(toggleRemoveButton(QItemSelection))); connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData())); } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 8f0c23044..ac26dd879 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -319,14 +319,14 @@ MainWindow::MainWindow() #endif connect(m_ui->tabWidget, - SIGNAL(messageGlobal(QString, MessageWidget::MessageType)), + SIGNAL(messageGlobal(QString,MessageWidget::MessageType)), this, - SLOT(displayGlobalMessage(QString, MessageWidget::MessageType))); + SLOT(displayGlobalMessage(QString,MessageWidget::MessageType))); connect(m_ui->tabWidget, SIGNAL(messageDismissGlobal()), this, SLOT(hideGlobalMessage())); connect(m_ui->tabWidget, - SIGNAL(messageTab(QString, MessageWidget::MessageType)), + SIGNAL(messageTab(QString,MessageWidget::MessageType)), this, - SLOT(displayTabMessage(QString, MessageWidget::MessageType))); + SLOT(displayTabMessage(QString,MessageWidget::MessageType))); connect(m_ui->tabWidget, SIGNAL(messageDismissTab()), this, SLOT(hideTabMessage())); m_screenLockListener = new ScreenLockListener(this); diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp index 0dae1486d..a91744cdb 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp @@ -38,7 +38,7 @@ DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent) m_ui->customDataTable->setModel(m_customDataModel); settingsWarning(); - connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(toggleRemoveButton(QItemSelection))); connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedKey())); connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SLOT(convertAttributesToCustomData())); diff --git a/src/gui/entry/AutoTypeMatchView.cpp b/src/gui/entry/AutoTypeMatchView.cpp index 0eb257237..087f2cc64 100644 --- a/src/gui/entry/AutoTypeMatchView.cpp +++ b/src/gui/entry/AutoTypeMatchView.cpp @@ -44,7 +44,7 @@ AutoTypeMatchView::AutoTypeMatchView(QWidget* parent) connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); connect( - selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SIGNAL(matchSelectionChanged())); + selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(matchSelectionChanged())); } void AutoTypeMatchView::keyPressEvent(QKeyEvent* event) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 49c19280a..8a54b23e3 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -103,8 +103,8 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) connect(this, SIGNAL(rejected()), SLOT(cancel())); connect(this, SIGNAL(apply()), SLOT(commitEntry())); connect(m_iconsWidget, - SIGNAL(messageEditEntry(QString, MessageWidget::MessageType)), - SLOT(showMessage(QString, MessageWidget::MessageType))); + SIGNAL(messageEditEntry(QString,MessageWidget::MessageType)), + SLOT(showMessage(QString,MessageWidget::MessageType))); connect(m_iconsWidget, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); m_mainUi->passwordGenerator->layout()->setContentsMargins(0, 0, 0, 0); @@ -161,7 +161,7 @@ void EditEntryWidget::setupAdvanced() connect(m_advancedUi->protectAttributeButton, SIGNAL(toggled(bool)), SLOT(protectCurrentAttribute(bool))); connect(m_advancedUi->revealAttributeButton, SIGNAL(clicked(bool)), SLOT(revealCurrentAttribute())); connect(m_advancedUi->attributesView->selectionModel(), - SIGNAL(currentChanged(QModelIndex, QModelIndex)), + SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(updateCurrentAttribute())); connect(m_advancedUi->fgColorButton, SIGNAL(clicked()), SLOT(pickColor())); connect(m_advancedUi->bgColorButton, SIGNAL(clicked()), SLOT(pickColor())); @@ -192,11 +192,11 @@ void EditEntryWidget::setupAutoType() connect(m_autoTypeUi->assocAddButton, SIGNAL(clicked()), SLOT(insertAutoTypeAssoc())); connect(m_autoTypeUi->assocRemoveButton, SIGNAL(clicked()), SLOT(removeAutoTypeAssoc())); connect(m_autoTypeUi->assocView->selectionModel(), - SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), + SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(updateAutoTypeEnabled())); connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(updateAutoTypeEnabled())); connect(m_autoTypeUi->assocView->selectionModel(), - SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), + SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(loadCurrentAssoc(QModelIndex))); connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc())); connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)), SLOT(applyCurrentAssoc())); @@ -225,8 +225,8 @@ void EditEntryWidget::setupHistory() connect(m_historyUi->historyView, SIGNAL(activated(QModelIndex)), SLOT(histEntryActivated(QModelIndex))); connect(m_historyUi->historyView->selectionModel(), - SIGNAL(currentChanged(QModelIndex, QModelIndex)), - SLOT(updateHistoryButtons(QModelIndex, QModelIndex))); + SIGNAL(currentChanged(QModelIndex,QModelIndex)), + SLOT(updateHistoryButtons(QModelIndex,QModelIndex))); connect(m_historyUi->showButton, SIGNAL(clicked()), SLOT(showHistoryEntry())); connect(m_historyUi->restoreButton, SIGNAL(clicked()), SLOT(restoreHistoryEntry())); connect(m_historyUi->deleteButton, SIGNAL(clicked()), SLOT(deleteHistoryEntry())); @@ -236,14 +236,14 @@ void EditEntryWidget::setupHistory() void EditEntryWidget::setupEntryUpdate() { // Entry tab - connect(m_mainUi->titleEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); - connect(m_mainUi->usernameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); - connect(m_mainUi->passwordEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); - connect(m_mainUi->passwordRepeatEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); - connect(m_mainUi->urlEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); + connect(m_mainUi->titleEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); + connect(m_mainUi->usernameEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); + connect(m_mainUi->passwordEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); + connect(m_mainUi->passwordRepeatEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); + connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->notesEnabled, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); - connect(m_mainUi->expireDatePicker, SIGNAL(dateTimeChanged(const QDateTime&)), this, SLOT(setUnsavedChanges())); + connect(m_mainUi->expireDatePicker, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->notesEdit, SIGNAL(textChanged()), this, SLOT(setUnsavedChanges())); // Advanced tab @@ -261,10 +261,10 @@ void EditEntryWidget::setupEntryUpdate() connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_autoTypeUi->inheritSequenceButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges())); connect(m_autoTypeUi->customSequenceButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges())); - connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); - connect(m_autoTypeUi->sequenceEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); + connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); + connect(m_autoTypeUi->sequenceEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_autoTypeUi->windowTitleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setUnsavedChanges())); - connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(const QString&)), this, SLOT(setUnsavedChanges())); + connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)), this, SLOT(setUnsavedChanges())); // Properties and History tabs don't need extra connections @@ -275,8 +275,8 @@ void EditEntryWidget::setupEntryUpdate() connect(m_sshAgentUi->externalFileRadioButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->attachmentComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setUnsavedChanges())); connect( - m_sshAgentUi->attachmentComboBox, SIGNAL(editTextChanged(const QString&)), this, SLOT(setUnsavedChanges())); - connect(m_sshAgentUi->externalFileEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges())); + m_sshAgentUi->attachmentComboBox, SIGNAL(editTextChanged(QString)), this, SLOT(setUnsavedChanges())); + connect(m_sshAgentUi->externalFileEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->addKeyToAgentCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->removeKeyFromAgentCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect( diff --git a/src/gui/entry/EntryAttachmentsWidget.cpp b/src/gui/entry/EntryAttachmentsWidget.cpp index c1897d898..6f42a1994 100644 --- a/src/gui/entry/EntryAttachmentsWidget.cpp +++ b/src/gui/entry/EntryAttachmentsWidget.cpp @@ -44,7 +44,7 @@ EntryAttachmentsWidget::EntryAttachmentsWidget(QWidget* parent) connect(this, SIGNAL(readOnlyChanged(bool)), SLOT(updateButtonsEnabled())); connect(m_attachmentsModel, SIGNAL(modelReset()), SLOT(updateButtonsEnabled())); connect(m_ui->attachmentsView->selectionModel(), - SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(updateButtonsEnabled())); connect(m_ui->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex))); diff --git a/src/gui/entry/EntryAttributesModel.cpp b/src/gui/entry/EntryAttributesModel.cpp index ddb31d624..373fcc6c7 100644 --- a/src/gui/entry/EntryAttributesModel.cpp +++ b/src/gui/entry/EntryAttributesModel.cpp @@ -47,8 +47,8 @@ void EntryAttributesModel::setEntryAttributes(EntryAttributes* entryAttributes) connect(m_entryAttributes, SIGNAL(aboutToBeRemoved(QString)), SLOT(attributeAboutToRemove(QString))); connect(m_entryAttributes, SIGNAL(removed(QString)), SLOT(attributeRemove())); connect( - m_entryAttributes, SIGNAL(aboutToRename(QString, QString)), SLOT(attributeAboutToRename(QString, QString))); - connect(m_entryAttributes, SIGNAL(renamed(QString, QString)), SLOT(attributeRename(QString, QString))); + m_entryAttributes, SIGNAL(aboutToRename(QString,QString)), SLOT(attributeAboutToRename(QString,QString))); + connect(m_entryAttributes, SIGNAL(renamed(QString,QString)), SLOT(attributeRename(QString,QString))); connect(m_entryAttributes, SIGNAL(aboutToBeReset()), SLOT(aboutToReset())); connect(m_entryAttributes, SIGNAL(reset()), SLOT(reset())); } diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 05c55ad82..d63260c39 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -51,7 +51,7 @@ EntryView::EntryView(QWidget* parent) connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex))); connect( - selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SIGNAL(entrySelectionChanged())); + selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); connect(m_model, SIGNAL(switchedToListMode()), SLOT(switchToListMode())); connect(m_model, SIGNAL(switchedToSearchMode()), SLOT(switchToSearchMode())); connect(m_model, SIGNAL(usernamesHiddenChanged()), SIGNAL(viewStateChanged())); @@ -97,10 +97,10 @@ EntryView::EntryView(QWidget* parent) header()->setContextMenuPolicy(Qt::CustomContextMenu); connect(header(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(showHeaderMenu(QPoint))); - connect(header(), SIGNAL(sectionCountChanged(int, int)), SIGNAL(viewStateChanged())); - connect(header(), SIGNAL(sectionMoved(int, int, int)), SIGNAL(viewStateChanged())); - connect(header(), SIGNAL(sectionResized(int, int, int)), SIGNAL(viewStateChanged())); - connect(header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), SIGNAL(viewStateChanged())); + connect(header(), SIGNAL(sectionCountChanged(int,int)), SIGNAL(viewStateChanged())); + connect(header(), SIGNAL(sectionMoved(int,int,int)), SIGNAL(viewStateChanged())); + connect(header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(viewStateChanged())); + connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), SIGNAL(viewStateChanged())); resetFixedColumns(); diff --git a/src/gui/group/EditGroupWidget.cpp b/src/gui/group/EditGroupWidget.cpp index 2abfa5544..44276a9f3 100644 --- a/src/gui/group/EditGroupWidget.cpp +++ b/src/gui/group/EditGroupWidget.cpp @@ -49,8 +49,8 @@ EditGroupWidget::EditGroupWidget(QWidget* parent) connect(this, SIGNAL(rejected()), SLOT(cancel())); connect(m_editGroupWidgetIcons, - SIGNAL(messageEditEntry(QString, MessageWidget::MessageType)), - SLOT(showMessage(QString, MessageWidget::MessageType))); + SIGNAL(messageEditEntry(QString,MessageWidget::MessageType)), + SLOT(showMessage(QString,MessageWidget::MessageType))); connect(m_editGroupWidgetIcons, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); } diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp index a3c72b792..791f6ce33 100644 --- a/src/gui/group/GroupModel.cpp +++ b/src/gui/group/GroupModel.cpp @@ -44,11 +44,11 @@ void GroupModel::changeDatabase(Database* newDb) m_db = newDb; connect(m_db, SIGNAL(groupDataChanged(Group*)), SLOT(groupDataChanged(Group*))); - connect(m_db, SIGNAL(groupAboutToAdd(Group*, int)), SLOT(groupAboutToAdd(Group*, int))); + connect(m_db, SIGNAL(groupAboutToAdd(Group*,int)), SLOT(groupAboutToAdd(Group*,int))); connect(m_db, SIGNAL(groupAdded()), SLOT(groupAdded())); connect(m_db, SIGNAL(groupAboutToRemove(Group*)), SLOT(groupAboutToRemove(Group*))); connect(m_db, SIGNAL(groupRemoved()), SLOT(groupRemoved())); - connect(m_db, SIGNAL(groupAboutToMove(Group*, Group*, int)), SLOT(groupAboutToMove(Group*, Group*, int))); + connect(m_db, SIGNAL(groupAboutToMove(Group*,Group*,int)), SLOT(groupAboutToMove(Group*,Group*,int))); connect(m_db, SIGNAL(groupMoved()), SLOT(groupMoved())); endResetModel(); diff --git a/src/gui/group/GroupView.cpp b/src/gui/group/GroupView.cpp index fd6748006..ee3913948 100644 --- a/src/gui/group/GroupView.cpp +++ b/src/gui/group/GroupView.cpp @@ -36,10 +36,10 @@ GroupView::GroupView(Database* db, QWidget* parent) connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(expandedChanged(QModelIndex))); connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(expandedChanged(QModelIndex))); - connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(syncExpandedState(QModelIndex, int, int))); + connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(syncExpandedState(QModelIndex,int,int))); connect(m_model, SIGNAL(modelReset()), SLOT(modelReset())); - connect(selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), SLOT(emitGroupChanged())); + connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(emitGroupChanged())); connect(this, SIGNAL(clicked(QModelIndex)), SLOT(emitGroupPressed(QModelIndex))); diff --git a/src/gui/masterkey/KeyComponentWidget.cpp b/src/gui/masterkey/KeyComponentWidget.cpp index 45f133b44..72367ecbe 100644 --- a/src/gui/masterkey/KeyComponentWidget.cpp +++ b/src/gui/masterkey/KeyComponentWidget.cpp @@ -38,8 +38,8 @@ KeyComponentWidget::KeyComponentWidget(const QString& name, QWidget* parent) connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(reset())); - connect(this, SIGNAL(nameChanged(const QString&)), SLOT(updateComponentName(const QString&))); - connect(this, SIGNAL(descriptionChanged(const QString&)), SLOT(updateComponentDescription(const QString&))); + connect(this, SIGNAL(nameChanged(QString)), SLOT(updateComponentName(QString))); + connect(this, SIGNAL(descriptionChanged(QString)), SLOT(updateComponentDescription(QString))); connect(this, SIGNAL(componentAddRequested()), SLOT(doAdd())); connect(this, SIGNAL(componentEditRequested()), SLOT(doEdit())); connect(this, SIGNAL(componentRemovalRequested()), SLOT(doRemove())); diff --git a/src/gui/masterkey/PasswordEditWidget.cpp b/src/gui/masterkey/PasswordEditWidget.cpp index dc43f9159..4006a0b1e 100644 --- a/src/gui/masterkey/PasswordEditWidget.cpp +++ b/src/gui/masterkey/PasswordEditWidget.cpp @@ -107,7 +107,7 @@ void PasswordEditWidget::showPasswordGenerator() layout->addWidget(pwGenerator); pwGenerator->setStandaloneMode(false); - connect(pwGenerator, SIGNAL(appliedPassword(const QString&)), SLOT(setPassword(const QString&))); + connect(pwGenerator, SIGNAL(appliedPassword(QString)), SLOT(setPassword(QString))); connect(pwGenerator, SIGNAL(dialogTerminated()), &pwDialog, SLOT(close())); pwGenerator->setPasswordVisible(isPasswordVisible()); diff --git a/src/gui/masterkey/YubiKeyEditWidget.cpp b/src/gui/masterkey/YubiKeyEditWidget.cpp index bae3a3d1e..27e0a4cf0 100644 --- a/src/gui/masterkey/YubiKeyEditWidget.cpp +++ b/src/gui/masterkey/YubiKeyEditWidget.cpp @@ -75,7 +75,7 @@ QWidget* YubiKeyEditWidget::componentEditWidget() #ifdef WITH_XC_YUBIKEY connect(m_compUi->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); - 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(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); pollYubikey(); From e06eae423e0d90deb71da232a57bd9862bcd3588 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Mon, 29 Oct 2018 21:14:35 +0100 Subject: [PATCH 43/84] Add missing 'override' for overridden functions --- src/browser/NativeMessagingHost.h | 4 ++-- src/cli/Add.h | 2 +- src/cli/Clip.h | 2 +- src/cli/Diceware.h | 2 +- src/cli/Edit.h | 2 +- src/cli/Estimate.h | 2 +- src/cli/Extract.h | 2 +- src/cli/Generate.h | 2 +- src/cli/List.h | 2 +- src/cli/Locate.h | 2 +- src/cli/Merge.h | 2 +- src/cli/Remove.h | 2 +- src/cli/Show.h | 2 +- src/format/KeePass1Reader.cpp | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/browser/NativeMessagingHost.h b/src/browser/NativeMessagingHost.h index da9ac5346..774306d30 100644 --- a/src/browser/NativeMessagingHost.h +++ b/src/browser/NativeMessagingHost.h @@ -41,8 +41,8 @@ signals: void quit(); private: - void readLength(); - bool readStdIn(const quint32 length); + void readLength() override; + bool readStdIn(const quint32 length) override; void sendReplyToAllClients(const QJsonObject& json); private slots: diff --git a/src/cli/Add.h b/src/cli/Add.h index 5769249c9..dd0c3d8b5 100644 --- a/src/cli/Add.h +++ b/src/cli/Add.h @@ -25,7 +25,7 @@ class Add : public Command public: Add(); ~Add(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; }; #endif // KEEPASSXC_ADD_H diff --git a/src/cli/Clip.h b/src/cli/Clip.h index 929ddf678..e5e6390ae 100644 --- a/src/cli/Clip.h +++ b/src/cli/Clip.h @@ -25,7 +25,7 @@ class Clip : public Command public: Clip(); ~Clip(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; int clipEntry(Database* database, const QString& entryPath, const QString& timeout); }; diff --git a/src/cli/Diceware.h b/src/cli/Diceware.h index 61fe724ca..f439681b7 100644 --- a/src/cli/Diceware.h +++ b/src/cli/Diceware.h @@ -25,7 +25,7 @@ class Diceware : public Command public: Diceware(); ~Diceware(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; }; #endif // KEEPASSXC_DICEWARE_H diff --git a/src/cli/Edit.h b/src/cli/Edit.h index 2c413bea0..001b5abaf 100644 --- a/src/cli/Edit.h +++ b/src/cli/Edit.h @@ -25,7 +25,7 @@ class Edit : public Command public: Edit(); ~Edit(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; }; #endif // KEEPASSXC_EDIT_H diff --git a/src/cli/Estimate.h b/src/cli/Estimate.h index 15f922752..c15fed9b3 100644 --- a/src/cli/Estimate.h +++ b/src/cli/Estimate.h @@ -25,7 +25,7 @@ class Estimate : public Command public: Estimate(); ~Estimate(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; }; #endif // KEEPASSXC_ESTIMATE_H diff --git a/src/cli/Extract.h b/src/cli/Extract.h index 2939afddb..1a4f6288b 100644 --- a/src/cli/Extract.h +++ b/src/cli/Extract.h @@ -25,7 +25,7 @@ class Extract : public Command public: Extract(); ~Extract(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; }; #endif // KEEPASSXC_EXTRACT_H diff --git a/src/cli/Generate.h b/src/cli/Generate.h index 607fc105c..64ef81623 100644 --- a/src/cli/Generate.h +++ b/src/cli/Generate.h @@ -25,7 +25,7 @@ class Generate : public Command public: Generate(); ~Generate(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; }; #endif // KEEPASSXC_GENERATE_H diff --git a/src/cli/List.h b/src/cli/List.h index 5697d9390..92ade262b 100644 --- a/src/cli/List.h +++ b/src/cli/List.h @@ -25,7 +25,7 @@ class List : public Command public: List(); ~List(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; int listGroup(Database* database, bool recursive, const QString& groupPath = {}); }; diff --git a/src/cli/Locate.h b/src/cli/Locate.h index 3355d41ec..ae32951b9 100644 --- a/src/cli/Locate.h +++ b/src/cli/Locate.h @@ -25,7 +25,7 @@ class Locate : public Command public: Locate(); ~Locate(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; int locateEntry(Database* database, const QString& searchTerm); }; diff --git a/src/cli/Merge.h b/src/cli/Merge.h index 496c66b86..1f138b65f 100644 --- a/src/cli/Merge.h +++ b/src/cli/Merge.h @@ -25,7 +25,7 @@ class Merge : public Command public: Merge(); ~Merge(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; }; #endif // KEEPASSXC_MERGE_H diff --git a/src/cli/Remove.h b/src/cli/Remove.h index 33d62f4bd..d45e04af2 100644 --- a/src/cli/Remove.h +++ b/src/cli/Remove.h @@ -27,7 +27,7 @@ class Remove : public Command public: Remove(); ~Remove(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; int removeEntry(Database* database, const QString& databasePath, const QString& entryPath); }; diff --git a/src/cli/Show.h b/src/cli/Show.h index fe16546c3..6d49d1207 100644 --- a/src/cli/Show.h +++ b/src/cli/Show.h @@ -25,7 +25,7 @@ class Show : public Command public: Show(); ~Show(); - int execute(const QStringList& arguments); + int execute(const QStringList& arguments) override; int showEntry(Database* database, QStringList attributes, const QString& entryPath); }; diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp index 7c0ea652e..a991b572f 100644 --- a/src/format/KeePass1Reader.cpp +++ b/src/format/KeePass1Reader.cpp @@ -37,7 +37,7 @@ class KeePass1Key : public CompositeKey { public: - virtual QByteArray rawKey() const; + QByteArray rawKey() const override; virtual void clear(); void setPassword(const QByteArray& password); void setKeyfileData(const QByteArray& keyfileData); From 156b480f93670c5a1d09d44b05da29474aa0c43d Mon Sep 17 00:00:00 2001 From: louis Date: Fri, 9 Nov 2018 18:36:15 -0500 Subject: [PATCH 44/84] Better error message for invalid db file --- src/core/Database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Database.cpp b/src/core/Database.cpp index fe9ef600b..c44df7ffd 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -484,11 +484,11 @@ Database* Database::openDatabaseFile(const QString& fileName, QSharedPointer Date: Thu, 8 Nov 2018 14:42:44 -0500 Subject: [PATCH 45/84] Document list -R option. --- src/cli/List.cpp | 2 +- src/cli/keepassxc-cli.1 | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 6d85404ff..98d08d3b2 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -51,7 +51,7 @@ int List::execute(const QStringList& arguments) QObject::tr("path")); parser.addOption(keyFile); QCommandLineOption recursiveOption(QStringList() << "R" << "recursive", - QObject::tr("Recursive mode, list elements recursively")); + QObject::tr("Recursively list the elements of the group.")); parser.addOption(recursiveOption); parser.addHelpOption(); parser.process(arguments); diff --git a/src/cli/keepassxc-cli.1 b/src/cli/keepassxc-cli.1 index cd428e105..9d29534e5 100644 --- a/src/cli/keepassxc-cli.1 +++ b/src/cli/keepassxc-cli.1 @@ -121,6 +121,12 @@ otherwise the program will fail. If the wordlist has < 4000 words a warning will be printed to STDERR. +.SS "List options" + +.IP "-R, --recursive" +Recursively list the elements of the group. + + .SS "Generate options" .IP "-L, --length " From d7f5a15f682f64830050ba99980529ff9f6884fb Mon Sep 17 00:00:00 2001 From: Weslly Date: Thu, 8 Nov 2018 13:42:26 -0200 Subject: [PATCH 46/84] Add missing header to cmakelists when building without YubiKey --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c7620063..5cd974a98 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -239,7 +239,7 @@ endif() if(WITH_XC_YUBIKEY) list(APPEND keepassx_SOURCES keys/drivers/YubiKey.cpp) else() - list(APPEND keepassx_SOURCES keys/drivers/YubiKeyStub.cpp) + list(APPEND keepassx_SOURCES keys/drivers/YubiKey.h keys/drivers/YubiKeyStub.cpp) endif() if(WITH_XC_TOUCHID) From b1d481893ef133f121baef12c115f9c4de593710 Mon Sep 17 00:00:00 2001 From: louis Date: Sun, 4 Nov 2018 15:59:13 -0500 Subject: [PATCH 47/84] Adding tests for modified signal on merge. --- tests/TestMerge.cpp | 33 +++++++++++++++++++++++++++++++++ tests/TestMerge.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/tests/TestMerge.cpp b/tests/TestMerge.cpp index 728b0a751..60e3a9ea8 100644 --- a/tests/TestMerge.cpp +++ b/tests/TestMerge.cpp @@ -23,6 +23,8 @@ #include "core/Metadata.h" #include "crypto/Crypto.h" +#include + QTEST_GUILESS_MAIN(TestMerge) namespace @@ -1355,6 +1357,37 @@ void TestMerge::testResolveGroupConflictOlder() QVERIFY(groupDestinationMerged != nullptr); } +void TestMerge::testMergeNotModified() +{ + QScopedPointer dbDestination(createTestDatabase()); + QScopedPointer dbSource( + createTestDatabaseStructureClone(dbDestination.data(), Entry::CloneNoFlags, Group::CloneIncludeEntries)); + + QSignalSpy modifiedSignalSpy(dbDestination.data(), SIGNAL(modified())); + Merger merger(dbSource.data(), dbDestination.data()); + merger.merge(); + QVERIFY(modifiedSignalSpy.empty()); +} + +void TestMerge::testMergeModified() +{ + QScopedPointer dbDestination(createTestDatabase()); + QScopedPointer dbSource( + createTestDatabaseStructureClone(dbDestination.data(), Entry::CloneNoFlags, Group::CloneIncludeEntries)); + + QSignalSpy modifiedSignalSpy(dbDestination.data(), SIGNAL(modified())); + // Make sure the two changes have a different timestamp. + QTest::qSleep(1); + Entry* entry = dbSource->rootGroup()->findEntryByPath("entry1"); + entry->beginUpdate(); + entry->setTitle("new title"); + entry->endUpdate(); + + Merger merger(dbSource.data(), dbDestination.data()); + merger.merge(); + QVERIFY(!modifiedSignalSpy.empty()); +} + Database* TestMerge::createTestDatabase() { Database* db = new Database(); diff --git a/tests/TestMerge.h b/tests/TestMerge.h index 15c18e43b..7a18d2a39 100644 --- a/tests/TestMerge.h +++ b/tests/TestMerge.h @@ -36,6 +36,8 @@ private slots: void testResolveConflictNewer(); void testResolveConflictExisting(); void testResolveGroupConflictOlder(); + void testMergeNotModified(); + void testMergeModified(); void testResolveConflictDuplicate(); void testResolveConflictEntry_Synchronize(); void testResolveConflictEntry_KeepLocal(); From c339470496e01d41123f011f26497aced93c1b0c Mon Sep 17 00:00:00 2001 From: louib Date: Fri, 9 Nov 2018 19:15:50 -0500 Subject: [PATCH 48/84] Updating CLI man page authorship (#2456) --- src/cli/keepassxc-cli.1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cli/keepassxc-cli.1 b/src/cli/keepassxc-cli.1 index 9d29534e5..877346646 100644 --- a/src/cli/keepassxc-cli.1 +++ b/src/cli/keepassxc-cli.1 @@ -1,4 +1,4 @@ -.TH KEEPASSXC-CLI 1 "Jan 19, 2018" +.TH KEEPASSXC-CLI 1 "Nov 04, 2018" .SH NAME keepassxc-cli \- command line interface for the \fBKeePassXC\fP password manager. @@ -153,4 +153,5 @@ Use extended ASCII characters for the generated password. [Default: Disabled] Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues. .SH AUTHOR -This manual page was written by Manolis Agkopian . +This manual page was originally written by Manolis Agkopian , +and is maintained by the KeePassXC Team . From 91bccf75d5b7b8f645736b55e97a5c4c43a8ca76 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Thu, 1 Nov 2018 13:07:51 +0200 Subject: [PATCH 49/84] Include older settings type to conversion --- src/browser/BrowserService.cpp | 10 ++++++++-- src/browser/BrowserService.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index cca76c15f..a69508026 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -36,6 +36,7 @@ #include "gui/MainWindow.h" const char BrowserService::KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; +const char BrowserService::KEEPASSXCBROWSER_OLD_NAME[] = "keepassxc-browser Settings"; const char BrowserService::ASSOCIATE_KEY_PREFIX[] = "KPXC_BROWSER_"; static const char KEEPASSXCBROWSER_GROUP_NAME[] = "KeePassXC-Browser Passwords"; static int KEEPASSXCBROWSER_DEFAULT_ICON = 1; @@ -467,11 +468,16 @@ void BrowserService::convertAttributesToCustomData(Database *currentDb) if (moveSettingsToCustomData(entry, KEEPASSHTTP_NAME)) { ++counter; } + + if (moveSettingsToCustomData(entry, KEEPASSXCBROWSER_OLD_NAME)) { + ++counter; + } + if (moveSettingsToCustomData(entry, KEEPASSXCBROWSER_NAME)) { ++counter; } - if (entry->title() == KEEPASSHTTP_NAME || entry->title() == KEEPASSXCBROWSER_NAME) { + if (entry->title() == KEEPASSHTTP_NAME || entry->title().contains(KEEPASSXCBROWSER_NAME, Qt::CaseInsensitive)) { keyCounter += moveKeysToCustomData(entry, db); delete entry; } @@ -860,7 +866,7 @@ bool BrowserService::checkLegacySettings() QList entries = db->rootGroup()->entriesRecursive(); for (const auto& e : entries) { if ((e->attributes()->contains(KEEPASSHTTP_NAME) || e->attributes()->contains(KEEPASSXCBROWSER_NAME)) || - (e->title() == KEEPASSHTTP_NAME || e->title() == KEEPASSXCBROWSER_NAME)) { + (e->title() == KEEPASSHTTP_NAME || e->title().contains(KEEPASSXCBROWSER_NAME, Qt::CaseInsensitive))) { legacySettingsFound = true; break; } diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 482bf99cb..bf93edf72 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -59,6 +59,7 @@ public: public: static const char KEEPASSXCBROWSER_NAME[]; + static const char KEEPASSXCBROWSER_OLD_NAME[]; static const char ASSOCIATE_KEY_PREFIX[]; static const char LEGACY_ASSOCIATE_KEY_PREFIX[]; From a7dd9f19f45c63e31df6c502a234aa62abc931c3 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 10 Nov 2018 03:58:42 +0100 Subject: [PATCH 50/84] CLI: add commands to show and copy TOTP to clipboard (#2454) * Add CLI commands show --totp and totp-clip for handling TOTPs, resolves #2429. * Adding tests for new CLI TOTP commands * Update keepassxc-cli man page. --- src/cli/Clip.cpp | 27 +++++++++++-- src/cli/Clip.h | 2 +- src/cli/Show.cpp | 24 ++++++++++-- src/cli/Show.h | 2 +- src/cli/keepassxc-cli.1 | 17 ++++++-- tests/TestCli.cpp | 74 +++++++++++++++++++++++++++++++++++ tests/TestCli.h | 2 + tests/data/NewDatabase.kdbx | Bin 8350 -> 11486 bytes tests/data/NewDatabase2.kdbx | Bin 0 -> 8350 bytes tests/gui/TestGui.cpp | 7 +++- 10 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 tests/data/NewDatabase2.kdbx diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index a9135eff4..2cc15411b 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -51,6 +51,9 @@ int Clip::execute(const QStringList& arguments) QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); + QCommandLineOption totp(QStringList() << "t" << "totp", + QObject::tr("Copy the current TOTP to the clipboard.")); + parser.addOption(totp); parser.addPositionalArgument("entry", QObject::tr("Path of the entry to clip.", "clip = copy to clipboard")); parser.addPositionalArgument("timeout", QObject::tr("Timeout in seconds before clearing the clipboard."), "[timeout]"); @@ -68,10 +71,10 @@ int Clip::execute(const QStringList& arguments) return EXIT_FAILURE; } - return clipEntry(db, args.at(1), args.value(2)); + return clipEntry(db, args.at(1), args.value(2), parser.isSet(totp)); } -int Clip::clipEntry(Database* database, const QString& entryPath, const QString& timeout) +int Clip::clipEntry(Database* database, const QString& entryPath, const QString& timeout, bool clipTotp) { TextStream err(Utils::STDERR); @@ -90,12 +93,28 @@ int Clip::clipEntry(Database* database, const QString& entryPath, const QString& return EXIT_FAILURE; } - int exitCode = Utils::clipText(entry->password()); + QString value; + if (clipTotp) { + if (!entry->hasTotp()) { + err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl; + return EXIT_FAILURE; + } + + value = entry->totp(); + } else { + value = entry->password(); + } + + int exitCode = Utils::clipText(value); if (exitCode != EXIT_SUCCESS) { return exitCode; } - outputTextStream << QObject::tr("Entry's password copied to the clipboard!") << endl; + if (clipTotp) { + outputTextStream << QObject::tr("Entry's current TOTP copied to the clipboard!") << endl; + } else { + outputTextStream << QObject::tr("Entry's password copied to the clipboard!") << endl; + } if (!timeoutSeconds) { return exitCode; diff --git a/src/cli/Clip.h b/src/cli/Clip.h index e5e6390ae..9f7151322 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); + int clipEntry(Database* database, const QString& entryPath, const QString& timeout, bool clipTotp); }; #endif // KEEPASSXC_CLIP_H diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index e474e2489..7b42de7ab 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -50,6 +50,9 @@ int Show::execute(const QStringList& arguments) QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); + QCommandLineOption totp(QStringList() << "t" << "totp", + QObject::tr("Show the entry's current TOTP.")); + parser.addOption(totp); QCommandLineOption attributes( QStringList() << "a" << "attributes", QObject::tr( @@ -73,10 +76,10 @@ int Show::execute(const QStringList& arguments) return EXIT_FAILURE; } - return showEntry(db.data(), parser.values(attributes), args.at(1)); + return showEntry(db.data(), parser.values(attributes), parser.isSet(totp), args.at(1)); } -int Show::showEntry(Database* database, QStringList attributes, const QString& entryPath) +int Show::showEntry(Database* database, QStringList attributes, bool showTotp, const QString& entryPath) { TextStream in(Utils::STDIN, QIODevice::ReadOnly); TextStream out(Utils::STDOUT, QIODevice::WriteOnly); @@ -88,9 +91,14 @@ int Show::showEntry(Database* database, QStringList attributes, const QString& e return EXIT_FAILURE; } + if (showTotp && !entry->hasTotp()) { + err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl; + return EXIT_FAILURE; + } + // If no attributes specified, output the default attribute set. - bool showAttributeNames = attributes.isEmpty(); - if (attributes.isEmpty()) { + bool showAttributeNames = attributes.isEmpty() && !showTotp; + if (attributes.isEmpty() && !showTotp) { attributes = EntryAttributes::DefaultAttributes; } @@ -107,5 +115,13 @@ int Show::showEntry(Database* database, QStringList attributes, const QString& e } out << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl; } + + if (showTotp) { + if (showAttributeNames) { + out << "TOTP: "; + } + out << entry->totp() << endl; + } + return sawUnknownAttribute ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cli/Show.h b/src/cli/Show.h index 6d49d1207..32dab7efb 100644 --- a/src/cli/Show.h +++ b/src/cli/Show.h @@ -26,7 +26,7 @@ public: Show(); ~Show(); int execute(const QStringList& arguments) override; - int showEntry(Database* database, QStringList attributes, const QString& entryPath); + int showEntry(Database* database, QStringList attributes, bool showTotp, const QString& entryPath); }; #endif // KEEPASSXC_SHOW_H diff --git a/src/cli/keepassxc-cli.1 b/src/cli/keepassxc-cli.1 index 877346646..0d618c9d1 100644 --- a/src/cli/keepassxc-cli.1 +++ b/src/cli/keepassxc-cli.1 @@ -17,7 +17,7 @@ keepassxc-cli \- command line interface for the \fBKeePassXC\fP password manager Adds a new entry to a database. A password can be generated (\fI-g\fP option), or a prompt can be displayed to input the password (\fI-p\fP option). .IP "clip [options] [timeout]" -Copies the password of a database entry to the clipboard. If multiple entries with the same name exist in different groups, only the password for the first one is going to be copied. For copying the password of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name. Optionally, a timeout in seconds can be specified to automatically clear the clipboard. +Copies the password or the current TOTP (\fI-t\fP option) of a database entry to the clipboard. If multiple entries with the same name exist in different groups, only the password for the first one is going to be copied. For copying the password of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name. Optionally, a timeout in seconds can be specified to automatically clear the clipboard. .IP "diceware [options]" Generate a random diceware passphrase. @@ -47,7 +47,7 @@ Merges two databases together. The first database file is going to be replaced b Removes an entry from a database. If the database has a recycle bin, the entry will be moved there. If the entry is already in the recycle bin, it will be removed permanently. .IP "show [options] " -Shows the title, username, password, URL and notes of a database entry. Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the \fIclip\fP command section also applies here. +Shows the title, username, password, URL and notes of a database entry. Can also show the current TOTP. Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the \fIclip\fP command section also applies here. .SH OPTIONS @@ -102,12 +102,23 @@ Specify the title of the entry. Perform advanced analysis on the password. +.SS "Clip options" + +.IP "-t, --totp" +Copy the current TOTP instead of current password to clipboard. Will report an error +if no TOTP is configured for the entry. + + .SS "Show options" .IP "-a, --attributes ..." Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are -specified, a summary of the default attributes is given. +specified and \fI-t\fP is not specified, a summary of the default attributes is given. + +.IP "-t, --totp" +Also show the current TOTP. Will report an error if no TOTP is configured for the +entry. .SS "Diceware options" diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index c95b1f32b..b1c3a82e8 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -67,6 +67,12 @@ void TestCli::initTestCase() QVERIFY(sourceDbFile.open(QIODevice::ReadOnly)); QVERIFY(Tools::readAllFromDevice(&sourceDbFile, m_dbData)); sourceDbFile.close(); + + // Load the NewDatabase.kdbx file into temporary storage + QFile sourceDbFile2(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase2.kdbx")); + QVERIFY(sourceDbFile2.open(QIODevice::ReadOnly)); + QVERIFY(Tools::readAllFromDevice(&sourceDbFile2, m_dbData2)); + sourceDbFile2.close(); } void TestCli::init() @@ -76,6 +82,11 @@ void TestCli::init() m_dbFile->write(m_dbData); m_dbFile->close(); + m_dbFile2.reset(new TemporaryFile()); + m_dbFile2->open(); + m_dbFile2->write(m_dbData2); + m_dbFile2->close(); + m_stdinFile.reset(new TemporaryFile()); m_stdinFile->open(); m_stdinHandle = fdopen(m_stdinFile->handle(), "r+"); @@ -96,6 +107,8 @@ void TestCli::cleanup() { m_dbFile.reset(); + m_dbFile2.reset(); + m_stdinFile.reset(); m_stdinHandle = stdin; Utils::STDIN = stdin; @@ -168,6 +181,19 @@ void TestCli::testAdd() QCOMPARE(entry->password(), QString("newpassword")); } +bool isTOTP(const QString & value) { + QString val = value.trimmed(); + if (val.length() < 5 || val.length() > 6) { + return false; + } + for (int i = 0; i < val.length(); ++i) { + if (!value[i].isDigit()) { + return false; + } + } + return true; +} + void TestCli::testClip() { QClipboard* clipboard = QGuiApplication::clipboard(); @@ -177,6 +203,7 @@ void TestCli::testClip() QVERIFY(!clipCmd.name.isEmpty()); QVERIFY(clipCmd.getDescriptionLine().contains(clipCmd.name)); + // Password Utils::Test::setNextPassword("a"); clipCmd.execute({"clip", m_dbFile->fileName(), "/Sample Entry"}); @@ -190,6 +217,13 @@ void TestCli::testClip() QCOMPARE(clipboard->text(), QString("Password")); + // TOTP + Utils::Test::setNextPassword("a"); + clipCmd.execute({"clip", m_dbFile->fileName(), "/Sample Entry", "--totp"}); + + QVERIFY(isTOTP(clipboard->text())); + + // Password with timeout Utils::Test::setNextPassword("a"); QFuture future = QtConcurrent::run(&clipCmd, &Clip::execute, QStringList{"clip", m_dbFile->fileName(), "/Sample Entry", "1"}); @@ -197,6 +231,21 @@ void TestCli::testClip() QTRY_COMPARE_WITH_TIMEOUT(clipboard->text(), QString(""), 1500); future.waitForFinished(); + + // TOTP with timeout + Utils::Test::setNextPassword("a"); + future = QtConcurrent::run(&clipCmd, &Clip::execute, QStringList{"clip", m_dbFile->fileName(), "/Sample Entry", "1", "-t"}); + + QTRY_VERIFY_WITH_TIMEOUT(isTOTP(clipboard->text()), 500); + QTRY_COMPARE_WITH_TIMEOUT(clipboard->text(), QString(""), 1500); + + future.waitForFinished(); + + qint64 posErr = m_stderrFile->pos(); + Utils::Test::setNextPassword("a"); + clipCmd.execute({"clip", m_dbFile2->fileName(), "--totp", "/Sample Entry"}); + m_stderrFile->seek(posErr); + QCOMPARE(m_stderrFile->readAll(), QByteArray("Entry with path /Sample Entry has no TOTP set up.\n")); } void TestCli::testDiceware() @@ -756,4 +805,29 @@ void TestCli::testShow() m_stderrFile->reset(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); QCOMPARE(m_stderrFile->readAll(), QByteArray("ERROR: unknown attribute DoesNotExist.\n")); + + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + showCmd.execute({"show", "-t", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QVERIFY(isTOTP(m_stdoutFile->readAll())); + + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + showCmd.execute({"show", "-a", "Title", m_dbFile->fileName(), "--totp", "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readLine(), QByteArray("Sample Entry\n")); + QVERIFY(isTOTP(m_stdoutFile->readAll())); + + pos = m_stdoutFile->pos(); + qint64 posErr = m_stderrFile->pos(); + Utils::Test::setNextPassword("a"); + showCmd.execute({"show", m_dbFile2->fileName(), "--totp", "/Sample Entry"}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->seek(posErr); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll(), QByteArray("Entry with path /Sample Entry has no TOTP set up.\n")); } diff --git a/tests/TestCli.h b/tests/TestCli.h index 691269840..2c411f2ca 100644 --- a/tests/TestCli.h +++ b/tests/TestCli.h @@ -58,7 +58,9 @@ private slots: private: QByteArray m_dbData; + QByteArray m_dbData2; QScopedPointer m_dbFile; + QScopedPointer m_dbFile2; QScopedPointer m_stdoutFile; QScopedPointer m_stderrFile; QScopedPointer m_stdinFile; diff --git a/tests/data/NewDatabase.kdbx b/tests/data/NewDatabase.kdbx index 4e77724c7e8cfdf22bcd951ccfcaa4ae7f49d460..a8dfb5bd5e077aebc59017a1217a0d0b68109ae1 100644 GIT binary patch literal 11486 zcmV<4EFsea*`k_f`%AR}00RI55CAd3^5(yBLr}h01tDtuTK@wC0000000bZaCCY0) z+aEf;j*>tm@TKGnYm6GfCen~FW;)wRhAK-w1t0)LlR_3SEWY=2mo*w00000000LN08>67y2;Rx)_~+yuEJNuG6*05k@Kq~)Ne7S z(>9PCg$6ey?j2*m&OA9^zQc)htN6H(2_OJ;{e(Q!RxEfq4PC07<;Lkj*3~`o0r@o4 zVy-8jd`!PmP}9xT@SJJ_Tny|86%Xl{1OK0fgWoYcuyhH-5ze9p!Q_hzJ{^FHL?V^j zx*rCazM1&me5?5)wnP;-il)bh&C_beXSIYaabyfKbirNOYXZ(5SmHB3PCPAQgvts= z@V$hC+i_|w(fmn9PXGF>oIsCjR17S0ea29Xb_%Xok9#k{6OuHsyHWd(CL{mBNC)I0 zZVDS4-6?EoKcG(9a@xQBUs>#DAE|$gmtAlPH}!>+rP+I8go}1=%HS3e)3XTbb!jri zbA1ta^l*3@=lzYzzZerI^oY?dl?==>)9GV?c%IX61Rv|t$W^Qvg97Zg36aE(l6Rp# zV$HOOJCApv&^cbv#}tufOEEI2^q>E}6Vwyu%roG)1TnliRBujKw;~-w$U)zJ?ZxGp z;>dIZl7@LJgG}5WP1=kFn~cTu>sxU#U+bnf9sw^A;J)tQ@2duhDU~dpo?~vIbk}>^ ztC3ZR`8T3lw}fO_|5DWW^*@xFc2Tvk|L{tV1A|C~b9WW1+{Kqc?y@6f^`0f3rGfgL z!fn*WN+#a;e{nLo)%xk}5B(mB4TM9qnhi~ZWL}(MzFfQxP;nlU2*<%@JUIo>vk=b6%rdrh(!^gYJQH=E6F2I?4BWdygO1c0D8^<8WC6|pb=kE`ZRgDS2G02;$Dj)95jje$H*12fnXf6`8D!};Am%; zYyvI5vKig7YumXrOXUOJgqLYE^*6!FprTJH{N(WOLy^-Q0n=Uzv+U3(n6ZR{mcmc&Os z6&gkFA?+DuI_MOM%iC4Jr`_dDEV!Rx5HDt=7bwtcC6rD0ikD&i*T&Ldi7<9sux44& z#wI-DhikW1`V=@;Z7KJ`MkR%J^2(r5Cn+6eb>TDF0QGRbN`rn0}<8XjCACP5AOMC9`) z#R!bZ;>uak4xpE&s}L|fK~#{7Wx`vPF?)go)2^r-CDJuVbp&V{0)Mw`P?aRqSwkBh zF-#1!!^||&nX>Y6ByX*Ag<==*R%>A>lv(;-C&mCOILw$(CMV?Js6P8 zF}z$er&{|_Q06lBk$N?*NsCF51#Y(@{~=fXrw#9|8Ek;|V|FV{=8iq=(?mB+;Kn=+ zb>3HE$qdzpN2WdpPhE2*sGXrR*Y(lA52RNbKo=w|Yhm=1Fz;z}RKsP01NPVHjG=Sc zMgqC^3SIz)!l)(mdJx!q1;SkV7Oyl7At~-QNH6lr12nMjden~*yeIEDj18M_`dZd2 z#{H&C_3Ll5A8`m)1dKFWV<#&pLsyFvEJByOo5?-U}}06-lyQUKjMnT zPee{;?=mgf%r)Xp%;aWO ztwZ(Yj~Do$w7;U&(RYohB|89G;WGgE51iHdnR zblJk2MBaA$pKpk_0Q`&b!{&djGG}&R!7-6Uy^Y0}XK30CPd6WL)!ozSsiX<)B?bUZaL{AE zm>3c)gNB)%x0qcb_Qw)?hq>qmpiSnz5Ca)Gz8kI~;aHwO)u-An3M!*-%-2e(+n{v6 zoiGxe`L7kh{*+Vv=vS`kHs^hBmjO|cDB8&Z00|5zM)|_T|*1qk=FTNqNY; zQcO-HYR8ck6o*`gVVAO9<1qk%o3$#IQ!$bJ%>FSe29A<*%xxGtr)lE=6C~p~k`Fa+ zRUbwwgpQxjhg}yxiea>1*t0CPbQXMLe~%(S z&p9lcOFr5xi4l{xTyt=xz+)a9exX~LvkIB8D%9jSUWp<3gV_AujsTRJ9&SrZRv)uE zAB8ykFBaCoB}QMN>w-gEtEi1uMeZh#58*hGBmI%^C)V%_nDTNNsL_~RI4 zQF&6Dkfgjjnk3JTqlRo|(l1ZLp^AK-6;l4gFnUDPCtd^iw@GC<3T(SvvS}Lr?pUy4 zqw!nlJ^vnhv!__tdbvWF_~gr140!A?n#xqB$K_Tm{ktqdO+naBeG58Krg%EBOy$GH zhVqzfbJ7fxx3O1b@*{-c?8kr4A7=k&CHp*jjgi&HhPBRK7aK3_@;ai9i_|^U7$Z@< z%GoG*bY3ZZ0*QgBqnmYRP!Y|6PjfwgtnDheZ*tAM;hh#<(9aWs@xkjvVOy-jY^&j{ z#1wE?A-ST<1Ch0Fh5olGx`Ksjra3fuY7t=Gvn*X&8z(pYQ*PnTACjMG@>D+bOw^LI zZHHD=Dl?!GR_Pm?7Xfvw+zx@GqsT;}H)<(0hh{;}j@V*X8|We|{Vcs?IxJiRT4JD3 zh9BOv7z`?B83Ste5n5E%QKE7!!V>lecEDR~3$M%R$2FitHn0IFCp|9zY(w*Oj zgasvJ?{BQGv|FK*%H=jHA+DoG2(N{Qp~`lgW;B6HDXt1HsJ!+wyF>BF;@FAJkb>9# zIhI_X`Wq(dJ%o3bG8Phm0@V30buX4lj>P}8a)QHe{;xm`-yOG(v+BZ!EHK!8MudIo=VlCO0zZ)dErT44Y`WijDY*MP-JVs`a_CYb)y#*kH(+JCA z>Ju`DZ>5n_#{fxkpYmp1%E=zv3A|yPe-?(07GEvX$G#pzu6N=KE_y~BxR+x=>5YAJRC+i4E3CV(sD?n&IAbvRWLyF>Pl!-*h5a%Hc9wh~XLlG#XBys?9~} zfZ0^o_*OiOIAp2H92zj@#pf4xSh03O{6qkn>Ram=@7L*1lC z-HsGiJ4huF+d zg4o%HskH1xuV_J6W_KaZ7{|k@1P3q8Y@x|*hL4!2e1(h>e_-0zp0O|;C@o(aWcF8j z^`oNwPXevbXy9Y|@C^_tgp$aB-7D@i6$ei&+W$iYIv^v)?&aJ>{qNvz1LiQF!3N?- z7i9*p|8%^GjrWdsYSS_CaITd-#f?|~tl>FEQEB~3gpNcRlrd>gnQ!F7o@Ms{(SS@( zKgM)hVR{w3b1n0(BB1h>Ur9A?aqbH73(Gng;s0^NWcOqj=i2I9V|VNe4d4O z=M5ZZS2K6TtdNw`5LNE7*Xmz+K7%bl#ctDT=H(Imxd<_0N* z2`2j>upDh-2aio6-H50C#5J}IIZ49<%oxv9ueS+Mq4BYJw0yT0Efpl(S|Y-S5v-*5 z&9v(~e>Vu5AU*$z0w)M{lVXCt1|=Y*_F1F}bPvmDG{lD)c{!)`;9ch-_4C;oIl+SX zR8Akh)pyUZ+h5eR;$$Y9{y(^YF~g;M&WE`LdBq&^Q+7|c(U(ln+% zV@Ev=0m(2p;9gQB~6zQvjnAcY^m&1yBQyup-tYxJ-6wFNP z|LOGnc{@7SoeELPek+03d0uqspCQ;D61;95Wga?xME#_XX_m>UXw1L{u6cDHW~lSl zM&zw6m*`N%l4MmAP!cdWBDJSai zXGso56!-gR?>AXYF|4KJyShpOH+UzF>wn$^UQv&o){ zfq{-!w;7l1Xzv()Q}j&8SPCKvEJU^X)Y3I*Y#_0w6ZI@u*3}G_r+`$=71toUfeT1* zDh0Igf7#4i10}hWN|WWIYIaXexttH%sa^k#5vp_XID)1IQkL!_2rG0N^O-xc>iH1Y znd}#tyC7zPT5`y=3zSfqB8h@|)~N7rk37(gr6-Wm<9AA#Tcr}lG?}@r9Rc})I_`Wa z7kFD&i|7ZJ%dA<(LPPGA z-V%c3Zj`7M>I{<|Cw3$o>j1UpzaqloI>9Sp=`OM`w^H;K zu!T$NN_$#0wv0H&IE1=TS1T7R#mq~Aj;i&nvE|>f%R#wzYz-MKnoyX2)9H#QUu*9m zrbMrN6Cpou@a2#=c>XA1PIEFXQF>yiqV(49XRIAJ$xZX-2Ega39sP; zs}N^$=jQ1_g9Jy$-V0lliaXI%ASBA^uyU<*Z}eZpiB)N#P{Bc@Z~e(LjT77VVfsXE zx~n%GW2__eBOzxl2pn4`4}RiA1s;TGz(h$89mj>b0u0BXvqsNvUK4vEF2a?gZ`WlR z7L-$p9z>~NR6pJiV(<}2jN^{3wx+~!b=I{ldGdT6-H|K>NDjHC+$D~D0j!S$u@R1C z?^^rKH%;5lg^`R&hhsilFIp&|J9IcHrj0CEJn_<3$Z);j?{pmj)STmy&zVA~8y%bp}U# zC{XVwQ#}dUU$Y=5NNIGj1QQj%wHge@;Cdbxr5O#ovax}UaS@)zI7Y{8lNXX#Z2=1R z3M$Alq{S3bs6@ER14b|38L=RMI#<_nW`{3MM@hR&uxt18W-KJJqvD^w9Q|;(MJ&Aw zAK!F;W_{bby9cKC!;u!z=m=n6NMhF0e{TMNRu^&xMzAbd6unjJR{#T3@YUp)st+}U zI+j-*f8!Jl1=Mqk!~iMp=Lh#=HrUiHKZc{OpyN%AT~&bZ$xy;DhA3D&lEro+J#ZlG ze4%e9{YQGt;9L)kFHk`q;Ni!vr5gAQs=6Gs!?Nr-4|Zizl#59EoY{_@6TRqlL2wki zTu45eAMRo;q#`UWdU_Wimi@Mi;c}Jl`_+E#u-gu}+KGD_`|C|`+$E*3eE*u*2k?rYS5iPG>eykrpA;L0dlp)JimtGB=r?YGLW zgATuCfTg3Rno7bOzk3g31;|k4(JQ}smdaq$e+cIt@iKV(|0iaP{|Ilaz|>oO{IF1j zeIcaQZETMxh+{JRS(Q7{`Zg-P!aW*SJLlza^(^hYgt7U$Wz%;>GgB#Bp9(^R~dvxj-p+*5VyqpbY|nGIaDFgnlCg5`Ok_+;S zb<>*I&=_S{oLv>7W+bKjD|3xQ17d9&`#`KPEh& zDXCxFeri<=)KlbF*rFKKC>2@kRD2?uS(11SrZPr;@3@h*T3MVi{j*GP&cj8R$thyb z;zo~Cu&ju=-UXQ3JTAsQ`^g-F@M9AOnDgaF6i@_u-fKv~50-By#V{ldL)M1&R`JDc z2e@gZj-OIT^dd34PY4~LDL7mSs==t%`u@=NLGRWWc9vQ3+eSGrMeu-Ctr*lu-&nwDE?1mL&9j4f4GHMq9q6ub zViyJRt&|Nt9`b(#z0!4^g@H4nqABTp;jjJX7cfQ(qKzXNZ{-XRHwf;k`L8R5=?c-L zPHZ(xRs4Jj&wa6(6SbnXG_c|xFMRGAn7flX&7x}hmrLXCDedG1g5snQ2pE4>kX+CA zicI!WOblAf?{5pDsKOX@?TS324BD5!)S{!)R!gs_u`PfpuC~Gv*Q(8IcISPI4EEb5 z-UKcVVCAn5QTn0qg%wTdCq!t9Z~D?zru6;rrcDKD>Q)rr%o03l$|j0-q7G+II|S(R z!`9J_W`Ez|(}kC=uptIS5jWw>FK8i%=N6Y_NU1BELr z2NCpMzzY#?#)_bsa?@&R&3X~$!_fcn$ z&=SNBZ>;<$TfV*|e-c%y-_o^Y81g2poZN1Y-6SLEHO(@!iz}ij5Z1GdhK0!KKT5#@ z7HUb>b}8h4i}ZfOssH6Pe22EuIm4O(g`1L3$>>cp@Tlf~n$!Ym%_2{vb9io6){IJE z7BDDsw1o=YD`N*m*g3P|Rc0aWc)PI6;2Q(P*qv-f&>)3T_RsR&#Z-mPhJN-*xI-9; zdBDM7gz?aAt?E!ZZ}%B>4@6{i(txuKvE<*DMls~9J|iUv_5knB3=ZaGEMkJ*(V;+9 z|9x#oNM{t#aGP+DWU7~Q!PHg6ZZToT%7Wr>f4XCNsV!?BvNkNX@t`~OW^;mk^jX=) z-DB|V^fq}$Tk8g?@3#D8$q&6K6ZYknjT9>>D&g*!;Z!PxxyyZS?CQ)%oo%BPisQ_;WpBvv|jKx@egE0Hi1FN(hRbNLXvv> zkYo^tUkus~`wbXcbnWw{{ws>n@AKGAo#sTg9Yzph$aC+M@edxL7dk&AYL-36-BAO< zc%VH?4>S5Wv@Zq&b@VdJ8@Y2R`LAHaj)WJd;&ZfQk4?|FkBBboU?P&cqVy$K4?1N1 zFF1ZnUKw7GlP_*O)>T_H|0c~DlEPmuL;}>a)6aG(yS#KcbG`~U&F4GOAX7SzPH=I- zIHt^^FddZ>;hV$S)nX};K#^B;01AdrxB4{(lJqG0MO#C7S0|v3?tte6z%e z4K9XBwH}kbgRsKW@F%re`X8qd5^)DJpj4TF%_3q#Y;b<&HF7(8ePk_z8sZC*@*|Y8 z6HYpD5zFlw=p*PdwGrFDYz7jxA01^LJ%%I7RHb3(yMxl=H??z7llG1o%pwC;&~%omt+Gi1#-hZle@Lh zin-qsQ0r}pM$}R0qU!qbUPfJbBM<#lusxwks*h7it#KNkFF;)7rBm0m`o*vIzx0sA z4c)Y>I0@=jkLyBN?pcA%K=Y9ZO7R@Kr*V9Yu*@AydaN|)f^}AAFmDYamh34NMvcMx zIbC+*A)x+LT_)qv;;BJdwZlcczqZ>9*Eee99ZL)CaCepH>jD?zdczHgIEvDaDW^zGSx zG`w$G#j0k5HYp=YvfgzNHe&1zL@c@aKrj+L|6tQw>?hd!Ds@PZyi^s%iE zazCs4+~%h%Jnfb3UT7Iss{PG9Hm8;rkOSMV^q`u)8P-2&8Fi5D^S! zz5LhnGM?kmM9hYZ_)vh?N%`j>U~-6{H~GL9f@6{(<*?u;JLPC{V%7y&zSnHv7=Z0& zrW_+WZKDwTH(g^T0aX|_J55g;?77aw2(#rmAg~$UX%boBw++1qURC9`FxGGMK-J6g z14duhSE#je0~gMK=gWlaAe$!G_;!9c#i>#bVEBg|a~=AFQv0JbtmcTbC2(D(SHp}NQHI4&tR=`(bs@6|;;LDS#9iEdP*L*hdf8z{5P2UR@uwWu~| z8WXA{;mJ@g^Wc;*6cRJK3aeB39_3AIh#V4!pqtRB`GTcaAJ7oeK*bIl8%kQu$haVD zRZ^)S-GI-1b2rjsNhRmbrh5jHRIG!7VkSdUuDd@{=s(PYB@8HOL!>Tc@<36aPF*E3 zUC6cBqZ6VIwuDL}tX;#F7Q0Q)?5^ulX!inwc6}JkFJi$7seu0^HltHSJu{8s<|FmY z^_AEXUQom27wZPu@$Go#T)^gwcGrIW^hPggjzMUOU`9ed^2-E^x{%-ZGTwYmPF)rj z`n`E<<q!RIb*N9ktbedy_b_<)I*A zRgrvzraE@5sj#Uye21hBwmDR}rIOhDiZ(3Acv)dwJf1gf*=?b8m(PLv!s+iAZ74&) zSu#@ybNujnM->f73?C5xt(Q;U;UZdL?qibsYh9W8Y&GQY) zA6xmzFs_xmo_pWgvqCvp%6kT5=->E_1Qhd#SxXIy82f|5aB`&>sGDUB-sXx1ggm=0 zJQ}LrU*Vm?dbz`Dt_4PJ0+O#YITwi+w(g>u!7Son?FAS$|F%#wy?07Kcs@Y*!hLLj zBh2u+-=Ei~EXR;Anf=r7t+hyl-pO~BN}q9vJzB;{{gUMYIvzbR;Fnwd!*}&Bucd|k zv$`0_esT=nTP0w;zsGnq(l@7svlT>C!ri!~`5GK}T>2&Z*bslW!jw3@DSRb`4QSVF zg3-Bv3K`q@&HDJd6Tg85HuKguP>T#EpY$vtrxtc;5hP46YZaXF-6(%MMPkrju4B^H zq9#%Siw$CNkD#~A22$r@6o0ruPUu{~sJHe|IiQ`wfV%&Gi%cBiJxvU4o|Z-}_o&?P z6W%M1lOR*UU`JpO@#`!s4;-bm6e!TZ^Lr&h3DAW5~Qd zOHi@xdlu#SGJd{h?$Q&ffoISwxTwe$Q0dWdk%G$#ctq=0GyICWHe289$v@g)oFHR5 zSum@cq!5e(^WP2VX{n~PEUBGSDvc98|C3)5l2DXR8R&v3Q~=$^i%6LVlnfjO8i7Vr zDI?I)81+xe>bzlhbzE_)e;}AB8Pa#fowL}7b}qhu#E%dCIn^cUmoS>XWDj%^ih3Ze zr6JTu=~MSFEs=<0@9|;)@r{@hec{B(E}nxqxr~W|>$soWBcudKV3224v2*Yx2sJv| z>UbB1!yh?=Xu8y3;APr9INgRq4pfP{7(vJiFuLk}d-oz3m7=&J&2x#=GCh%O#MQ=J zgWyT=eZyYNUw>SSx7;6U-+1!O%~i{ZzPS;YMF)j7&y$7x6KjTo9V?pBGo zG#;M5#lyym_}_SO9)c@8=lQ%>FDU&scO3L!X^H*p%zI{mC!6@P)X1OigU*YhN*{NAMnlXEN2||0~Y_L&n|N!?J&S%>ip;SQE%9PQn<@0n3YR`DTA^xOou}VyS<# zM&YPXTO`X10L&j(^s1^R-@;g~5w~f7VV0q-^t3#$H|zuNuH0yXXb0^JUb{a1xwUuXF*^v!qVLL#b8^(-eriVYVZZ<+ zG?l|-^%HpU4`e;;miSUo?u5A>m>EU+OU0aF8`d2+!72pK(Az~>R-U27+&gp?`2j9uGN)K9E(RBgIN>5=H?3qzl7guYTJrPOXF(EIRDxXal?(MHH}HHu`Ysi{L|u{ zedVq=;cCpf?2~V;e?c_Z7^wHTT5Sx$@P7JWhDF7$j4M1v8YR`fBIdpL-ZmQ@#TQ|f zj-l_(kK&q@+kXe4AqU-d2o*ZB{Rf|zPpcgDIZtJ~8$_#!(~xjR5Mx&y+2OS5sF zaGjZ|1d-9?exOd@S&ila4}r5u`DNh`D;TZq{jdC2>wmUoRzidf<1)4swn^{0S^yN3 zW|pOl2694Oh#)PFj=Bs1N`@>V-dO=I zjuPT=I5G{*y4dOsS@@P#Cd~O)c63(8(I3$!(PL4YfRkZlbP-_~zuQqiMgy^$3zr|*-O)p zJK=Cm$rV(v6YH`D2mo*w00000000LN07gDpp)jUB&SEHlAHd?#0SF)fhm~cnv5wjd zH2eh3#wGe$uHY7_xM^Z0*|9Pq+0Q?02_OLP9V>cz47`{J+>O&l#G1e)YAr@66YG!EYI$g-^G)JfZDjL^ZIfeM&K4bL&Scw}pcU zFLqvLd4HhMd2BL_locX_j`sMDsfc`Lq(puMq~9@aQXOHhLkuciEosV_J_RbZ@4`6H z)+fS9eV%|{dCii{m6E{K#1P<-Fx)IO zS`B4v8QS0d9G1HtRLTl8z`4Xp)F2Ld$bM4 zt@DhT!Uwf>0^wc!P{ol>PBA96P?cTn<&>Xk&_OeeO+9x_xF*5)C->?op`MD@`pmc zLZ(HZv(7nqC46+&3{gz`{3?Ftp;>C=$_~g1#nza9p!abGUKtp-9|~rNTvC7-*&Yz4 zoCEPiGcdEMiUTG;PI{tkc%1?oBAuPd+d+^6L&A&ElIKq+5f3%aWW7kL9;}{UmNn)l zibI~h_yVs7nSe`2UaO}6%P5tOSH)CGujXck&_g@@yYZBzv)(sn^IE% zuK}Be>EwdSxSSrcK|FGfJ8-PWMB;v|f-3QV4Zsl4Rc7Dxwv9KrNOX+7&hz<3&ZM7k z&H4hP56~32*LpQl8aBYZ`V0!NRKUNs1rFH23=#%Z|m;fbm6V|RJ8n}cdGeCUBLc(9aU7*=b zw?^$yY}%-QYL|E>@{-VwbBs*!sDQCOo*`{jg2X@OVe*aCE^a<43N=t5JK_Hvs`I6E zjXyl1Sz~Iv+FKzFJgN(%_xFW7~Pb82xeydPO|wo#Jt#AMslUh>aB( zx=9cH+hi<80$oG1D@yd^G^^K)E6a5wzX+*wTu8RjvqTJp=0sIDFaAq5c`OcG#r4%O zN>J-21AdMz0@6y)B@odqN4u_Z+c0aUJcdwWRQ&E^r|?23l%H!1D(=dU-A=L!&xl_F zPN!j}BAq?Knr6je%P*L1;f)V&ilO873Atg>K$)gDtoY!l!1fS@0V>P}kbMj@02Tw* z>kSp~QBA;AbbdoUSa{V#F1&Hgd#@TWaOEQR+#p0($czySkk2rQIx9pyd1TZviC6v4 z_ejLCRUl>ovBC5{DB; znt;4BX^{b)3#4lj>5m{f?5PCkX%ahe3NM{Jrg(_J-&)3~3{pI38xg-cp{f8dJ2(*N z$6SJ&((tptMu$X1}dpLXRg~LY;F`oUkshQPe&RlUSJ(>g;_agU#S$KC! z?QA(kf04*8)s_ZbD6+y;JxFGU&Q9PVa6j3&j%n0Oh1L*h7*InhAO+2$8ZDnVsntEP z{kg@XOKS={pGjM!v!8@vV#?@T-MS`|P)jckkf{AW>et#Rvwy4ygD+BrJ=`WkxmlN2 z6!$ade`)5ihEt`iE;d;9uY20tgRGl%2X2@h}{$&&G_x>#Vni5WDUzEL#_8)?x9 zsJL?F29KzdhiKd&ITo2pPduSJ_w@_g3qV%4$_jHpaIalV)$ILLplGki=iV0TVdqw{ zkQeFshRRD2N3vTzrpa3+0Mo^pZ}x`A?-Y?T4b6?6i1A!p@Y#ti$9^<^Wq$rLs>f^O zNVwX=C1i{ndWivtLKpV!K@PJ>YKV5u&{MZ>UrAfE*K19voL!Jj&kQOwL~mZ#=1KFt zGbrzl-Xzuk)ICMhf|N_pu84MMCOzi+xrCtnu}K2{H0c>0>?2eqde=n>y?mTVo^1BOHsPW1!@a)P*v$Kuj47BA07zOipK8UMLn?DmG z1f%Q@5Cf7(XDtD?*@{Ou%FVo6jRO9L7P_DGUER@h?x&`qqyJRr&6)(W^Z8Fb5D*?f zCcsjqDMBVZR=wgN|U+_bVTuxY&SrV zJf0kFcreks%c>sjxR16g|9AJ@gs5BC2BqMjiV~+7BxYp}5_!Sz^*Am|9Hm_7zG+xm=FE9%f?L)v^8N~MMl>se}Bhz zj}C-qjXm(2iZwnH(ZJvXTpcn%%o|0($wg||6@h0rh!|)Up?Hj=lol;EwJxxxk@G5_ ze<;u%7R}d!HV=6yo|91VAGht}1q?3CgBFfG`0u|@Sb?AVY)&deWiq_i8^@%3(w>(T zgGawQ6Pt@AE^g)4{<9$tsXJip;ro^}<>Q?wbs;0G0ag3I54g3L=L0kS-pTf5u!x}f z>L_)T#9MVX;5<)+`p`lF>L(EfL02D*{@VJyEJ{hz@8uv@Isn%T&F|?UW~=}jjaLVw zfxK`B|6U<30sOY^ural3xy82y`Hu(i^GmE$kBW8lPVq+t)w;-u;q2W6&4njeenq1d z_$5wyWR`!oQ9=FD`GWNRMUb4ZX&g@X14+q~(%1z^%8=}@zYvsD@3+k_b@aiKikg(h z&%@6a=9a%H?MtqRdc>=g!jO)v@hr}WWBqVG*$9laafrw$`F=}y#le0Q?aL_nusxar zD*eV`*9j*gAKw=kGuVbb_uiCU&Ftcp%cR*I)SH=(mp$3dx)KvWpqNVS<=vJgsJG=l z;&$AhsS*6YS9!ZY3z(HBIWar=UOG>(T!)XON!GRDKZ}UfD*ODRd$x?6a-#D3F5Jx=^Yb4ymcu2pP!BtPAxJ z*u3H-b}Sz~Q}&O6OY5L#NFi2?Ve=bK+;qN4TolqJz)8Q$k`xYIg0qh!9NC(3tTUcE zi}g+zR;Ko>`r*)d1)s7xOp!NnQ0H>tp8wW2Dc3FhT18$vL~glGJUP$D@m&e42W?+gO4{V6Il_~`5Ih;o1m3c z)c!9GNP7=pEvY@;wmU<)lZ8gJ40?u!2lBr%+AQ!OABFN1P-+7sQ=efLyls5X6C=0X zl);s{&cI@!ok2_}^k=arJ3!I7Sy!Vw`69N)_b`eJ6*MVNL1S?=oqZ7nx zl>&2tj^@(T2#Ief*eGpHQBm(-YMZ=*b6%_nMKomML_ z1h*%TKP;ixC787qSR;Q*5waWE-dM8`Ybe!Uzv4*Jk%2?&kQ>*-M|(nZ-rKlUgL*;o zW+!Frl$v3}psr=uPv%G45QnL6_pq zO=j)p-?4s}KtSvml??PJaMIXdIExK)pn4$tzIB0~kPQ3APsMNnlvF%b32+L)U>-Z@ zYpOG&CHyc1A`IrK9+U974Ve=TTO3L|y#c-rh8vS=dbuIO@50Iu*@_8{@R*8|s6H7`x<1_U zzX}lsNL%lSo7)m}{wfyEF9;~BSJc)t4u&)%*Myz5mLQpO1UfMxG4B^Sb1s4uhnr={ zPJJZ41K*RIPVVSEB0NPTaqBwmzm3^Ac8jAaoa_?wiZldd-2w$SV*5u}hhVth#K`kl z#f0uwdBi|2SEyjuUZ<~-tlZoJ4$upq+v1BrGG)+4^4kbBw-!F!AIvAu?YJ0%;O!;v z_L&8N{0sepzG~Dv_h{V*o9bgCgF?yp`ruvwT*$6QfT+!gDRk}yYL(=z_>_nbC;Wo4 z=GzYchd$OKhLlde>FAQQcrRyf8NeT>-CtJY6_X#p4ca|xxcF{$J>;dUVbl1;TC!L2 zGsP1E9rvI?TgPD5jDRc)B0Y+ATGU>*fJQWug#Hzn#;n|n`S$ckbZVOKcZ74K{<3-G zOlWN&+z+!Q?JQml^@u1%l$Up?uuEtR-31)?tWN1`PTp*$I{di!g$0&8@sC$J5sgaCtd8s%rJk~h;OqSrX6WMSEwM})^DWV_5G2U-5jn{` z_KS&PFY(0-v%uHh0pUQ&f{7(`{vQtpeNR}kvTc1fhIEeX{P`O^(r-mD>wnV=!PV};oRx701WeeK4!(TOZzWyRG z&@j~OeGQO#Hy^5^YLhH`y>YNuwc!W{9AXLp0yA*y@qHG3bA6Ibx$$5<^@KY$yb{3D z^0idJ{7~EvEb`m@>AP6mjUXkD9e*oBFu;0fKlgBwrg}B55f!+9n&rHI36(x4jsv^v zNsTHW2~G#u)w)g1Z?R$*J!d z;1y&X*|4e-O=LXX35smlTc0ez#J#=(_SU& zB#}0WFTs8rsOP#7D!?<;YD4qx$v|5F^}f%*^~>rFS@t8O}?PP)i^shHg-(^*zDM*+yBdgMfA%BixXg^$?h=7C* zpaW=^0{x~v0+1ms9bfUwwvK_VVRfe@=kVEZ-`!)$c^}3yjQYtXjOHkKe%U;=yfJCi zj)gWt)uPDHnU{l#@hv1-1FQbwJt|$k_UO_J4+5T{zsj$sYYa+#iI#4?TjKTOwCv0l zBe=$OZ-xW=5`Lg}@@>@8wp9GHu`Uc9WnM1BYX=HD(p4izKvnpy{7#g|h0WTilxf(9+P)@w`$8hiPjq!Y`MH`~VUlajW06 z)evsHZ&H&5M`~xQBQ14H7TsEV=PvN7JxQxs#;TaL!vNccwCGr{$SDjyjBzWkX>OZstdThn>0rv4q>--`6dy`ZvX@$+q{ff* z?tH*74M^zuE=yJK|fm_o^S~HLKKN|om#sY4G0Us7^}CE4nCUhsKT4i_Ap3cD15xh+>I&b}+OSGJ)WdRW9w zPHOqv{CNpitVR%Co-zOl%8$<$<_{4N<9=4cD+j>kcKs9L^o&xIIK>q9_gF!IZ}IBy zLcDul887$EXjY5nL9eF)Y9J0E*rc|gs@NDn;K!~Fe+nfl=aW5B!Z1h4EsId_19WHy z)ISJ>0b(Q#Qq)2~m_nCx{;%q$da&!UNI6~|m5CvTLJGTS!U&1{&|&S>DeGsoC}u%3 zIu6LnPo+cg$|i4B9BSfg=XyDBZU+v-X#q5&)|;xY2uF1WnD`vlDBUVGV#XUd#mm|C$Id3{O+{=$f2hEW`YOIi!^rphlnjkE1P9SqCm~;DADituDnO*mW>qf zhio(VdqggTubm9Ykj=kBfL(_T8V8c73;G64 zqSD{jR8{QIgkL8p$0s#he*-4Ph5lYI<$fmN=LP63iXDXdhI@8g>@-C^2Vbrn6tWX! zAZw>|9=;Wfx}r||OQxVYTXPAP!O zjcfr0_<>F{I5K|sS^)X51%}+FCE%Y_Vf%4g1Cxb4^-E= z<>cdYrmB1~t9f%q5ZL`h^RM`Dvtnw3dy->5C!s$(vu7h$w)kQ3OCW{~_K(0FC?P)w z?$YJ40cvzg${i5dJEa$uoVM-@kY#87IGRLCqINMn_HkOGzbzHseOWn55}|xywH5N` z2W8$QC!uj_Dg%TZKY_a;EA<}gMpIkI(%f1)u8TG`BXvoJ9DIa>6&D_9Q?RwNq(O9+ zAqaQ-_WTc^+gs}Rto?zEAjTinh+oe48wrl-@VsFwD773Zf`qx}`=I@N@6d{FWP|?E zpy7W1xmqU32D0yWmT&Rs1n&v$xoBpnVX4HbbXtXPYmthBstv_lnG?t_Leh<$47-9-d!pJlA2!CQph;Q0W|AhA@R}0?V&=@k1?c%l7pxE5qoLXqv|a( zl#z4gL^na;wBcAA#a*09I`bHQo3L0XIA^|899<{~@el#lCwPNF=+Ab+2D&Tsy{b0T z);&81yXn6_(Kq*!il|U0QThafeG{3@i+K$gIbKP+Nnu@kD+0u zVU6yo!U~VUx^jw258?}Chof)|pJ^6dwUVJiB~q`ESK?WUXr?abqCxMsc;z$y5GM8{ zyD{5zuXZlrCXmDc+nr=_21J+hLPBE34t#Ku6pTr-rZ&=pNJGfb|6DDrlp~WDV+|c{ zBf@_)vX&&}V~>aXvV@1!0_VRXru4rqm=k~8$=va84c2X| zXzzEQTD7=&fnzu6w(8%GI53MzTnob0{7jt%rPPtldpRX2a?Cwfm(c#lMWw{AaJqliCdE z|B(g7q%BM*=;L=;%rZDnK)lvt4Yc+SR+@&zn|?_?Y!bN~a<14(L!bSW!;3N>PHV2p zQsl0owL<}6C$EahMRckLK+d5F^>Ld$M87!SrMG{FAZC5Nw|n)Kf>zA zC844{au=0yHv=zj1!)=ryP9+p%Wo*192N94{tL3XclUh*rN6!TICh$j)3eeY1b_3q zTotV1=N7Ib@u5j6A$$%L#?ojkLOdl+yEx~rZE~ab`_1|2Jc5xgAU#Xm&tglKd%gKa zzJJ|f@e*N}07x9f&p?`s7WRFALdOK)B7zKr`zQC%-D?qj2dbzq5I)dTNoHc0vKZ`- zGf247XmusW`!3mj7#EZ&{v8FgY+Cy%Smg4;%Ff-Apv|w?#%- zf~m__*snYIe6Y68q}L+rFv+j?eopEJ_fU;|>Yoca3$pJ13g_Uy_NSFV3UijkITYbO z%2M^8sV>P383j?CTmv*vmym*iu%Vg}V+o?Sf;fBQpo{LLl91d#FY(B$SF0NpK9B$z*Ms0^}^3}5aR}hbOQwXE6H-nv!XkCiq zcN7RWBmBHdky~{MVLWM=HaoeXza!F*O59TTkaPJmRaM5%X0M64*XY@=TZ295ulQ6Q z7GnWZ7u{7VJEH2-B8`AHyI{sqo^#ns0=UY~w<5{=S61=RfhfSh$r1Id0O1Wi3fl0o zLc{)oLQGEmmINJxh&98sLzv)lS^27sB{S6iu+|A^F;?zei2JGJR~pEe<=`aD7b%7N z8Nl1)HynlP#`~}Bxe6f1y}wPs%r*_uE%#Gn?c!@<`Zg(CKIsvALqQWH=S3N<{Xo-@ z$*+~o#H2Hjj)n=L|Ce#R9-TDonV#<6To6r7N$1AD<{+0C*RxYF6(wP+DwhE{YFB*0>VOhJ9Am=BSMN%AktkUW?|R=2kLJmrGWmU^fZ{_#O_9s z0Er-C7&F;a?efuPERASuJ|JoBF#`~C!h!PBLUF^x50>*~bg_{Ob z~a1eleCKv@Gt~!j5O`F(=2rDZ*Fc=u< zEyoMUs?FrgDcj@wDq5%^j4Q|`=^4xR!cqwoEFSZ~*_4fUWzQ~RYy>8u9khWyrZxg{ zRd2lu4QyWofTpd%d(&kHZFX*Tc8;qKd*&oD+Y-8NC(ks-j#GJZ%U}vk^1XY6>Rn21 ohWrdUDzo?Q%vq*Rhad$AXZ(jMv{h#2H_=&?N{ucinMVNj7A)%Tn*aa+ diff --git a/tests/data/NewDatabase2.kdbx b/tests/data/NewDatabase2.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..4e77724c7e8cfdf22bcd951ccfcaa4ae7f49d460 GIT binary patch literal 8350 zcmV;PAYtDF*`k_f`%AS100IC45CAd3^5(yBLr}h01tDtuTK@wC0000000bZaaDhty z76ZqhAc+=}hbj_{u2zr|*-O)p zJK=Cm$rV(v6YH`D2mo*w00000000LN07gDpp)jUB&SEHlAHd?#0SF)fhm~cnv5wjd zH2eh3#wGe$uHY7_xM^Z0*|9Pq+0Q?02_OLP9V>cz47`{J+>O&l#G1e)YAr@66YG!EYI$g-^G)JfZDjL^ZIfeM&K4bL&Scw}pcU zFLqvLd4HhMd2BL_locX_j`sMDsfc`Lq(puMq~9@aQXOHhLkuciEosV_J_RbZ@4`6H z)+fS9eV%|{dCii{m6E{K#1P<-Fx)IO zS`B4v8QS0d9G1HtRLTl8z`4Xp)F2Ld$bM4 zt@DhT!Uwf>0^wc!P{ol>PBA96P?cTn<&>Xk&_OeeO+9x_xF*5)C->?op`MD@`pmc zLZ(HZv(7nqC46+&3{gz`{3?Ftp;>C=$_~g1#nza9p!abGUKtp-9|~rNTvC7-*&Yz4 zoCEPiGcdEMiUTG;PI{tkc%1?oBAuPd+d+^6L&A&ElIKq+5f3%aWW7kL9;}{UmNn)l zibI~h_yVs7nSe`2UaO}6%P5tOSH)CGujXck&_g@@yYZBzv)(sn^IE% zuK}Be>EwdSxSSrcK|FGfJ8-PWMB;v|f-3QV4Zsl4Rc7Dxwv9KrNOX+7&hz<3&ZM7k z&H4hP56~32*LpQl8aBYZ`V0!NRKUNs1rFH23=#%Z|m;fbm6V|RJ8n}cdGeCUBLc(9aU7*=b zw?^$yY}%-QYL|E>@{-VwbBs*!sDQCOo*`{jg2X@OVe*aCE^a<43N=t5JK_Hvs`I6E zjXyl1Sz~Iv+FKzFJgN(%_xFW7~Pb82xeydPO|wo#Jt#AMslUh>aB( zx=9cH+hi<80$oG1D@yd^G^^K)E6a5wzX+*wTu8RjvqTJp=0sIDFaAq5c`OcG#r4%O zN>J-21AdMz0@6y)B@odqN4u_Z+c0aUJcdwWRQ&E^r|?23l%H!1D(=dU-A=L!&xl_F zPN!j}BAq?Knr6je%P*L1;f)V&ilO873Atg>K$)gDtoY!l!1fS@0V>P}kbMj@02Tw* z>kSp~QBA;AbbdoUSa{V#F1&Hgd#@TWaOEQR+#p0($czySkk2rQIx9pyd1TZviC6v4 z_ejLCRUl>ovBC5{DB; znt;4BX^{b)3#4lj>5m{f?5PCkX%ahe3NM{Jrg(_J-&)3~3{pI38xg-cp{f8dJ2(*N z$6SJ&((tptMu$X1}dpLXRg~LY;F`oUkshQPe&RlUSJ(>g;_agU#S$KC! z?QA(kf04*8)s_ZbD6+y;JxFGU&Q9PVa6j3&j%n0Oh1L*h7*InhAO+2$8ZDnVsntEP z{kg@XOKS={pGjM!v!8@vV#?@T-MS`|P)jckkf{AW>et#Rvwy4ygD+BrJ=`WkxmlN2 z6!$ade`)5ihEt`iE;d;9uY20tgRGl%2X2@h}{$&&G_x>#Vni5WDUzEL#_8)?x9 zsJL?F29KzdhiKd&ITo2pPduSJ_w@_g3qV%4$_jHpaIalV)$ILLplGki=iV0TVdqw{ zkQeFshRRD2N3vTzrpa3+0Mo^pZ}x`A?-Y?T4b6?6i1A!p@Y#ti$9^<^Wq$rLs>f^O zNVwX=C1i{ndWivtLKpV!K@PJ>YKV5u&{MZ>UrAfE*K19voL!Jj&kQOwL~mZ#=1KFt zGbrzl-Xzuk)ICMhf|N_pu84MMCOzi+xrCtnu}K2{H0c>0>?2eqde=n>y?mTVo^1BOHsPW1!@a)P*v$Kuj47BA07zOipK8UMLn?DmG z1f%Q@5Cf7(XDtD?*@{Ou%FVo6jRO9L7P_DGUER@h?x&`qqyJRr&6)(W^Z8Fb5D*?f zCcsjqDMBVZR=wgN|U+_bVTuxY&SrV zJf0kFcreks%c>sjxR16g|9AJ@gs5BC2BqMjiV~+7BxYp}5_!Sz^*Am|9Hm_7zG+xm=FE9%f?L)v^8N~MMl>se}Bhz zj}C-qjXm(2iZwnH(ZJvXTpcn%%o|0($wg||6@h0rh!|)Up?Hj=lol;EwJxxxk@G5_ ze<;u%7R}d!HV=6yo|91VAGht}1q?3CgBFfG`0u|@Sb?AVY)&deWiq_i8^@%3(w>(T zgGawQ6Pt@AE^g)4{<9$tsXJip;ro^}<>Q?wbs;0G0ag3I54g3L=L0kS-pTf5u!x}f z>L_)T#9MVX;5<)+`p`lF>L(EfL02D*{@VJyEJ{hz@8uv@Isn%T&F|?UW~=}jjaLVw zfxK`B|6U<30sOY^ural3xy82y`Hu(i^GmE$kBW8lPVq+t)w;-u;q2W6&4njeenq1d z_$5wyWR`!oQ9=FD`GWNRMUb4ZX&g@X14+q~(%1z^%8=}@zYvsD@3+k_b@aiKikg(h z&%@6a=9a%H?MtqRdc>=g!jO)v@hr}WWBqVG*$9laafrw$`F=}y#le0Q?aL_nusxar zD*eV`*9j*gAKw=kGuVbb_uiCU&Ftcp%cR*I)SH=(mp$3dx)KvWpqNVS<=vJgsJG=l z;&$AhsS*6YS9!ZY3z(HBIWar=UOG>(T!)XON!GRDKZ}UfD*ODRd$x?6a-#D3F5Jx=^Yb4ymcu2pP!BtPAxJ z*u3H-b}Sz~Q}&O6OY5L#NFi2?Ve=bK+;qN4TolqJz)8Q$k`xYIg0qh!9NC(3tTUcE zi}g+zR;Ko>`r*)d1)s7xOp!NnQ0H>tp8wW2Dc3FhT18$vL~glGJUP$D@m&e42W?+gO4{V6Il_~`5Ih;o1m3c z)c!9GNP7=pEvY@;wmU<)lZ8gJ40?u!2lBr%+AQ!OABFN1P-+7sQ=efLyls5X6C=0X zl);s{&cI@!ok2_}^k=arJ3!I7Sy!Vw`69N)_b`eJ6*MVNL1S?=oqZ7nx zl>&2tj^@(T2#Ief*eGpHQBm(-YMZ=*b6%_nMKomML_ z1h*%TKP;ixC787qSR;Q*5waWE-dM8`Ybe!Uzv4*Jk%2?&kQ>*-M|(nZ-rKlUgL*;o zW+!Frl$v3}psr=uPv%G45QnL6_pq zO=j)p-?4s}KtSvml??PJaMIXdIExK)pn4$tzIB0~kPQ3APsMNnlvF%b32+L)U>-Z@ zYpOG&CHyc1A`IrK9+U974Ve=TTO3L|y#c-rh8vS=dbuIO@50Iu*@_8{@R*8|s6H7`x<1_U zzX}lsNL%lSo7)m}{wfyEF9;~BSJc)t4u&)%*Myz5mLQpO1UfMxG4B^Sb1s4uhnr={ zPJJZ41K*RIPVVSEB0NPTaqBwmzm3^Ac8jAaoa_?wiZldd-2w$SV*5u}hhVth#K`kl z#f0uwdBi|2SEyjuUZ<~-tlZoJ4$upq+v1BrGG)+4^4kbBw-!F!AIvAu?YJ0%;O!;v z_L&8N{0sepzG~Dv_h{V*o9bgCgF?yp`ruvwT*$6QfT+!gDRk}yYL(=z_>_nbC;Wo4 z=GzYchd$OKhLlde>FAQQcrRyf8NeT>-CtJY6_X#p4ca|xxcF{$J>;dUVbl1;TC!L2 zGsP1E9rvI?TgPD5jDRc)B0Y+ATGU>*fJQWug#Hzn#;n|n`S$ckbZVOKcZ74K{<3-G zOlWN&+z+!Q?JQml^@u1%l$Up?uuEtR-31)?tWN1`PTp*$I{di!g$0&8@sC$J5sgaCtd8s%rJk~h;OqSrX6WMSEwM})^DWV_5G2U-5jn{` z_KS&PFY(0-v%uHh0pUQ&f{7(`{vQtpeNR}kvTc1fhIEeX{P`O^(r-mD>wnV=!PV};oRx701WeeK4!(TOZzWyRG z&@j~OeGQO#Hy^5^YLhH`y>YNuwc!W{9AXLp0yA*y@qHG3bA6Ibx$$5<^@KY$yb{3D z^0idJ{7~EvEb`m@>AP6mjUXkD9e*oBFu;0fKlgBwrg}B55f!+9n&rHI36(x4jsv^v zNsTHW2~G#u)w)g1Z?R$*J!d z;1y&X*|4e-O=LXX35smlTc0ez#J#=(_SU& zB#}0WFTs8rsOP#7D!?<;YD4qx$v|5F^}f%*^~>rFS@t8O}?PP)i^shHg-(^*zDM*+yBdgMfA%BixXg^$?h=7C* zpaW=^0{x~v0+1ms9bfUwwvK_VVRfe@=kVEZ-`!)$c^}3yjQYtXjOHkKe%U;=yfJCi zj)gWt)uPDHnU{l#@hv1-1FQbwJt|$k_UO_J4+5T{zsj$sYYa+#iI#4?TjKTOwCv0l zBe=$OZ-xW=5`Lg}@@>@8wp9GHu`Uc9WnM1BYX=HD(p4izKvnpy{7#g|h0WTilxf(9+P)@w`$8hiPjq!Y`MH`~VUlajW06 z)evsHZ&H&5M`~xQBQ14H7TsEV=PvN7JxQxs#;TaL!vNccwCGr{$SDjyjBzWkX>OZstdThn>0rv4q>--`6dy`ZvX@$+q{ff* z?tH*74M^zuE=yJK|fm_o^S~HLKKN|om#sY4G0Us7^}CE4nCUhsKT4i_Ap3cD15xh+>I&b}+OSGJ)WdRW9w zPHOqv{CNpitVR%Co-zOl%8$<$<_{4N<9=4cD+j>kcKs9L^o&xIIK>q9_gF!IZ}IBy zLcDul887$EXjY5nL9eF)Y9J0E*rc|gs@NDn;K!~Fe+nfl=aW5B!Z1h4EsId_19WHy z)ISJ>0b(Q#Qq)2~m_nCx{;%q$da&!UNI6~|m5CvTLJGTS!U&1{&|&S>DeGsoC}u%3 zIu6LnPo+cg$|i4B9BSfg=XyDBZU+v-X#q5&)|;xY2uF1WnD`vlDBUVGV#XUd#mm|C$Id3{O+{=$f2hEW`YOIi!^rphlnjkE1P9SqCm~;DADituDnO*mW>qf zhio(VdqggTubm9Ykj=kBfL(_T8V8c73;G64 zqSD{jR8{QIgkL8p$0s#he*-4Ph5lYI<$fmN=LP63iXDXdhI@8g>@-C^2Vbrn6tWX! zAZw>|9=;Wfx}r||OQxVYTXPAP!O zjcfr0_<>F{I5K|sS^)X51%}+FCE%Y_Vf%4g1Cxb4^-E= z<>cdYrmB1~t9f%q5ZL`h^RM`Dvtnw3dy->5C!s$(vu7h$w)kQ3OCW{~_K(0FC?P)w z?$YJ40cvzg${i5dJEa$uoVM-@kY#87IGRLCqINMn_HkOGzbzHseOWn55}|xywH5N` z2W8$QC!uj_Dg%TZKY_a;EA<}gMpIkI(%f1)u8TG`BXvoJ9DIa>6&D_9Q?RwNq(O9+ zAqaQ-_WTc^+gs}Rto?zEAjTinh+oe48wrl-@VsFwD773Zf`qx}`=I@N@6d{FWP|?E zpy7W1xmqU32D0yWmT&Rs1n&v$xoBpnVX4HbbXtXPYmthBstv_lnG?t_Leh<$47-9-d!pJlA2!CQph;Q0W|AhA@R}0?V&=@k1?c%l7pxE5qoLXqv|a( zl#z4gL^na;wBcAA#a*09I`bHQo3L0XIA^|899<{~@el#lCwPNF=+Ab+2D&Tsy{b0T z);&81yXn6_(Kq*!il|U0QThafeG{3@i+K$gIbKP+Nnu@kD+0u zVU6yo!U~VUx^jw258?}Chof)|pJ^6dwUVJiB~q`ESK?WUXr?abqCxMsc;z$y5GM8{ zyD{5zuXZlrCXmDc+nr=_21J+hLPBE34t#Ku6pTr-rZ&=pNJGfb|6DDrlp~WDV+|c{ zBf@_)vX&&}V~>aXvV@1!0_VRXru4rqm=k~8$=va84c2X| zXzzEQTD7=&fnzu6w(8%GI53MzTnob0{7jt%rPPtldpRX2a?Cwfm(c#lMWw{AaJqliCdE z|B(g7q%BM*=;L=;%rZDnK)lvt4Yc+SR+@&zn|?_?Y!bN~a<14(L!bSW!;3N>PHV2p zQsl0owL<}6C$EahMRckLK+d5F^>Ld$M87!SrMG{FAZC5Nw|n)Kf>zA zC844{au=0yHv=zj1!)=ryP9+p%Wo*192N94{tL3XclUh*rN6!TICh$j)3eeY1b_3q zTotV1=N7Ib@u5j6A$$%L#?ojkLOdl+yEx~rZE~ab`_1|2Jc5xgAU#Xm&tglKd%gKa zzJJ|f@e*N}07x9f&p?`s7WRFALdOK)B7zKr`zQC%-D?qj2dbzq5I)dTNoHc0vKZ`- zGf247XmusW`!3mj7#EZ&{v8FgY+Cy%Smg4;%Ff-Apv|w?#%- zf~m__*snYIe6Y68q}L+rFv+j?eopEJ_fU;|>Yoca3$pJ13g_Uy_NSFV3UijkITYbO z%2M^8sV>P383j?CTmv*vmym*iu%Vg}V+o?Sf;fBQpo{LLl91d#FY(B$SF0NpK9B$z*Ms0^}^3}5aR}hbOQwXE6H-nv!XkCiq zcN7RWBmBHdky~{MVLWM=HaoeXza!F*O59TTkaPJmRaM5%X0M64*XY@=TZ295ulQ6Q z7GnWZ7u{7VJEH2-B8`AHyI{sqo^#ns0=UY~w<5{=S61=RfhfSh$r1Id0O1Wi3fl0o zLc{)oLQGEmmINJxh&98sLzv)lS^27sB{S6iu+|A^F;?zei2JGJR~pEe<=`aD7b%7N z8Nl1)HynlP#`~}Bxe6f1y}wPs%r*_uE%#Gn?c!@<`Zg(CKIsvALqQWH=S3N<{Xo-@ z$*+~o#H2Hjj)n=L|Ce#R9-TDonV#<6To6r7N$1AD<{+0C*RxYF6(wP+DwhE{YFB*0>VOhJ9Am=BSMN%AktkUW?|R=2kLJmrGWmU^fZ{_#O_9s z0Er-C7&F;a?efuPERASuJ|JoBF#`~C!h!PBLUF^x50>*~bg_{Ob z~a1eleCKv@Gt~!j5O`F(=2rDZ*Fc=u< zEyoMUs?FrgDcj@wDq5%^j4Q|`=^4xR!cqwoEFSZ~*_4fUWzQ~RYy>8u9khWyrZxg{ zRd2lu4QyWofTpd%d(&kHZFX*Tc8;qKd*&oD+Y-8NC(ks-j#GJZ%U}vk^1XY6>Rn21 ohWrdUDzo?Q%vq*Rhad$AXZ(jMv{h#2H_=&?N{ucinMVNj7A)%Tn*aa+ literal 0 HcmV?d00001 diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 450f09474..8613d184e 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -405,10 +405,9 @@ void TestGui::testTabs() void TestGui::testEditEntry() { auto* toolBar = m_mainWindow->findChild("toolBar"); - int editCount = 0; + auto* entryView = m_dbWidget->findChild("entryView"); // Select the first entry in the database - auto* entryView = m_dbWidget->findChild("entryView"); QModelIndex entryItem = entryView->model()->index(0, 1); Entry* entry = entryView->entryFromIndex(entryItem); clickIndex(entryItem, entryView, Qt::LeftButton); @@ -420,6 +419,9 @@ void TestGui::testEditEntry() QVERIFY(entryEditWidget->isVisible()); QVERIFY(entryEditWidget->isEnabled()); + // Record current history count + int editCount = entry->historyItems().size(); + // Edit the first entry ("Sample Entry") QTest::mouseClick(entryEditWidget, Qt::LeftButton); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); @@ -748,6 +750,7 @@ void TestGui::testTotp() QApplication::processEvents(); auto* seedEdit = setupTotpDialog->findChild("seedEdit"); + seedEdit->setText(""); QString exampleSeed = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq"; QTest::keyClicks(seedEdit, exampleSeed); From f06742cf41151417154b54f41d43d0db45c4812c Mon Sep 17 00:00:00 2001 From: louib Date: Fri, 9 Nov 2018 21:59:16 -0500 Subject: [PATCH 51/84] CLI Merge: Only save database file when modified. (#2466) * Merge: detect if database was changed. * Adding unit test. * Only saving on change. --- src/cli/Merge.cpp | 16 ++++++++++------ tests/TestCli.cpp | 9 +++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index e81b7aa99..65a03f38b 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -84,14 +84,18 @@ int Merge::execute(const QStringList& arguments) } Merger merger(db2.data(), db1.data()); - merger.merge(); + bool databaseChanged = merger.merge(); - QString errorMessage = db1->saveToFile(args.at(0)); - if (!errorMessage.isEmpty()) { - err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl; - return EXIT_FAILURE; + if (databaseChanged) { + QString errorMessage = db1->saveToFile(args.at(0)); + if (!errorMessage.isEmpty()) { + err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl; + return EXIT_FAILURE; + } + out << "Successfully merged the database files." << endl; + } else { + out << "Database was not modified by merge operation." << endl; } - out << "Successfully merged the database files." << endl; return EXIT_SUCCESS; } diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index b1c3a82e8..36ca479d5 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -698,6 +698,15 @@ void TestCli::testMerge() QVERIFY(entry1); QCOMPARE(entry1->title(), QString("Some Website")); QCOMPARE(entry1->password(), QString("secretsecretsecret")); + + // making sure that the message is different if the database was not + // modified by the merge operation. + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + mergeCmd.execute({"merge", "-s", sourceFile.fileName(), sourceFile.fileName()}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Database was not modified by merge operation.\n")); } void TestCli::testRemove() From ee9c71e11e96073c6dc77e543ee51e62f97e07b1 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Fri, 16 Nov 2018 10:00:59 -0500 Subject: [PATCH 52/84] Fix multiple issues with entries and keyboard shortcuts (#2431) * Cleanup entry change notification with entryview focus in/out * Change Open URL shortcut to CTRL+SHIFT+U to conform with an "action" including SHIFT * Change Copy URL shortcut to CTRL+U to conform with "copy" without SHIFT * Entry specific toolbar and menu items are disabled unless the entry row has focus (prevents unintended actions) * Reword security setting for password visibility in entry edit view * Add shortcut to hide/unhide usernames (CTRL+SHIFT+B) * Organize entry menu * Fix #1588 - show keyboard shortcuts in context menu * Fix #2403 - Change auto-type shortcut to CTRL + SHIFT + V * Fix #2096 - Add (CTRL+F) to search bar background * Fix #2031 & Fix #2266 - add shortcut to hide/unhide passwords (CTRL+SHIFT+C) * Fix #2166 - Add reveal password button to entry preview --- src/gui/ApplicationSettingsWidgetSecurity.ui | 2 +- src/gui/DatabaseWidget.cpp | 102 +++----- src/gui/DatabaseWidget.h | 4 +- src/gui/EntryPreviewWidget.cpp | 36 ++- src/gui/EntryPreviewWidget.h | 1 + src/gui/EntryPreviewWidget.ui | 259 ++++++++++++------- src/gui/MainWindow.cpp | 65 +++-- src/gui/MainWindow.h | 2 + src/gui/MainWindow.ui | 72 ++++-- src/gui/SearchWidget.cpp | 4 +- src/gui/SearchWidget.ui | 2 +- src/gui/entry/EntryModel.cpp | 22 +- src/gui/entry/EntryModel.h | 12 +- src/gui/entry/EntryView.cpp | 38 ++- src/gui/entry/EntryView.h | 4 +- tests/gui/TestGui.cpp | 27 +- 16 files changed, 396 insertions(+), 256 deletions(-) diff --git a/src/gui/ApplicationSettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui index 4052e5d63..344c2b81c 100644 --- a/src/gui/ApplicationSettingsWidgetSecurity.ui +++ b/src/gui/ApplicationSettingsWidgetSecurity.ui @@ -175,7 +175,7 @@ - Show passwords in cleartext by default + Don't hide passwords when editing them diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 388919952..3faf43a65 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -114,7 +114,6 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_previewView = new EntryPreviewWidget(this); m_previewView->hide(); - 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))); @@ -183,7 +182,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_entryView, SIGNAL(entryActivated(Entry*,EntryModel::ModelColumn)), SLOT(entryActivationSignalReceived(Entry*,EntryModel::ModelColumn))); - connect(m_entryView, SIGNAL(entrySelectionChanged()), SIGNAL(entrySelectionChanged())); + connect(m_entryView, SIGNAL(entrySelectionChanged()), SLOT(emitEntrySelectionChanged())); 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())); @@ -202,9 +201,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_groupView, SIGNAL(groupPressed(Group*)), SLOT(emitPressedGroup(Group*))); connect(m_groupView, SIGNAL(groupChanged(Group*)), SLOT(emitPressedGroup(Group*))); - connect(m_entryView, SIGNAL(entryPressed(Entry*)), SLOT(emitPressedEntry(Entry*))); - connect(m_entryView, SIGNAL(entrySelectionChanged()), SLOT(emitPressedEntry())); - connect(m_editEntryWidget, SIGNAL(editFinished(bool)), SLOT(emitPressedEntry())); + connect(m_editEntryWidget, SIGNAL(editFinished(bool)), SLOT(emitEntrySelectionChanged())); m_databaseModified = false; @@ -506,80 +503,60 @@ void DatabaseWidget::setFocus() void DatabaseWidget::copyTitle() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->title())); } - - setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->title())); } void DatabaseWidget::copyUsername() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->username())); } - - setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->username())); } void DatabaseWidget::copyPassword() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->password())); } - - setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->password())); } void DatabaseWidget::copyURL() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->url())); } - - setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->url())); } void DatabaseWidget::copyNotes() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->notes())); } - - setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->notes())); } void DatabaseWidget::copyAttribute(QAction* action) { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + setClipboardTextAndMinimize( + currentEntry->resolveMultiplePlaceholders( + currentEntry->attributes()->value(action->data().toString()))); } - - setClipboardTextAndMinimize( - currentEntry->resolveMultiplePlaceholders(currentEntry->attributes()->value(action->data().toString()))); } void DatabaseWidget::showTotpKeyQrCode() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + auto totpDisplayDialog = new TotpExportSettingsDialog(this, currentEntry); + totpDisplayDialog->open(); } - - auto totpDisplayDialog = new TotpExportSettingsDialog(this, currentEntry); - totpDisplayDialog->open(); } void DatabaseWidget::setClipboardTextAndMinimize(const QString& text) @@ -593,27 +570,22 @@ void DatabaseWidget::setClipboardTextAndMinimize(const QString& text) void DatabaseWidget::performAutoType() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + autoType()->performAutoType(currentEntry, window()); } - - autoType()->performAutoType(currentEntry, window()); } void DatabaseWidget::openUrl() { Entry* currentEntry = m_entryView->currentEntry(); - Q_ASSERT(currentEntry); - if (!currentEntry) { - return; + if (currentEntry) { + openUrlForEntry(currentEntry); } - - openUrlForEntry(currentEntry); } void DatabaseWidget::openUrlForEntry(Entry* entry) { + Q_ASSERT(entry); QString cmdString = entry->resolveMultiplePlaceholders(entry->url()); if (cmdString.startsWith("cmd://")) { // check if decision to execute command was stored @@ -1076,10 +1048,11 @@ void DatabaseWidget::setSearchLimitGroup(bool state) void DatabaseWidget::onGroupChanged(Group* group) { // Intercept group changes if in search mode - if (isInSearchMode()) + if (isInSearchMode()) { search(m_lastSearchText); - else + } else { m_entryView->setGroup(group); + } } QString DatabaseWidget::getCurrentSearch() @@ -1114,20 +1087,14 @@ void DatabaseWidget::emitEntryContextMenuRequested(const QPoint& pos) emit entryContextMenuRequested(m_entryView->viewport()->mapToGlobal(pos)); } -void DatabaseWidget::emitPressedEntry() +void DatabaseWidget::emitEntrySelectionChanged() { Entry* currentEntry = m_entryView->currentEntry(); - emitPressedEntry(currentEntry); -} - -void DatabaseWidget::emitPressedEntry(Entry* currentEntry) -{ - if (!currentEntry) { - // if no entry is pressed, leave in details the last entry - return; + if (currentEntry) { + m_previewView->setEntry(currentEntry); } - emit pressedEntry(currentEntry); + emit entrySelectionChanged(); } void DatabaseWidget::emitPressedGroup(Group* currentGroup) @@ -1354,7 +1321,12 @@ void DatabaseWidget::restoreGroupEntryFocus(const QUuid& groupUuid, const QUuid& bool DatabaseWidget::isGroupSelected() const { - return m_groupView->currentGroup() != nullptr; + return m_groupView->currentGroup(); +} + +bool DatabaseWidget::currentEntryHasFocus() +{ + return m_entryView->numberOfSelectedEntries() > 0 && m_entryView->hasFocus(); } bool DatabaseWidget::currentEntryHasTitle() diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 828aace51..a5cf538d7 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -99,6 +99,7 @@ public: QByteArray entryViewState() const; bool setEntryViewState(const QByteArray& state) const; void clearAllWidgets(); + bool currentEntryHasFocus(); bool currentEntryHasTitle(); bool currentEntryHasUsername(); bool currentEntryHasPassword(); @@ -198,9 +199,8 @@ private slots: void switchToGroupEdit(Group* entry, bool create); void emitGroupContextMenuRequested(const QPoint& pos); void emitEntryContextMenuRequested(const QPoint& pos); - void emitPressedEntry(); - void emitPressedEntry(Entry* currentEntry); void emitPressedGroup(Group* currentGroup); + void emitEntrySelectionChanged(); void openDatabase(bool accepted); void mergeDatabase(bool accepted); void unlockDatabase(bool accepted); diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index af6ffac33..b9fa26383 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -47,12 +47,14 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent) // Entry m_ui->entryTotpButton->setIcon(filePath()->icon("actions", "chronometer")); m_ui->entryCloseButton->setIcon(filePath()->icon("actions", "dialog-close")); + m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); m_ui->entryAttachmentsWidget->setReadOnly(true); m_ui->entryAttachmentsWidget->setButtonsVisible(false); connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool))); connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide())); + connect(m_ui->togglePasswordButton, SIGNAL(clicked(bool)), SLOT(setPasswordVisible(bool))); connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection); connect(&m_totpTimer, SIGNAL(timeout()), this, SLOT(updateTotpLabel())); @@ -152,18 +154,40 @@ void EntryPreviewWidget::updateEntryTotp() } } +void EntryPreviewWidget::setPasswordVisible(bool state) +{ + const QString password = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()); + auto flags = m_ui->entryPasswordLabel->textInteractionFlags(); + if (state) { + m_ui->entryPasswordLabel->setRawText(password); + m_ui->entryPasswordLabel->setToolTip(password); + m_ui->entryPasswordLabel->setTextInteractionFlags(flags | Qt::TextSelectableByMouse); + } else { + m_ui->entryPasswordLabel->setTextInteractionFlags(flags & ~Qt::TextSelectableByMouse); + m_ui->entryPasswordLabel->setToolTip({}); + if (password.isEmpty() && config()->get("security/passwordemptynodots").toBool()) { + m_ui->entryPasswordLabel->setRawText(""); + } else { + m_ui->entryPasswordLabel->setRawText(QString("\u25cf").repeated(6)); + } + } +} + void EntryPreviewWidget::updateEntryGeneralTab() { Q_ASSERT(m_currentEntry); m_ui->entryUsernameLabel->setText(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->username())); - if (!config()->get("security/HidePasswordPreviewPanel").toBool()) { - const QString password = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()); - m_ui->entryPasswordLabel->setRawText(password); - m_ui->entryPasswordLabel->setToolTip(password); + if (config()->get("security/HidePasswordPreviewPanel").toBool()) { + // Hide password + setPasswordVisible(false); + // Show the password toggle button if there are dots in the label + m_ui->togglePasswordButton->setVisible(!m_ui->entryPasswordLabel->rawText().isEmpty()); + m_ui->togglePasswordButton->setChecked(false); } else { - m_ui->entryPasswordLabel->setRawText(QString("\u25cf").repeated(6)); - m_ui->entryPasswordLabel->setToolTip({}); + // Show password + setPasswordVisible(true); + m_ui->togglePasswordButton->setVisible(false); } m_ui->entryUrlLabel->setRawText(m_currentEntry->displayUrl()); diff --git a/src/gui/EntryPreviewWidget.h b/src/gui/EntryPreviewWidget.h index 88c729626..9e687c6d8 100644 --- a/src/gui/EntryPreviewWidget.h +++ b/src/gui/EntryPreviewWidget.h @@ -51,6 +51,7 @@ private slots: void updateEntryAttributesTab(); void updateEntryAttachmentsTab(); void updateEntryAutotypeTab(); + void setPasswordVisible(bool state); void updateGroupHeaderLine(); void updateGroupGeneralTab(); diff --git a/src/gui/EntryPreviewWidget.ui b/src/gui/EntryPreviewWidget.ui index 1fde8aa3c..7e84d4120 100644 --- a/src/gui/EntryPreviewWidget.ui +++ b/src/gui/EntryPreviewWidget.ui @@ -6,8 +6,8 @@ 0 0 - 280 - 267 + 573 + 330 @@ -168,7 +168,7 @@ - + 0 @@ -181,65 +181,21 @@ 0 - - - - - 0 - 0 - + + + + Qt::Horizontal - + + QSizePolicy::Fixed + + - 100 - 0 + 20 + 20 - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - PointingHandCursor - - - - - - Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - + @@ -266,6 +222,50 @@ + + + + + 0 + 0 + + + + + 100 + 0 + + + + username + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Password + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -288,7 +288,45 @@ - + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + PointingHandCursor + + + https://example.com + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + @@ -296,6 +334,9 @@ 0 + + expired + @@ -320,8 +361,47 @@ - - + + + + 6 + + + 4 + + + + + + + + true + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + password + + + + + + + Qt::Horizontal @@ -336,37 +416,34 @@ - - - - - 0 - 0 - - - - - 75 - true - - - - Password - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - + + - Qt::Vertical + Qt::Horizontal + + + QSizePolicy::Fixed - 0 - 0 + 20 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 @@ -688,6 +765,12 @@ Qt::Vertical + + + 0 + 0 + + diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index ac26dd879..d9f9a0557 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -183,7 +183,7 @@ MainWindow::MainWindow() m_ui->actionDatabaseNew->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_N); setShortcut(m_ui->actionDatabaseOpen, QKeySequence::Open, Qt::CTRL + Qt::Key_O); setShortcut(m_ui->actionDatabaseSave, QKeySequence::Save, Qt::CTRL + Qt::Key_S); - setShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs); + setShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs, Qt::CTRL + Qt::SHIFT + Qt::Key_S); setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W); m_ui->actionLockDatabases->setShortcut(Qt::CTRL + Qt::Key_L); setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q); @@ -196,16 +196,37 @@ MainWindow::MainWindow() m_ui->actionEntryCopyTotp->setShortcut(Qt::CTRL + Qt::Key_T); m_ui->actionEntryCopyUsername->setShortcut(Qt::CTRL + Qt::Key_B); m_ui->actionEntryCopyPassword->setShortcut(Qt::CTRL + Qt::Key_C); - setShortcut(m_ui->actionEntryAutoType, QKeySequence::Paste, Qt::CTRL + Qt::Key_V); - m_ui->actionEntryOpenUrl->setShortcut(Qt::CTRL + Qt::Key_U); - m_ui->actionEntryCopyURL->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_U); + m_ui->actionEntryAutoType->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V); + m_ui->actionEntryOpenUrl->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_U); + m_ui->actionEntryCopyURL->setShortcut(Qt::CTRL + Qt::Key_U); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + // Qt 5.10 introduced a new "feature" to hide shortcuts in context menus + // Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them + m_ui->actionEntryNew->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryEdit->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryDelete->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryClone->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryTotp->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryCopyTotp->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryCopyUsername->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryCopyPassword->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryAutoType->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryOpenUrl->setShortcutVisibleInContextMenu(true); + m_ui->actionEntryCopyURL->setShortcutVisibleInContextMenu(true); +#endif + + // Control window state new QShortcut(Qt::CTRL + Qt::Key_M, this, SLOT(showMinimized())); new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_M, this, SLOT(hideWindow())); + // Control database tabs new QShortcut(Qt::CTRL + Qt::Key_Tab, this, SLOT(selectNextDatabaseTab())); new QShortcut(Qt::CTRL + Qt::Key_PageUp, this, SLOT(selectNextDatabaseTab())); new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab, this, SLOT(selectPreviousDatabaseTab())); new QShortcut(Qt::CTRL + Qt::Key_PageDown, this, SLOT(selectPreviousDatabaseTab())); + // Toggle password and username visibility in entry view + new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C, this, SLOT(togglePasswordsHidden())); + new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_B, this, SLOT(toggleUsernamesHidden())); m_ui->actionDatabaseNew->setIcon(filePath()->icon("actions", "document-new")); m_ui->actionDatabaseOpen->setIcon(filePath()->icon("actions", "document-open")); @@ -457,8 +478,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) switch (mode) { case DatabaseWidget::ViewMode: { // bool inSearch = dbWidget->isInSearchMode(); - bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1; - bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0; + bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1 && dbWidget->currentEntryHasFocus(); + bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0 && dbWidget->currentEntryHasFocus(); bool groupSelected = dbWidget->isGroupSelected(); bool recycleBinSelected = dbWidget->isRecycleBinSelected(); @@ -472,7 +493,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionEntryCopyURL->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl()); m_ui->actionEntryCopyNotes->setEnabled(singleEntrySelected && dbWidget->currentEntryHasNotes()); m_ui->menuEntryCopyAttribute->setEnabled(singleEntrySelected); - m_ui->menuEntryTotp->setEnabled(true); + m_ui->menuEntryTotp->setEnabled(singleEntrySelected); m_ui->actionEntryAutoType->setEnabled(singleEntrySelected); m_ui->actionEntryOpenUrl->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl()); m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp()); @@ -507,13 +528,6 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) for (QAction* action : groupActions) { action->setEnabled(false); } - m_ui->actionEntryCopyTitle->setEnabled(false); - m_ui->actionEntryCopyUsername->setEnabled(false); - m_ui->actionEntryCopyPassword->setEnabled(false); - m_ui->actionEntryCopyURL->setEnabled(false); - m_ui->actionEntryCopyNotes->setEnabled(false); - m_ui->menuEntryCopyAttribute->setEnabled(false); - m_ui->menuEntryTotp->setEnabled(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); @@ -539,13 +553,6 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) for (QAction* action : groupActions) { action->setEnabled(false); } - m_ui->actionEntryCopyTitle->setEnabled(false); - m_ui->actionEntryCopyUsername->setEnabled(false); - m_ui->actionEntryCopyPassword->setEnabled(false); - m_ui->actionEntryCopyURL->setEnabled(false); - m_ui->actionEntryCopyNotes->setEnabled(false); - m_ui->menuEntryCopyAttribute->setEnabled(false); - m_ui->menuEntryTotp->setEnabled(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); @@ -731,6 +738,22 @@ void MainWindow::databaseTabChanged(int tabIndex) m_actionMultiplexer.setCurrentObject(m_ui->tabWidget->currentDatabaseWidget()); } +void MainWindow::togglePasswordsHidden() +{ + auto dbWidget = m_ui->tabWidget->currentDatabaseWidget(); + if (dbWidget) { + dbWidget->setPasswordsHidden(!dbWidget->isPasswordsHidden()); + } +} + +void MainWindow::toggleUsernamesHidden() +{ + auto dbWidget = m_ui->tabWidget->currentDatabaseWidget(); + if (dbWidget) { + dbWidget->setUsernamesHidden(!dbWidget->isUsernamesHidden()); + } +} + void MainWindow::closeEvent(QCloseEvent* event) { // ignore double close events (happens on macOS when closing from the dock) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 174e47564..caf2a0c58 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -112,6 +112,8 @@ private slots: void showErrorMessage(const QString& message); void selectNextDatabaseTab(); void selectPreviousDatabaseTab(); + void togglePasswordsHidden(); + void toggleUsernamesHidden(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 2c74c706e..c719cb673 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -180,7 +180,7 @@ 0 0 800 - 30 + 21 @@ -197,7 +197,7 @@ - Import + &Import @@ -234,8 +234,11 @@ false + + + - Copy att&ribute to clipboard + Copy att&ribute... @@ -247,23 +250,25 @@ false - Time-based one-time password + TOTP... - - - - - - + - + + + + + + + + @@ -366,12 +371,18 @@ - &New database + &New database... + + + Create a new database - Merge from KeePassX database + &Merge from database... + + + Merge from another KDBX database @@ -379,7 +390,10 @@ false - &Add new entry + &New entry + + + Add a new entry @@ -387,7 +401,10 @@ false - &View/Edit entry + &Edit entry + + + View or edit entry @@ -403,7 +420,10 @@ false - &Add new group + &New group + + + Add a new group @@ -435,7 +455,7 @@ false - Change &master key... + Change master &key... @@ -443,7 +463,7 @@ false - &Database settings + &Database settings... Database settings @@ -476,7 +496,7 @@ false - Cop&y password + Copy &password Copy password to clipboard @@ -506,7 +526,7 @@ false - &Perform Auto-Type + Perform &Auto-Type @@ -514,7 +534,7 @@ false - &Open URL + Open &URL @@ -568,12 +588,18 @@ - Import KeePass 1 database... + KeePass 1 database... + + + Import a KeePass 1 database - Import CSV file... + CSV file... + + + Import a CSV file diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index c84ba8582..40c63036c 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -41,9 +41,11 @@ SearchWidget::SearchWidget(QWidget* parent) connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(startSearch())); connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear())); - new QShortcut(Qt::CTRL + Qt::Key_F, this, SLOT(searchFocus()), nullptr, Qt::ApplicationShortcut); + new QShortcut(QKeySequence::Find, this, SLOT(searchFocus()), nullptr, Qt::ApplicationShortcut); new QShortcut(Qt::Key_Escape, m_ui->searchEdit, SLOT(clear()), nullptr, Qt::ApplicationShortcut); + m_ui->searchEdit->setPlaceholderText(tr("Search (%1)...", "Search placeholder text, %1 is the keyboard shortcut") + .arg(QKeySequence(QKeySequence::Find).toString(QKeySequence::NativeText))); m_ui->searchEdit->installEventFilter(this); QMenu* searchMenu = new QMenu(); diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui index 98395b68a..438a242c7 100644 --- a/src/gui/SearchWidget.ui +++ b/src/gui/SearchWidget.ui @@ -48,7 +48,7 @@ padding:3px - Search... + false diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 8cbf4bfe4..2edc49b25 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -468,9 +468,10 @@ bool EntryModel::isUsernamesHidden() const /** * Set state of 'Hide Usernames' setting and signal change */ -void EntryModel::setUsernamesHidden(const bool hide) +void EntryModel::setUsernamesHidden(bool hide) { m_hideUsernames = hide; + emit dataChanged(index(0, 0), index(rowCount()-1, columnCount() - 1)); emit usernamesHiddenChanged(); } @@ -485,28 +486,13 @@ bool EntryModel::isPasswordsHidden() const /** * Set state of 'Hide Passwords' setting and signal change */ -void EntryModel::setPasswordsHidden(const bool hide) +void EntryModel::setPasswordsHidden(bool hide) { m_hidePasswords = hide; + emit dataChanged(index(0, 0), index(rowCount()-1, columnCount() - 1)); emit passwordsHiddenChanged(); } -/** - * Toggle state of 'Hide Usernames' setting - */ -void EntryModel::toggleUsernamesHidden(const bool hide) -{ - setUsernamesHidden(hide); -} - -/** - * Toggle state of 'Hide Passwords' setting - */ -void EntryModel::togglePasswordsHidden(const bool hide) -{ - setPasswordsHidden(hide); -} - void EntryModel::setPaperClipPixmap(const QPixmap& paperclip) { m_paperClipPixmap = paperclip; diff --git a/src/gui/entry/EntryModel.h b/src/gui/entry/EntryModel.h index e8c90f7e4..3e9f2824a 100644 --- a/src/gui/entry/EntryModel.h +++ b/src/gui/entry/EntryModel.h @@ -61,13 +61,9 @@ public: QMimeData* mimeData(const QModelIndexList& indexes) const override; void setEntryList(const QList& entries); - - bool isUsernamesHidden() const; - void setUsernamesHidden(const bool hide); - bool isPasswordsHidden() const; - void setPasswordsHidden(const bool hide); - void setPaperClipPixmap(const QPixmap& paperclip); + bool isUsernamesHidden() const; + bool isPasswordsHidden() const; signals: void switchedToListMode(); @@ -77,8 +73,8 @@ signals: public slots: void setGroup(Group* group); - void toggleUsernamesHidden(const bool hide); - void togglePasswordsHidden(const bool hide); + void setUsernamesHidden(bool hide); + void setPasswordsHidden(bool hide); private slots: void entryAboutToAdd(Entry* entry); diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index d63260c39..ecdccd7bb 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -50,21 +50,21 @@ EntryView::EntryView(QWidget* parent) setDefaultDropAction(Qt::MoveAction); connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex))); - connect( - selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); + connect(selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); + connect(m_model, SIGNAL(switchedToListMode()), SLOT(switchToListMode())); connect(m_model, SIGNAL(switchedToSearchMode()), SLOT(switchToSearchMode())); connect(m_model, SIGNAL(usernamesHiddenChanged()), SIGNAL(viewStateChanged())); connect(m_model, SIGNAL(passwordsHiddenChanged()), SIGNAL(viewStateChanged())); - connect(this, SIGNAL(clicked(QModelIndex)), SLOT(emitEntryPressed(QModelIndex))); m_headerMenu = new QMenu(this); m_headerMenu->setTitle(tr("Customize View")); m_headerMenu->addSection(tr("Customize View")); - m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(toggleUsernamesHidden(bool))); + m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(setUsernamesHidden(bool))); m_hideUsernamesAction->setCheckable(true); - m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(togglePasswordsHidden(bool))); + m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(setPasswordsHidden(bool))); m_hidePasswordsAction->setCheckable(true); m_headerMenu->addSeparator(); @@ -146,6 +146,18 @@ void EntryView::keyPressEvent(QKeyEvent* event) QTreeView::keyPressEvent(event); } +void EntryView::focusInEvent(QFocusEvent* event) +{ + emit entrySelectionChanged(); + QTreeView::focusInEvent(event); +} + +void EntryView::focusOutEvent(QFocusEvent* event) +{ + emit entrySelectionChanged(); + QTreeView::focusOutEvent(event); +} + void EntryView::setGroup(Group* group) { m_model->setGroup(group); @@ -176,15 +188,9 @@ bool EntryView::inSearchMode() void EntryView::emitEntryActivated(const QModelIndex& index) { Entry* entry = entryFromIndex(index); - emit entryActivated(entry, static_cast(m_sortModel->mapToSource(index).column())); } -void EntryView::emitEntryPressed(const QModelIndex& index) -{ - emit entryPressed(entryFromIndex(index)); -} - void EntryView::setModel(QAbstractItemModel* model) { Q_UNUSED(model); @@ -268,6 +274,11 @@ bool EntryView::isUsernamesHidden() const */ void EntryView::setUsernamesHidden(const bool hide) { + bool block = m_hideUsernamesAction->signalsBlocked(); + m_hideUsernamesAction->blockSignals(true); + m_hideUsernamesAction->setChecked(hide); + m_hideUsernamesAction->blockSignals(block); + m_model->setUsernamesHidden(hide); } @@ -285,6 +296,11 @@ bool EntryView::isPasswordsHidden() const */ void EntryView::setPasswordsHidden(const bool hide) { + bool block = m_hidePasswordsAction->signalsBlocked(); + m_hidePasswordsAction->blockSignals(true); + m_hidePasswordsAction->setChecked(hide); + m_hidePasswordsAction->blockSignals(block); + m_model->setPasswordsHidden(hide); } diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 3d166c482..2030f0ec7 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -55,16 +55,16 @@ public slots: signals: void entryActivated(Entry* entry, EntryModel::ModelColumn column); - void entryPressed(Entry* entry); void entrySelectionChanged(); void viewStateChanged(); protected: void keyPressEvent(QKeyEvent* event) override; + void focusInEvent(QFocusEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; private slots: void emitEntryActivated(const QModelIndex& index); - void emitEntryPressed(const QModelIndex& index); void switchToListMode(); void switchToSearchMode(); void showHeaderMenu(const QPoint& position); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 8613d184e..9b04dd18e 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -736,11 +736,9 @@ void TestGui::testTotp() auto* entryView = m_dbWidget->findChild("entryView"); QCOMPARE(entryView->model()->rowCount(), 1); - QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode); QModelIndex item = entryView->model()->index(0, 1); Entry* entry = entryView->entryFromIndex(item); - clickIndex(item, entryView, Qt::LeftButton); triggerAction("actionEntrySetupTotp"); @@ -749,21 +747,28 @@ void TestGui::testTotp() QApplication::processEvents(); + QString exampleSeed = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq"; auto* seedEdit = setupTotpDialog->findChild("seedEdit"); seedEdit->setText(""); - - QString exampleSeed = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq"; QTest::keyClicks(seedEdit, exampleSeed); auto* setupTotpButtonBox = setupTotpDialog->findChild("buttonBox"); QTest::mouseClick(setupTotpButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); + QTRY_VERIFY(!setupTotpDialog->isVisible()); + + // Make sure the entryView is selected and active + entryView->activateWindow(); + QApplication::processEvents(); + QTRY_VERIFY(entryView->hasFocus()); auto* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction); + QVERIFY(entryEditWidget->isVisible()); + QVERIFY(entryEditWidget->isEnabled()); QTest::mouseClick(entryEditWidget, Qt::LeftButton); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); - auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); editEntryWidget->setCurrentPage(1); auto* attrTextEdit = editEntryWidget->findChild("attributesEdit"); QTest::mouseClick(editEntryWidget->findChild("revealAttributeButton"), Qt::LeftButton); @@ -836,15 +841,19 @@ void TestGui::testSearch() // Ensure Down focuses on entry view when search text is selected QTest::keyClick(searchTextEdit, Qt::Key_Down); QTRY_VERIFY(entryView->hasFocus()); - // Refocus back to search edit - QTest::mouseClick(searchTextEdit, Qt::LeftButton); - QTRY_VERIFY(searchTextEdit->hasFocus()); - // Test password copy + // Test that password copies (entry has focus) QClipboard* clipboard = QApplication::clipboard(); QTest::keyClick(searchTextEdit, Qt::Key_C, Qt::ControlModifier); QModelIndex searchedItem = entryView->model()->index(0, 1); Entry* searchedEntry = entryView->entryFromIndex(searchedItem); QTRY_COMPARE(searchedEntry->password(), clipboard->text()); + // Refocus back to search edit + QTest::mouseClick(searchTextEdit, Qt::LeftButton); + QTRY_VERIFY(searchTextEdit->hasFocus()); + // Test that password does not copy + searchTextEdit->selectAll(); + QTest::keyClick(searchTextEdit, Qt::Key_C, Qt::ControlModifier); + QTRY_COMPARE(clipboard->text(), QString("someTHING")); // Test case sensitive search searchWidget->setCaseSensitive(true); From a5e7da67d89b5433d6880824ef7605b9f5d17151 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sat, 17 Nov 2018 08:28:23 -0500 Subject: [PATCH 53/84] Correct CI badges in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc63beda2..f1fd8f20f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # KeePassXC -[![TeamCity Build Status](https://ci.keepassxc.org/app/rest/builds/buildType:\(id:KeepassXC_TeamCityCi\)/statusIcon?guest=1)](https://ci.keepassxc.org/viewType.html?buildTypeId=KeepassXC_TeamCityCi&guest=1) [![Build status](https://ci.appveyor.com/api/projects/status/qmcar8rnqjh4oxof?svg=true)](https://ci.appveyor.com/project/droidmonkey/keepassxc) [![codecov](https://codecov.io/gh/keepassxreboot/keepassxc/branch/develop/graph/badge.svg)](https://codecov.io/gh/keepassxreboot/keepassxc) +[![TeamCity Build Status](https://ci.keepassxc.org/app/rest/builds/buildType:\(project:KeepassXC\)/statusIcon)](https://ci.keepassxc.org/?guest=1) [![codecov](https://codecov.io/gh/keepassxreboot/keepassxc/branch/develop/graph/badge.svg)](https://codecov.io/gh/keepassxreboot/keepassxc) ## About KeePassXC From d8d758f0e17d616840d2321476733b497a5c1b3a Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Mon, 19 Mar 2018 23:16:22 -0400 Subject: [PATCH 54/84] Streamlined searcher code * Remove searching of group title and notes * End search when selecting a new group * Correct entry searcher tests to align with new code --- src/core/EntrySearcher.cpp | 68 +++++++++++++------------------------ src/core/EntrySearcher.h | 6 ++-- src/gui/DatabaseWidget.cpp | 7 ++-- src/gui/DatabaseWidget.h | 2 +- src/gui/SearchWidget.cpp | 1 + src/gui/SearchWidget.h | 8 ++--- tests/TestEntrySearcher.cpp | 7 ++-- 7 files changed, 39 insertions(+), 60 deletions(-) diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index 3413f1cd0..b181ad389 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -22,47 +22,44 @@ QList EntrySearcher::search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) { - if (!group->resolveSearchingEnabled()) { - return QList(); + QList results; + + if (group->resolveSearchingEnabled()) { + results.append(searchEntries(searchTerm, group->entries(), caseSensitivity)); } - return searchEntries(searchTerm, group, caseSensitivity); -} - -QList -EntrySearcher::searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) -{ - QList searchResult; - - const QList& entryList = group->entries(); - for (Entry* entry : entryList) { - searchResult.append(matchEntry(searchTerm, entry, caseSensitivity)); - } - - const QList& children = group->children(); - for (Group* childGroup : children) { - if (childGroup->searchingEnabled() != Group::Disable) { - if (matchGroup(searchTerm, childGroup, caseSensitivity)) { - searchResult.append(childGroup->entriesRecursive()); - } else { - searchResult.append(searchEntries(searchTerm, childGroup, caseSensitivity)); - } + for (Group* childGroup : group->children()) { + if (childGroup->resolveSearchingEnabled()) { + results.append(searchEntries(searchTerm, childGroup->entries(), caseSensitivity)); } } - return searchResult; + return results; } -QList EntrySearcher::matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity) +QList EntrySearcher::searchEntries(const QString& searchTerm, const QList& entries, + Qt::CaseSensitivity caseSensitivity) +{ + QList results; + for (Entry* entry : entries) { + if (matchEntry(searchTerm, entry, caseSensitivity)) { + results.append(entry); + } + } + return results; +} + +bool EntrySearcher::matchEntry(const QString& searchTerm, Entry* entry, + Qt::CaseSensitivity caseSensitivity) { const QStringList wordList = searchTerm.split(QRegExp("\\s"), QString::SkipEmptyParts); for (const QString& word : wordList) { if (!wordMatch(word, entry, caseSensitivity)) { - return QList(); + return false; } } - return QList() << entry; + return true; } bool EntrySearcher::wordMatch(const QString& word, Entry* entry, Qt::CaseSensitivity caseSensitivity) @@ -72,20 +69,3 @@ bool EntrySearcher::wordMatch(const QString& word, Entry* entry, Qt::CaseSensiti || entry->resolvePlaceholder(entry->url()).contains(word, caseSensitivity) || entry->resolvePlaceholder(entry->notes()).contains(word, caseSensitivity); } - -bool EntrySearcher::matchGroup(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) -{ - const QStringList wordList = searchTerm.split(QRegExp("\\s"), QString::SkipEmptyParts); - for (const QString& word : wordList) { - if (!wordMatch(word, group, caseSensitivity)) { - return false; - } - } - - return true; -} - -bool EntrySearcher::wordMatch(const QString& word, const Group* group, Qt::CaseSensitivity caseSensitivity) -{ - return group->name().contains(word, caseSensitivity) || group->notes().contains(word, caseSensitivity); -} diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h index 343734737..def5eb8f6 100644 --- a/src/core/EntrySearcher.h +++ b/src/core/EntrySearcher.h @@ -30,11 +30,9 @@ public: QList search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); private: - QList searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); - QList matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity); + QList searchEntries(const QString& searchTerm, const QList& entries, Qt::CaseSensitivity caseSensitivity); + bool matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity); bool wordMatch(const QString& word, Entry* entry, Qt::CaseSensitivity caseSensitivity); - bool matchGroup(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); - bool wordMatch(const QString& word, const Group* group, Qt::CaseSensitivity caseSensitivity); }; #endif // KEEPASSX_ENTRYSEARCHER_H diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 3faf43a65..a4f014bf6 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1047,9 +1047,12 @@ void DatabaseWidget::setSearchLimitGroup(bool state) void DatabaseWidget::onGroupChanged(Group* group) { - // Intercept group changes if in search mode - if (isInSearchMode()) { + if (isInSearchMode() && m_searchLimitGroup) { + // Perform new search if we are limiting search to the current group search(m_lastSearchText); + } else if (isInSearchMode()) { + // Otherwise cancel search + emit clearSearch(); } else { m_entryView->setGroup(group); } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index a5cf538d7..8f268c94a 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -138,7 +138,7 @@ signals: void mainSplitterSizesChanged(); void previewSplitterSizesChanged(); void entryViewStateChanged(); - void updateSearch(QString text); + void clearSearch(); public slots: void createEntry(); diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index 40c63036c..96bd05a5b 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -113,6 +113,7 @@ void SearchWidget::connectSignals(SignalMultiplexer& mx) mx.connect(this, SIGNAL(limitGroupChanged(bool)), SLOT(setSearchLimitGroup(bool))); mx.connect(this, SIGNAL(copyPressed()), SLOT(copyPassword())); mx.connect(this, SIGNAL(downPressed()), SLOT(setFocus())); + mx.connect(SIGNAL(clearSearch()), m_ui->searchEdit, SLOT(clear())); mx.connect(m_ui->searchEdit, SIGNAL(returnPressed()), SLOT(switchToEntryEdit())); } diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h index 0ec3287c1..39e17bcf4 100644 --- a/src/gui/SearchWidget.h +++ b/src/gui/SearchWidget.h @@ -36,14 +36,16 @@ class SearchWidget : public QWidget public: explicit SearchWidget(QWidget* parent = nullptr); - ~SearchWidget(); + ~SearchWidget() override; + + Q_DISABLE_COPY(SearchWidget) void connectSignals(SignalMultiplexer& mx); void setCaseSensitive(bool state); void setLimitGroup(bool state); protected: - bool eventFilter(QObject* obj, QEvent* event); + bool eventFilter(QObject* obj, QEvent* event) override; signals: void search(const QString& text); @@ -69,8 +71,6 @@ private: QTimer* m_searchTimer; QAction* m_actionCaseSensitive; QAction* m_actionLimitGroup; - - Q_DISABLE_COPY(SearchWidget) }; #endif // SEARCHWIDGET_H diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 659f7a489..0c0a2c3e4 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -53,7 +53,6 @@ void TestEntrySearcher::testSearch() group2111->setParent(group211); group1->setSearchingEnabled(Group::Disable); - group11->setSearchingEnabled(Group::Enable); Entry* eRoot = new Entry(); eRoot->setNotes("test search term test"); @@ -88,15 +87,13 @@ void TestEntrySearcher::testSearch() e3b->setGroup(group3); m_searchResult = m_entrySearcher.search("search term", m_groupRoot, Qt::CaseInsensitive); - QCOMPARE(m_searchResult.count(), 3); + QCOMPARE(m_searchResult.count(), 2); m_searchResult = m_entrySearcher.search("search term", group211, Qt::CaseInsensitive); QCOMPARE(m_searchResult.count(), 1); + // Parent group disabled search m_searchResult = m_entrySearcher.search("search term", group11, Qt::CaseInsensitive); - QCOMPARE(m_searchResult.count(), 1); - - m_searchResult = m_entrySearcher.search("search term", group1, Qt::CaseInsensitive); QCOMPARE(m_searchResult.count(), 0); } From 4b57fcb5633229667622e08e505c0eb2512cb1d0 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Wed, 21 Mar 2018 21:52:57 -0400 Subject: [PATCH 55/84] Clean up Entry Model/View code --- src/gui/DatabaseWidget.cpp | 12 ++++---- src/gui/DatabaseWidget.h | 4 +-- src/gui/entry/EntryModel.cpp | 4 +-- src/gui/entry/EntryModel.h | 16 +++++----- src/gui/entry/EntryView.cpp | 58 ++++++++++-------------------------- src/gui/entry/EntryView.h | 11 +++---- tests/TestEntryModel.cpp | 4 +-- 7 files changed, 37 insertions(+), 72 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index a4f014bf6..ebec29b46 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -100,7 +100,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_entryView = new EntryView(rightHandSideWidget); m_entryView->setObjectName("entryView"); m_entryView->setContextMenuPolicy(Qt::CustomContextMenu); - m_entryView->setGroup(db->rootGroup()); + m_entryView->displayGroup(db->rootGroup()); connect(m_entryView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(emitEntryContextMenuRequested(QPoint))); // Add a notification for when we are searching @@ -291,7 +291,7 @@ bool DatabaseWidget::isUsernamesHidden() const /** * Set state of entry view 'Hide Usernames' setting */ -void DatabaseWidget::setUsernamesHidden(const bool hide) +void DatabaseWidget::setUsernamesHidden(bool hide) { m_entryView->setUsernamesHidden(hide); } @@ -307,7 +307,7 @@ bool DatabaseWidget::isPasswordsHidden() const /** * Set state of entry view 'Hide Passwords' setting */ -void DatabaseWidget::setPasswordsHidden(const bool hide) +void DatabaseWidget::setPasswordsHidden(bool hide) { m_entryView->setPasswordsHidden(hide); } @@ -1018,7 +1018,7 @@ void DatabaseWidget::search(const QString& searchtext) QList searchResult = EntrySearcher().search(searchtext, searchGroup, caseSensitive); - m_entryView->setEntryList(searchResult); + m_entryView->displaySearch(searchResult); m_lastSearchText = searchtext; // Display a label detailing our search results @@ -1054,7 +1054,7 @@ void DatabaseWidget::onGroupChanged(Group* group) // Otherwise cancel search emit clearSearch(); } else { - m_entryView->setGroup(group); + m_entryView->displayGroup(group); } } @@ -1069,7 +1069,7 @@ void DatabaseWidget::endSearch() emit listModeAboutToActivate(); // Show the normal entry view of the current group - m_entryView->setGroup(currentGroup()); + m_entryView->displayGroup(currentGroup()); emit listModeActivated(); } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 8f268c94a..a5d881ff9 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -93,9 +93,9 @@ public: QList previewSplitterSizes() const; void setPreviewSplitterSizes(const QList& sizes); bool isUsernamesHidden() const; - void setUsernamesHidden(const bool hide); + void setUsernamesHidden(bool hide); bool isPasswordsHidden() const; - void setPasswordsHidden(const bool hide); + void setPasswordsHidden(bool hide); QByteArray entryViewState() const; bool setEntryViewState(const QByteArray& state) const; void clearAllWidgets(); diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 2edc49b25..194c4b571 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -72,10 +72,9 @@ void EntryModel::setGroup(Group* group) makeConnections(group); endResetModel(); - emit switchedToListMode(); } -void EntryModel::setEntryList(const QList& entries) +void EntryModel::setEntries(const QList& entries) { beginResetModel(); @@ -109,7 +108,6 @@ void EntryModel::setEntryList(const QList& entries) } endResetModel(); - emit switchedToSearchMode(); } int EntryModel::rowCount(const QModelIndex& parent) const diff --git a/src/gui/entry/EntryModel.h b/src/gui/entry/EntryModel.h index 3e9f2824a..5f405bd41 100644 --- a/src/gui/entry/EntryModel.h +++ b/src/gui/entry/EntryModel.h @@ -60,22 +60,20 @@ public: QStringList mimeTypes() const override; QMimeData* mimeData(const QModelIndexList& indexes) const override; - void setEntryList(const QList& entries); - void setPaperClipPixmap(const QPixmap& paperclip); + void setGroup(Group* group); + void setEntries(const QList& entries); + bool isUsernamesHidden() const; + void setUsernamesHidden(bool hide); bool isPasswordsHidden() const; + void setPasswordsHidden(bool hide); + + void setPaperClipPixmap(const QPixmap& paperclip); signals: - void switchedToListMode(); - void switchedToSearchMode(); void usernamesHiddenChanged(); void passwordsHiddenChanged(); -public slots: - void setGroup(Group* group); - void setUsernamesHidden(bool hide); - void setPasswordsHidden(bool hide); - private slots: void entryAboutToAdd(Entry* entry); void entryAdded(Entry* entry); diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index ecdccd7bb..28d5ec2bb 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -50,11 +50,7 @@ EntryView::EntryView(QWidget* parent) setDefaultDropAction(Qt::MoveAction); connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex))); - connect(selectionModel(), - SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); - - connect(m_model, SIGNAL(switchedToListMode()), SLOT(switchToListMode())); - connect(m_model, SIGNAL(switchedToSearchMode()), SLOT(switchToSearchMode())); + connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); connect(m_model, SIGNAL(usernamesHiddenChanged()), SIGNAL(viewStateChanged())); connect(m_model, SIGNAL(passwordsHiddenChanged()), SIGNAL(viewStateChanged())); @@ -158,16 +154,25 @@ void EntryView::focusOutEvent(QFocusEvent* event) QTreeView::focusOutEvent(event); } -void EntryView::setGroup(Group* group) +void EntryView::displayGroup(Group* group) { m_model->setGroup(group); + header()->hideSection(EntryModel::ParentGroup); setFirstEntryActive(); + m_inSearchMode = false; } -void EntryView::setEntryList(const QList& entries) +void EntryView::displaySearch(const QList& entries) { - m_model->setEntryList(entries); + m_model->setEntries(entries); + header()->showSection(EntryModel::ParentGroup); + + // Reset sort column to 'Group', overrides DatabaseWidgetStateSync + m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder); + sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder); + setFirstEntryActive(); + m_inSearchMode = true; } void EntryView::setFirstEntryActive() @@ -227,39 +232,6 @@ Entry* EntryView::entryFromIndex(const QModelIndex& index) } } -/** - * Switch to list mode, i.e. list entries of group - */ -void EntryView::switchToListMode() -{ - if (!m_inSearchMode) { - return; - } - - header()->hideSection(EntryModel::ParentGroup); - m_inSearchMode = false; -} - -/** - * Switch to search mode, i.e. list search results - */ -void EntryView::switchToSearchMode() -{ - if (m_inSearchMode) { - return; - } - - header()->showSection(EntryModel::ParentGroup); - - // Always set sorting to column 'Group', as it does not feel right to - // have the last known sort configuration of search view restored by - // 'DatabaseWidgetStateSync', which is what happens without this - m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder); - sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder); - - m_inSearchMode = true; -} - /** * Get current state of 'Hide Usernames' setting (NOTE: just pass-through for * m_model) @@ -272,7 +244,7 @@ bool EntryView::isUsernamesHidden() const /** * Set state of 'Hide Usernames' setting (NOTE: just pass-through for m_model) */ -void EntryView::setUsernamesHidden(const bool hide) +void EntryView::setUsernamesHidden(bool hide) { bool block = m_hideUsernamesAction->signalsBlocked(); m_hideUsernamesAction->blockSignals(true); @@ -294,7 +266,7 @@ bool EntryView::isPasswordsHidden() const /** * Set state of 'Hide Passwords' setting (NOTE: just pass-through for m_model) */ -void EntryView::setPasswordsHidden(const bool hide) +void EntryView::setPasswordsHidden(bool hide) { bool block = m_hidePasswordsAction->signalsBlocked(); m_hidePasswordsAction->blockSignals(true); diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 2030f0ec7..fa002c717 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -39,19 +39,18 @@ public: Entry* currentEntry(); void setCurrentEntry(Entry* entry); Entry* entryFromIndex(const QModelIndex& index); - void setEntryList(const QList& entries); bool inSearchMode(); int numberOfSelectedEntries(); void setFirstEntryActive(); bool isUsernamesHidden() const; - void setUsernamesHidden(const bool hide); + void setUsernamesHidden(bool hide); bool isPasswordsHidden() const; - void setPasswordsHidden(const bool hide); + void setPasswordsHidden(bool hide); QByteArray viewState() const; bool setViewState(const QByteArray& state); -public slots: - void setGroup(Group* group); + void displayGroup(Group* group); + void displaySearch(const QList& entries); signals: void entryActivated(Entry* entry, EntryModel::ModelColumn column); @@ -65,8 +64,6 @@ protected: private slots: void emitEntryActivated(const QModelIndex& index); - void switchToListMode(); - void switchToSearchMode(); void showHeaderMenu(const QPoint& position); void toggleColumnVisibility(QAction* action); void fitColumnsToWindow(); diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp index 49939e256..e32de2466 100644 --- a/tests/TestEntryModel.cpp +++ b/tests/TestEntryModel.cpp @@ -307,7 +307,7 @@ void TestEntryModel::testProxyModel() QList entryList; entryList << entry; - modelSource->setEntryList(entryList); + modelSource->setEntries(entryList); /** * @author Fonic @@ -346,7 +346,7 @@ void TestEntryModel::testDatabaseDelete() Entry* entry2 = new Entry(); entry2->setGroup(db2->rootGroup()); - model->setEntryList(QList() << entry1 << entry2); + model->setEntries(QList() << entry1 << entry2); QCOMPARE(model->rowCount(), 2); From 4b983251cb464d104aafd95ea56e19b5c404247a Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sun, 25 Mar 2018 16:24:30 -0400 Subject: [PATCH 56/84] Add advanced search term parser * Support quoted strings & per-field searching * Support regex and exact matching * Simplify search sequence * Make search widget larger * Add regex converter to Tools namespace --- src/browser/BrowserService.cpp | 2 +- src/core/EntrySearcher.cpp | 144 ++++++++++++++++++++++++++++----- src/core/EntrySearcher.h | 39 ++++++++- src/core/Tools.cpp | 29 +++++++ src/core/Tools.h | 3 + src/gui/DatabaseWidget.cpp | 11 ++- src/gui/DatabaseWidget.h | 3 +- src/gui/SearchWidget.cpp | 2 +- src/gui/SearchWidget.ui | 15 ++++ src/gui/entry/EntryView.cpp | 4 +- src/gui/entry/EntryView.h | 6 +- tests/TestEntrySearcher.cpp | 107 +++++++++++++++++------- tests/TestEntrySearcher.h | 3 +- tests/gui/TestGui.cpp | 3 +- 14 files changed, 303 insertions(+), 68 deletions(-) diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index a69508026..a1315ad49 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -390,7 +390,7 @@ QList BrowserService::searchEntries(Database* db, const QString& hostnam return entries; } - for (Entry* entry : EntrySearcher().search(baseDomain(hostname), rootGroup, Qt::CaseInsensitive)) { + for (Entry* entry : EntrySearcher().search(baseDomain(hostname), rootGroup)) { QString entryUrl = entry->url(); QUrl entryQUrl(entryUrl); QString entryScheme = entryQUrl.scheme(); diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index b181ad389..6614ab463 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -19,42 +19,91 @@ #include "EntrySearcher.h" #include "core/Group.h" +#include "core/Tools.h" -QList EntrySearcher::search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) +EntrySearcher::EntrySearcher(bool caseSensitive) + : m_caseSensitive(caseSensitive) + , m_termParser(R"re(([-*+]+)?(?:(\w*):)?(?:(?=")"((?:[^"\\]|\\.)*)"|([^ ]*))( |$))re") + // Group 1 = modifiers, Group 2 = field, Group 3 = quoted string, Group 4 = unquoted string { +} + +QList EntrySearcher::search(const QString& searchString, const Group* baseGroup, bool forceSearch) +{ + Q_ASSERT(baseGroup); + QList results; - - if (group->resolveSearchingEnabled()) { - results.append(searchEntries(searchTerm, group->entries(), caseSensitivity)); - } - - for (Group* childGroup : group->children()) { - if (childGroup->resolveSearchingEnabled()) { - results.append(searchEntries(searchTerm, childGroup->entries(), caseSensitivity)); + for (const auto group : baseGroup->groupsRecursive(true)) { + if (forceSearch || group->resolveSearchingEnabled()) { + results.append(searchEntries(searchString, group->entries())); } } return results; } -QList EntrySearcher::searchEntries(const QString& searchTerm, const QList& entries, - Qt::CaseSensitivity caseSensitivity) +QList EntrySearcher::searchEntries(const QString& searchString, const QList& entries) { QList results; for (Entry* entry : entries) { - if (matchEntry(searchTerm, entry, caseSensitivity)) { + if (searchEntryImpl(searchString, entry)) { results.append(entry); } } return results; } -bool EntrySearcher::matchEntry(const QString& searchTerm, Entry* entry, - Qt::CaseSensitivity caseSensitivity) +void EntrySearcher::setCaseSensitive(bool state) { - const QStringList wordList = searchTerm.split(QRegExp("\\s"), QString::SkipEmptyParts); - for (const QString& word : wordList) { - if (!wordMatch(word, entry, caseSensitivity)) { + m_caseSensitive = state; +} + +bool EntrySearcher::isCaseSensitive() +{ + return m_caseSensitive; +} + +bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry) +{ + // Pre-load in case they are needed + auto attributes = QStringList(entry->attributes()->keys()); + auto attachments = QStringList(entry->attachments()->keys()); + + bool found; + auto searchTerms = parseSearchTerms(searchString); + + for (const auto& term : searchTerms) { + switch (term->field) { + case Field::Title: + found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch(); + break; + case Field::Username: + found = term->regex.match(entry->resolvePlaceholder(entry->username())).hasMatch(); + break; + case Field::Password: + found = term->regex.match(entry->resolvePlaceholder(entry->password())).hasMatch(); + break; + case Field::Url: + found = term->regex.match(entry->resolvePlaceholder(entry->url())).hasMatch(); + break; + case Field::Notes: + found = term->regex.match(entry->notes()).hasMatch(); + break; + case Field::Attribute: + found = !attributes.filter(term->regex).empty(); + break; + case Field::Attachment: + found = !attachments.filter(term->regex).empty(); + break; + default: + found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch() || + term->regex.match(entry->resolvePlaceholder(entry->username())).hasMatch() || + term->regex.match(entry->resolvePlaceholder(entry->url())).hasMatch() || + term->regex.match(entry->notes()).hasMatch(); + } + + // Short circuit if we failed to match or we matched and are excluding this term + if (!found || term->exclude) { return false; } } @@ -62,10 +111,61 @@ bool EntrySearcher::matchEntry(const QString& searchTerm, Entry* entry, return true; } -bool EntrySearcher::wordMatch(const QString& word, Entry* entry, Qt::CaseSensitivity caseSensitivity) +QList > EntrySearcher::parseSearchTerms(const QString& searchString) { - return entry->resolvePlaceholder(entry->title()).contains(word, caseSensitivity) - || entry->resolvePlaceholder(entry->username()).contains(word, caseSensitivity) - || entry->resolvePlaceholder(entry->url()).contains(word, caseSensitivity) - || entry->resolvePlaceholder(entry->notes()).contains(word, caseSensitivity); + auto terms = QList >(); + + auto results = m_termParser.globalMatch(searchString); + while (results.hasNext()) { + auto result = results.next(); + auto term = QSharedPointer::create(); + + // Quoted string group + term->word = result.captured(3); + + // If empty, use the unquoted string group + if (term->word.isEmpty()) { + term->word = result.captured(4); + } + + // If still empty, ignore this match + if (term->word.isEmpty()) { + continue; + } + + auto mods = result.captured(1); + + // Convert term to regex + term->regex = Tools::convertToRegex(term->word, !mods.contains("*"), mods.contains("+"), m_caseSensitive); + + // Exclude modifier + term->exclude = mods.contains("-"); + + // Determine the field to search + QString field = result.captured(2); + if (!field.isEmpty()) { + auto cs = Qt::CaseInsensitive; + if (field.compare("title", cs) == 0) { + term->field = Field::Title; + } else if (field.startsWith("user", cs)) { + term->field = Field::Username; + } else if (field.startsWith("pass", cs)) { + term->field = Field::Password; + } else if (field.compare("url", cs) == 0) { + term->field = Field::Url; + } else if (field.compare("notes", cs) == 0) { + term->field = Field::Notes; + } else if (field.startsWith("attr", cs)) { + term->field = Field::Attribute; + } else if (field.startsWith("attach", cs)) { + term->field = Field::Attachment; + } else { + term->field = Field::Undefined; + } + } + + terms.append(term); + } + + return terms; } diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h index def5eb8f6..ec71a7ce1 100644 --- a/src/core/EntrySearcher.h +++ b/src/core/EntrySearcher.h @@ -20,6 +20,7 @@ #define KEEPASSX_ENTRYSEARCHER_H #include +#include class Group; class Entry; @@ -27,12 +28,42 @@ class Entry; class EntrySearcher { public: - QList search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); + explicit EntrySearcher(bool caseSensitive = false); + + QList search(const QString& searchString, const Group* baseGroup, bool forceSearch = false); + QList searchEntries(const QString& searchString, const QList& entries); + + void setCaseSensitive(bool state); + bool isCaseSensitive(); private: - QList searchEntries(const QString& searchTerm, const QList& entries, Qt::CaseSensitivity caseSensitivity); - bool matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity); - bool wordMatch(const QString& word, Entry* entry, Qt::CaseSensitivity caseSensitivity); + bool searchEntryImpl(const QString& searchString, Entry* entry); + + enum class Field { + Undefined, + Title, + Username, + Password, + Url, + Notes, + Attribute, + Attachment + }; + + struct SearchTerm + { + Field field; + QString word; + QRegularExpression regex; + bool exclude; + }; + + QList > parseSearchTerms(const QString& searchString); + + bool m_caseSensitive; + QRegularExpression m_termParser; + + friend class TestEntrySearcher; }; #endif // KEEPASSX_ENTRYSEARCHER_H diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index ded3a1651..362cfa937 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -26,6 +26,8 @@ #include #include #include +#include + #include #include @@ -199,4 +201,31 @@ void wait(int ms) } } +// Escape common regex symbols except for *, ?, and | +auto regexEscape = QRegularExpression(R"re(([-[\]{}()+.,\\\/^$#]))re"); + +QRegularExpression convertToRegex(const QString& string, bool useWildcards, bool exactMatch, bool caseSensitive) +{ + QString pattern = string; + + // Wildcard support (*, ?, |) + if (useWildcards) { + pattern.replace(regexEscape, "\\\\1"); + pattern.replace("*", ".*"); + pattern.replace("?", "."); + } + + // Exact modifier + if (exactMatch) { + pattern = "^" + pattern + "$"; + } + + auto regex = QRegularExpression(pattern); + if (!caseSensitive) { + regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + } + + return regex; +} + } // namespace Tools diff --git a/src/core/Tools.h b/src/core/Tools.h index 13d9869f7..37214f069 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -27,6 +27,7 @@ #include class QIODevice; +class QRegularExpression; namespace Tools { @@ -38,6 +39,8 @@ bool isHex(const QByteArray& ba); bool isBase64(const QByteArray& ba); void sleep(int ms); void wait(int ms); +QRegularExpression convertToRegex(const QString& string, bool useWildcards = false, bool exactMatch = false, + bool caseSensitive = false); template RandomAccessIterator binaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T& value) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index ebec29b46..701ba588c 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -209,7 +209,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_fileWatchUnblockTimer.setSingleShot(true); m_ignoreAutoReload = false; - m_searchCaseSensitive = false; + m_EntrySearcher = new EntrySearcher(false); m_searchLimitGroup = config()->get("SearchLimitGroup", false).toBool(); #ifdef WITH_XC_SSHAGENT @@ -227,6 +227,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) DatabaseWidget::~DatabaseWidget() { + delete m_EntrySearcher; } DatabaseWidget::Mode DatabaseWidget::currentMode() const @@ -1012,17 +1013,15 @@ void DatabaseWidget::search(const QString& searchtext) emit searchModeAboutToActivate(); - Qt::CaseSensitivity caseSensitive = m_searchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; - Group* searchGroup = m_searchLimitGroup ? currentGroup() : m_db->rootGroup(); - QList searchResult = EntrySearcher().search(searchtext, searchGroup, caseSensitive); + QList searchResult = m_EntrySearcher->search(searchtext, searchGroup); m_entryView->displaySearch(searchResult); m_lastSearchText = searchtext; // Display a label detailing our search results - if (searchResult.size() > 0) { + if (!searchResult.isEmpty()) { m_searchingLabel->setText(tr("Search Results (%1)").arg(searchResult.size())); } else { m_searchingLabel->setText(tr("No Results")); @@ -1035,7 +1034,7 @@ void DatabaseWidget::search(const QString& searchtext) void DatabaseWidget::setSearchCaseSensitive(bool state) { - m_searchCaseSensitive = state; + m_EntrySearcher->setCaseSensitive(state); refreshSearch(); } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index a5d881ff9..d0c4e2042 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -37,6 +37,7 @@ class EditEntryWidget; class EditGroupWidget; class Entry; class EntryView; +class EntrySearcher; class Group; class GroupView; class KeePass1OpenWidget; @@ -246,8 +247,8 @@ private: QString m_databaseFileName; // Search state + EntrySearcher* m_EntrySearcher; QString m_lastSearchText; - bool m_searchCaseSensitive; bool m_searchLimitGroup; // CSV import state diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index 96bd05a5b..ba8b616d8 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -137,7 +137,7 @@ void SearchWidget::startSearchTimer() if (!m_searchTimer->isActive()) { m_searchTimer->stop(); } - m_searchTimer->start(100); + m_searchTimer->start(300); } void SearchWidget::startSearch() diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui index 438a242c7..19b274a88 100644 --- a/src/gui/SearchWidget.ui +++ b/src/gui/SearchWidget.ui @@ -34,6 +34,9 @@ Qt::Horizontal + + QSizePolicy::Minimum + 30 @@ -44,6 +47,18 @@ + + + 0 + 0 + + + + + 0 + 0 + + padding:3px diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 28d5ec2bb..64eca5ee3 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -58,9 +58,9 @@ EntryView::EntryView(QWidget* parent) m_headerMenu->setTitle(tr("Customize View")); m_headerMenu->addSection(tr("Customize View")); - m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(setUsernamesHidden(bool))); + m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), this, SLOT(setUsernamesHidden(bool))); m_hideUsernamesAction->setCheckable(true); - m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(setPasswordsHidden(bool))); + m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), this, SLOT(setPasswordsHidden(bool))); m_hidePasswordsAction->setCheckable(true); m_headerMenu->addSeparator(); diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index fa002c717..766699599 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -43,9 +43,7 @@ public: int numberOfSelectedEntries(); void setFirstEntryActive(); bool isUsernamesHidden() const; - void setUsernamesHidden(bool hide); bool isPasswordsHidden() const; - void setPasswordsHidden(bool hide); QByteArray viewState() const; bool setViewState(const QByteArray& state); @@ -57,6 +55,10 @@ signals: void entrySelectionChanged(); void viewStateChanged(); +public slots: + void setUsernamesHidden(bool hide); + void setPasswordsHidden(bool hide); + protected: void keyPressEvent(QKeyEvent* event) override; void focusInEvent(QFocusEvent* event) override; diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 0c0a2c3e4..51be468b5 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -22,23 +22,32 @@ QTEST_GUILESS_MAIN(TestEntrySearcher) void TestEntrySearcher::initTestCase() { - m_groupRoot = new Group(); + m_rootGroup = new Group(); } void TestEntrySearcher::cleanupTestCase() { - delete m_groupRoot; + delete m_rootGroup; } void TestEntrySearcher::testSearch() { + /** + * Root + * - group1 (search disabled) + * - group11 + * - group2 + * - group21 + * - group211 + * - group2111 + */ Group* group1 = new Group(); Group* group2 = new Group(); Group* group3 = new Group(); - group1->setParent(m_groupRoot); - group2->setParent(m_groupRoot); - group3->setParent(m_groupRoot); + group1->setParent(m_rootGroup); + group2->setParent(m_rootGroup); + group3->setParent(m_rootGroup); Group* group11 = new Group(); @@ -55,15 +64,15 @@ void TestEntrySearcher::testSearch() group1->setSearchingEnabled(Group::Disable); Entry* eRoot = new Entry(); - eRoot->setNotes("test search term test"); - eRoot->setGroup(m_groupRoot); + eRoot->setTitle("test search term test"); + eRoot->setGroup(m_rootGroup); Entry* eRoot2 = new Entry(); eRoot2->setNotes("test term test"); - eRoot2->setGroup(m_groupRoot); + eRoot2->setGroup(m_rootGroup); Entry* e1 = new Entry(); - e1->setNotes("test search term test"); + e1->setUsername("test search term test"); e1->setGroup(group1); Entry* e11 = new Entry(); @@ -71,29 +80,37 @@ void TestEntrySearcher::testSearch() e11->setGroup(group11); Entry* e2111 = new Entry(); - e2111->setNotes("test search term test"); + e2111->setTitle("test search term test"); e2111->setGroup(group2111); Entry* e2111b = new Entry(); e2111b->setNotes("test search test"); + e2111b->setPassword("testpass"); e2111b->setGroup(group2111); Entry* e3 = new Entry(); - e3->setNotes("test search term test"); + e3->setUrl("test search term test"); e3->setGroup(group3); Entry* e3b = new Entry(); - e3b->setNotes("test search test"); + e3b->setTitle("test search test"); + e3b->setPassword("realpass"); e3b->setGroup(group3); - m_searchResult = m_entrySearcher.search("search term", m_groupRoot, Qt::CaseInsensitive); - QCOMPARE(m_searchResult.count(), 2); + m_searchResult = m_entrySearcher.search("search", m_rootGroup); + QCOMPARE(m_searchResult.count(), 5); - m_searchResult = m_entrySearcher.search("search term", group211, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("search term", m_rootGroup); + QCOMPARE(m_searchResult.count(), 3); + + m_searchResult = m_entrySearcher.search("search term", group211); QCOMPARE(m_searchResult.count(), 1); - // Parent group disabled search - m_searchResult = m_entrySearcher.search("search term", group11, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("password:testpass", m_rootGroup); + QCOMPARE(m_searchResult.count(), 1); + + // Parent group has search disabled + m_searchResult = m_entrySearcher.search("search term", group11); QCOMPARE(m_searchResult.count(), 0); } @@ -102,38 +119,74 @@ void TestEntrySearcher::testAndConcatenationInSearch() Entry* entry = new Entry(); entry->setNotes("abc def ghi"); entry->setTitle("jkl"); - entry->setGroup(m_groupRoot); + entry->setGroup(m_rootGroup); - m_searchResult = m_entrySearcher.search("", m_groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("", m_rootGroup); QCOMPARE(m_searchResult.count(), 1); - m_searchResult = m_entrySearcher.search("def", m_groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("def", m_rootGroup); QCOMPARE(m_searchResult.count(), 1); - m_searchResult = m_entrySearcher.search(" abc ghi ", m_groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search(" abc ghi ", m_rootGroup); QCOMPARE(m_searchResult.count(), 1); - m_searchResult = m_entrySearcher.search("ghi ef", m_groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("ghi ef", m_rootGroup); QCOMPARE(m_searchResult.count(), 1); - m_searchResult = m_entrySearcher.search("abc ef xyz", m_groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("abc ef xyz", m_rootGroup); QCOMPARE(m_searchResult.count(), 0); - m_searchResult = m_entrySearcher.search("abc kl", m_groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("abc kl", m_rootGroup); QCOMPARE(m_searchResult.count(), 1); } void TestEntrySearcher::testAllAttributesAreSearched() { Entry* entry = new Entry(); - entry->setGroup(m_groupRoot); + entry->setGroup(m_rootGroup); entry->setTitle("testTitle"); entry->setUsername("testUsername"); entry->setUrl("testUrl"); entry->setNotes("testNote"); - m_searchResult = - m_entrySearcher.search("testTitle testUsername testUrl testNote", m_groupRoot, Qt::CaseInsensitive); + // Default is to AND all terms together + m_searchResult = m_entrySearcher.search("testTitle testUsername testUrl testNote", m_rootGroup); QCOMPARE(m_searchResult.count(), 1); } + +void TestEntrySearcher::testSearchTermParser() +{ + // Test standard search terms + auto terms = m_entrySearcher.parseSearchTerms("-test \"quoted \\\"string\\\"\" user:user pass:\"test me\" noquote "); + + QCOMPARE(terms.length(), 5); + + QCOMPARE(terms[0]->field, EntrySearcher::Field::Undefined); + QCOMPARE(terms[0]->word, QString("test")); + QCOMPARE(terms[0]->exclude, true); + + QCOMPARE(terms[1]->field, EntrySearcher::Field::Undefined); + QCOMPARE(terms[1]->word, QString("quoted \\\"string\\\"")); + QCOMPARE(terms[1]->exclude, false); + + QCOMPARE(terms[2]->field, EntrySearcher::Field::Username); + QCOMPARE(terms[2]->word, QString("user")); + + QCOMPARE(terms[3]->field, EntrySearcher::Field::Password); + QCOMPARE(terms[3]->word, QString("test me")); + + QCOMPARE(terms[4]->field, EntrySearcher::Field::Undefined); + QCOMPARE(terms[4]->word, QString("noquote")); + + // Test wildcard and regex search terms + terms = m_entrySearcher.parseSearchTerms("+url:*.google.com *user:\\d+\\w{2}"); + + QCOMPARE(terms.length(), 2); + + QCOMPARE(terms[0]->field, EntrySearcher::Field::Url); + QCOMPARE(terms[0]->regex.pattern(), QString("^.*\\.google\\.com$")); + + QCOMPARE(terms[1]->field, EntrySearcher::Field::Username); + QCOMPARE(terms[1]->regex.pattern(), QString("\\d+\\w{2}")); +} diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index 17d486c22..f385d618e 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -34,9 +34,10 @@ private slots: void testAndConcatenationInSearch(); void testSearch(); void testAllAttributesAreSearched(); + void testSearchTermParser(); private: - Group* m_groupRoot; + Group* m_rootGroup; EntrySearcher m_entrySearcher; QList m_searchResult; }; diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 9b04dd18e..f48d0777c 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -841,9 +841,10 @@ void TestGui::testSearch() // Ensure Down focuses on entry view when search text is selected QTest::keyClick(searchTextEdit, Qt::Key_Down); QTRY_VERIFY(entryView->hasFocus()); + QCOMPARE(entryView->selectionModel()->currentIndex().row(), 0); // Test that password copies (entry has focus) QClipboard* clipboard = QApplication::clipboard(); - QTest::keyClick(searchTextEdit, Qt::Key_C, Qt::ControlModifier); + QTest::keyClick(entryView, Qt::Key_C, Qt::ControlModifier); QModelIndex searchedItem = entryView->model()->index(0, 1); Entry* searchedEntry = entryView->entryFromIndex(searchedItem); QTRY_COMPARE(searchedEntry->password(), clipboard->text()); From d6ffee5e99905a3bf237fc554b5e9271097cc849 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Thu, 15 Nov 2018 17:37:16 -0500 Subject: [PATCH 57/84] Implement search auto-clear and goto group * Search clears if the search box does not have focus for 5 minutes (fixes #2178) * Goto group from search results after double clicking the group name (fixes #2043) --- src/gui/DatabaseWidget.cpp | 8 ++++++++ src/gui/SearchWidget.cpp | 10 +++++++++- src/gui/SearchWidget.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 701ba588c..5c8d7bc9d 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -893,6 +893,14 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod setupTotp(); } break; + case EntryModel::ParentGroup: + // Call this first to clear out of search mode, otherwise + // the desired entry is not properly selected + endSearch(); + emit clearSearch(); + m_groupView->setCurrentGroup(entry->group()); + m_entryView->setCurrentEntry(entry); + break; // TODO: switch to 'Notes' tab in details view/pane // case EntryModel::Notes: // break; diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index ba8b616d8..6441502b0 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -30,15 +30,18 @@ SearchWidget::SearchWidget(QWidget* parent) : QWidget(parent) , m_ui(new Ui::SearchWidget()) + , m_searchTimer(new QTimer(this)) + , m_clearSearchTimer(new QTimer(this)) { m_ui->setupUi(this); - m_searchTimer = new QTimer(this); m_searchTimer->setSingleShot(true); + m_clearSearchTimer->setSingleShot(true); connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), SLOT(startSearchTimer())); connect(m_ui->clearIcon, SIGNAL(triggered(bool)), m_ui->searchEdit, SLOT(clear())); connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(startSearch())); + connect(m_clearSearchTimer, SIGNAL(timeout()), m_ui->searchEdit, SLOT(clear())); connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear())); new QShortcut(QKeySequence::Find, this, SLOT(searchFocus()), nullptr, Qt::ApplicationShortcut); @@ -101,6 +104,11 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event) return true; } } + } else if (event->type() == QEvent::FocusOut) { + // Auto-clear search after 5 minutes + m_clearSearchTimer->start(300000); + } else if (event->type() == QEvent::FocusIn) { + m_clearSearchTimer->stop(); } return QObject::eventFilter(obj, event); diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h index 39e17bcf4..6f4387004 100644 --- a/src/gui/SearchWidget.h +++ b/src/gui/SearchWidget.h @@ -69,6 +69,7 @@ private slots: private: const QScopedPointer m_ui; QTimer* m_searchTimer; + QTimer* m_clearSearchTimer; QAction* m_actionCaseSensitive; QAction* m_actionLimitGroup; }; From 880c3aeb349d2990ae3a297f06d79d79f25b4402 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Thu, 1 Nov 2018 21:33:27 -0400 Subject: [PATCH 58/84] Add search help pop-up * Support ! modifier (same as '-') * Create reusable PopupHelpWidget as self-contained popup that can be positioned around a parent widget and will follow the movement and sizing of the window * Eliminated KEEPASSXC_MAIN_WINDOW macro and replaced with getMainWindow() function * Add tests to cover search help show/hide --- .../application/16x16/actions/system-help.png | Bin 0 -> 897 bytes .../application/22x22/actions/system-help.png | Bin 0 -> 1222 bytes .../application/32x32/actions/system-help.png | Bin 0 -> 2120 bytes src/CMakeLists.txt | 3 +- src/browser/BrowserService.cpp | 4 +- src/core/EntrySearcher.cpp | 3 +- src/gui/Application.cpp | 11 - src/gui/Application.h | 4 - src/gui/MainWindow.cpp | 5 + src/gui/MainWindow.h | 10 +- src/gui/SearchHelpWidget.ui | 458 ++++++++++++++++++ src/gui/SearchWidget.cpp | 29 +- src/gui/SearchWidget.h | 5 + src/gui/SearchWidget.ui | 5 + src/gui/masterkey/KeyFileEditWidget.cpp | 4 +- src/gui/widgets/PopupHelpWidget.cpp | 99 ++++ src/gui/widgets/PopupHelpWidget.h | 48 ++ src/keys/YkChallengeResponseKey.cpp | 6 +- src/main.cpp | 1 - tests/gui/TestGui.cpp | 12 + 20 files changed, 671 insertions(+), 36 deletions(-) create mode 100644 share/icons/application/16x16/actions/system-help.png create mode 100644 share/icons/application/22x22/actions/system-help.png create mode 100644 share/icons/application/32x32/actions/system-help.png create mode 100644 src/gui/SearchHelpWidget.ui create mode 100644 src/gui/widgets/PopupHelpWidget.cpp create mode 100644 src/gui/widgets/PopupHelpWidget.h diff --git a/share/icons/application/16x16/actions/system-help.png b/share/icons/application/16x16/actions/system-help.png new file mode 100644 index 0000000000000000000000000000000000000000..75ebaf7f5f73f57df9cc93159479a672b49176e4 GIT binary patch literal 897 zcmV-{1AhF8P)ll^X4{Z>T-^z>G~pp+RTU*DH>_u zACN&16chTPK!1pS$uAKU{TieonFJR0O9X{qP?pLdjm$y~@e9tTuJ-NJx!t+Do?SZB z7rZ$h?mq9&`+1(vQL-%KAWcn8wAE^L7>&j%mSuB7q0k7=^Pikf=Nb+|4ECv~r>D{H z_iu`#C~s_R$fQtFBodLw#>T$3wY6FHexFRx-rlY*E-oIi+wEoHa2S(IK}>og;G=OI zH88MSIUF`<5s$|sE|<$$S6BBM1R6W!=H|XEDJdzN^Q@ue!xBbUInahI==5m_(CK*E z@59IMK9FRb!{KNn4J}E7uCA`C^78U){y-3q`u>2;$bqyO#0|Rvcdz84+I9#=eJTd$ z;~1Rb6VbWC!ov4S1Jb=w5Cpoz?T4Vt#=Kv|R8WPMcT;%M^9?7hnJ{xAyde=CE-zx+ z6!P=)&ye#lsX#E9OsBTCw&3=NSfjTQiwY28fIF(jJv$VJXV)>aA|Wo)7@Ozeu%&|I zxEdva#bU`Od&2_HAhac6JtkpOkU%4jTbGaHRo_nxEy)OOituj;?ema9)$a}avK*eJ>X4h59 zT0xdjR*(isk`j#v6oZwOm0^uWgWG4dV8jSWQ{-haXt!(3(4CCYD51X5t zrQ{@bc6L_O*4FluSjeh>ey#qW#2lTBOSUxJsWd|*#naQ%U#hFC&w;ROkTfYR zE$ucK3??$NP^na)X-C2g7Z-Vt$Ftnq+xwRMZ2bV1iB?Iiv@Zcg0H7vfccdYrhyK1{*`rB9wAjfeS6r z-haLReodR)NO|;|d~>+>96smVbH2Y*!8A?$A9COT34&k)P)c@_#Z=3Z$(t#MXkiIt z5U)5B)CHivySw|PmX;P@6{)18qzWWSlKy6mkB`p{4h~KMctDJ;eGmqx%}nyinwpx+ zv$L~z4Z|>ZcXv%$md$WDY_6@Xam&xi_dJHj0(gNeSXumFUFpf5o}RY8zP?VI&E|e8 zM=>Jw1Jm z_w&Z%5WfCx15IbDA?5%Hf|@CZBMNTc{}b=OR*W}Kd*E<5u3aP@~7uUZQkyRitj~T~gl5{1N$G@H!7^7A&sve6sXl?| z&K|z|c?P$ag?-(}kLG~2zP=t{mKD6oWlDL(iK9*+oo%w+CLkwEz_gr;sjwY+xehq# znPP>s>M9G9UmFUA76EL)3g+5(I-Pj$tP9UXgGuP2T;SOvz*{8Xn@ctL>f(!Ns0?W(G6I9ld09O86(uGvgZ_}*T6h(30nU7%LR~g$*H~piI zgs#C<6;MbJ-Bsbzws7@AB^ta195uPMwe^bM@Bfi+I&0j(F9<%x7fM%GS7(#AK&miTYt=@#&Tzj=0Y kmc;^DPgzT~hM58KFXXsKDcM2yZ~y=R07*qoM6N<$g81D##{d8T literal 0 HcmV?d00001 diff --git a/share/icons/application/32x32/actions/system-help.png b/share/icons/application/32x32/actions/system-help.png new file mode 100644 index 0000000000000000000000000000000000000000..8a9eb1a827fd0eaf1f5be508de78c32eed68a163 GIT binary patch literal 2120 zcmV-O2)Fl%P)MvdBPMi;O~2rGiiMI;q3v1v_PDPFi(2|^bZVYx3ZW`)A;0t*WZqJtMC zB_hky^Gx?+J~rLL^iR*sGv9vaJLi3$^WM(8-vBTsi1AHWIu z98VPy5%G3iUEK|*(>Wv*qQAc%eSLlC?d=@_&hgv8z`zjKb07Cp&X@_vPpwoc{Ypzq z^9Bb8osS+p!o!CTaqr$e+`fGqU0q#pI2`Ec=pgu;bNt3NTuU-e?x!42*6^f!LU3^K zvKBFfa`0q5Jw2cTyWI{kjppWN@M*Kz2>Be>a4q+6FXvk*k8&x0Gy>zf_|Ba>J;Icr zX}i0-L7`NTQ_+Y6=M31CT!NJ`I=rFQVMVkS!SRI%KUSImntDY0^x+8S9cO5P8Lpgu-qd&TU6j zh8Z!J1evTapt*Ddk?AHJyktgnb`w;8sl%I*S8=hZ4y~=NpbW~QOub&8M?I1&Mwp&a z%YdWbXgadbEHz`zi8}1R(usptZzDXb9p{TJ=>O_5V zH6rA+3Be!NV5#adEX{2o0m`K88)8u3FacIB77yk*(}(6_5N$nU!1}Xo*qq*tkiT|e z(>aHDZ-wg5wWw+A#Ld~ zu~<5(S0XTz7YfsaC6z1#4pqP}z72kf9q`q(K@r;mpBO8ijg|;}0dG|~TD$H;8VMI` z;I$VT%^yf0c(PLO;j24OvjUlg5%w+0;SwQLjYc8Qy9nz(w?x--p$hm)P!+ zz>EkZ;xcYR8b)&`Ccl@5r?+1d1au<_Y|O@lx6gu#DVsX(-n~0Wz3vu}2#jih=_&&Z z<}OHMO{@;z*-?zA-!GOEkXt}bKxP4149H!u{}KJhR^jf0Y*V_vFBT&*%8wuS+mV5TF&l zMqqs=RA)*?1fjuTaAkq44#?9mucir#C==$!+d%?KK|pc13Ccqa@I6!yKlM#4P*-Db zT0LHgt-`Zm<(Rpz3{!WPVDgS4Jo#S1zX^m~!MLBLA-BRjA`R2i(_M8yRtee3NQH>L z+I-3YWsC)Lj|f*BvEfIFRxC=kU~x(V0+VXd+0*B`2Y_FkG+<$DCHzGJQiSUk*)-#1c(479J|coG_6M?_Pz^yXVo;=AdG^`DJ&3+il1;O1g~5IJ-ia{h$#))R+-? zq6y1CvEp?>U|B*1?||W(aD9>vOX91rSW|@;)fMpBTY!n1Gmu%_K;I9aUNkyRl}hEh z4L$S&vg?>vsUXpeMWVj{;P6enoFa()t^rGuO?W9h7rRrncy)g^7Dkuh#h6Mw_hA`k z?bc%6&P-(L8yWc{u4A1}C+i11bVTgo^~QcT-ZEQ(Psey-7?S(u$Cmp3HB#j=iQl%9}1oV+EIOvvnrOs~bdgkmgE#|AzqvQ1Jo(tW zVmS$KZ|@g`&k7$rc;NgW+H&T*Ge1ASkaFb8N3(#Jz%&7c05`r76ciLCnn_w?Xe@tB znCC@BMJ=mWua4q=mmGyl?o*x!NGE^N^+(g!wQ;GyTLK~e{{A1S)#?JRR@))kx?T}r zkA!80_!JFBuQ+W-baZq9*Ks}falcEBQsBERKAs4;llhjwx5-#wvcL=%sRaV`=L&%! yfwcmw1YQ>i5bzh6E%1GTi8Q^tQg;IXSNsdZr>XzdpmVeU0000bringToFront(); + getMainWindow()->bringToFront(); m_bringToFrontRequested = true; } @@ -901,7 +901,7 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget) { if (dbWidget) { if (m_bringToFrontRequested) { - KEEPASSXC_MAIN_WINDOW->lower(); + getMainWindow()->lower(); m_bringToFrontRequested = false; } emit databaseUnlocked(); diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index 6614ab463..f582d62e7 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -96,6 +96,7 @@ bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry) found = !attachments.filter(term->regex).empty(); break; default: + // Terms without a specific field try to match title, username, url, and notes found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch() || term->regex.match(entry->resolvePlaceholder(entry->username())).hasMatch() || term->regex.match(entry->resolvePlaceholder(entry->url())).hasMatch() || @@ -139,7 +140,7 @@ QList > EntrySearcher::parseSearchTerm term->regex = Tools::convertToRegex(term->word, !mods.contains("*"), mods.contains("+"), m_caseSensitive); // Exclude modifier - term->exclude = mods.contains("-"); + term->exclude = mods.contains("-") || mods.contains("!"); // Determine the field to search QString field = result.captured(2); diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index b67f542c6..03cd0e551 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -49,7 +49,6 @@ namespace Application::Application(int& argc, char** argv) : QApplication(argc, argv) - , m_mainWindow(nullptr) #ifdef Q_OS_UNIX , m_unixSignalNotifier(nullptr) #endif @@ -143,16 +142,6 @@ Application::~Application() } } -QWidget* Application::mainWindow() const -{ - return m_mainWindow; -} - -void Application::setMainWindow(QWidget* mainWindow) -{ - m_mainWindow = mainWindow; -} - bool Application::event(QEvent* event) { // Handle Apple QFileOpenEvent from finder (double click on .kdbx file) diff --git a/src/gui/Application.h b/src/gui/Application.h index 7b1a77f60..9a3ef756b 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -37,9 +37,7 @@ class Application : public QApplication public: Application(int& argc, char** argv); - QWidget* mainWindow() const; ~Application() override; - void setMainWindow(QWidget* mainWindow); bool event(QEvent* event) override; bool isAlreadyRunning() const; @@ -60,8 +58,6 @@ private slots: void socketReadyRead(); private: - QWidget* m_mainWindow; - #if defined(Q_OS_UNIX) /** * Register Unix signals such as SIGINT and SIGTERM for clean shutdown. diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index d9f9a0557..67e453392 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -109,12 +109,17 @@ public: const QString MainWindow::BaseWindowTitle = "KeePassXC"; +MainWindow* g_MainWindow = nullptr; +MainWindow* getMainWindow() { return g_MainWindow; } + MainWindow::MainWindow() : m_ui(new Ui::MainWindow()) , m_trayIcon(nullptr) , m_appExitCalled(false) , m_appExiting(false) { + g_MainWindow = this; + m_ui->setupUi(this); #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index caf2a0c58..38b1c9308 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -148,8 +148,12 @@ private: bool m_appExiting; }; -#define KEEPASSXC_MAIN_WINDOW \ - (qobject_cast(qApp) ? qobject_cast(qobject_cast(qApp)->mainWindow()) \ - : nullptr) +/** + * Return instance of MainWindow created on app load + * non-gui instances will return nullptr + * + * @return MainWindow instance or nullptr + */ +MainWindow* getMainWindow(); #endif // KEEPASSX_MAINWINDOW_H diff --git a/src/gui/SearchHelpWidget.ui b/src/gui/SearchHelpWidget.ui new file mode 100644 index 000000000..daa3a851e --- /dev/null +++ b/src/gui/SearchHelpWidget.ui @@ -0,0 +1,458 @@ + + + SearchHelpWidget + + + + 0 + 0 + 334 + 249 + + + + Search Help + + + false + + + #SearchHelpWidget { background-color: #ffffff } + + + QFrame::Box + + + QFrame::Plain + + + + 6 + + + QLayout::SetDefaultConstraint + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 75 + true + + + + Search terms are as follows: [modifiers][field:]["]term["] + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Every search term must match (ie, logical AND) + + + + + + + + + + 50 + false + + + + Modifiers + + + + 8 + + + 8 + + + 9 + + + 10 + + + 9 + + + 9 + + + + + + 10 + 0 + + + + + 75 + true + + + + ! + + + false + + + Qt::AlignCenter + + + + + + + exclude term from results + + + + + + + match term exactly + + + + + + + + 10 + 0 + + + + + 75 + true + + + + * + + + Qt::AlignCenter + + + + + + + use regex in term + + + + + + + + 10 + 0 + + + + + 75 + true + + + + + + + + Qt::AlignCenter + + + + + + + + + + + 50 + false + + + + Fields + + + false + + + + 15 + + + 10 + + + 15 + + + 8 + + + 5 + + + + + username + + + + + + + password + + + + + + + title + + + + + + + url + + + + + + + notes + + + + + + + attribute + + + + + + + attachment + + + + + + + + + + + + + + Term Wildcards + + + + 8 + + + 8 + + + + + + 10 + 0 + + + + + 75 + true + + + + * + + + false + + + Qt::AlignCenter + + + + + + + + 10 + 0 + + + + + 75 + true + + + + ? + + + Qt::AlignCenter + + + + + + + + 10 + 0 + + + + + 75 + true + + + + | + + + Qt::AlignCenter + + + + + + + match anything + + + + + + + match one + + + + + + + logical OR + + + + + + + + + + Examples + + + + 8 + + + + + + 0 + 0 + + + + user:name1 url:google + + + + + + + + 0 + 0 + + + + user:"name1|name2" + + + + + + + + 0 + 0 + + + + +user:name1 *notes:"secret \d" + + + + + + + + + + + + + diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp index 6441502b0..cde899576 100644 --- a/src/gui/SearchWidget.cpp +++ b/src/gui/SearchWidget.cpp @@ -18,6 +18,7 @@ #include "SearchWidget.h" #include "ui_SearchWidget.h" +#include "ui_SearchHelpWidget.h" #include #include @@ -26,6 +27,7 @@ #include "core/Config.h" #include "core/FilePath.h" +#include "gui/widgets/PopupHelpWidget.h" SearchWidget::SearchWidget(QWidget* parent) : QWidget(parent) @@ -35,11 +37,17 @@ SearchWidget::SearchWidget(QWidget* parent) { m_ui->setupUi(this); + m_helpWidget = new PopupHelpWidget(m_ui->searchEdit); + m_helpWidget->setOffset(QPoint(0,1)); + Ui::SearchHelpWidget helpUi; + helpUi.setupUi(m_helpWidget); + m_searchTimer->setSingleShot(true); m_clearSearchTimer->setSingleShot(true); connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), SLOT(startSearchTimer())); connect(m_ui->clearIcon, SIGNAL(triggered(bool)), m_ui->searchEdit, SLOT(clear())); + connect(m_ui->helpIcon, SIGNAL(triggered()), SLOT(toggleHelp())); connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(startSearch())); connect(m_clearSearchTimer, SIGNAL(timeout()), m_ui->searchEdit, SLOT(clear())); connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear())); @@ -65,6 +73,9 @@ SearchWidget::SearchWidget(QWidget* parent) m_ui->searchIcon->setMenu(searchMenu); m_ui->searchEdit->addAction(m_ui->searchIcon, QLineEdit::LeadingPosition); + m_ui->helpIcon->setIcon(filePath()->icon("actions", "system-help")); + m_ui->searchEdit->addAction(m_ui->helpIcon, QLineEdit::TrailingPosition); + m_ui->clearIcon->setIcon(filePath()->icon("actions", "edit-clear-locationbar-rtl")); m_ui->clearIcon->setVisible(false); m_ui->searchEdit->addAction(m_ui->clearIcon, QLineEdit::TrailingPosition); @@ -86,13 +97,6 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event) if (keyEvent->key() == Qt::Key_Escape) { emit escapePressed(); return true; - } else if (keyEvent->matches(QKeySequence::Copy)) { - // If Control+C is pressed in the search edit when no text - // is selected, copy the password of the current entry - if (!m_ui->searchEdit->hasSelectedText()) { - emit copyPressed(); - return true; - } } else if (keyEvent->matches(QKeySequence::MoveToNextLine)) { if (m_ui->searchEdit->cursorPosition() == m_ui->searchEdit->text().length()) { // If down is pressed at EOL, move the focus to the entry view @@ -111,7 +115,7 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event) m_clearSearchTimer->stop(); } - return QObject::eventFilter(obj, event); + return QWidget::eventFilter(obj, event); } void SearchWidget::connectSignals(SignalMultiplexer& mx) @@ -188,3 +192,12 @@ void SearchWidget::searchFocus() m_ui->searchEdit->setFocus(); m_ui->searchEdit->selectAll(); } + +void SearchWidget::toggleHelp() +{ + if (m_helpWidget->isVisible()) { + m_helpWidget->hide(); + } else { + m_helpWidget->show(); + } +} diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h index 6f4387004..43dd76430 100644 --- a/src/gui/SearchWidget.h +++ b/src/gui/SearchWidget.h @@ -30,6 +30,8 @@ namespace Ui class SearchWidget; } +class PopupHelpWidget; + class SearchWidget : public QWidget { Q_OBJECT @@ -45,6 +47,7 @@ public: void setLimitGroup(bool state); protected: + // Filter key presses in the search field bool eventFilter(QObject* obj, QEvent* event) override; signals: @@ -65,9 +68,11 @@ private slots: void updateCaseSensitive(); void updateLimitGroup(); void searchFocus(); + void toggleHelp(); private: const QScopedPointer m_ui; + PopupHelpWidget* m_helpWidget; QTimer* m_searchTimer; QTimer* m_clearSearchTimer; QAction* m_actionCaseSensitive; diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui index 19b274a88..93fbbdee5 100644 --- a/src/gui/SearchWidget.ui +++ b/src/gui/SearchWidget.ui @@ -81,6 +81,11 @@ Clear + + + Search Help + + searchEdit diff --git a/src/gui/masterkey/KeyFileEditWidget.cpp b/src/gui/masterkey/KeyFileEditWidget.cpp index 14ac4879c..c694e2c5a 100644 --- a/src/gui/masterkey/KeyFileEditWidget.cpp +++ b/src/gui/masterkey/KeyFileEditWidget.cpp @@ -45,7 +45,7 @@ bool KeyFileEditWidget::addToCompositeKey(QSharedPointer key) } if (fileKey->type() != FileKey::Hashed) { - QMessageBox::warning(KEEPASSXC_MAIN_WINDOW, + QMessageBox::warning(getMainWindow(), tr("Legacy key file format"), tr("You are using a legacy key file format which may become\n" "unsupported in the future.\n\n" @@ -100,7 +100,7 @@ void KeyFileEditWidget::createKeyFile() QString errorMsg; bool created = FileKey::create(fileName, &errorMsg); if (!created) { - MessageBox::critical(KEEPASSXC_MAIN_WINDOW, tr("Error creating key file"), + MessageBox::critical(getMainWindow(), tr("Error creating key file"), tr("Unable to create key file: %1").arg(errorMsg), QMessageBox::Button::Ok); } else { m_compUi->keyFileCombo->setEditText(fileName); diff --git a/src/gui/widgets/PopupHelpWidget.cpp b/src/gui/widgets/PopupHelpWidget.cpp new file mode 100644 index 000000000..45e19e81e --- /dev/null +++ b/src/gui/widgets/PopupHelpWidget.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PopupHelpWidget.h" + +#include + +#include "gui/MainWindow.h" + +PopupHelpWidget::PopupHelpWidget(QWidget* parent) + : QFrame(parent) + , m_parentWindow(parent->window()) + , m_appWindow(getMainWindow()) + , m_offset({0, 0}) + , m_corner(Qt::BottomLeftCorner) +{ + Q_ASSERT(parent); + + setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); + hide(); + + m_appWindow->installEventFilter(this); + parent->installEventFilter(this); +} + +PopupHelpWidget::~PopupHelpWidget() +{ + m_parentWindow->removeEventFilter(this); + parentWidget()->removeEventFilter(this); +} + +void PopupHelpWidget::setOffset(const QPoint& offset) +{ + m_offset = offset; + if (isVisible()) { + alignWithParent(); + } +} + +void PopupHelpWidget::setPosition(Qt::Corner corner) +{ + m_corner = corner; + if (isVisible()) { + alignWithParent(); + } +} + +bool PopupHelpWidget::eventFilter(QObject* obj, QEvent* event) +{ + if (obj == parentWidget() && event->type() == QEvent::FocusOut) { + hide(); + } else if (obj == m_appWindow && (event->type() == QEvent::Move || event->type() == QEvent::Resize)) { + if (isVisible()) { + alignWithParent(); + } + } + return QFrame::eventFilter(obj, event); +} + +void PopupHelpWidget::showEvent(QShowEvent* event) +{ + alignWithParent(); + QFrame::showEvent(event); +} + +void PopupHelpWidget::alignWithParent() +{ + QPoint pos; + switch (m_corner) { + case Qt::TopLeftCorner: + pos = parentWidget()->geometry().topLeft() + m_offset - QPoint(0, height()); + break; + case Qt::TopRightCorner: + pos = parentWidget()->geometry().topRight() + m_offset - QPoint(width(), height()); + break; + case Qt::BottomRightCorner: + pos = parentWidget()->geometry().bottomRight() + m_offset - QPoint(width(), 0); + break; + default: + pos = parentWidget()->geometry().bottomLeft() + m_offset; + break; + } + + move(m_parentWindow->mapToGlobal(pos)); +} \ No newline at end of file diff --git a/src/gui/widgets/PopupHelpWidget.h b/src/gui/widgets/PopupHelpWidget.h new file mode 100644 index 000000000..66dac2b30 --- /dev/null +++ b/src/gui/widgets/PopupHelpWidget.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_POPUPHELPWIDGET_H +#define KEEPASSXC_POPUPHELPWIDGET_H + +#include +#include + +class PopupHelpWidget : public QFrame +{ + Q_OBJECT +public: + explicit PopupHelpWidget(QWidget* parent); + ~PopupHelpWidget() override; + + void setOffset(const QPoint& offset); + void setPosition(Qt::Corner corner); + +protected: + bool eventFilter(QObject* obj, QEvent* event) override; + void showEvent(QShowEvent* event) override; + +private: + void alignWithParent(); + QPointer m_parentWindow; + QPointer m_appWindow; + + QPoint m_offset; + Qt::Corner m_corner; +}; + + +#endif //KEEPASSXC_POPUPHELPWIDGET_H diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index b2a40bd23..ade1b6324 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -37,9 +37,9 @@ YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) , m_slot(slot) , m_blocking(blocking) { - if (KEEPASSXC_MAIN_WINDOW) { - connect(this, SIGNAL(userInteractionRequired()), KEEPASSXC_MAIN_WINDOW, SLOT(showYubiKeyPopup())); - connect(this, SIGNAL(userConfirmed()), KEEPASSXC_MAIN_WINDOW, SLOT(hideYubiKeyPopup())); + if (getMainWindow()) { + connect(this, SIGNAL(userInteractionRequired()), getMainWindow(), SLOT(showYubiKeyPopup())); + connect(this, SIGNAL(userConfirmed()), getMainWindow(), SLOT(hideYubiKeyPopup())); } } diff --git a/src/main.cpp b/src/main.cpp index 9764c52d1..c811fe62c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -111,7 +111,6 @@ int main(int argc, char** argv) } MainWindow mainWindow; - app.setMainWindow(&mainWindow); QObject::connect(&app, SIGNAL(anotherInstanceStarted()), &mainWindow, SLOT(bringToFront())); QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront())); QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString))); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index f48d0777c..0adeabd95 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -802,10 +802,22 @@ void TestGui::testSearch() auto* clearButton = searchWidget->findChild("clearIcon"); QVERIFY(!clearButton->isVisible()); + auto* helpButton = searchWidget->findChild("helpIcon"); + auto* helpPanel = searchWidget->findChild("SearchHelpWidget"); + QVERIFY(helpButton->isVisible()); + QVERIFY(!helpPanel->isVisible()); + // Enter search QTest::mouseClick(searchTextEdit, Qt::LeftButton); QTRY_VERIFY(searchTextEdit->hasFocus()); QTRY_VERIFY(!clearButton->isVisible()); + // Show/Hide search help + helpButton->trigger(); + QTRY_VERIFY(helpPanel->isVisible()); + QTest::mouseClick(searchTextEdit, Qt::LeftButton); + QTRY_VERIFY(helpPanel->isVisible()); + helpButton->trigger(); + QTRY_VERIFY(!helpPanel->isVisible()); // Search for "ZZZ" QTest::keyClicks(searchTextEdit, "ZZZ"); QTRY_COMPARE(searchTextEdit->text(), QString("ZZZ")); From 340076974e05ee58ea43635d09f110dcb987caa5 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sat, 17 Nov 2018 09:55:57 -0500 Subject: [PATCH 59/84] Correct logic error in EntrySearcher and add more tests --- src/core/EntrySearcher.cpp | 4 ++-- tests/TestEntrySearcher.cpp | 23 +++++++++++++++++++++-- tests/TestEntrySearcher.h | 4 ++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index f582d62e7..82e6eaa0c 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -23,7 +23,7 @@ EntrySearcher::EntrySearcher(bool caseSensitive) : m_caseSensitive(caseSensitive) - , m_termParser(R"re(([-*+]+)?(?:(\w*):)?(?:(?=")"((?:[^"\\]|\\.)*)"|([^ ]*))( |$))re") + , m_termParser(R"re(([-!*+]+)?(?:(\w*):)?(?:(?=")"((?:[^"\\]|\\.)*)"|([^ ]*))( |$))re") // Group 1 = modifiers, Group 2 = field, Group 3 = quoted string, Group 4 = unquoted string { } @@ -104,7 +104,7 @@ bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry) } // Short circuit if we failed to match or we matched and are excluding this term - if (!found || term->exclude) { + if ((!found && !term->exclude) || (found && term->exclude)) { return false; } } diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 51be468b5..eee9b9101 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -20,12 +20,12 @@ QTEST_GUILESS_MAIN(TestEntrySearcher) -void TestEntrySearcher::initTestCase() +void TestEntrySearcher::init() { m_rootGroup = new Group(); } -void TestEntrySearcher::cleanupTestCase() +void TestEntrySearcher::cleanup() { delete m_rootGroup; } @@ -71,6 +71,7 @@ void TestEntrySearcher::testSearch() eRoot2->setNotes("test term test"); eRoot2->setGroup(m_rootGroup); + // Searching is disabled for these Entry* e1 = new Entry(); e1->setUsername("test search term test"); e1->setGroup(group1); @@ -78,6 +79,7 @@ void TestEntrySearcher::testSearch() Entry* e11 = new Entry(); e11->setNotes("test search term test"); e11->setGroup(group11); + // End searching disabled Entry* e2111 = new Entry(); e2111->setTitle("test search term test"); @@ -85,6 +87,7 @@ void TestEntrySearcher::testSearch() Entry* e2111b = new Entry(); e2111b->setNotes("test search test"); + e2111b->setUsername("user123"); e2111b->setPassword("testpass"); e2111b->setGroup(group2111); @@ -94,9 +97,11 @@ void TestEntrySearcher::testSearch() Entry* e3b = new Entry(); e3b->setTitle("test search test"); + e3b->setUsername("test@email.com"); e3b->setPassword("realpass"); e3b->setGroup(group3); + // Simple search term testing m_searchResult = m_entrySearcher.search("search", m_rootGroup); QCOMPARE(m_searchResult.count(), 5); @@ -106,9 +111,23 @@ void TestEntrySearcher::testSearch() m_searchResult = m_entrySearcher.search("search term", group211); QCOMPARE(m_searchResult.count(), 1); + // Test advanced search terms m_searchResult = m_entrySearcher.search("password:testpass", m_rootGroup); QCOMPARE(m_searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("!user:email.com", m_rootGroup); + QCOMPARE(m_searchResult.count(), 5); + + m_searchResult = m_entrySearcher.search("*user:\".*@.*\\.com\"", m_rootGroup); + QCOMPARE(m_searchResult.count(), 1); + + m_searchResult = m_entrySearcher.search("+user:email", m_rootGroup); + QCOMPARE(m_searchResult.count(), 0); + + // Terms are logical AND together + m_searchResult = m_entrySearcher.search("password:pass user:user", m_rootGroup); + QCOMPARE(m_searchResult.count(), 1); + // Parent group has search disabled m_searchResult = m_entrySearcher.search("search term", group11); QCOMPARE(m_searchResult.count(), 0); diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index f385d618e..e10b1b544 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -28,8 +28,8 @@ class TestEntrySearcher : public QObject Q_OBJECT private slots: - void initTestCase(); - void cleanupTestCase(); + void init(); + void cleanup(); void testAndConcatenationInSearch(); void testSearch(); From d612cad09a4f1cd8ef9bf6be8bf90b75248ecfef Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Thu, 22 Nov 2018 11:47:31 +0100 Subject: [PATCH 60/84] Refactor Database and Database widgets (#2491) The Database, DatabaseWidget, and DatabaseTabWidget classes share many responsibilities in inconsistent ways resulting in impenetrable and unmaintainable code and a diverse set of bugs and architecture restrictions. This patch reworks the architecture, responsibilities of, and dependencies between these classes. The core changes are: * Move loading and saving logic from widgets into the Database class * Get rid of the DatabaseManagerStruct and move all the information contained in it into the Database * Let database objects keep track of modifications and dirty/clean state instead of handing this to external widgets * Move GUI interactions for loading and saving from the DatabaseTabWidget into the DatabaseWidget (resolves #2494 as a side-effect) * Heavily clean up DatabaseTabWidget and degrade it to a slightly glorified QTabWidget * Use QSharedPointers for all Database objects * Remove the modifiedImmediate signal and replace it with a markAsModified() method * Implement proper tabName() method instead of reading back titles from GUI widgets (resolves #1389 and its duplicates #2146 #855) * Fix unwanted AES-KDF downgrade if database uses Argon2 and has CustomData * Improve code This patch is also the first major step towards solving issues #476 and #2322. --- src/autotype/AutoType.cpp | 4 +- src/autotype/AutoType.h | 2 +- src/browser/BrowserEntrySaveDialog.cpp | 9 +- src/browser/BrowserService.cpp | 95 +- src/browser/BrowserService.h | 15 +- src/cli/Add.cpp | 8 +- src/cli/Clip.cpp | 4 +- src/cli/Clip.h | 2 +- src/cli/Edit.cpp | 6 +- src/cli/Extract.cpp | 3 +- src/cli/List.cpp | 2 +- src/cli/Locate.cpp | 2 +- src/cli/Merge.cpp | 20 +- src/cli/Remove.cpp | 6 +- src/cli/Show.cpp | 2 +- src/core/Config.cpp | 1 + src/core/CustomData.cpp | 10 +- src/core/CustomData.h | 2 +- src/core/Database.cpp | 644 +++++++------ src/core/Database.h | 144 +-- src/core/Entry.cpp | 39 +- src/core/Entry.h | 5 +- src/core/EntryAttachments.cpp | 10 +- src/core/EntryAttachments.h | 2 +- src/core/EntryAttributes.cpp | 12 +- src/core/EntryAttributes.h | 2 +- src/core/Group.cpp | 131 ++- src/core/Group.h | 18 +- src/core/Metadata.cpp | 14 +- src/core/Metadata.h | 3 +- src/crypto/SymmetricCipher.cpp | 4 +- src/format/CsvExporter.cpp | 4 +- src/format/CsvExporter.h | 5 +- src/format/Kdbx3Reader.cpp | 57 +- src/format/Kdbx3Reader.h | 10 +- src/format/Kdbx3Writer.cpp | 4 +- src/format/Kdbx4Reader.cpp | 61 +- src/format/Kdbx4Reader.h | 10 +- src/format/Kdbx4Writer.cpp | 4 +- src/format/KdbxReader.cpp | 20 +- src/format/KdbxReader.h | 24 +- src/format/KdbxXmlReader.cpp | 8 +- src/format/KdbxXmlReader.h | 4 +- src/format/KdbxXmlWriter.cpp | 2 +- src/format/KeePass1Reader.cpp | 68 +- src/format/KeePass1Reader.h | 9 +- src/format/KeePass2Reader.cpp | 28 +- src/format/KeePass2Reader.h | 4 +- src/format/KeePass2Writer.cpp | 4 + src/gui/DatabaseOpenWidget.cpp | 35 +- src/gui/DatabaseOpenWidget.h | 6 +- src/gui/DatabaseTabWidget.cpp | 852 ++++++------------ src/gui/DatabaseTabWidget.h | 93 +- src/gui/DatabaseWidget.cpp | 629 +++++++------ src/gui/DatabaseWidget.h | 152 ++-- src/gui/DatabaseWidgetStateSync.cpp | 4 +- src/gui/EditWidget.cpp | 2 +- src/gui/EditWidgetIcons.cpp | 39 +- src/gui/EditWidgetIcons.h | 7 +- src/gui/EntryPreviewWidget.cpp | 5 +- src/gui/KeePass1OpenWidget.cpp | 4 +- src/gui/MainWindow.cpp | 81 +- src/gui/MainWindow.h | 5 +- src/gui/SearchWidget.cpp | 2 +- src/gui/UnlockDatabaseDialog.cpp | 4 +- src/gui/UnlockDatabaseDialog.h | 2 +- src/gui/csvImport/CsvParserModel.cpp | 4 +- src/gui/csvImport/CsvParserModel.h | 2 +- src/gui/dbsettings/DatabaseSettingsDialog.cpp | 2 +- src/gui/dbsettings/DatabaseSettingsDialog.h | 7 +- src/gui/dbsettings/DatabaseSettingsWidget.cpp | 2 +- src/gui/dbsettings/DatabaseSettingsWidget.h | 6 +- .../DatabaseSettingsWidgetGeneral.cpp | 6 +- .../DatabaseSettingsWidgetMasterKey.cpp | 2 +- .../DatabaseSettingsWidgetMasterKey.h | 2 +- src/gui/entry/AutoTypeAssociationsModel.cpp | 2 +- src/gui/entry/AutoTypeAssociationsModel.h | 2 +- src/gui/entry/EditEntryWidget.cpp | 36 +- src/gui/entry/EditEntryWidget.h | 13 +- src/gui/group/EditGroupWidget.cpp | 9 +- src/gui/group/EditGroupWidget.h | 4 +- src/gui/group/GroupModel.cpp | 8 +- src/gui/group/GroupView.cpp | 4 +- src/gui/group/GroupView.h | 2 +- src/gui/widgets/ElidedLabel.cpp | 1 - src/gui/wizard/NewDatabaseWizard.cpp | 23 +- src/gui/wizard/NewDatabaseWizard.h | 6 +- src/gui/wizard/NewDatabaseWizardPage.cpp | 6 +- src/gui/wizard/NewDatabaseWizardPage.h | 4 +- src/keys/drivers/YubiKey.cpp | 6 +- src/sshagent/SSHAgent.cpp | 4 +- src/sshagent/SSHAgent.h | 2 +- tests/TestAutoType.cpp | 3 +- tests/TestAutoType.h | 5 +- tests/TestCli.cpp | 12 +- tests/TestCsvExporter.cpp | 20 +- tests/TestCsvExporter.h | 5 +- tests/TestDatabase.cpp | 37 +- tests/TestDeletedObjects.cpp | 27 +- tests/TestDeletedObjects.h | 2 +- tests/TestKdbx2.cpp | 17 +- tests/TestKdbx2.h | 2 +- tests/TestKdbx3.cpp | 34 +- tests/TestKdbx3.h | 8 +- tests/TestKdbx4.cpp | 28 +- tests/TestKdbx4.h | 8 +- tests/TestKeePass1Reader.cpp | 27 +- tests/TestKeePass1Reader.h | 5 +- tests/TestKeePass2Format.cpp | 17 +- tests/TestKeePass2Format.h | 16 +- tests/TestKeys.cpp | 25 +- tests/TestMerge.cpp | 8 +- tests/TestModified.cpp | 234 +++-- tests/gui/TestGui.cpp | 104 +-- tests/gui/TestGui.h | 3 +- 115 files changed, 2116 insertions(+), 2165 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& dbList) +void AutoType::performGlobalAutoType(const QList>& dbList) { if (!m_plugin) { return; @@ -287,7 +287,7 @@ void AutoType::performGlobalAutoType(const QList& dbList) QList matchList; - for (Database* db : dbList) { + for (const auto& db : dbList) { const QList dbEntries = db->rootGroup()->entriesRecursive(); for (Entry* entry : dbEntries) { const QSet 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& dbList); + void performGlobalAutoType(const QList>& 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& 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 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, 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 BrowserService::searchEntries(Database* db, const QString& hostname, const QString& url) +QList BrowserService::searchEntries(QSharedPointer db, const QString& hostname, const QString& url) { QList entries; - Group* rootGroup = db->rootGroup(); + auto* rootGroup = db->rootGroup(); if (!rootGroup) { return entries; } @@ -415,12 +412,12 @@ QList BrowserService::searchEntries(Database* db, const QString& hostnam QList BrowserService::searchEntries(const QString& url, const StringPairList& keyList) { // Get the list of databases to search - QList databases; + QList> databases; if (browserSettings()->searchInAllDatabases()) { const int count = m_dbTabWidget->count(); for (int i = 0; i < count; ++i) { - if (DatabaseWidget* dbWidget = qobject_cast(m_dbTabWidget->widget(i))) { - if (Database* db = dbWidget->database()) { + if (auto* dbWidget = qobject_cast(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 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 BrowserService::searchEntries(const QString& url, const StringPair QString hostname = QUrl(url).host(); QList 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 BrowserService::searchEntries(const QString& url, const StringPair return entries; } -void BrowserService::convertAttributesToCustomData(Database *currentDb) +void BrowserService::convertAttributesToCustomData(QSharedPointer 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 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 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 BrowserService::selectedDatabase() { QList 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 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 searchEntries(Database* db, const QString& hostname, const QString& url); + QSharedPointer selectedDb = {}); + QList searchEntries(QSharedPointer db, const QString& hostname, const QString& url); QList searchEntries(const QString& url, const StringPairList& keyList); - void convertAttributesToCustomData(Database *currentDb = nullptr); + void convertAttributesToCustomData(QSharedPointer 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 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 getDatabase(); + QSharedPointer selectedDatabase(); bool moveSettingsToCustomData(Entry* entry, const QString& name) const; - int moveKeysToCustomData(Entry* entry, Database* db) const; + int moveKeysToCustomData(Entry* entry, QSharedPointer 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(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, 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, 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 db(reader.readDatabase(&dbFile, compositeKey)); + auto db = QSharedPointer::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 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 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 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 db2; + QSharedPointer 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::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 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 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 * Copyright (C) 2010 Felix Geyer - * Copyright (C) 2017 KeePassXC Team * * 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,60 +18,325 @@ #include "Database.h" -#include -#include -#include -#include -#include -#include -#include -#include - #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 Database::m_uuidMap; +#include +#include +#include +#include +#include +#include + +QHash> Database::s_uuidMap; +QHash> 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::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(); + } +} + +QUuid Database::uuid() const +{ + return m_uuid; +} + +/** + * 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 key, QString* error, bool readOnly) +{ + Q_ASSERT(!m_data.filePath.isEmpty()); + if (m_data.filePath.isEmpty()) { + return false; + } + return open(m_data.filePath, std::move(key), error, readOnly); +} + +/** + * 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 key, QString* error, bool readOnly) +{ + if (isInitialized() && m_modified) { + emit databaseDiscarded(); + } + + 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; +} + +/** + * 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) +{ + 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); +} + +/** + * 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) +{ + 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; +} + +bool Database::writeDatabase(QIODevice* device, QString* error) +{ + 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; +} + +/** + * Remove the old backup and replace it with a new one + * backups are named .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$|(?setParent(this); } @@ -104,115 +380,23 @@ const Metadata* Database::metadata() const QString Database::filePath() const { - return m_filePath; + return m_data.filePath; } void Database::setFilePath(const QString& filePath) { - m_filePath = filePath; -} - -Entry* Database::resolveEntry(const QUuid& uuid) -{ - return findEntryRecursive(uuid, m_rootGroup); -} - -Entry* Database::resolveEntry(const QString& text, EntryReferenceType referenceType) -{ - return findEntryRecursive(text, referenceType, m_rootGroup); -} - -Entry* Database::findEntryRecursive(const QUuid& uuid, Group* group) -{ - const QList entryList = group->entries(); - for (Entry* entry : entryList) { - if (entry->uuid() == uuid) { - return entry; - } + if (filePath == m_data.filePath) { + return; } - const QList children = group->children(); - for (Group* child : children) { - Entry* result = findEntryRecursive(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; -} - -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 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; - } - - if (found) { - return entry; - } - } - - const QList children = group->children(); - for (Group* child : children) { - Entry* result = findEntryRecursive(text, referenceType, child); - if (result) { - return result; - } - } - - return nullptr; -} - -Group* Database::resolveGroup(const QUuid& uuid) -{ - return findGroupRecursive(uuid, m_rootGroup); -} - -Group* Database::findGroupRecursive(const QUuid& uuid, Group* group) -{ - if (group->uuid() == uuid) { - return group; - } - - const QList children = group->children(); - for (Group* child : children) { - Group* result = findGroupRecursive(uuid, child); - if (result) { - return result; - } - } - - return nullptr; + emit filePathChanged(oldPath, filePath); } QList 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(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& 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& 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 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 Database::key() const return m_data.key; } -Database* Database::openDatabaseFile(const QString& fileName, QSharedPointer 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::unlockFromStdin(const QString& databaseFilename, const QString& keyFilename, + FILE* outputDescriptor, FILE* errorDescriptor) { auto compositeKey = QSharedPointer::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 .old.kdbx - * - * @param filePath Path to the file to backup - * @return - */ -bool Database::backupDatabase(const QString& filePath) -{ - QString backupFilePath = filePath; - auto re = QRegularExpression("\\.kdbx$|(?::create(); + db->open(databaseFilename, compositeKey, nullptr, false); + return db; } QSharedPointer Database::kdf() const @@ -650,11 +747,14 @@ QSharedPointer Database::kdf() const void Database::setKdf(QSharedPointer kdf) { + Q_ASSERT(!m_data.isReadOnly); m_data.kdf = std::move(kdf); } bool Database::changeKdf(const QSharedPointer& kdf) { + Q_ASSERT(!m_data.isReadOnly); + kdf->randomizeSeed(); QByteArray transformedMasterKey; if (!m_data.key) { @@ -666,7 +766,7 @@ bool Database::changeKdf(const QSharedPointer& 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 * Copyright (C) 2010 Felix Geyer - * Copyright (C) 2017 KeePassXC Team * * 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 #include #include +#include #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; - QSharedPointer 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 key, QString* error = nullptr, bool readOnly = false); + bool open(const QString& filePath, QSharedPointer 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); + + 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); Metadata* metadata(); const Metadata* metadata() 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); + 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 deletedObjects(); const QList& deletedObjects() const; void addDeletedObject(const DeletedObject& delObj); @@ -99,42 +100,33 @@ public: bool containsDeletedObject(const DeletedObject& uuid) const; void setDeletedObjects(const QList& delObjs); - const QUuid& cipher() const; - Database::CompressionAlgorithm compressionAlgo() const; - QSharedPointer kdf() const; - QByteArray transformedMasterKey() const; + bool hasKey() const; QSharedPointer key() const; + bool setKey(const QSharedPointer& 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); - bool setKey(const QSharedPointer& key, bool updateChangedTime = true, bool updateTransformSalt = false); - bool hasKey() const; bool verifyKey(const QSharedPointer& 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() const; + void setKdf(QSharedPointer kdf); bool changeKdf(const QSharedPointer& kdf); + QByteArray transformedMasterKey() const; static Database* databaseByUuid(const QUuid& uuid); - static Database* openDatabaseFile(const QString& fileName, QSharedPointer key); - static Database* unlockFromStdin(const QString& databaseFilename, const QString& keyFilename = {}, - FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr); + static Database* databaseByFilePath(const QString& filePath); + static QSharedPointer 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 = QSharedPointer::create(true); + QSharedPointer 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 m_deletedObjects; - QTimer* m_timer; - DatabaseData m_data; + QPointer m_timer; + bool m_initialized = false; + bool m_modified = false; bool m_emitModified; - QString m_filePath; - QUuid m_uuid; - static QHash m_uuidMap; + static QHash> s_uuidMap; + static QHash> 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 #include #include #include @@ -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 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& historyEntries) @@ -547,7 +546,7 @@ void Entry::removeHistoryItems(const QList& 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 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 groups = groupsRecursive(true); + + for (const Group* group : groups) { + bool found = false; + const QList& 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& children() const; QList entries(); const QList& entries() const; + Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group = nullptr); QList entriesRecursive(bool includeHistoryItems = false) const; QList groupsRecursive(bool includeSelf) const; QList 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 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 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 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 - 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 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 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 +#include 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 db); + bool exportDatabase(QIODevice* device, QSharedPointer 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 -Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device, - const QByteArray& headerData, - QSharedPointer key, - bool keepDatabase) +bool Kdbx3Reader::readDatabaseImpl(QIODevice* device, + const QByteArray& headerData, + QSharedPointer 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 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 key, - bool keepDatabase) override; + bool readDatabaseImpl(QIODevice* device, + const QByteArray& headerData, + QSharedPointer 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(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toRfc4122())); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::CompressionFlags, - Endian::sizedIntToBytes(db->compressionAlgo(), + Endian::sizedIntToBytes(db->compressionAlgorithm(), KeePass2::BYTEORDER))); auto kdf = db->kdf(); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::MasterSeed, masterSeed)); @@ -112,7 +112,7 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db) QIODevice* outputDevice = nullptr; QScopedPointer 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 key, - bool keepDatabase) +bool Kdbx4Reader::readDatabaseImpl(QIODevice* device, + const QByteArray& headerData, + QSharedPointer 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 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 key, - bool keepDatabase) override; + bool readDatabaseImpl(QIODevice* device, + const QByteArray& headerData, + QSharedPointer key, + Database* db) override; QHash binaryPoolInverse() const; QHash 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(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toRfc4122())); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::CompressionFlags, - Endian::sizedIntToBytes(static_cast(db->compressionAlgo()), + Endian::sizedIntToBytes(static_cast(db->compressionAlgorithm()), KeePass2::BYTEORDER))); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::MasterSeed, masterSeed)); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::EncryptionIV, encryptionIV)); @@ -138,7 +138,7 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db) QIODevice* outputDevice = nullptr; QScopedPointer 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 key, bool keepDatabase) +bool KdbxReader::readDatabase(QIODevice* device, QSharedPointer 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, QSharedPointersetCompressionAlgo(static_cast(id)); + m_db->setCompressionAlgorithm(static_cast(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 key, bool keepDatabase = false); + bool readDatabase(QIODevice* device, QSharedPointer 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 key, - bool keepDatabase) = 0; + virtual bool readDatabaseImpl(QIODevice* device, + const QByteArray& headerData, + QSharedPointer 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 m_db; - - QPair m_kdbxSignature; quint32 m_kdbxVersion = 0; QByteArray m_masterSeed; @@ -102,6 +99,9 @@ protected: QByteArray m_xmlData; private: + QPair m_kdbxSignature; + QPointer 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 binary * @param device input file * @return pointer to the new database */ -Database* KdbxXmlReader::readDatabase(const QString& filename) +QSharedPointer 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 KdbxXmlReader::readDatabase(QIODevice* device) { - auto db = new Database(); - readDatabase(device, db); + auto db = QSharedPointer::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 binaryPool); virtual ~KdbxXmlReader() = default; - virtual Database* readDatabase(const QString& filename); - virtual Database* readDatabase(QIODevice* device); + virtual QSharedPointer readDatabase(const QString& filename); + virtual QSharedPointer 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 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 db(new Database()); + auto db = QSharedPointer::create(); QScopedPointer 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(m_device, KeePass1::BYTEORDER, &ok); if (!ok || signature1 != KeePass1::SIGNATURE_1) { raiseError(tr("Not a KeePass database.")); - return nullptr; + return {}; } auto signature2 = Endian::readSizedInt(m_device, KeePass1::BYTEORDER, &ok); if (!ok || signature2 != KeePass1::SIGNATURE_2) { raiseError(tr("Not a KeePass database.")); - return nullptr; + return {}; } m_encryptionFlags = Endian::readSizedInt(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(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(m_device, KeePass1::BYTEORDER, &ok); if (!ok) { raiseError(tr("Invalid number of groups")); - return nullptr; + return {}; } auto numEntries = Endian::readSizedInt(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(m_device, KeePass1::BYTEORDER, &ok); if (!ok) { raiseError(tr("Invalid number of transform rounds")); - return nullptr; + return {}; } auto kdf = QSharedPointer::create(true); kdf->setRounds(m_transformRounds); @@ -168,14 +167,14 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor QScopedPointer cipherStream(testKeys(password, keyfileData, contentPos)); if (!cipherStream) { - return nullptr; + return {}; } QList 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 KeePass1Reader::readDatabase(QIODevice* device, const QString& password, const QString& keyfileName) { QScopedPointer keyFile; if (!keyfileName.isEmpty()) { keyFile.reset(new QFile(keyfileName)); if (!keyFile->open(QFile::ReadOnly)) { raiseError(keyFile->errorString()); - return nullptr; + return {}; } } - QScopedPointer db(readDatabase(device, password, keyFile.data())); - - return db.take(); + return QSharedPointer(readDatabase(device, password, keyFile.data())); } -Database* KeePass1Reader::readDatabase(const QString& filename, const QString& password, const QString& keyfileName) +QSharedPointer 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 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 #include #include +#include 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 readDatabase(QIODevice* device, const QString& password, QIODevice* keyfileDevice); + QSharedPointer readDatabase(QIODevice* device, const QString& password, const QString& keyfileName); + QSharedPointer 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 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 -#include /** * 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 key) +bool KeePass2Reader::readDatabase(const QString& filename, QSharedPointer key, Database* db) { QFile file(filename); if (!file.open(QFile::ReadOnly)) { raiseError(file.errorString()); - return nullptr; + return false; } - QScopedPointer 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 key, bool keepDatabase) +bool KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer key, Database* db) { m_error = false; m_errorStr.clear(); @@ -69,7 +69,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer '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, QSharedPointersetSaveXml(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 key); - Database* readDatabase(QIODevice* device, QSharedPointer key, bool keepDatabase = false); + bool readDatabase(const QString& filename, QSharedPointer key, Database* db); + bool readDatabase(QIODevice* device, QSharedPointer 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 DatabaseOpenWidget::database() { return m_db; } @@ -178,9 +177,8 @@ void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile) void DatabaseOpenWidget::openDatabase() { - KeePass2Reader reader; QSharedPointer masterKey = databaseKey(); - if (masterKey.isNull()) { + if (!masterKey) { return; } @@ -189,19 +187,18 @@ 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); + m_db.reset(new Database()); + QString error; + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + 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; } - delete m_db; - - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - m_db = reader.readDatabase(&file, masterKey); - QApplication::restoreOverrideCursor(); - if (m_db) { #ifdef WITH_XC_TOUCHID QHash useTouchID = config()->get("UseTouchID").toHash(); @@ -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 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(); 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 m_ui; - Database* m_db; + QSharedPointer 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 - * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2018 KeePassXC Team * * 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 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 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 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 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; + auto* dbWidget = new DatabaseWidget(QSharedPointer::create(filePath), this); + addDatabaseTab(dbWidget); + updateLastDatabases(filePath); +} - // 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(); +/** + * Add a new database tab containing the given DatabaseWidget + * @param filePath + */ +void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget) +{ + auto db = dbWidget->database(); + Q_ASSERT(db); - Database* db = new Database(); - dbStruct.dbWidget = new DatabaseWidget(db, this); - dbStruct.fileInfo = fileInfo; + int index = addTab(dbWidget, ""); + updateTabName(index); + setCurrentIndex(index); + toggleTabbar(); - insertDatabase(db, dbStruct); - - if (dbStruct.readOnly) { - emit messageTab(tr("File opened in read only mode."), MessageWidget::Warning); - } - - updateLastDatabases(dbStruct.fileInfo.absoluteFilePath()); - - if (!pw.isNull() || !keyFile.isEmpty()) { - dbStruct.dbWidget->switchToOpenDatabase(dbStruct.fileInfo.absoluteFilePath(), pw, keyFile); - } else { - dbStruct.dbWidget->switchToOpenDatabase(dbStruct.fileInfo.absoluteFilePath()); - } - - 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::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(); + return closeDatabaseTab(qobject_cast(sender())); +} - int index = databaseIndex(db); +/** + * 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) +{ + return closeDatabaseTab(qobject_cast(widget(index))); +} - removeTab(index); +/** + * 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) +{ + int tabIndex = indexOf(dbWidget); + if (!dbWidget || tabIndex < 0) { + return false; + } + + QString filePath = dbWidget->database()->filePath(); + if (!dbWidget->close()) { + return false; + } + + removeTab(tabIndex); + dbWidget->deleteLater(); toggleTabbar(); - m_dbList.remove(db); - delete dbStruct.dbWidget; - delete db; - - if (emitDatabaseWithFileClosed) { - emit databaseWithFileClosed(filePath); - } -} - -bool DatabaseTabWidget::closeAllDatabases() -{ - while (!m_dbList.isEmpty()) { - if (!closeDatabase()) { - return false; - } - } + emit databaseClosed(filePath); return true; } -bool DatabaseTabWidget::saveDatabase(Database* db, QString filePath) +/** + * 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() { - 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; + while (count() > 0) { + if (!closeDatabaseTab(0)) { + return false; } - - emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage), MessageWidget::Error); - 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 - return false; - } -} - -bool DatabaseTabWidget::closeDatabase(int index) -{ - if (index == -1) { - index = currentIndex(); } - setCurrentIndex(index); - - return closeDatabase(indexDatabase(index)); -} - -void DatabaseTabWidget::closeDatabaseFromSender() -{ - Q_ASSERT(sender()); - DatabaseWidget* dbWidget = static_cast(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(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; -} - -QString DatabaseTabWidget::databasePath(int index) -{ - if (index == -1) { - index = currentIndex(); - } - - return indexDatabaseManagerStruct(index).fileInfo.absoluteFilePath(); -} - -void DatabaseTabWidget::updateTabName(Database* db) -{ - int index = databaseIndex(db); - Q_ASSERT(index != -1); - - const DatabaseManagerStruct& dbStruct = m_dbList.value(db); - - QString tabName; - QString fileName; - - if (dbStruct.fileInfo.exists()) { - if (db->metadata()->name().isEmpty()) { - tabName = dbStruct.fileInfo.fileName(); - } else { - tabName = db->metadata()->name(); - } - - fileName = dbStruct.fileInfo.fileName(); - setTabToolTip(index, dbStruct.fileInfo.absoluteFilePath()); - } else { - if (db->metadata()->name().isEmpty()) { - tabName = tr("New database"); - } else { - tabName = tr("%1 [New database]", "tab modifier").arg(db->metadata()->name()); - } - } - - if (dbStruct.dbWidget->currentMode() == DatabaseWidget::LockedMode) { - tabName = tr("%1 [locked]", "tab modifier").arg(tabName); - } - - if (dbStruct.modified) { - tabName.append("*"); - } - - dbStruct.dbWidget->setDatabaseName(tabName); - dbStruct.dbWidget->setDatabaseFileName(fileName); - - setTabText(index, tabName); - emit tabNameChanged(); -} - -void DatabaseTabWidget::updateTabNameFromDbSender() -{ - Q_ASSERT(qobject_cast(sender())); - - updateTabName(static_cast(sender())); -} - -void DatabaseTabWidget::updateTabNameFromDbWidgetSender() -{ - Q_ASSERT(qobject_cast(sender())); - Q_ASSERT(databaseFromDatabaseWidget(qobject_cast(sender()))); - - DatabaseWidget* dbWidget = static_cast(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 i(m_dbList); - while (i.hasNext()) { - i.next(); - if (i.value().dbWidget == dbWidget) { - return i.key(); - } - } - - return nullptr; -} - -DatabaseManagerStruct DatabaseTabWidget::indexDatabaseManagerStruct(int index) -{ - QWidget* dbWidget = widget(index); - - QHashIterator i(m_dbList); - while (i.hasNext()) { - i.next(); - if (i.value().dbWidget == dbWidget) { - return i.value(); - } - } - - return DatabaseManagerStruct(); -} - -Database* DatabaseTabWidget::databaseFromDatabaseWidget(DatabaseWidget* dbWidget) -{ - QHashIterator i(m_dbList); - while (i.hasNext()) { - i.next(); - if (i.value().dbWidget == dbWidget) { - return i.key(); - } - } - - return nullptr; -} - -void DatabaseTabWidget::insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct) -{ - 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())); -} - -DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget() -{ - Database* db = indexDatabase(currentIndex()); - if (db) { - return m_dbList[db].dbWidget; - } else { - return nullptr; - } + return !isReadOnly(index) && isModified(index); } bool DatabaseTabWidget::hasLockableDatabases() const { - QHashIterator 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()) { + for (int i = 0, c = count(); i < c; ++i) { + if (!databaseWidgetFromIndex(i)->isLocked()) { return true; } } - return false; } +/** + * 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) +{ + if (index == -1 || index > count()) { + return ""; + } + + auto* dbWidget = databaseWidgetFromIndex(index); + + auto db = dbWidget->database(); + Q_ASSERT(db); + if (!db) { + return ""; + } + + QString tabName; + + if (!db->filePath().isEmpty()) { + QFileInfo fileInfo(db->filePath()); + + if (db->metadata()->name().isEmpty()) { + tabName = fileInfo.fileName(); + } else { + tabName = db->metadata()->name(); + } + + setTabToolTip(index, fileInfo.absoluteFilePath()); + } else { + if (db->metadata()->name().isEmpty()) { + tabName = tr("New Database"); + } else { + tabName = tr("%1 [New Database]", "Database tab name modifier").arg(db->metadata()->name()); + } + } + + if (dbWidget->isLocked()) { + tabName = tr("%1 [Locked]", "Database tab name modifier").arg(tabName); + } + + if (db->isReadOnly()) { + tabName = tr("%1 [Read-only]", "Database tab name modifier").arg(tabName); + } + + if (db->isModified()) { + tabName.append("*"); + } + + return tabName; +} + +/** + * Update of the given tab index or of the sending + * DatabaseWidget if `index` == -1. + */ +void DatabaseTabWidget::updateTabName(int index) +{ + auto* dbWidget = databaseWidgetFromIndex(index); + if (!dbWidget) { + dbWidget = qobject_cast(sender()); + } + Q_ASSERT(dbWidget); + if (!dbWidget) { + return; + } + index = indexOf(dbWidget); + setTabText(index, tabName(index)); + emit tabNameChanged(); +} + +DatabaseWidget* DatabaseTabWidget::databaseWidgetFromIndex(int index) const +{ + return qobject_cast(widget(index)); +} + +DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget() +{ + return qobject_cast(currentWidget()); +} + void DatabaseTabWidget::lockDatabases() { - clipboard()->clearCopiedText(); - - for (int i = 0; i < count(); i++) { - DatabaseWidget* dbWidget = static_cast(widget(i)); - Database* db = databaseFromDatabaseWidget(dbWidget); - - if (dbWidget->currentMode() == DatabaseWidget::LockedMode || !dbWidget->dbHasKey()) { - continue; + for (int i = 0, c = count(); i < c; ++i) { + if (!databaseWidgetFromIndex(i)->lock()) { + return; } - - // 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; - } - } - - 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(sender())); - - Database* db = static_cast(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(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(sender())); -} - -void DatabaseTabWidget::connectDatabase(Database* newDb, Database* oldDb) -{ - if (oldDb) { - oldDb->disconnect(this); + auto* dbWidget = qobject_cast(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 unlockedDatabases; + QList> unlockedDatabases; - QHashIterator i(m_dbList); - while (i.hasNext()) { - i.next(); - DatabaseWidget::Mode mode = i.value().dbWidget->currentMode(); - - 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 - * Copyright (C) 2011 Felix Geyer + * Copyright (C) 2018 KeePassXC Team * * 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 -#include -#include - -#include "gui/DatabaseWidget.h" #include "gui/MessageWidget.h" +#include +#include + +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); - DatabaseWidget* currentDatabaseWidget(); - bool hasLockableDatabases() const; - DatabaseManagerStruct indexDatabaseManagerStruct(int index); + void mergeDatabase(const QString& filePath); - static const int LastDatabasesCount; + QString tabName(int index); + DatabaseWidget* currentDatabaseWidget(); + DatabaseWidget* databaseWidgetFromIndex(int index) const; + + 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 execNewDatabaseWizard(); void updateLastDatabases(const QString& filename); - void connectDatabase(Database* newDb, Database* oldDb = nullptr); - QHash m_dbList; QPointer m_dbWidgetStateSync; QPointer 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 + * Copyright (C) 2018 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -32,6 +32,7 @@ #include #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 db, QWidget* parent) : QStackedWidget(parent) - , m_db(db) - , m_newGroup(nullptr) - , m_newEntry(nullptr) - , m_newParent(nullptr) -{ - m_mainWidget = new QWidget(this); + , m_db(std::move(db)) - m_messageWidget = new MessageWidget(this); + , 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_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::create(filePath), parent) +{ +} + DatabaseWidget::~DatabaseWidget() { delete m_EntrySearcher; } +QSharedPointer 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 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() { - if (accepted) { - replaceDatabase(static_cast(sender())->database()); - setCurrentWidget(m_mainWidget); - emit unlockedDatabase(); + // 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())); +} - // 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); +void DatabaseWidget::loadDatabase(bool accepted) +{ + auto* openWidget = qobject_cast(sender()); + Q_ASSERT(openWidget); + if (!openWidget) { + return; + } + + if (accepted) { + replaceDatabase(openWidget->database()); + setCurrentWidget(m_mainWidget); + 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(sender())->database(); + auto srcDb = qobject_cast(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::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 dbList; + QList> 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 -{ - return m_entryView->inSearchMode(); -} - Group* DatabaseWidget::currentGroup() const { return m_groupView->currentGroup(); } -void DatabaseWidget::lock() +void DatabaseWidget::closeEvent(QCloseEvent* event) { - Q_ASSERT(currentMode() != DatabaseWidget::LockedMode); + if (!isLocked() && !lock()) { + event->ignore(); + return; + } + + event->accept(); +} + +void DatabaseWidget::showEvent(QShowEvent* event) +{ + if (!m_db->isInitialized() || isLocked()) { + switchToOpenDatabase(); + } + + event->accept(); +} + +bool DatabaseWidget::lock() +{ + 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::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); + QString error; + auto db = QSharedPointer::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); - 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; - } + if (result == QMessageBox::Yes) { + // Merge the old database into the new one + Merger merger(m_db.data(), db.data()); + merger.merge(); } - - QUuid groupBeforeReload; - if (m_groupView && m_groupView->currentGroup()) { - groupBeforeReload = m_groupView->currentGroup()->uuid(); - } else { - groupBeforeReload = m_db->rootGroup()->uuid(); - } - - QUuid entryBeforeReload; - if (m_entryView && m_entryView->currentEntry()) { - entryBeforeReload = m_entryView->currentEntry()->uuid(); - } - - replaceDatabase(db); - restoreGroupEntryFocus(groupBeforeReload, entryBeforeReload); } + + QUuid groupBeforeReload; + if (m_groupView && m_groupView->currentGroup()) { + groupBeforeReload = m_groupView->currentGroup()->uuid(); + } else { + groupBeforeReload = m_db->rootGroup()->uuid(); + } + + 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) { - m_unlockDatabaseDialog->close(); + // 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; } -void DatabaseWidget::showMessage(const QString& text, - MessageWidget::MessageType type, - bool showClosebutton, - int autoHideTimeout) +/** + * Save database under a new user-selected filename. + * + * @return true on success + */ +bool DatabaseWidget::saveAs() +{ + 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) { 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 * Copyright (C) 2010 Felix Geyer - * Copyright (C) 2017 KeePassXC Team * * 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 db, QWidget* parent = nullptr); + explicit DatabaseWidget(const QString& filePath, QWidget* parent = nullptr); ~DatabaseWidget(); - Database* database(); - bool dbHasKey() const; - bool canDeleteCurrentGroup() const; - bool isInSearchMode() const; - QString getCurrentSearch(); - Group* currentGroup() const; - int addWidget(QWidget* w); - void setCurrentIndex(int index); - void setCurrentWidget(QWidget* widget); + + QSharedPointer database() const; + + bool lock(); + void prepareUnlock(); + bool save(int attempt = 0); + bool saveAs(); + DatabaseWidget::Mode currentMode() const; - void lock(); - void updateFilePath(const QString& filePath); - int numberOfSelectedEntries() const; - QStringList customEntryAttributes() const; + bool isLocked() const; + bool isSearchActive() const; + + QString getCurrentSearch(); + void refreshSearch(); + + GroupView* groupView(); + EntryView* entryView(); + + Group* currentGroup() const; + bool canDeleteCurrentGroup() const; bool isGroupSelected() const; - bool isInEditMode() const; + bool isRecycleBinSelected() const; + int numberOfSelectedEntries() const; + + QStringList customEntryAttributes() const; bool isEditWidgetModified() const; - QList mainSplitterSizes() const; - void setMainSplitterSizes(const QList& sizes); - QList previewSplitterSizes() const; - void setPreviewSplitterSizes(const QList& 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 mainSplitterSizes() const; + void setMainSplitterSizes(const QList& sizes); + QList previewSplitterSizes() const; + void setPreviewSplitterSizes(const QList& 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 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 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 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 m_db; + + QPointer m_mainWidget; + QPointer m_mainSplitter; + QPointer m_messageWidget; + QPointer m_previewView; + QPointer m_previewSplitter; + QPointer m_searchingLabel; + QPointer m_csvImportWizard; + QPointer m_editEntryWidget; + QPointer m_editGroupWidget; + QPointer m_historyEditEntryWidget; + QPointer m_databaseSettingDialog; + QPointer m_databaseOpenWidget; + QPointer m_databaseOpenMergeWidget; + QPointer m_keepass1OpenWidget; + QPointer m_unlockDatabaseWidget; + QPointer m_unlockDatabaseDialog; + QPointer m_groupView; + QPointer m_entryView; + + QPointer m_newGroup; + QPointer m_newEntry; + QPointer 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, + 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 allEntries = m_database->rootGroup()->entriesRecursive(true); + const QList allEntries = m_db->rootGroup()->entriesRecursive(true); QList entriesWithSameIcon; QList historyEntriesWithSameIcon; @@ -427,7 +430,7 @@ void EditWidgetIcons::removeCustomIcon() } } - const QList allGroups = m_database->rootGroup()->groupsRecursive(true); + const QList allGroups = m_db->rootGroup()->groupsRecursive(true); QList 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, + const IconStruct& iconStruct, + const QString& url = ""); public slots: void setUrl(const QString& url); @@ -97,7 +100,7 @@ private slots: private: const QScopedPointer m_ui; - Database* m_database; + QSharedPointer 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 #include #include @@ -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::create(); + auto key = QSharedPointer::create(); + if (!pw.isEmpty()) { + key->addKey(QSharedPointer::create(pw)); + } + if (!keyFile.isEmpty()) { + auto fileKey = QSharedPointer::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 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 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(); 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 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 #include +#include +#include class Database; class DatabaseSettingsWidgetGeneral; @@ -47,7 +48,7 @@ public: ~DatabaseSettingsDialog() override; Q_DISABLE_COPY(DatabaseSettingsDialog); - void load(Database* db); + void load(QSharedPointer db); void showMasterKeySettings(); signals: @@ -66,7 +67,7 @@ private: Security = 1 }; - QPointer m_db; + QSharedPointer m_db; const QScopedPointer m_ui; QPointer m_generalWidget; QPointer 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 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 +#include class Database; @@ -36,7 +36,7 @@ public: Q_DISABLE_COPY(DatabaseSettingsWidget); ~DatabaseSettingsWidget() override; - virtual void load(Database* db); + virtual void load(QSharedPointer db); signals: /** @@ -45,7 +45,7 @@ signals: void sizeChanged(); protected: - QPointer m_db; + QSharedPointer 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 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 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 + /* * Copyright (C) 2010 Felix Geyer * Copyright (C) 2017 KeePassXC Team @@ -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) { 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(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); - 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 m_entry; + QSharedPointer 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) { 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); void clear(); signals: @@ -65,7 +65,7 @@ private: QPointer m_editWidgetProperties; QPointer m_group; - QPointer m_database; + QSharedPointer 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 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 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 #include 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 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::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 -#include +#include #include class Database; @@ -36,14 +36,14 @@ public: explicit NewDatabaseWizard(QWidget* parent = nullptr); ~NewDatabaseWizard() override; - Database* takeDatabase(); + QSharedPointer takeDatabase(); bool validateCurrentPage() override; protected: void initializePage(int id) override; private: - QScopedPointer m_db; + QSharedPointer m_db; QList> 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 + /* * Copyright (C) 2018 KeePassXC Team * @@ -66,9 +68,9 @@ DatabaseSettingsWidget* NewDatabaseWizardPage::pageWidget() * * @param db database object to be configured */ -void NewDatabaseWizardPage::setDatabase(Database* db) +void NewDatabaseWizardPage::setDatabase(QSharedPointer 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 db); void initializePage() override; bool validatePage() override; @@ -53,7 +53,7 @@ public slots: protected: QPointer m_pageWidget; - QPointer m_db; + QSharedPointer m_db; const QScopedPointer 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 -#include - #include #include #include @@ -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 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::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 +#include class AutoType; class AutoTypePlatformInterface; @@ -53,8 +54,8 @@ private: AutoTypePlatformInterface* m_platform; AutoTypeTestInterface* m_test; AutoType* m_autoType; - Database* m_db; - QList m_dbList; + QSharedPointer m_db; + QList> 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 mergedDb(reader.readDatabase(&readBack, oldKey)); + auto mergedDb = QSharedPointer::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::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::create("a")); QFile readBack(m_dbFile->fileName()); readBack.open(QIODevice::ReadOnly); - QScopedPointer readBackDb(reader.readDatabase(&readBack, key)); + auto readBackDb = QSharedPointer::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::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::create(); + m_csvExporter = QSharedPointer::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 +#include class Database; class CsvExporter; @@ -40,8 +41,8 @@ private slots: void testNestedGroups(); private: - Database* m_db; - CsvExporter* m_csvExporter; + QSharedPointer m_db; + QSharedPointer 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::create(); key->addKey(QSharedPointer::create("123")); - QScopedPointer db(Database::openDatabaseFile(filename, key)); - QVERIFY(db); + auto db = QSharedPointer::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::create(); key->addKey(QSharedPointer::create("123")); - QScopedPointer db(Database::openDatabaseFile(filename, key)); - QVERIFY(db); + auto db = QSharedPointer::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::create(); key->addKey(QSharedPointer::create("123")); - QScopedPointer db(Database::openDatabaseFile(filename, key)); - QVERIFY(db); + auto db = QSharedPointer::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::create(); key->addKey(QSharedPointer::create("123")); - QScopedPointer db(Database::openDatabaseFile(filename, key)); - QVERIFY(db); + auto db = QSharedPointer::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 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::create(); createAndDelete(db, 0); - - delete db; } void TestDeletedObjects::testDatabaseChange() { - Database* db = new Database(); + auto db = QSharedPointer::create(); Group* root = db->rootGroup(); int delObjectsSize = 0; - Database* db2 = new Database(); + auto db2 = QSharedPointer::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 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 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::create(); key->addKey(QSharedPointer::create("a")); + auto db = QSharedPointer::create(); KeePass2Reader reader; - QScopedPointer 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::create(); key->addKey(QSharedPointer::create("a")); + auto db = QSharedPointer::create(); KeePass2Reader reader; - QScopedPointer 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 targetDb(reader.readDatabase(&buffer, key)); + auto targetDb = QSharedPointer::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 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 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 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 key, - QScopedPointer& db, + QSharedPointer 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 key, - QScopedPointer& db, + QSharedPointer 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::create(); key->addKey(QSharedPointer::create("a")); KeePass2Reader reader; - QScopedPointer db(reader.readDatabase(filename, key)); + auto db = QSharedPointer::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::create(); key->addKey(QSharedPointer::create(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6"))); KeePass2Reader reader; - QScopedPointer db(reader.readDatabase(filename, key)); + auto db = QSharedPointer::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::create(); key->addKey(QSharedPointer::create("")); KeePass2Reader reader; - QScopedPointer db(reader.readDatabase(filename, key)); + auto db = QSharedPointer::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::create(); key->addKey(QSharedPointer::create("masterpw")); KeePass2Reader reader; - QScopedPointer db(reader.readDatabase(filename, key)); + auto db = QSharedPointer::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::create(); key->addKey(QSharedPointer::create("")); - KeePass2Reader reader; - QScopedPointer db(reader.readDatabase(filename, key)); - QVERIFY(!db.data()); - QVERIFY(reader.hasError()); + auto db = QSharedPointer::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 readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override; + QSharedPointer 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 key, - QScopedPointer& db, + QSharedPointer db, bool& hasError, QString& errorString) override; void readKdbx(const QString& path, QSharedPointer key, - QScopedPointer& db, + QSharedPointer 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 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 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 key, - QScopedPointer& db, + QSharedPointer 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 key, - QScopedPointer& db, + QSharedPointer 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::create(); key->addKey(QSharedPointer::create("t")); KeePass2Reader reader; - QScopedPointer db(reader.readDatabase(filename, key)); + auto db = QSharedPointer::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 targetDb(reader.readDatabase(&buffer, key)); + auto targetDb = QSharedPointer::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 db2; - db2.reset(reader.readDatabase(&buffer, QSharedPointer::create())); + auto db2 = QSharedPointer::create(); + reader.readDatabase(&buffer, QSharedPointer::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::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 newDb(reader.readDatabase(&buffer, QSharedPointer::create())); + auto newDb = QSharedPointer::create(); + reader.readDatabase(&buffer, QSharedPointer::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 readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override; + QSharedPointer 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 key, - QScopedPointer& db, + QSharedPointer db, bool& hasError, QString& errorString) override; void readKdbx(QIODevice* device, QSharedPointer key, - QScopedPointer& db, + QSharedPointer 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 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 newDb(reader.readDatabase(&buffer, key)); - QVERIFY(newDb); + auto newDb = QSharedPointer::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 #include +#include 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 db, const QString& password, const QString& keyfileName); - Database* m_db; + QSharedPointer 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::create(); key->addKey(QSharedPointer::create("test")); - m_kdbxSourceDb.reset(new Database()); + m_kdbxSourceDb = QSharedPointer::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 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 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 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 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::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 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 db(new Database()); + auto db = QSharedPointer::create(); db->setKey(QSharedPointer::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 #include #include -#include +#include #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 readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) = 0; + virtual QSharedPointer 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 key, - QScopedPointer& db, + QSharedPointer db, bool& hasError, QString& errorString) = 0; virtual void readKdbx(const QString& path, QSharedPointer key, - QScopedPointer& db, + QSharedPointer db, bool& hasError, QString& errorString) = 0; virtual void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) = 0; - QScopedPointer m_xmlDb; - QScopedPointer m_kdbxSourceDb; - QScopedPointer m_kdbxTargetDb; + QSharedPointer m_xmlDb; + QSharedPointer m_kdbxSourceDb; + QSharedPointer 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 db(reader.readDatabase(dbFilename, compositeKey)); - QVERIFY(db); + auto db = QSharedPointer::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 dbRead(reader.readDatabase(&dbBuffer, compositeKey)); + auto dbRead = QSharedPointer::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 db1(new Database()); + auto db1 = QSharedPointer::create(); db1->setKey(compositeKeyEnc); KeePass2Writer writer; @@ -245,27 +246,27 @@ void TestKeys::testCompositeKeyComponents() QVERIFY(writer.writeDatabase(&buffer, db1.data())); buffer.seek(0); - QScopedPointer db2; + auto db2 = QSharedPointer::create(); KeePass2Reader reader; auto compositeKeyDec1 = QSharedPointer::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::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::create(); @@ -298,6 +299,6 @@ void TestKeys::testCompositeKeyComponents() compositeKeyDec4->addKey(fileKeyEnc); compositeKeyDec4->addChallengeResponseKey(QSharedPointer::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 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 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 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 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("databaseOpenWidget"); + auto* databaseOpenWidget = m_tabWidget->currentDatabaseWidget()->findChild("databaseOpenWidget"); + QVERIFY(databaseOpenWidget); auto* editPassword = databaseOpenWidget->findChild("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))); + QApplication::processEvents(); // set file to merge from fileDialog()->setNextFileName(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx")); triggerAction("actionDatabaseMerge"); - auto* databaseOpenMergeWidget = m_mainWindow->findChild("databaseOpenMergeWidget"); + auto* databaseOpenMergeWidget = m_tabWidget->currentDatabaseWidget()->findChild("databaseOpenMergeWidget"); auto* editPasswordMerge = databaseOpenMergeWidget->findChild("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"); auto* titleEdit = editEntryWidget->findChild("titleEdit"); QTest::keyClicks(titleEdit, "_test"); // Apply the edit auto* editEntryWidgetButtonBox = editEntryWidget->findChild("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"); 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("usernameEdit")->setText("multiline\nusername"); editEntryWidget->findChild("passwordEdit")->setText("multiline\npassword"); @@ -550,11 +552,11 @@ void TestGui::testSearchEditEntry() auto* searchTextEdit = searchWidget->findChild("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")->findChild("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"); @@ -586,7 +588,7 @@ void TestGui::testAddEntry() auto* editEntryWidgetButtonBox = editEntryWidget->findChild("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"); @@ -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"); @@ -736,7 +738,7 @@ void TestGui::testTotp() auto* entryView = m_dbWidget->findChild("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->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"); @@ -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("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"); @@ -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("keepass1OpenWidget"); + auto* keepass1OpenWidget = m_tabWidget->currentDatabaseWidget()->findChild("keepass1OpenWidget"); auto* editPassword = keepass1OpenWidget->findChild("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("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("actionDatabaseMerge", Qt::FindChildrenRecursively); QCOMPARE(actionDatabaseMerge->isEnabled(), true); @@ -1304,10 +1306,8 @@ void TestGui::checkDatabase(QString dbFileName) auto key = QSharedPointer::create(); key->addKey(QSharedPointer::create("a")); - KeePass2Reader reader; - QScopedPointer dbSaved(reader.readDatabase(dbFileName, key)); - QVERIFY(dbSaved); - QVERIFY(!reader.hasError()); + auto dbSaved = QSharedPointer::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 #include #include +#include class Database; class DatabaseTabWidget; @@ -88,7 +89,7 @@ private: QScopedPointer m_mainWindow; QPointer m_tabWidget; QPointer m_dbWidget; - QPointer m_db; + QSharedPointer m_db; QByteArray m_dbData; QScopedPointer m_dbFile; QString m_dbFileName; From 785a64cc3b3123541657831e615b0e97d3341ca5 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 23 Nov 2018 13:49:55 +0100 Subject: [PATCH 61/84] Fix bugs introduced by database refactor #2491 (#2503) * Fix SSHAgent identity removal on database lock * Refactor storage and manipulation of SSHAgent keys to streamline process with multiple db's * Clear password field when widget is hidden, resolves #2502 --- src/gui/DatabaseOpenWidget.cpp | 11 +- src/gui/DatabaseWidget.cpp | 9 +- src/gui/PasswordEdit.cpp | 2 +- src/gui/PasswordEdit.h | 3 +- src/gui/entry/EditEntryWidget.cpp | 13 +- src/gui/entry/EditEntryWidget.h | 1 + src/sshagent/SSHAgent.cpp | 238 ++++++++++++++++-------------- src/sshagent/SSHAgent.h | 9 +- 8 files changed, 155 insertions(+), 131 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 73c41ae0c..b3427dc49 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -124,6 +124,12 @@ void DatabaseOpenWidget::hideEvent(QHideEvent* event) disconnect(YubiKey::instance(), nullptr, this, nullptr); m_yubiKeyBeingPolled = false; #endif + + if (isVisible()) { + return; + } + + clearForms(); } void DatabaseOpenWidget::load(const QString& filename) @@ -148,8 +154,9 @@ void DatabaseOpenWidget::load(const QString& filename) void DatabaseOpenWidget::clearForms() { - m_ui->editPassword->clear(); + m_ui->editPassword->setText(""); m_ui->comboKeyFile->clear(); + m_ui->comboKeyFile->setEditText(""); m_ui->checkPassword->setChecked(true); m_ui->checkKeyFile->setChecked(false); m_ui->checkChallengeResponse->setChecked(false); @@ -225,7 +232,7 @@ void DatabaseOpenWidget::openDatabase() } else { m_ui->messageWidget->showMessage(tr("Unable to open the database:\n%1").arg(error), MessageWidget::Error); - m_ui->editPassword->clear(); + m_ui->editPassword->setText(""); #ifdef WITH_XC_TOUCHID // unable to unlock database, reset TouchID for the current database diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 71db6258e..60c529fe6 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -212,11 +212,8 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) #ifdef WITH_XC_SSHAGENT if (config()->get("SSHAgent", false).toBool()) { - connect(this, - SIGNAL(currentModeChanged(DatabaseWidget::Mode)), - SSHAgent::instance(), - SLOT(databaseModeChanged(DatabaseWidget::Mode))); - connect(this, SIGNAL(closeRequest()), SSHAgent::instance(), SLOT(databaseModeChanged())); + connect(this, SIGNAL(databaseLocked()), SSHAgent::instance(), SLOT(databaseModeChanged())); + connect(this, SIGNAL(databaseUnlocked()), SSHAgent::instance(), SLOT(databaseModeChanged())); } #endif @@ -1147,7 +1144,7 @@ bool DatabaseWidget::lock() if (!m_db->save(nullptr, false, false)) { return false; } - } else if (isLocked()) { + } 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()); diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 02067ccc4..6ec044f9e 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -61,7 +61,7 @@ void PasswordEdit::setShowPassword(bool show) setText(m_basePasswordEdit->text()); } else { // This fix a bug when the QLineEdit is disabled while switching config - if (isEnabled() == false) { + if (!isEnabled()) { setEnabled(true); setReadOnly(false); } diff --git a/src/gui/PasswordEdit.h b/src/gui/PasswordEdit.h index 222376d09..29b33f0bd 100644 --- a/src/gui/PasswordEdit.h +++ b/src/gui/PasswordEdit.h @@ -20,6 +20,7 @@ #define KEEPASSX_PASSWORDEDIT_H #include +#include class PasswordEdit : public QLineEdit { @@ -46,7 +47,7 @@ private slots: private: bool passwordsEqual() const; - PasswordEdit* m_basePasswordEdit; + QPointer m_basePasswordEdit; }; #endif // KEEPASSX_PASSWORDEDIT_H diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 657c352b8..a19a89332 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -1,5 +1,3 @@ -#include - /* * Copyright (C) 2010 Felix Geyer * Copyright (C) 2017 KeePassXC Team @@ -443,6 +441,8 @@ void EditEntryWidget::updateSSHAgentKeyInfo() if (SSHAgent::instance()->isAgentRunning()) { m_sshAgentUi->addToAgentButton->setEnabled(true); m_sshAgentUi->removeFromAgentButton->setEnabled(true); + + SSHAgent::instance()->setAutoRemoveOnLock(key, m_sshAgentUi->removeKeyFromAgentCheckBox->isChecked()); } } @@ -558,21 +558,18 @@ void EditEntryWidget::addKeyToAgent() m_sshAgentUi->commentTextLabel->setText(key.comment()); m_sshAgentUi->publicKeyEdit->document()->setPlainText(key.publicKey()); - quint32 lifetime = 0; + int lifetime = 0; bool confirm = m_sshAgentUi->requireUserConfirmationCheckBox->isChecked(); if (m_sshAgentUi->lifetimeCheckBox->isChecked()) { lifetime = m_sshAgentUi->lifetimeSpinBox->value(); } - if (!SSHAgent::instance()->addIdentity(key, lifetime, confirm)) { + if (!SSHAgent::instance()->addIdentity(key, m_sshAgentUi->removeKeyFromAgentCheckBox->isChecked(), + static_cast(lifetime), confirm)) { showMessage(SSHAgent::instance()->errorString(), MessageWidget::Error); return; } - - if (m_sshAgentUi->removeKeyFromAgentCheckBox->isChecked()) { - SSHAgent::instance()->removeIdentityAtLock(key, m_entry->uuid()); - } } void EditEntryWidget::removeKeyFromAgent() diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index a594d2072..d45b29726 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "config-keepassx.h" #include "gui/EditWidget.h" diff --git a/src/sshagent/SSHAgent.cpp b/src/sshagent/SSHAgent.cpp index 05299091a..6244e8d45 100644 --- a/src/sshagent/SSHAgent.cpp +++ b/src/sshagent/SSHAgent.cpp @@ -38,16 +38,17 @@ SSHAgent::SSHAgent(QObject* parent) SSHAgent::~SSHAgent() { - for (const QSet& keys : m_keys.values()) { - for (OpenSSHKey key : keys) { - removeIdentity(key); - } + auto it = m_addedKeys.begin(); + while (it != m_addedKeys.end()) { + OpenSSHKey key = it.key(); + removeIdentity(key); + it = m_addedKeys.erase(it); } } SSHAgent* SSHAgent::instance() { - if (m_instance == nullptr) { + if (!m_instance) { qFatal("Race condition: instance wanted before it was initialized, this is a bug."); } @@ -159,7 +160,16 @@ bool SSHAgent::sendMessage(const QByteArray& in, QByteArray& out) #endif } -bool SSHAgent::addIdentity(OpenSSHKey& key, quint32 lifetime, bool confirm) +/** + * Add the identity to the SSH agent. + * + * @param key identity / key to add + * @param lifetime time after which the key should expire + * @param confirm ask for confirmation before adding the key + * @param removeOnLock autoremove from agent when the Database is locked + * @return true on success + */ +bool SSHAgent::addIdentity(OpenSSHKey& key, bool removeOnLock, quint32 lifetime, bool confirm) { if (!isAgentRunning()) { m_error = tr("No agent running, cannot add identity."); @@ -201,9 +211,18 @@ bool SSHAgent::addIdentity(OpenSSHKey& key, quint32 lifetime, bool confirm) return false; } + OpenSSHKey keyCopy = key; + keyCopy.clearPrivate(); + m_addedKeys[keyCopy] = removeOnLock; return true; } +/** + * Remove an identity from the SSH agent. + * + * @param key identity to remove + * @return true on success + */ bool SSHAgent::removeIdentity(OpenSSHKey& key) { if (!isAgentRunning()) { @@ -222,120 +241,121 @@ bool SSHAgent::removeIdentity(OpenSSHKey& key) request.writeString(keyData); QByteArray responseData; - if (!sendMessage(requestData, responseData)) { - return false; - } - - if (responseData.length() < 1 || static_cast(responseData[0]) != SSH_AGENT_SUCCESS) { - m_error = tr("Agent does not have this identity."); - return false; - } - - return true; + return sendMessage(requestData, responseData); } -void SSHAgent::removeIdentityAtLock(const OpenSSHKey& key, const QUuid& uuid) +/** + * Change "remove identity on lock" setting for a key already added to the agent. + * Will to nothing if the key has not been added to the agent. + * + * @param key key to change setting for + * @param autoRemove whether to remove the key from the agent when database is locked + */ +void SSHAgent::setAutoRemoveOnLock(const OpenSSHKey& key, bool autoRemove) { - OpenSSHKey copy = key; - copy.clearPrivate(); - m_keys[uuid].insert(copy); + if (m_addedKeys.contains(key)) { + m_addedKeys[key] = autoRemove; + } } -void SSHAgent::databaseModeChanged(DatabaseWidget::Mode mode) +void SSHAgent::databaseModeChanged() { - DatabaseWidget* widget = qobject_cast(sender()); - - if (widget == nullptr) { + auto* widget = qobject_cast(sender()); + if (!widget) { return; } - const QUuid& uuid = widget->database()->uuid(); - - if (mode == DatabaseWidget::Mode::LockedMode && m_keys.contains(uuid)) { - - QSet keys = m_keys.take(uuid); - for (OpenSSHKey key : keys) { - if (!removeIdentity(key)) { - emit error(m_error); - } - } - } else if (mode == DatabaseWidget::Mode::ViewMode && !m_keys.contains(uuid)) { - for (Entry* e : widget->database()->rootGroup()->entriesRecursive()) { - - if (widget->database()->metadata()->recycleBinEnabled() - && e->group() == widget->database()->metadata()->recycleBin()) { - continue; - } - - if (!e->attachments()->hasKey("KeeAgent.settings")) { - continue; - } - - KeeAgentSettings settings; - settings.fromXml(e->attachments()->value("KeeAgent.settings")); - - if (!settings.allowUseOfSshKey()) { - continue; - } - - QByteArray keyData; - QString fileName; - if (settings.selectedType() == "attachment") { - fileName = settings.attachmentName(); - keyData = e->attachments()->value(fileName); - } else if (!settings.fileName().isEmpty()) { - QFile file(settings.fileName()); - QFileInfo fileInfo(file); - - fileName = fileInfo.fileName(); - - if (file.size() > 1024 * 1024) { - continue; - } - - if (!file.open(QIODevice::ReadOnly)) { - continue; - } - - keyData = file.readAll(); - } - - if (keyData.isEmpty()) { - continue; - } - - OpenSSHKey key; - - if (!key.parse(keyData)) { - continue; - } - - if (!key.openPrivateKey(e->password())) { - continue; - } - - if (key.comment().isEmpty()) { - key.setComment(e->username()); - } - - if (key.comment().isEmpty()) { - key.setComment(fileName); - } - - if (settings.removeAtDatabaseClose()) { - removeIdentityAtLock(key, uuid); - } - - if (settings.addAtDatabaseOpen()) { - int lifetime = 0; - - if (settings.useLifetimeConstraintWhenAdding()) { - lifetime = settings.lifetimeConstraintDuration(); - } - - if (!addIdentity(key, lifetime, settings.useConfirmConstraintWhenAdding())) { + if (widget->isLocked()) { + auto it = m_addedKeys.begin(); + while (it != m_addedKeys.end()) { + OpenSSHKey key = it.key(); + if (it.value()) { + if (!removeIdentity(key)) { emit error(m_error); } + it = m_addedKeys.erase(it); + } else { + // don't remove it yet + m_addedKeys[key] = false; + ++it; + } + } + + return; + } + + for (Entry* e : widget->database()->rootGroup()->entriesRecursive()) { + + if (widget->database()->metadata()->recycleBinEnabled() + && e->group() == widget->database()->metadata()->recycleBin()) { + continue; + } + + if (!e->attachments()->hasKey("KeeAgent.settings")) { + continue; + } + + KeeAgentSettings settings; + settings.fromXml(e->attachments()->value("KeeAgent.settings")); + + if (!settings.allowUseOfSshKey()) { + continue; + } + + QByteArray keyData; + QString fileName; + if (settings.selectedType() == "attachment") { + fileName = settings.attachmentName(); + keyData = e->attachments()->value(fileName); + } else if (!settings.fileName().isEmpty()) { + QFile file(settings.fileName()); + QFileInfo fileInfo(file); + + fileName = fileInfo.fileName(); + + if (file.size() > 1024 * 1024) { + continue; + } + + if (!file.open(QIODevice::ReadOnly)) { + continue; + } + + keyData = file.readAll(); + } + + if (keyData.isEmpty()) { + continue; + } + + OpenSSHKey key; + + if (!key.parse(keyData)) { + continue; + } + + if (!key.openPrivateKey(e->password())) { + continue; + } + + if (key.comment().isEmpty()) { + key.setComment(e->username()); + } + + if (key.comment().isEmpty()) { + key.setComment(fileName); + } + + if (!m_addedKeys.contains(key) && settings.addAtDatabaseOpen()) { + quint32 lifetime = 0; + + if (settings.useLifetimeConstraintWhenAdding()) { + lifetime = static_cast(settings.lifetimeConstraintDuration()); + } + + if (!addIdentity(key, settings.removeAtDatabaseClose(), + lifetime, settings.useConfirmConstraintWhenAdding())) { + emit error(m_error); } } } diff --git a/src/sshagent/SSHAgent.h b/src/sshagent/SSHAgent.h index 7cd8a1f1f..4311a911a 100644 --- a/src/sshagent/SSHAgent.h +++ b/src/sshagent/SSHAgent.h @@ -21,6 +21,7 @@ #include "OpenSSHKey.h" #include +#include #include #include "gui/DatabaseWidget.h" @@ -35,15 +36,15 @@ public: const QString errorString() const; bool isAgentRunning() const; - bool addIdentity(OpenSSHKey& key, quint32 lifetime = 0, bool confirm = false); + bool addIdentity(OpenSSHKey& key, bool removeOnLock, quint32 lifetime, bool confirm); bool removeIdentity(OpenSSHKey& key); - void removeIdentityAtLock(const OpenSSHKey& key, const QUuid& uuid); + void setAutoRemoveOnLock(const OpenSSHKey& key, bool autoRemove); signals: void error(const QString& message); public slots: - void databaseModeChanged(DatabaseWidget::Mode mode = DatabaseWidget::Mode::LockedMode); + void databaseModeChanged(); private: const quint8 SSH_AGENT_FAILURE = 5; @@ -71,7 +72,7 @@ private: const quint32 AGENT_COPYDATA_ID = 0x804e50ba; #endif - QMap> m_keys; + QHash m_addedKeys; QString m_error; }; From ff7191eef3f10a5d70a975177308872dbb750291 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Fri, 23 Nov 2018 13:24:59 -0500 Subject: [PATCH 62/84] Re-implement AutoOpen functionality after refactor (#2504) The database refactor in #2491 removed auto-open functionality. --- src/gui/DatabaseTabWidget.cpp | 30 ++++++++++++++++------ src/gui/DatabaseTabWidget.h | 4 +-- src/gui/DatabaseWidget.cpp | 48 +++++++++++++++++++++++++++++++++++ src/gui/DatabaseWidget.h | 3 +++ 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 49c5a1545..7f1f9942e 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -126,8 +126,10 @@ void DatabaseTabWidget::openDatabase() * database has been opened already. * * @param filePath database file path + * @param password optional, password to unlock database + * @param inBackground optional, don't focus tab after opening */ -void DatabaseTabWidget::addDatabaseTab(const QString& filePath) +void DatabaseTabWidget::addDatabaseTab(const QString& filePath, bool inBackground, const QString& password) { QFileInfo fileInfo(filePath); QString canonicalFilePath = fileInfo.canonicalFilePath(); @@ -140,32 +142,44 @@ void DatabaseTabWidget::addDatabaseTab(const QString& filePath) 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)); + if (!password.isEmpty()) { + dbWidget->performUnlockDatabase(password); + } + if (!inBackground) { + // switch to existing tab if file is already open + setCurrentIndex(indexOf(dbWidget)); + } return; } } auto* dbWidget = new DatabaseWidget(QSharedPointer::create(filePath), this); - addDatabaseTab(dbWidget); + addDatabaseTab(dbWidget, inBackground); + if (!password.isEmpty()) { + dbWidget->performUnlockDatabase(password); + } updateLastDatabases(filePath); } /** * Add a new database tab containing the given DatabaseWidget * @param filePath + * @param inBackground optional, don't focus tab after opening */ -void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget) +void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget, bool inBackground) { - auto db = dbWidget->database(); - Q_ASSERT(db); + Q_ASSERT(dbWidget->database()); int index = addTab(dbWidget, ""); updateTabName(index); - setCurrentIndex(index); toggleTabbar(); + if (!inBackground) { + setCurrentIndex(index); + } + connect(dbWidget, SIGNAL(databaseFilePathChanged(QString,QString)), SLOT(updateTabName())); + connect(dbWidget, SIGNAL(requestOpenDatabase(QString,bool,QString)), SLOT(addDatabaseTab(QString,bool,QString))); connect(dbWidget, SIGNAL(closeRequest()), SLOT(closeDatabaseTabFromSender())); connect(dbWidget, SIGNAL(databaseModified()), SLOT(updateTabName())); connect(dbWidget, SIGNAL(databaseSaved()), SLOT(updateTabName())); diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index d24b45af8..f9d4bdf15 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -48,8 +48,8 @@ public: bool hasLockableDatabases() const; public slots: - void addDatabaseTab(const QString& filePath); - void addDatabaseTab(DatabaseWidget* dbWidget); + void addDatabaseTab(const QString& filePath, bool inBackground = false, const QString& password = {}); + void addDatabaseTab(DatabaseWidget* dbWidget, bool inBackground = false); bool closeDatabaseTab(int index); bool closeDatabaseTab(DatabaseWidget* dbWidget); bool closeAllDatabaseTabs(); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 60c529fe6..782c9b83f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -397,6 +397,7 @@ void DatabaseWidget::replaceDatabase(QSharedPointer db) m_db = std::move(db); connectDatabaseSignals(); m_groupView->changeDatabase(m_db); + processAutoOpen(); } void DatabaseWidget::cloneEntry() @@ -954,6 +955,7 @@ void DatabaseWidget::switchToOpenDatabase(const QString& filePath) setCurrentWidget(m_unlockDatabaseWidget); } } + void DatabaseWidget::switchToCsvImport(const QString& filePath) { setCurrentWidget(m_csvImportWizard); @@ -980,6 +982,18 @@ void DatabaseWidget::switchToImportKeepass1(const QString& filePath) setCurrentWidget(m_keepass1OpenWidget); } +void DatabaseWidget::performUnlockDatabase(const QString& password, const QString& keyfile) +{ + if (password.isEmpty() && keyfile.isEmpty()) { + return; + } + + if (!m_db->isInitialized() || isLocked()) { + switchToOpenDatabase(); + m_databaseOpenWidget->enterKey(password, keyfile); + } +} + void DatabaseWidget::refreshSearch() { if (isSearchActive()) { @@ -1561,3 +1575,37 @@ void DatabaseWidget::emptyRecycleBin() refreshSearch(); } } + +void DatabaseWidget::processAutoOpen() +{ + Q_ASSERT(m_db); + + auto* autoopenGroup = m_db->rootGroup()->findGroupByPath("/AutoOpen"); + if (!autoopenGroup) { + return; + } + + for (const auto* entry : autoopenGroup->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()) { + QFileInfo currentpath(m_db->filePath()); + filepath.setFile(currentpath.absoluteDir(), entry->url()); + } + } + + if (!filepath.isFile()) { + continue; + } + + // Request to open the database file in the background + emit requestOpenDatabase(filepath.canonicalFilePath(), true, entry->password()); + } +} diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 1a908cf19..addabe060 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -132,6 +132,7 @@ signals: void currentModeChanged(DatabaseWidget::Mode mode); void groupChanged(); void entrySelectionChanged(); + void requestOpenDatabase(const QString& filePath, bool inBackground, const QString& password); void databaseMerged(QSharedPointer mergedDb); void groupContextMenuRequested(const QPoint& globalPos); void entryContextMenuRequested(const QPoint& globalPos); @@ -180,6 +181,7 @@ public slots: void switchToOpenMergeDatabase(const QString& filePath); void switchToOpenMergeDatabase(const QString& filePath, const QString& password, const QString& keyFile); void switchToImportKeepass1(const QString& filePath); + void performUnlockDatabase(const QString& password, const QString& keyfile = {}); void emptyRecycleBin(); // Search related slots @@ -226,6 +228,7 @@ private: int addChildWidget(QWidget* w); void setClipboardTextAndMinimize(const QString& text); void setIconFromParent(); + void processAutoOpen(); QSharedPointer m_db; From 3c362ac82279123432f421df8c4a1bc71bb44b24 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 24 Nov 2018 00:57:41 +0100 Subject: [PATCH 63/84] Refactor DatabaseOpenWidget/Dialog and Auto-Type Database unlocking. This patch removes redundant lock widget members of the DatabaseWidget and consolidates all unlocking functionality into a single DatabaseOpenWidget (with the exception of KeePass1OpenWidget). Distinction between different unlock actions is now done via a dedicated Intent enum class instead of using individual widgets. Further, the DatabaseUnlockDialog has been generalized so that it is usable for unlock intents other than just Auto-Type and is now also used for merging databases which is less confusing to the user. The KeePassXC main window is no longer a parent of the DatabaseUnlockDialog and has the Qt::ForeignWindow flag set, which should cause fewer issues with Auto-Type trying to type into KeePassXC after unlock instead of the intended target window. In addition, its instance has been moved into the DatabaseTabWidget class so that it is no longer bound to individual DatabaseWidgets, potentially allowing for database selection during Auto-Type. The actual selection has not yet been implemented, but Auto-Type has been adjusted to use the currently selected tab instead of the first one as an intermediary improvement. --- src/CMakeLists.txt | 3 +- src/gui/DatabaseOpenDialog.cpp | 89 +++++++ ...kDatabaseDialog.h => DatabaseOpenDialog.h} | 41 ++-- src/gui/DatabaseTabWidget.cpp | 66 +++++- src/gui/DatabaseTabWidget.h | 7 +- src/gui/DatabaseWidget.cpp | 221 ++++++++---------- src/gui/DatabaseWidget.h | 37 ++- src/gui/KeePass1OpenWidget.cpp | 3 +- src/gui/MainWindow.cpp | 2 +- src/gui/UnlockDatabaseDialog.cpp | 56 ----- src/gui/UnlockDatabaseWidget.cpp | 28 --- src/gui/UnlockDatabaseWidget.h | 31 --- tests/gui/TestGui.cpp | 12 +- 13 files changed, 296 insertions(+), 300 deletions(-) create mode 100644 src/gui/DatabaseOpenDialog.cpp rename src/gui/{UnlockDatabaseDialog.h => DatabaseOpenDialog.h} (60%) delete mode 100644 src/gui/UnlockDatabaseDialog.cpp delete mode 100644 src/gui/UnlockDatabaseWidget.cpp delete mode 100644 src/gui/UnlockDatabaseWidget.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f68afc8f6..f8e7b2269 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,8 +123,7 @@ set(keepassx_SOURCES gui/TotpSetupDialog.cpp gui/TotpDialog.cpp gui/TotpExportSettingsDialog.cpp - gui/UnlockDatabaseWidget.cpp - gui/UnlockDatabaseDialog.cpp + gui/DatabaseOpenDialog.cpp gui/WelcomeWidget.cpp gui/csvImport/CsvImportWidget.cpp gui/csvImport/CsvImportWizard.cpp diff --git a/src/gui/DatabaseOpenDialog.cpp b/src/gui/DatabaseOpenDialog.cpp new file mode 100644 index 000000000..1b8b018e2 --- /dev/null +++ b/src/gui/DatabaseOpenDialog.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "DatabaseOpenDialog.h" +#include "DatabaseOpenWidget.h" +#include "DatabaseWidget.h" +#include "core/Database.h" + +DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent) + : QDialog(parent) + , m_view(new DatabaseOpenWidget(this)) +{ + setWindowTitle(tr("Unlock Database - KeePassXC")); + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint | Qt::ForeignWindow); + connect(m_view, SIGNAL(dialogFinished(bool)), this, SLOT(complete(bool))); +} + +void DatabaseOpenDialog::setFilePath(const QString& filePath) +{ + m_view->load(filePath); +} + +/** + * Set target DatabaseWidget to which signals are connected. + * + * @param dbWidget database widget + */ +void DatabaseOpenDialog::setTargetDatabaseWidget(DatabaseWidget* dbWidget) +{ + if (m_dbWidget) { + disconnect(this, nullptr, m_dbWidget, nullptr); + } + m_dbWidget = dbWidget; + connect(this, SIGNAL(dialogFinished(bool)), dbWidget, SLOT(unlockDatabase(bool))); +} + +void DatabaseOpenDialog::setIntent(DatabaseOpenDialog::Intent intent) +{ + m_intent = intent; +} + +DatabaseOpenDialog::Intent DatabaseOpenDialog::intent() const +{ + return m_intent; +} + +void DatabaseOpenDialog::clearForms() +{ + m_view->clearForms(); + m_db.reset(); + m_intent = Intent::None; + if (m_dbWidget) { + disconnect(this, nullptr, m_dbWidget, nullptr); + m_dbWidget = nullptr; + } +} + +QSharedPointer DatabaseOpenDialog::database() +{ + return m_db; +} + +void DatabaseOpenDialog::complete(bool accepted) +{ + // save DB, since DatabaseOpenWidget will reset its data after accept() is called + m_db = m_view->database(); + + if (accepted) { + accept(); + } else { + reject(); + } + emit dialogFinished(accepted); + clearForms(); +} diff --git a/src/gui/UnlockDatabaseDialog.h b/src/gui/DatabaseOpenDialog.h similarity index 60% rename from src/gui/UnlockDatabaseDialog.h rename to src/gui/DatabaseOpenDialog.h index 7f06029c8..236d2e0c2 100644 --- a/src/gui/UnlockDatabaseDialog.h +++ b/src/gui/DatabaseOpenDialog.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 KeePassXC Team + * Copyright (C) 2018 KeePassXC Team * * 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,32 +18,47 @@ #ifndef KEEPASSX_AUTOTYPEUNLOCKDIALOG_H #define KEEPASSX_AUTOTYPEUNLOCKDIALOG_H -#include - -//#include - #include "core/Global.h" -class UnlockDatabaseWidget; -class Database; +#include +#include +#include -class UnlockDatabaseDialog : public QDialog +class Database; +class DatabaseWidget; +class DatabaseOpenWidget; + +class DatabaseOpenDialog : public QDialog { Q_OBJECT + public: - explicit UnlockDatabaseDialog(QWidget* parent = nullptr); + enum class Intent + { + None, + AutoType, + Merge + }; + + explicit DatabaseOpenDialog(QWidget* parent = nullptr); void setFilePath(const QString& filePath); - void clearForms(); + void setTargetDatabaseWidget(DatabaseWidget* dbWidget); + void setIntent(Intent intent); + Intent intent() const; QSharedPointer database(); + void clearForms(); signals: - void unlockDone(bool); + void dialogFinished(bool); public slots: - void complete(bool r); + void complete(bool accepted); private: - UnlockDatabaseWidget* const m_view; + QPointer m_view; + QSharedPointer m_db; + QPointer m_dbWidget; + Intent m_intent = Intent::None; }; #endif // KEEPASSX_AUTOTYPEUNLOCKDIALOG_H diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 7f1f9942e..c7d752bd2 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -28,6 +28,7 @@ #include "core/Global.h" #include "core/Group.h" #include "core/Metadata.h" +#include "core/Tools.h" #include "format/CsvExporter.h" #include "gui/Clipboard.h" #include "gui/DatabaseWidget.h" @@ -35,7 +36,7 @@ #include "gui/DragTabBar.h" #include "gui/FileDialog.h" #include "gui/MessageBox.h" -#include "gui/UnlockDatabaseDialog.h" +#include "gui/DatabaseOpenDialog.h" #include "gui/entry/EntryView.h" #include "gui/group/GroupView.h" #include "gui/wizard/NewDatabaseWizard.h" @@ -43,7 +44,8 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) : QTabWidget(parent) , m_dbWidgetStateSync(new DatabaseWidgetStateSync(this)) - , m_dbPendingLock(nullptr) + , m_dbWidgetPendingLock(nullptr) + , m_databaseOpenDialog(new DatabaseOpenDialog()) { auto* tabBar = new DragTabBar(this); setTabBar(tabBar); @@ -54,6 +56,7 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*))); connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType())); connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase())); + connect(autoType(), SIGNAL(autotypeRejected()), SLOT(relockPendingDatabase())); } DatabaseTabWidget::~DatabaseTabWidget() @@ -220,9 +223,9 @@ void DatabaseTabWidget::mergeDatabase() } } -void DatabaseTabWidget::mergeDatabase(const QString& fileName) +void DatabaseTabWidget::mergeDatabase(const QString& filePath) { - currentDatabaseWidget()->switchToOpenMergeDatabase(fileName); + unlockDatabaseInDialog(currentDatabaseWidget(), DatabaseOpenDialog::Intent::Merge, filePath); } void DatabaseTabWidget::importKeePass1Database() @@ -513,23 +516,60 @@ void DatabaseTabWidget::lockDatabases() } } +/** + * Unlock a database with an unlock popup dialog. + * + * @param dbWidget DatabaseWidget which to connect signals to + * @param intent intent for unlocking + */ +void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent) +{ + unlockDatabaseInDialog(dbWidget, intent, dbWidget->database()->filePath()); +} + +/** + * Unlock a database with an unlock popup dialog. + * + * @param dbWidget DatabaseWidget which to connect signals to + * @param intent intent for unlocking + * @param file path of the database to be unlocked + */ +void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent, + const QString& filePath) +{ + m_databaseOpenDialog->setTargetDatabaseWidget(dbWidget); + m_databaseOpenDialog->setIntent(intent); + m_databaseOpenDialog->setFilePath(filePath); + +#ifdef Q_OS_MACOS + if (intent == DatabaseOpenDialog::Intent::AutoType) { + autoType()->raiseWindow(); + Tools::wait(500); + } +#endif + + m_databaseOpenDialog->show(); + m_databaseOpenDialog->raise(); + m_databaseOpenDialog->activateWindow(); +} + /** * This function relock the pending database when autotype has been performed successfully * A database is marked as pending when it's unlocked after a global Auto-Type invocation */ void DatabaseTabWidget::relockPendingDatabase() { - if (!m_dbPendingLock || !config()->get("security/relockautotype").toBool()) { + if (!m_dbWidgetPendingLock || !config()->get("security/relockautotype").toBool()) { return; } - if (m_dbPendingLock->isLocked() || !m_dbPendingLock->database()->hasKey()) { - m_dbPendingLock = nullptr; + if (m_dbWidgetPendingLock->isLocked() || !m_dbWidgetPendingLock->database()->hasKey()) { + m_dbWidgetPendingLock = nullptr; return; } - m_dbPendingLock->lock(); - m_dbPendingLock = nullptr; + m_dbWidgetPendingLock->lock(); + m_dbWidgetPendingLock = nullptr; } void DatabaseTabWidget::updateLastDatabases(const QString& filename) @@ -579,11 +619,13 @@ void DatabaseTabWidget::performGlobalAutoType() } } + // TODO: allow for database selection during Auto-Type instead of using the current tab if (!unlockedDatabases.isEmpty()) { autoType()->performGlobalAutoType(unlockedDatabases); } 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(); + if (config()->get("security/relockautotype").toBool()) { + m_dbWidgetPendingLock = currentDatabaseWidget(); + } + unlockDatabaseInDialog(currentDatabaseWidget(), DatabaseOpenDialog::Intent::AutoType); } } diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index f9d4bdf15..a659e4fe6 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -19,6 +19,7 @@ #define KEEPASSX_DATABASETABWIDGET_H #include "gui/MessageWidget.h" +#include "DatabaseOpenDialog.h" #include #include @@ -27,7 +28,6 @@ class Database; class DatabaseWidget; class DatabaseWidgetStateSync; class DatabaseOpenWidget; -class QFile; class DatabaseTabWidget : public QTabWidget { @@ -68,6 +68,8 @@ public slots: void lockDatabases(); void closeDatabaseFromSender(); + void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent); + void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent, const QString& filePath); void relockPendingDatabase(); void changeMasterKey(); @@ -93,7 +95,8 @@ private: void updateLastDatabases(const QString& filename); QPointer m_dbWidgetStateSync; - QPointer m_dbPendingLock; + QPointer m_dbWidgetPendingLock; + QScopedPointer m_databaseOpenDialog; }; #endif // KEEPASSX_DATABASETABWIDGET_H diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 782c9b83f..35beb5368 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -45,6 +45,7 @@ #include "gui/Clipboard.h" #include "gui/CloneDialog.h" #include "gui/DatabaseOpenWidget.h" +#include "gui/DatabaseOpenDialog.h" #include "gui/dbsettings/DatabaseSettingsDialog.h" #include "gui/EntryPreviewWidget.h" #include "gui/KeePass1OpenWidget.h" @@ -52,8 +53,6 @@ #include "gui/TotpSetupDialog.h" #include "gui/TotpDialog.h" #include "gui/TotpExportSettingsDialog.h" -#include "gui/UnlockDatabaseDialog.h" -#include "gui/UnlockDatabaseWidget.h" #include "gui/entry/EditEntryWidget.h" #include "gui/entry/EntryView.h" #include "gui/group/EditGroupWidget.h" @@ -86,16 +85,8 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) , 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_messageWidget->setHidden(true); @@ -153,10 +144,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) m_csvImportWizard->setObjectName("csvImportWizard"); m_databaseSettingDialog->setObjectName("databaseSettingsDialog"); m_databaseOpenWidget->setObjectName("databaseOpenWidget"); - m_databaseOpenMergeWidget->setObjectName("databaseOpenMergeWidget"); m_keepass1OpenWidget->setObjectName("keepass1OpenWidget"); - m_unlockDatabaseWidget->setObjectName("unlockDatabaseWidget"); - m_unlockDatabaseDialog->setObjectName("unlockDatabaseDialog"); addChildWidget(m_mainWidget); addChildWidget(m_editEntryWidget); @@ -165,9 +153,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) 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())); @@ -181,17 +167,14 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) 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(editFinished(bool)), SLOT(switchToMainView(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_editGroupWidget, SIGNAL(editFinished(bool)), SLOT(switchToMainView(bool))); + connect(m_databaseSettingDialog, SIGNAL(editFinished(bool)), SLOT(switchToMainView(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(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())); connect(&m_fileWatchUnblockTimer, SIGNAL(timeout()), this, SLOT(unblockAutoReload())); @@ -217,7 +200,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) } #endif - setCurrentWidget(m_mainWidget); + switchToMainView(); } DatabaseWidget::DatabaseWidget(const QString& filePath, QWidget* parent) @@ -239,12 +222,12 @@ DatabaseWidget::Mode DatabaseWidget::currentMode() const { if (currentWidget() == nullptr) { return DatabaseWidget::Mode::None; - } else if (currentWidget() == m_csvImportWizard) { - return DatabaseWidget::Mode::ImportMode; } else if (currentWidget() == m_mainWidget) { return DatabaseWidget::Mode::ViewMode; - } else if (currentWidget() == m_unlockDatabaseWidget || currentWidget() == m_databaseOpenWidget) { + } else if (currentWidget() == m_databaseOpenWidget || currentWidget() == m_keepass1OpenWidget) { return DatabaseWidget::Mode::LockedMode; + } else if (currentWidget() == m_csvImportWizard) { + return DatabaseWidget::Mode::ImportMode; } else { return DatabaseWidget::Mode::EditMode; } @@ -358,7 +341,7 @@ void DatabaseWidget::createEntry() return; } - m_newEntry = new Entry(); + m_newEntry.reset(new Entry()); if (isSearchActive()) { m_newEntry->setTitle(getCurrentSearch()); @@ -368,7 +351,7 @@ void DatabaseWidget::createEntry() m_newEntry->setUsername(m_db->metadata()->defaultUserName()); m_newParent = m_groupView->currentGroup(); setIconFromParent(); - switchToEntryEdit(m_newEntry, true); + switchToEntryEdit(m_newEntry.data(), true); } void DatabaseWidget::setIconFromParent() @@ -652,10 +635,10 @@ void DatabaseWidget::createGroup() return; } - m_newGroup = new Group(); + m_newGroup.reset(new Group()); m_newGroup->setUuid(QUuid::createUuid()); m_newParent = m_groupView->currentGroup(); - switchToGroupEdit(m_newGroup, true); + switchToGroupEdit(m_newGroup.data(), true); } void DatabaseWidget::deleteGroup() @@ -692,38 +675,27 @@ int DatabaseWidget::addChildWidget(QWidget* w) return index; } -void DatabaseWidget::csvImportFinished(bool accepted) -{ - if (!accepted) { - emit closeRequest(); - } else { - setCurrentWidget(m_mainWidget); - } -} - -void DatabaseWidget::switchToView(bool accepted) +void DatabaseWidget::switchToMainView(bool previousDialogAccepted) { if (m_newGroup) { - if (accepted) { + if (previousDialogAccepted) { m_newGroup->setParent(m_newParent); - m_groupView->setCurrentGroup(m_newGroup); + m_groupView->setCurrentGroup(m_newGroup.take()); m_groupView->expandGroup(m_newParent); } else { - delete m_newGroup; + m_newGroup.reset(); } - m_newGroup = nullptr; m_newParent = nullptr; } else if (m_newEntry) { - if (accepted) { + if (previousDialogAccepted) { m_newEntry->setGroup(m_newParent); m_entryView->setFocus(); - m_entryView->setCurrentEntry(m_newEntry); + m_entryView->setCurrentEntry(m_newEntry.take()); } else { - delete m_newEntry; + m_newEntry.reset(); } - m_newEntry = nullptr; m_newParent = nullptr; } @@ -789,7 +761,7 @@ void DatabaseWidget::loadDatabase(bool accepted) if (accepted) { replaceDatabase(openWidget->database()); - setCurrentWidget(m_mainWidget); + switchToMainView(); m_fileWatcher.addPath(m_db->filePath()); emit databaseUnlocked(); } else { @@ -809,7 +781,13 @@ void DatabaseWidget::mergeDatabase(bool accepted) return; } - auto srcDb = qobject_cast(sender())->database(); + auto* senderDialog = qobject_cast(sender()); + + Q_ASSERT(senderDialog); + if (!senderDialog) { + return; + } + auto srcDb = senderDialog->database(); if (!srcDb) { showMessage(tr("No source database, nothing to do."), MessageWidget::Error); @@ -820,25 +798,37 @@ void DatabaseWidget::mergeDatabase(bool accepted) merger.merge(); } - m_databaseOpenMergeWidget->clearForms(); - setCurrentWidget(m_mainWidget); + switchToMainView(); emit databaseMerged(m_db); } +/** + * Unlock the database. + * + * @param accepted true if the unlock dialog or widget was confirmed with OK + */ void DatabaseWidget::unlockDatabase(bool accepted) { + auto* senderDialog = qobject_cast(sender()); + if (!accepted) { - emit closeRequest(); + if (!senderDialog && (!m_db || !m_db->isInitialized())) { + emit closeRequest(); + } return; } - auto db = QSharedPointer::create(); - if (sender() == m_unlockDatabaseDialog) { - db = m_unlockDatabaseDialog->database(); - } else if (sender() == m_unlockDatabaseWidget) { - db = m_unlockDatabaseWidget->database(); + if (senderDialog && senderDialog->intent() == DatabaseOpenDialog::Intent::Merge) { + mergeDatabase(accepted); + return; } + QSharedPointer db; + if (senderDialog) { + db = senderDialog->database(); + } else { + db = m_databaseOpenWidget->database(); + } replaceDatabase(db); if (db->isReadOnly()) { showMessage(tr("File opened in read only mode."), MessageWidget::Warning, false, -1); @@ -848,11 +838,10 @@ void DatabaseWidget::unlockDatabase(bool accepted) m_groupBeforeLock = QUuid(); m_entryBeforeLock = QUuid(); - setCurrentWidget(m_mainWidget); - m_unlockDatabaseWidget->clearForms(); + switchToMainView(); emit databaseUnlocked(); - if (sender() == m_unlockDatabaseDialog) { + if (senderDialog && senderDialog->intent() == DatabaseOpenDialog::Intent::AutoType) { QList> dbList; dbList.append(m_db); autoType()->performGlobalAutoType(dbList); @@ -905,6 +894,52 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod } } +void DatabaseWidget::switchToDatabaseSettings() +{ + m_databaseSettingDialog->load(m_db); + setCurrentWidget(m_databaseSettingDialog); +} + +void DatabaseWidget::switchToOpenDatabase() +{ + switchToOpenDatabase(m_db->filePath()); +} + +void DatabaseWidget::switchToOpenDatabase(const QString& filePath) +{ + updateFilePath(filePath); + m_databaseOpenWidget->load(filePath); + setCurrentWidget(m_databaseOpenWidget); +} + +void DatabaseWidget::switchToOpenDatabase(const QString& filePath, const QString& password, const QString& keyFile) +{ + switchToOpenDatabase(filePath); + m_databaseOpenWidget->enterKey(password, keyFile); +} + +void DatabaseWidget::switchToCsvImport(const QString& filePath) +{ + setCurrentWidget(m_csvImportWizard); + m_csvImportWizard->load(filePath, m_db.data()); +} + +void DatabaseWidget::csvImportFinished(bool accepted) +{ + if (!accepted) { + emit closeRequest(); + } else { + switchToMainView(); + } +} + +void DatabaseWidget::switchToImportKeepass1(const QString& filePath) +{ + updateFilePath(filePath); + m_keepass1OpenWidget->load(filePath); + setCurrentWidget(m_keepass1OpenWidget); +} + void DatabaseWidget::switchToEntryEdit() { Entry* entry = m_entryView->currentEntry(); @@ -933,55 +968,6 @@ void DatabaseWidget::switchToMasterKeyChange() m_databaseSettingDialog->showMasterKeySettings(); } -void DatabaseWidget::switchToDatabaseSettings() -{ - m_databaseSettingDialog->load(m_db); - setCurrentWidget(m_databaseSettingDialog); -} - -void DatabaseWidget::switchToOpenDatabase() -{ - switchToOpenDatabase(m_db->filePath()); -} - -void DatabaseWidget::switchToOpenDatabase(const QString& filePath) -{ - updateFilePath(filePath); - if (m_databaseOpenWidget) { - m_databaseOpenWidget->load(filePath); - setCurrentWidget(m_databaseOpenWidget); - } else if (m_unlockDatabaseWidget) { - m_unlockDatabaseWidget->load(filePath); - setCurrentWidget(m_unlockDatabaseWidget); - } -} - -void DatabaseWidget::switchToCsvImport(const QString& filePath) -{ - setCurrentWidget(m_csvImportWizard); - m_csvImportWizard->load(filePath, m_db.data()); -} - -void DatabaseWidget::switchToOpenMergeDatabase(const QString& filePath) -{ - m_databaseOpenMergeWidget->clearForms(); - m_databaseOpenMergeWidget->load(filePath); - setCurrentWidget(m_databaseOpenMergeWidget); -} - -void DatabaseWidget::switchToOpenMergeDatabase(const QString& filePath, const QString& password, const QString& keyFile) -{ - switchToOpenMergeDatabase(filePath); - m_databaseOpenMergeWidget->enterKey(password, keyFile); -} - -void DatabaseWidget::switchToImportKeepass1(const QString& filePath) -{ - updateFilePath(filePath); - m_keepass1OpenWidget->load(filePath); - setCurrentWidget(m_keepass1OpenWidget); -} - void DatabaseWidget::performUnlockDatabase(const QString& password, const QString& keyfile) { if (password.isEmpty() && keyfile.isEmpty()) { @@ -1187,8 +1173,7 @@ bool DatabaseWidget::lock() endSearch(); clearAllWidgets(); - m_unlockDatabaseWidget->load(m_db->filePath()); - setCurrentWidget(m_unlockDatabaseWidget); + switchToOpenDatabase(m_db->filePath()); auto newDb = QSharedPointer::create(m_db->filePath()); replaceDatabase(newDb); @@ -1427,20 +1412,6 @@ EntryView* DatabaseWidget::entryView() return m_entryView; } -void DatabaseWidget::prepareUnlock() -{ - m_unlockDatabaseDialog->clearForms(); - m_unlockDatabaseDialog->setFilePath(m_db->filePath()); - -#if defined(Q_OS_MACOS) - autoType()->raiseWindow(); - Tools::wait(500); -#endif - - m_unlockDatabaseDialog->show(); - m_unlockDatabaseDialog->activateWindow(); -} - /** * Save the database to disk. * diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index addabe060..078ae9d23 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -24,12 +24,14 @@ #include #include +#include "DatabaseOpenDialog.h" #include "gui/entry/EntryModel.h" #include "gui/MessageWidget.h" #include "gui/csvImport/CsvImportWizard.h" #include "gui/entry/EntryModel.h" class DatabaseOpenWidget; +class KeePass1OpenWidget; class DatabaseSettingsDialog; class Database; class EditEntryWidget; @@ -39,15 +41,12 @@ class EntryView; class EntrySearcher; class Group; class GroupView; -class KeePass1OpenWidget; class QFile; class QMenu; class QSplitter; class QLabel; -class UnlockDatabaseWidget; class MessageWidget; class EntryPreviewWidget; -class UnlockDatabaseDialog; class QFileSystemWatcher; namespace Ui @@ -60,6 +59,8 @@ class DatabaseWidget : public QStackedWidget Q_OBJECT public: + friend class DatabaseOpenDialog; + enum class Mode { None, @@ -75,11 +76,6 @@ public: QSharedPointer database() const; - bool lock(); - void prepareUnlock(); - bool save(int attempt = 0); - bool saveAs(); - DatabaseWidget::Mode currentMode() const; bool isLocked() const; bool isSearchActive() const; @@ -148,6 +144,10 @@ signals: void clearSearch(); public slots: + bool lock(); + bool save(int attempt = 0); + bool saveAs(); + void replaceDatabase(QSharedPointer db); void createEntry(); void cloneEntry(); @@ -169,19 +169,18 @@ public slots: void createGroup(); void deleteGroup(); void onGroupChanged(Group* group); - void switchToView(bool accepted); + void switchToMainView(bool previousDialogAccepted = false); void switchToEntryEdit(); 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 performUnlockDatabase(const QString& password, const QString& keyfile = {}); + void csvImportFinished(bool accepted); + void switchToImportKeepass1(const QString& filePath); void emptyRecycleBin(); // Search related slots @@ -215,8 +214,8 @@ private slots: void emitEntrySelectionChanged(); void connectDatabaseSignals(); void loadDatabase(bool accepted); - void mergeDatabase(bool accepted); void unlockDatabase(bool accepted); + void mergeDatabase(bool accepted); void emitCurrentModeChanged(); // Database autoreload slots void onWatchedFileChanged(); @@ -244,23 +243,17 @@ private: QPointer m_historyEditEntryWidget; QPointer m_databaseSettingDialog; QPointer m_databaseOpenWidget; - QPointer m_databaseOpenMergeWidget; QPointer m_keepass1OpenWidget; - QPointer m_unlockDatabaseWidget; - QPointer m_unlockDatabaseDialog; QPointer m_groupView; QPointer m_entryView; - QPointer m_newGroup; - QPointer m_newEntry; + QScopedPointer m_newGroup; + QScopedPointer m_newEntry; QPointer m_newParent; QUuid m_groupBeforeLock; QUuid m_entryBeforeLock; - QString m_databaseName; - QString m_databaseFileName; - // Search state EntrySearcher* m_EntrySearcher; QString m_lastSearchText; diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp index 6b4c04a66..d033979b8 100644 --- a/src/gui/KeePass1OpenWidget.cpp +++ b/src/gui/KeePass1OpenWidget.cpp @@ -61,10 +61,9 @@ void KeePass1OpenWidget::openDatabase() if (m_db) { m_db->metadata()->setName(QFileInfo(m_filename).completeBaseName()); emit dialogFinished(true); + clearForms(); } else { m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n").append(reader.errorString()), MessageWidget::Error); - - m_ui->editPassword->clear(); } } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 649990416..1cd700b43 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -468,7 +468,7 @@ void MainWindow::openDatabase(const QString& filePath, const QString& pw, const if (db->open(filePath, key, nullptr, false)) { auto* dbWidget = new DatabaseWidget(db, this); m_ui->tabWidget->addDatabaseTab(dbWidget); - dbWidget->switchToView(true); + dbWidget->switchToMainView(true); } } diff --git a/src/gui/UnlockDatabaseDialog.cpp b/src/gui/UnlockDatabaseDialog.cpp deleted file mode 100644 index 8e2066ef4..000000000 --- a/src/gui/UnlockDatabaseDialog.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2016 KeePassXC Team - * - * 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 - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "UnlockDatabaseDialog.h" -#include "UnlockDatabaseWidget.h" - -#include "autotype/AutoType.h" -#include "core/Database.h" -#include "gui/DragTabBar.h" - -UnlockDatabaseDialog::UnlockDatabaseDialog(QWidget* parent) - : QDialog(parent) - , m_view(new UnlockDatabaseWidget(this)) -{ - setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); - connect(m_view, SIGNAL(dialogFinished(bool)), this, SLOT(complete(bool))); -} - -void UnlockDatabaseDialog::setFilePath(const QString& filePath) -{ - m_view->load(filePath); -} - -void UnlockDatabaseDialog::clearForms() -{ - m_view->clearForms(); -} - -QSharedPointer UnlockDatabaseDialog::database() -{ - return m_view->database(); -} - -void UnlockDatabaseDialog::complete(bool r) -{ - if (r) { - accept(); - emit unlockDone(true); - } else { - reject(); - } -} diff --git a/src/gui/UnlockDatabaseWidget.cpp b/src/gui/UnlockDatabaseWidget.cpp deleted file mode 100644 index ffd2bf225..000000000 --- a/src/gui/UnlockDatabaseWidget.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "UnlockDatabaseWidget.h" - -#include "core/Database.h" -#include "gui/MessageBox.h" -#include "ui_DatabaseOpenWidget.h" - -UnlockDatabaseWidget::UnlockDatabaseWidget(QWidget* parent) - : DatabaseOpenWidget(parent) -{ - m_ui->labelHeadline->setText(tr("Unlock database")); -} diff --git a/src/gui/UnlockDatabaseWidget.h b/src/gui/UnlockDatabaseWidget.h deleted file mode 100644 index c10fcf0a8..000000000 --- a/src/gui/UnlockDatabaseWidget.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2012 Felix Geyer - * - * 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 - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_UNLOCKDATABASEWIDGET_H -#define KEEPASSX_UNLOCKDATABASEWIDGET_H - -#include "gui/DatabaseOpenWidget.h" - -class UnlockDatabaseWidget : public DatabaseOpenWidget -{ - Q_OBJECT - -public: - explicit UnlockDatabaseWidget(QWidget* parent = nullptr); -}; - -#endif // KEEPASSX_UNLOCKDATABASEWIDGET_H diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 41499d16e..1964b0e0f 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -307,12 +307,10 @@ void TestGui::testMergeDatabase() fileDialog()->setNextFileName(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx")); triggerAction("actionDatabaseMerge"); - auto* databaseOpenMergeWidget = m_tabWidget->currentDatabaseWidget()->findChild("databaseOpenMergeWidget"); - auto* editPasswordMerge = databaseOpenMergeWidget->findChild("editPassword"); + QTRY_COMPARE(QApplication::focusWidget()->objectName(), QString("editPassword")); + auto* editPasswordMerge = QApplication::focusWidget(); QVERIFY(editPasswordMerge->isVisible()); - m_tabWidget->currentDatabaseWidget()->setCurrentWidget(databaseOpenMergeWidget); - QTest::keyClicks(editPasswordMerge, "a"); QTest::keyClick(editPasswordMerge, Qt::Key_Enter); @@ -1191,14 +1189,16 @@ void TestGui::testDatabaseLocking() auto* actionDatabaseSave = m_mainWindow->findChild("actionDatabaseSave", Qt::FindChildrenRecursively); QCOMPARE(actionDatabaseSave->isEnabled(), false); - QWidget* dbWidget = m_tabWidget->currentDatabaseWidget(); - auto* unlockDatabaseWidget = dbWidget->findChild("unlockDatabaseWidget"); + DatabaseWidget* dbWidget = m_tabWidget->currentDatabaseWidget(); + QVERIFY(dbWidget->isLocked()); + auto* unlockDatabaseWidget = dbWidget->findChild("databaseOpenWidget"); QWidget* editPassword = unlockDatabaseWidget->findChild("editPassword"); QVERIFY(editPassword); QTest::keyClicks(editPassword, "a"); QTest::keyClick(editPassword, Qt::Key_Enter); + QVERIFY(!dbWidget->isLocked()); QCOMPARE(m_tabWidget->tabName(0), origDbName); actionDatabaseMerge = m_mainWindow->findChild("actionDatabaseMerge", Qt::FindChildrenRecursively); From a90a577ee11cb2b30dee21ec5f527e0c124fec56 Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Sat, 24 Nov 2018 14:30:55 -0800 Subject: [PATCH 64/84] Add favicon fetch button next to entry's url edit textbox (#2439) When WITH_XC_NETWORKING is defined, create a QToolButton beside the Edit Entry -> Entry -> URL, which when pressed, acts as though the Edit Entry -> Icon -> Download Favicon button is pressed. This button is disabled (grayed-out) when the URL text is empty, and enabled when the text is present. Fixes #936 * Add favicon download button * Remove the progress dialog that appears when downloading an entry's URL's favicon since (when working correctly) it disappears before it can be read. When downloading icons from the button located next to the URL text box, display a message panel that confirms the download was a success. * Do not show successful icon download msg if icon alread exists --- COPYING | 6 ++++ .../16x16/actions/favicon-download.png | Bin 0 -> 755 bytes .../22x22/actions/favicon-download.png | Bin 0 -> 1054 bytes .../32x32/actions/favicon-download.png | Bin 0 -> 1618 bytes src/gui/EditWidgetIcons.cpp | 34 ++---------------- src/gui/EditWidgetIcons.h | 12 ------- src/gui/entry/EditEntryWidget.cpp | 21 +++++++++++ src/gui/entry/EditEntryWidget.h | 3 ++ src/gui/entry/EditEntryWidgetMain.ui | 14 ++++++-- 9 files changed, 44 insertions(+), 46 deletions(-) create mode 100644 share/icons/application/16x16/actions/favicon-download.png create mode 100644 share/icons/application/22x22/actions/favicon-download.png create mode 100644 share/icons/application/32x32/actions/favicon-download.png diff --git a/COPYING b/COPYING index b91037658..b3eddd766 100644 --- a/COPYING +++ b/COPYING @@ -151,6 +151,12 @@ Copyright: 2003-2004, David Vignoni License: LGPL-2.1 Comment: based on Nuvola icon theme +Files: share/icons/application/*/actions/favicon-download.png +Copyright: 2003-2004, David Vignoni + 2018, Kyle Kneitinger +License: LGPL-2.1 +Comment: based on Nuvola icon theme + Files: share/icons/application/*/actions/application-exit.png share/icons/application/*/actions/chronometer.png share/icons/application/*/actions/configure.png diff --git a/share/icons/application/16x16/actions/favicon-download.png b/share/icons/application/16x16/actions/favicon-download.png new file mode 100644 index 0000000000000000000000000000000000000000..80bc4a65dcc7178850d20dcd9b3575b873aec980 GIT binary patch literal 755 zcmVg^Lr^cRe zy0(<_dww8G1(nY!hW$RmOO^rt!`ix=BT>!2oww4H0!6O7Kt5HfZ;Y@S&;Vr`6MLZsX zwF@BW(>3$>fp&K;@y39)@4KONZyMjDlW29z`2G6M*MR4kElf?+IQM6IG}ldG7&@k@ zV;DNw8JTo9KPBsV0YoGolfPu^5b21;oR;YejEAp_`uh53X=!N(l~TB_yXnMrU3#a# z7VT^FWoDOp0Z7FpAMedXotI;0h%9t%I1+&UFIiu5y}UT@*{v){ud7PXYJ=X(GE002ovPDHLkV1i|YPL%)v literal 0 HcmV?d00001 diff --git a/share/icons/application/22x22/actions/favicon-download.png b/share/icons/application/22x22/actions/favicon-download.png new file mode 100644 index 0000000000000000000000000000000000000000..96596c4bd6bf5220c395bc24af9ed38ce9d0298f GIT binary patch literal 1054 zcmV+(1mXLMP)3OoElCrSB{AA)ZB5M@1;swthp6~s zt<^r{!9YYT6p;iJ#LFr^Dp(C@ZNWlpY5Nc@#27Ti%R?yIhqkE-R!f#fVk0K$CQWve z-Lsc-nekzhxZ7+uh=2GP24;SI^Ua+95z%OLA`*$TYOVRNvMfst4-daKHa7MVHc(;0qD=~=G+KjF8qh2ARTVb-{ZbAN4(fe$cCH|RSN7e@le-@S5P)3g7(fWkq}@!*t{!SONkTpl0vr$0SxstDk)3l% z%z7+LJ0#*Be@}mgUbSAP%)%h3s+DYRlGIm2&;%*K_8^rL%m)ml0m=g>ugF;%Dc6}S z=NM{2g62A?4?)2E*KxHUObM zRglg@x>gdbkp!zHQi_cogTOQ(=!4ce#oe_U$5Ck4A~t@Ng^bJ4q5T+*?c^*)&QhFQ zoMv=mb4-g3t;i|7M8eAHIdWe+P5>71YPp z99;%3GYNZcZ9?m(7+Kmw(!RvJ^&J#Fllb^3hkBmo%*Y@IPkl?w^6~knZ<9*dELbzh z1`{bLFDVesK7kP1)`r|JbIQ->N6q>2zEOlmO z%lUvr-r4Z7*c$Hk=5i<3q9}%Ht?@jMaXCTLa{(GR$^i&>_-WV~Zi^3OT9;2R{~!y; zErn{Wd3484j_f@^qF*C33cpE388*y(V&#{=j=#t3!n>x{nyRY6THh_s_VI@wVdVDX zbRFoW=J_na=JJilzOJ}>FuRl;n|+hS(ow(_ot>Qn!C>&g;y;5@3Z)bPN-1)VEzd3e zwI%XWV;~#}l|Yk!=J~mAdM5YlR4=K_2%w0zR{C%Zc5uKGf0PaT@>= z7vr3M|NIp@Hnopz_S}XSQK5uvZ#&lc@r(MIBfZ+VXZu-T`~Lx!R_nGXceL~ax7=*W YKi7`zc6e9Jp8x;=07*qoM6N<$f>fU5t^fc4 literal 0 HcmV?d00001 diff --git a/share/icons/application/32x32/actions/favicon-download.png b/share/icons/application/32x32/actions/favicon-download.png new file mode 100644 index 0000000000000000000000000000000000000000..2f838d9d759ae341f505e65ca2210658b1ffb7ff GIT binary patch literal 1618 zcmV-Y2CeytP)1bUdv~_uI2Q-1q$Q3_aUr-QSVcuu zvDy!PQKd>nLRHmTEkZOZf>te&p!7|Bp{N9ilJ1PUk>6qQJEFs4)_5~!t3 zaAG_4IgTCY?D$@HcV_xw@6G0poFm#tnw{I(+4=wH|2*^Dvtnpy=+OH0>%S(10Ki(i ztQlh(j$f*2tx2cTM-Cr8{6!#Iq=7l>)~);M!w>JO0|Kxs`U-*C??!$7`<~}=#~rs1 zI*#-CFbt10B_O55T1zfhq*Mx*Em}|FQsedDN=dr9BuXi)wHum&12~mMFJ8>^%ilai zCL4kPAwfX-0$8vBCIT`Rtg%=FMpIQM)hYSK13Nzq^MeEcq!f%!IDG8O4-s#dcnN{y z3Y3Z%7;OoBi(jx5bB6qqA-8DAW(-Ri&Ceix_aRp%C(A(ASafI!eGNbq(6#|wQw1ziLd;WibxPJGpv?o<0jInf zZJ`uGt^}?lQRN55SacAn6BI4XgOu_z6>y=e4b~(e8H2b3O4bnw%Bm<32#l75fh8ze zN(DuglvQ9aVQ>?Ik`uYcw&sD_DUebS zbD%92fhS_n)2`VZFA-?V$(+ZmAMp(h_&Rb&J|S`AhFDw@a}9wMmlFd30wutUMLS&| zw+y60bSTi;uw4~6y4a4kg1ClLqX(IoJV9*Z0NDjYKBMt-hCoa)eC&1NagV8mS?q@7 zHoD?V2;S$Pb7M^v5JDgXC=tn?N`RwOH4S)Z-g)%^BS&82D|bK0!RPP6-=bil&fgxe z6~6u4XmpZ%)EsM@upTkkx`eOb-EjfXNjKL_wIDndH#!dngqHX3m`Ecsfgepa`|# ziG7c8Vd@NLXEhtM3y3wUb#Q=og;h=h=&mPFj&TbJg4_&aN1x>6u@S~jmw5j1AM>s6 zevi>Pg_VZF(s{1v%JcU_`;k)Mlut#gZAEggDFD&gUA!3ZpDCZt4F!6)c;qi;ID34Q z9iQ7odz(jLDT~6V;7udN#%h*q)q)1;q3Qx5VB+03F`?yr`V{8@C;=I$UdwhR#APif zv}_F$R5cZYK*i#iFlcElDa5K3SfPH6KqUyT?(JnUn@5O7Q)^TgV*!+7;3FXbP%8P1 z3=da}iVCq>Un@v{`_x|c*#jS)0svvC2?AYL`9z@Bf-#yCljHcI55V-?EUFt~t0W+V zly+mPk1N(1V3Fge#;P%}yzHutf#tCm_VdWAPvUgd5Ie7NSM7pCpPR6s?EVvgKdsT5 zQg>Hlpc%NeaAV)~I4>$v-*ib{?KD~b(zfXb?Eoyk=`;IpKf{Gwewm1kz^%1>=DHhr z_TERxJ|oE;3s;?hs!c-XFFxn?<)-Y(^!?9c z1_pQb(3WyPFz@`S9K*ZDb#d(6e$q3)1KwXbr&e!7ZS~f>`L2$ux_^D!{Wm#RZt1KJ zpLpvmFa7cDLNGq{D6sE(+jo4!vh`DwB$KxT$^Q>{#rigH=BkhH#c{p_#9Ddfzo|47D|DA5 QJOBUy07*qoM6N<$f`6C!e*gdg literal 0 HcmV?d00001 diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 61bffe701..09aafbf11 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -39,25 +39,6 @@ IconStruct::IconStruct() { } -UrlFetchProgressDialog::UrlFetchProgressDialog(const QUrl &url, QWidget *parent) - : QProgressDialog(parent) -{ - setWindowTitle(tr("Download Progress")); - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - setLabelText(tr("Downloading %1.").arg(url.toDisplayString())); - setMinimumDuration(0); - setMinimumSize(QSize(400, 75)); -} - -void UrlFetchProgressDialog::networkReplyProgress(qint64 bytesRead, qint64 totalBytes) -{ - if (totalBytes > 0) { - setValue(static_cast(bytesRead / totalBytes)); - } else { - setValue(0); - } -} - EditWidgetIcons::EditWidgetIcons(QWidget* parent) : QWidget(parent) , m_ui(new Ui::EditWidgetIcons()) @@ -268,14 +249,14 @@ void EditWidgetIcons::fetchFinished() // No redirect, and we theoretically have some icon data now. image.loadFromData(m_bytesReceived); } - } else { - UrlFetchProgressDialog *progress = findChild(url.toString()); - progress->close(); } if (!image.isNull()) { if (!addCustomIcon(image)) { emit messageEditEntry(tr("Custom icon already exists"), MessageWidget::Information); + } else if (!this->isVisible()) { + // Show confirmation message if triggered from Entry tab download button + emit messageEditEntry(tr("Custom icon successfully downloaded"), MessageWidget::Positive); } } else if (!m_urlsToTry.empty()) { m_redirects = 0; @@ -316,15 +297,6 @@ void EditWidgetIcons::startFetchFavicon(const QUrl& url) m_reply = m_netMgr.get(request); connect(m_reply, &QNetworkReply::finished, this, &EditWidgetIcons::fetchFinished); connect(m_reply, &QIODevice::readyRead, this, &EditWidgetIcons::fetchReadyRead); - - UrlFetchProgressDialog *progress = new UrlFetchProgressDialog(url, this); - progress->setObjectName(url.toString()); - progress->setAttribute(Qt::WA_DeleteOnClose); - connect(m_reply, &QNetworkReply::finished, progress, &QProgressDialog::hide); - connect(m_reply, &QNetworkReply::downloadProgress, progress, &UrlFetchProgressDialog::networkReplyProgress); - connect(progress, &QProgressDialog::canceled, this, &EditWidgetIcons::fetchCanceled); - - progress->show(); #else Q_UNUSED(url); #endif diff --git a/src/gui/EditWidgetIcons.h b/src/gui/EditWidgetIcons.h index d00e064af..677cbebef 100644 --- a/src/gui/EditWidgetIcons.h +++ b/src/gui/EditWidgetIcons.h @@ -20,7 +20,6 @@ #define KEEPASSX_EDITWIDGETICONS_H #include -#include #include #include #include @@ -50,17 +49,6 @@ struct IconStruct int number; }; -class UrlFetchProgressDialog : public QProgressDialog -{ - Q_OBJECT - -public: - explicit UrlFetchProgressDialog(const QUrl &url, QWidget *parent = nullptr); - -public slots: - void networkReplyProgress(qint64 bytesRead, qint64 totalBytes); -}; - class EditWidgetIcons : public QWidget { Q_OBJECT diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index a19a89332..72d09c9ff 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -121,8 +121,19 @@ void EditEntryWidget::setupMain() m_mainUi->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); m_mainUi->togglePasswordGeneratorButton->setIcon(filePath()->icon("actions", "password-generator")); +#ifdef WITH_XC_NETWORKING + m_mainUi->fetchFaviconButton->setIcon(filePath()->icon("actions", "favicon-download")); + m_mainUi->fetchFaviconButton->setDisabled(true); +#else + m_mainUi->fetchFaviconButton->setVisible(false); +#endif + + connect(m_mainUi->togglePasswordButton, SIGNAL(toggled(bool)), m_mainUi->passwordEdit, SLOT(setShowPassword(bool))); connect(m_mainUi->togglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool))); +#ifdef WITH_XC_NETWORKING + connect(m_mainUi->fetchFaviconButton, SIGNAL(clicked()), m_iconsWidget, SLOT(downloadFavicon())); +#endif connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool))); connect(m_mainUi->notesEnabled, SIGNAL(toggled(bool)), this, SLOT(toggleHideNotes(bool))); m_mainUi->passwordRepeatEdit->enableVerifyMode(m_mainUi->passwordEdit); @@ -241,6 +252,9 @@ void EditEntryWidget::setupEntryUpdate() connect(m_mainUi->passwordEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->passwordRepeatEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); +#ifdef WITH_XC_NETWORKING + connect(m_mainUi->urlEdit, SIGNAL(textChanged(const QString&)), this, SLOT(updateFaviconButtonEnable(const QString&))); +#endif connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->notesEnabled, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->expireDatePicker, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(setUnsavedChanges())); @@ -995,6 +1009,13 @@ void EditEntryWidget::setGeneratedPassword(const QString& password) m_mainUi->togglePasswordGeneratorButton->setChecked(false); } +#ifdef WITH_XC_NETWORKING +void EditEntryWidget::updateFaviconButtonEnable(const QString& url) +{ + m_mainUi->fetchFaviconButton->setDisabled(url.isEmpty()); +} +#endif + void EditEntryWidget::insertAttribute() { Q_ASSERT(!m_history); diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index d45b29726..b0f5d8c94 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -79,6 +79,9 @@ private slots: void cancel(); void togglePasswordGeneratorButton(bool checked); void setGeneratedPassword(const QString& password); +#ifdef WITH_XC_NETWORKING + void updateFaviconButtonEnable(const QString& url); +#endif void insertAttribute(); void editCurrentAttribute(); void removeCurrentAttribute(); diff --git a/src/gui/entry/EditEntryWidgetMain.ui b/src/gui/entry/EditEntryWidgetMain.ui index dc07603aa..3e759fec7 100644 --- a/src/gui/entry/EditEntryWidgetMain.ui +++ b/src/gui/entry/EditEntryWidgetMain.ui @@ -19,6 +19,17 @@ + + + + + + + + + + + @@ -155,9 +166,6 @@ - - - From 4aab93084a5bf8c1dcff1cb4c3ae7cff860987a7 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Wed, 14 Nov 2018 14:17:13 -0500 Subject: [PATCH 65/84] Update Github templates --- .github/CONTRIBUTING.md | 64 +++++++++++++++++++++++++++++--- .github/ISSUE_TEMPLATE.md | 31 +++++++++------- .github/PULL_REQUEST_TEMPLATE.md | 48 ++++++++++++------------ 3 files changed, 101 insertions(+), 42 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f82f75088..ec7a896dc 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -94,7 +94,7 @@ The Branch Strategy is based on [git-flow-lite](http://nvie.com/posts/a-successf * **master** – points to the latest public release * **develop** – points to the development of the next release, contains tested and reviewed code * **feature/**[name] – points to a branch with a new feature, one which is candidate for merge into develop (subject to rebase) -* **hotfix/**[id]-[description] – points to a branch with a fix for a particular issue ID +* **hotfix/**[name] – points to a branch with a fix for a particular issue ID ### Git commit messages @@ -103,8 +103,7 @@ The Branch Strategy is based on [git-flow-lite](http://nvie.com/posts/a-successf * Use the imperative mood ("Move cursor to…" not "Moves cursor to…") * Limit the first line to 72 characters or less * Reference issues and pull requests liberally -* If your pull request fixes an existing issue, add "…, resolves #ISSUENUMBER" to your main commit -* When only changing documentation, include `[ci skip]` in the commit description +* If your pull request fixes an existing issue, add "Fixes #ISSUENUMBER" to your pull request description ### Coding styleguide @@ -125,15 +124,67 @@ For names made of multiple concatenated words, the first letter of the whole is For **C++ files** (*.cpp .h*): 4 spaces For **Qt-UI files** (*.ui*): 2 spaces -#### Pointers +#### Includes +```c +// Class includes +#include "MyWidget.h" +#include "ui_MyWidget.h" + +// Global includes +#include +#include + +// Application includes +#include "core/Config.h" +#include "core/FilePath.h" +``` + +#### Classes +```c +// Note: order is important, stay organized! +class MyWidget : public QWidget +{ + Q_OBJECT + +public: + explicit MyWidget(QWidget* parent); + ~MyWidget() override; + +signals: + void alert(); + +public slots: + void processEvent(Event* event); + +private slots: + void myEvent(Event* event); + +private: + const QScopedPointer m_ui; + int m_counter; +}; + +// Note: alignment of variable initialization +MyWidget::MyWidget(QWidget* parent) + : QWidget(parent) + , m_ui(new Ui::MyWidget()) +{ + +} +``` + +#### Pointers / References ```c int* count; +const QString& string; ``` #### Braces ```c if (condition) { doSomething(); +} else { + doSomethingElse(); } void ExampleClass::exampleFunction() @@ -144,15 +195,18 @@ void ExampleClass::exampleFunction() #### Switch statement ```c +// Note: avoid declaring variables in a switch statement switch (a) { case 1: doSomething(); break; -default: +// Note: use braces if necessary +default: { doSomethingElse(); break; } +} ``` #### Member variables diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index cf8b88f10..be6abf986 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,31 +1,34 @@ - +[TIP]: # ( Provide a general summary of the issue in the title above ^^ ) ## Expected Behavior - - +[NOTE]: # ( If you're describing a bug, tell us what should happen ) +[NOTE]: # ( If you're suggesting a change/improvement, tell us how it should work ) + ## Current Behavior - - +[NOTE]: # ( If describing a bug, tell us what happens instead of the expected behavior ) +[NOTE]: # ( If suggesting a change/improvement, explain the difference from the current behavior ) + ## Possible Solution - - +[NOTE]: # ( Not required, but suggest a fix/reason for the bug, ) +[NOTE]: # ( or ideas how to implement the addition or change ) -## Steps to Reproduce (for bugs) - - + +## Steps to Reproduce +[NOTE]: # ( Provide a link to a live example, or an unambiguous set of steps to ) +[NOTE]: # ( reproduce this bug. Include code to reproduce, if relevant ) 1. 2. 3. -4. ## Context - - +[NOTE]: # ( How has this issue affected you? What are you trying to accomplish? ) +[NOTE]: # ( Providing context helps us come up with a solution that is most useful in the real world ) + ## Debug Info - +[NOTE]: # ( Paste debug info from Help → About here ) KeePassXC - VERSION Revision: REVISION diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b9852f3c9..244a4c477 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,34 +1,36 @@ - +[TIP]: # ( Provide a general summary of your changes in the title above ^^ ) -## Description - - -## Motivation and context - - - -## How has this been tested? - - - - -## Screenshots (if appropriate): - -## Types of changes - - +## Type of change +[NOTE]: # ( Please remove all lines which don't apply. ) - ✅ Bug fix (non-breaking change which fixes an issue) +- ✅ Refactor (significant modification to existing code) - ✅ New feature (non-breaking change which adds functionality) - ✅ Breaking change (fix or feature that would cause existing functionality to change) +- ✅ Documentation (non-code change) + +## Description and Context +[NOTE]: # ( Describe your changes in detail, why is this change required? ) +[NOTE]: # ( Describe the context of your change. Explain large code modifications. ) +[NOTE]: # ( If it fixes an open issue, please add "Fixes #XXX" as necessary ) + + +## Screenshots +[TIP]: # ( Do not include screenshots of your actual repository! ) + + +## Testing strategy +[NOTE]: # ( Please describe in detail how you tested your changes. ) +[TIP]: # ( We expect all new code to be covered by unit tests! ) + ## Checklist: - - - - +[NOTE]: # ( Please go over all the following points. ) +[NOTE]: # ( Again, remove any lines which don't apply. ) +[NOTE]: # ( Pull Requests that don't fulfill all [REQUIRED] requisites are likely ) +[NOTE]: # ( to be sent back to you for correction or will be rejected. ) - ✅ I have read the **CONTRIBUTING** document. **[REQUIRED]** - ✅ My code follows the code style of this project. **[REQUIRED]** - ✅ All new and existing tests passed. **[REQUIRED]** - ✅ I have compiled and verified my code with `-DWITH_ASAN=ON`. **[REQUIRED]** -- ✅ My change requires a change to the documentation and I have updated it accordingly. +- ✅ My change requires a change to the documentation, and I have updated it accordingly. - ✅ I have added tests to cover my changes. From a67cac1322d4545c72c75cedb06059b9009aa7ae Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Thu, 22 Nov 2018 07:47:34 -0500 Subject: [PATCH 66/84] Address comments --- .github/CONTRIBUTING.md | 8 ++++---- .github/ISSUE_TEMPLATE.md | 1 + .github/PULL_REQUEST_TEMPLATE.md | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ec7a896dc..89c3bb2eb 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -130,13 +130,13 @@ For **Qt-UI files** (*.ui*): 2 spaces #include "MyWidget.h" #include "ui_MyWidget.h" -// Global includes -#include -#include - // Application includes #include "core/Config.h" #include "core/FilePath.h" + +// Global includes +#include +#include ``` #### Classes diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index be6abf986..5850e30a3 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,5 @@ [TIP]: # ( Provide a general summary of the issue in the title above ^^ ) +[TIP]: # ( Do not include screenshots of your actual database! ) ## Expected Behavior [NOTE]: # ( If you're describing a bug, tell us what should happen ) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 244a4c477..89b548554 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,12 +15,12 @@ ## Screenshots -[TIP]: # ( Do not include screenshots of your actual repository! ) +[TIP]: # ( Do not include screenshots of your actual database! ) ## Testing strategy [NOTE]: # ( Please describe in detail how you tested your changes. ) -[TIP]: # ( We expect all new code to be covered by unit tests! ) +[TIP]: # ( We expect new code to be covered by unit tests and documented with doc blocks! ) ## Checklist: From 4e49de1afb825a76a65cc90781f3be314494cb5c Mon Sep 17 00:00:00 2001 From: Lantizia Date: Wed, 28 Nov 2018 16:23:06 +0000 Subject: [PATCH 67/84] Ability to set 7-digit TOTP codes from the UI (#2517) --- src/gui/TotpSetupDialog.cpp | 4 ++++ src/gui/TotpSetupDialog.ui | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/gui/TotpSetupDialog.cpp b/src/gui/TotpSetupDialog.cpp index 69290e7a3..babeb7840 100644 --- a/src/gui/TotpSetupDialog.cpp +++ b/src/gui/TotpSetupDialog.cpp @@ -46,6 +46,8 @@ void TotpSetupDialog::saveSettings() uint digits = Totp::DEFAULT_DIGITS; if (m_ui->radio8Digits->isChecked()) { digits = 8; + } else if (m_ui->radio7Digits->isChecked()) { + digits = 7; } else if (m_ui->radioSteam->isChecked()) { digits = Totp::STEAM_DIGITS; encShortName = Totp::STEAM_SHORTNAME; @@ -75,6 +77,8 @@ void TotpSetupDialog::init() m_ui->radioCustom->setChecked(true); if (settings->digits == 8) { m_ui->radio8Digits->setChecked(true); + } else if (settings->digits == 7) { + m_ui->radio7Digits->setChecked(true); } else { m_ui->radio6Digits->setChecked(true); } diff --git a/src/gui/TotpSetupDialog.ui b/src/gui/TotpSetupDialog.ui index 7b3e9318b..6f2af49f1 100644 --- a/src/gui/TotpSetupDialog.ui +++ b/src/gui/TotpSetupDialog.ui @@ -153,6 +153,13 @@ + + + 7 digits + + + + 8 digits From fff0f11b3302692e9eb25d604fe295dacf21221a Mon Sep 17 00:00:00 2001 From: louib Date: Wed, 28 Nov 2018 11:24:12 -0500 Subject: [PATCH 68/84] Adding --quiet option to the CLI. (#2507) --- src/cli/Add.cpp | 16 +++-- src/cli/Clip.cpp | 16 +++-- src/cli/Clip.h | 6 +- src/cli/Command.cpp | 4 ++ src/cli/Command.h | 3 + src/cli/Diceware.cpp | 2 +- src/cli/Edit.cpp | 16 +++-- src/cli/Extract.cpp | 7 +- src/cli/List.cpp | 6 +- src/cli/Locate.cpp | 8 ++- src/cli/Merge.cpp | 22 +++++-- src/cli/Remove.cpp | 12 ++-- src/cli/Remove.h | 2 +- src/cli/Show.cpp | 10 ++- src/cli/TextStream.h | 2 +- src/cli/Utils.cpp | 14 +++- src/cli/Utils.h | 3 +- src/cli/keepassxc-cli.1 | 3 + src/core/Database.cpp | 5 +- tests/TestCli.cpp | 138 +++++++++++++++++++++++++++++++++++++++- tests/TestCli.h | 1 + 21 files changed, 252 insertions(+), 44 deletions(-) diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index 257ea7f6b..68240adb6 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -48,6 +48,7 @@ int Add::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); + parser.addOption(Command::QuietOption); QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), @@ -89,7 +90,10 @@ int Add::execute(const QStringList& arguments) const QString& databasePath = args.at(0); const QString& entryPath = args.at(1); - auto db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db = Database::unlockFromStdin(databasePath, + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } @@ -117,8 +121,10 @@ int Add::execute(const QStringList& arguments) } if (parser.isSet(prompt)) { - outputTextStream << QObject::tr("Enter password for new entry: ") << flush; - QString password = Utils::getPassword(); + if (!parser.isSet(Command::QuietOption)) { + outputTextStream << QObject::tr("Enter password for new entry: ") << flush; + } + QString password = Utils::getPassword(parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT); entry->setPassword(password); } else if (parser.isSet(generate)) { PasswordGenerator passwordGenerator; @@ -141,6 +147,8 @@ int Add::execute(const QStringList& arguments) return EXIT_FAILURE; } - outputTextStream << QObject::tr("Successfully added entry %1.").arg(entry->title()) << endl; + if (!parser.isSet(Command::QuietOption)) { + outputTextStream << QObject::tr("Successfully added entry %1.").arg(entry->title()) << endl; + } return EXIT_SUCCESS; } diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 6b466c5a7..224841f60 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -51,6 +51,7 @@ int Clip::execute(const QStringList& arguments) QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); + parser.addOption(Command::QuietOption); QCommandLineOption totp(QStringList() << "t" << "totp", QObject::tr("Copy the current TOTP to the clipboard.")); parser.addOption(totp); @@ -66,15 +67,22 @@ int Clip::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db = Database::unlockFromStdin(args.at(0), + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } - return clipEntry(db, args.at(1), args.value(2), parser.isSet(totp)); + return clipEntry(db, args.at(1), args.value(2), parser.isSet(totp), parser.isSet(Command::QuietOption)); } -int Clip::clipEntry(QSharedPointer database, const QString& entryPath, const QString& timeout, bool clipTotp) +int Clip::clipEntry(QSharedPointer database, + const QString& entryPath, + const QString& timeout, + bool clipTotp, + bool silent) { TextStream err(Utils::STDERR); @@ -86,7 +94,7 @@ int Clip::clipEntry(QSharedPointer database, const QString& entryPath, timeoutSeconds = timeout.toInt(); } - TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream outputTextStream(silent ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; diff --git a/src/cli/Clip.h b/src/cli/Clip.h index 87f56bb0e..bdc1de03d 100644 --- a/src/cli/Clip.h +++ b/src/cli/Clip.h @@ -26,7 +26,11 @@ public: Clip(); ~Clip(); int execute(const QStringList& arguments) override; - int clipEntry(QSharedPointer database, const QString& entryPath, const QString& timeout, bool clipTotp); + int clipEntry(QSharedPointer database, + const QString& entryPath, + const QString& timeout, + bool clipTotp, + bool silent); }; #endif // KEEPASSXC_CLIP_H diff --git a/src/cli/Command.cpp b/src/cli/Command.cpp index a95676ff0..a93452bcb 100644 --- a/src/cli/Command.cpp +++ b/src/cli/Command.cpp @@ -35,6 +35,10 @@ #include "Remove.h" #include "Show.h" +const QCommandLineOption Command::QuietOption = + QCommandLineOption(QStringList() << "q" + << "quiet", + QObject::tr("Silence password prompt and other secondary outputs.")); QMap commands; Command::~Command() diff --git a/src/cli/Command.h b/src/cli/Command.h index 4e4e076de..beb9d4087 100644 --- a/src/cli/Command.h +++ b/src/cli/Command.h @@ -18,6 +18,7 @@ #ifndef KEEPASSXC_COMMAND_H #define KEEPASSXC_COMMAND_H +#include #include #include #include @@ -36,6 +37,8 @@ public: static QList getCommands(); static Command* getCommand(const QString& commandName); + + static const QCommandLineOption QuietOption; }; #endif // KEEPASSXC_COMMAND_H diff --git a/src/cli/Diceware.cpp b/src/cli/Diceware.cpp index d2821e7cc..bb7931352 100644 --- a/src/cli/Diceware.cpp +++ b/src/cli/Diceware.cpp @@ -22,9 +22,9 @@ #include +#include "Utils.h" #include "cli/TextStream.h" #include "core/PassphraseGenerator.h" -#include "Utils.h" Diceware::Diceware() { diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index cb7c58baa..bbb1473c8 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -48,6 +48,7 @@ int Edit::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); + parser.addOption(Command::QuietOption); QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), @@ -93,7 +94,10 @@ int Edit::execute(const QStringList& arguments) const QString& databasePath = args.at(0); const QString& entryPath = args.at(1); - auto db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db = Database::unlockFromStdin(databasePath, + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } @@ -132,8 +136,10 @@ int Edit::execute(const QStringList& arguments) } if (parser.isSet(prompt)) { - out << QObject::tr("Enter new password for entry: ") << flush; - QString password = Utils::getPassword(); + if (!parser.isSet(Command::QuietOption)) { + out << QObject::tr("Enter new password for entry: ") << flush; + } + QString password = Utils::getPassword(parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT); entry->setPassword(password); } else if (parser.isSet(generate)) { PasswordGenerator passwordGenerator; @@ -158,6 +164,8 @@ int Edit::execute(const QStringList& arguments) return EXIT_FAILURE; } - out << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl; + if (!parser.isSet(Command::QuietOption)) { + out << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl; + } return EXIT_SUCCESS; } diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 01b3d1ebe..e6afdfbdb 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -49,6 +49,7 @@ int Extract::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database to extract.")); + parser.addOption(Command::QuietOption); QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); @@ -62,11 +63,13 @@ int Extract::execute(const QStringList& arguments) return EXIT_FAILURE; } - out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush; + if (!parser.isSet(Command::QuietOption)) { + out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush; + } auto compositeKey = QSharedPointer::create(); - QString line = Utils::getPassword(); + QString line = Utils::getPassword(parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT); auto passwordKey = QSharedPointer::create(); passwordKey->setPassword(line); compositeKey->addKey(passwordKey); diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 3b8938189..ca96799f0 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -46,6 +46,7 @@ int List::execute(const QStringList& arguments) parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), "[group]"); + parser.addOption(Command::QuietOption); QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); @@ -64,7 +65,10 @@ int List::execute(const QStringList& arguments) bool recursive = parser.isSet(recursiveOption); - auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db = Database::unlockFromStdin(args.at(0), + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index 8039f0693..ab45115db 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -25,9 +25,9 @@ #include "cli/TextStream.h" #include "cli/Utils.h" -#include "core/Global.h" #include "core/Database.h" #include "core/Entry.h" +#include "core/Global.h" #include "core/Group.h" Locate::Locate() @@ -48,6 +48,7 @@ int Locate::execute(const QStringList& arguments) parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addPositionalArgument("term", QObject::tr("Search term.")); + parser.addOption(Command::QuietOption); QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); @@ -61,7 +62,10 @@ int Locate::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db = Database::unlockFromStdin(args.at(0), + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index e5b01665c..b17d27f7e 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -20,9 +20,9 @@ #include #include "cli/TextStream.h" +#include "cli/Utils.h" #include "core/Database.h" #include "core/Merger.h" -#include "cli/Utils.h" #include @@ -45,10 +45,11 @@ int Merge::execute(const QStringList& arguments) parser.setApplicationDescription(description); parser.addPositionalArgument("database1", QObject::tr("Path of the database to merge into.")); parser.addPositionalArgument("database2", QObject::tr("Path of the database to merge from.")); + parser.addOption(Command::QuietOption); QCommandLineOption samePasswordOption(QStringList() << "s" << "same-credentials", QObject::tr("Use the same credentials for both database files.")); - + parser.addOption(samePasswordOption); QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); @@ -58,7 +59,6 @@ int Merge::execute(const QStringList& arguments) QObject::tr("path")); parser.addOption(keyFileFrom); - parser.addOption(samePasswordOption); parser.addHelpOption(); parser.process(arguments); @@ -68,14 +68,20 @@ int Merge::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db1 = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db1 = Database::unlockFromStdin(args.at(0), + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db1) { return EXIT_FAILURE; } QSharedPointer db2; if (!parser.isSet("same-credentials")) { - db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom), Utils::STDOUT, Utils::STDERR); + db2 = Database::unlockFromStdin(args.at(1), + parser.value(keyFileFrom), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); } else { db2 = QSharedPointer::create(); QString errorMessage; @@ -94,8 +100,10 @@ int Merge::execute(const QStringList& arguments) err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl; return EXIT_FAILURE; } - out << "Successfully merged the database files." << endl; - } else { + if (!parser.isSet(Command::QuietOption)) { + out << "Successfully merged the database files." << endl; + } + } else if (!parser.isSet(Command::QuietOption)) { out << "Database was not modified by merge operation." << endl; } diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 9f4877e5b..7e36d1e4a 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -49,6 +49,7 @@ int Remove::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::tr("main", "Remove an entry from the database.")); parser.addPositionalArgument("database", QCoreApplication::tr("main", "Path of the database.")); + parser.addOption(Command::QuietOption); QCommandLineOption keyFile(QStringList() << "k" << "key-file", QObject::tr("Key file of the database."), QObject::tr("path")); @@ -63,17 +64,20 @@ int Remove::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db = Database::unlockFromStdin(args.at(0), + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } - return removeEntry(db.data(), args.at(0), args.at(1)); + return removeEntry(db.data(), args.at(0), args.at(1), parser.isSet(Command::QuietOption)); } -int Remove::removeEntry(Database* database, const QString& databasePath, const QString& entryPath) +int Remove::removeEntry(Database* database, const QString& databasePath, const QString& entryPath, bool quiet) { - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream out(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly); TextStream err(Utils::STDERR, QIODevice::WriteOnly); QPointer entry = database->rootGroup()->findEntryByPath(entryPath); diff --git a/src/cli/Remove.h b/src/cli/Remove.h index d45e04af2..fa8bc7bd6 100644 --- a/src/cli/Remove.h +++ b/src/cli/Remove.h @@ -28,7 +28,7 @@ public: Remove(); ~Remove(); int execute(const QStringList& arguments) override; - int removeEntry(Database* database, const QString& databasePath, const QString& entryPath); + int removeEntry(Database* database, const QString& databasePath, const QString& entryPath, bool quiet); }; #endif // KEEPASSXC_REMOVE_H diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index 3678d0e3d..cf8dc895d 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -22,12 +22,12 @@ #include +#include "Utils.h" #include "cli/TextStream.h" #include "core/Database.h" #include "core/Entry.h" -#include "core/Group.h" #include "core/Global.h" -#include "Utils.h" +#include "core/Group.h" Show::Show() { @@ -50,6 +50,7 @@ int Show::execute(const QStringList& arguments) QObject::tr("Key file of the database."), QObject::tr("path")); parser.addOption(keyFile); + parser.addOption(Command::QuietOption); QCommandLineOption totp(QStringList() << "t" << "totp", QObject::tr("Show the entry's current TOTP.")); parser.addOption(totp); @@ -71,7 +72,10 @@ int Show::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR); + auto db = Database::unlockFromStdin(args.at(0), + parser.value(keyFile), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/TextStream.h b/src/cli/TextStream.h index 971f8651d..2dc116352 100644 --- a/src/cli/TextStream.h +++ b/src/cli/TextStream.h @@ -47,4 +47,4 @@ private: void detectCodec(); }; -#endif //KEEPASSXC_TEXTSTREAM_H +#endif // KEEPASSXC_TEXTSTREAM_H diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index a7bf83c13..7359f139c 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -43,6 +43,16 @@ FILE* STDERR = stderr; */ FILE* STDIN = stdin; +/** + * DEVNULL file handle for the CLI. + */ +#ifdef Q_OS_WIN +FILE* DEVNULL = fopen("nul", "w"); +#else +FILE* DEVNULL = fopen("/dev/null", "w"); +#endif + + void setStdinEcho(bool enable = true) { #ifdef Q_OS_WIN @@ -95,9 +105,9 @@ void setNextPassword(const QString& password) * * @return the password */ -QString getPassword() +QString getPassword(FILE* outputDescriptor) { - TextStream out(STDOUT, QIODevice::WriteOnly); + TextStream out(outputDescriptor, QIODevice::WriteOnly); // return preset password if one is set if (!Test::nextPasswords.isEmpty()) { diff --git a/src/cli/Utils.h b/src/cli/Utils.h index a5c995f10..8ea55da1e 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -26,9 +26,10 @@ namespace Utils extern FILE* STDOUT; extern FILE* STDERR; extern FILE* STDIN; +extern FILE* DEVNULL; void setStdinEcho(bool enable); -QString getPassword(); +QString getPassword(FILE* outputDescriptor = STDOUT); int clipText(const QString& text); namespace Test diff --git a/src/cli/keepassxc-cli.1 b/src/cli/keepassxc-cli.1 index 0d618c9d1..d7b0f9ada 100644 --- a/src/cli/keepassxc-cli.1 +++ b/src/cli/keepassxc-cli.1 @@ -56,6 +56,9 @@ Shows the title, username, password, URL and notes of a database entry. Can also .IP "-k, --key-file " Specifies a path to a key file for unlocking the database. In a merge operation this option is used to specify the key file path for the first database. +.IP "-q, --quiet " +Silence password prompt and other secondary outputs. + .IP "-h, --help" Displays help information. diff --git a/src/core/Database.cpp b/src/core/Database.cpp index bbea11d54..6d475acf9 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -708,10 +708,9 @@ QSharedPointer Database::unlockFromStdin(const QString& databaseFilena TextStream out(outputDescriptor); TextStream err(errorDescriptor); - out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename); - out.flush(); + out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename) << flush; - QString line = Utils::getPassword(); + QString line = Utils::getPassword(outputDescriptor); auto passwordKey = QSharedPointer::create(); passwordKey->setPassword(line); compositeKey->addKey(passwordKey); diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index 3c5afc347..d25201e2e 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -161,6 +161,9 @@ void TestCli::testAdd() Utils::Test::setNextPassword("a"); addCmd.execute({"add", "-u", "newuser", "--url", "https://example.com/", "-g", "-l", "20", m_dbFile->fileName(), "/newuser-entry"}); m_stderrFile->reset(); + m_stdoutFile->reset(); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added entry newuser-entry.\n")); auto db = readTestDatabase(); auto* entry = db->rootGroup()->findEntryByPath("/newuser-entry"); @@ -169,6 +172,16 @@ void TestCli::testAdd() QCOMPARE(entry->url(), QString("https://example.com/")); QCOMPARE(entry->password().size(), 20); + // Quiet option + qint64 pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + addCmd.execute({"add", "-q", "-u", "newuser", "-g", "-l", "20", m_dbFile->fileName(), "/newentry-quiet"}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + db = readTestDatabase(); + entry = db->rootGroup()->findEntryByPath("/newentry-quiet"); + QVERIFY(entry); + Utils::Test::setNextPassword("a"); Utils::Test::setNextPassword("newpassword"); addCmd.execute({"add", "-u", "newuser2", "--url", "https://example.net/", "-g", "-l", "20", "-p", m_dbFile->fileName(), "/newuser-entry2"}); @@ -181,7 +194,8 @@ void TestCli::testAdd() QCOMPARE(entry->password(), QString("newpassword")); } -bool isTOTP(const QString & value) { +bool isTOTP(const QString& value) +{ QString val = value.trimmed(); if (val.length() < 5 || val.length() > 6) { return false; @@ -208,6 +222,7 @@ void TestCli::testClip() clipCmd.execute({"clip", m_dbFile->fileName(), "/Sample Entry"}); m_stderrFile->reset(); + m_stdoutFile->reset(); QString errorOutput(m_stderrFile->readAll()); if (errorOutput.contains("Unable to start program") @@ -215,6 +230,17 @@ void TestCli::testClip() QSKIP("Clip test skipped due to missing clipboard tool"); } + QCOMPARE(clipboard->text(), QString("Password")); + m_stdoutFile->readLine(); // skip prompt line + QCOMPARE(m_stdoutFile->readLine(), QByteArray("Entry's password copied to the clipboard!\n")); + + // Quiet option + qint64 pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + clipCmd.execute({"clip", m_dbFile->fileName(), "/Sample Entry", "-q"}); + m_stdoutFile->seek(pos); + // Output should be empty when quiet option is set. + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); QCOMPARE(clipboard->text(), QString("Password")); // TOTP @@ -284,7 +310,7 @@ void TestCli::testDiceware() const auto words = passphrase.split(" "); QCOMPARE(words.size(), 11); QRegularExpression regex("^word\\d+$"); - for (const auto& word: words) { + for (const auto& word : words) { QVERIFY2(regex.match(word).hasMatch(), qPrintable("Word " + word + " was not on the word list")); } } @@ -297,6 +323,9 @@ void TestCli::testEdit() Utils::Test::setNextPassword("a"); editCmd.execute({"edit", "-u", "newuser", "--url", "https://otherurl.example.com/", "-t", "newtitle", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->reset(); + m_stdoutFile->readLine(); // skip prompt line + QCOMPARE(m_stdoutFile->readLine(), QByteArray("Successfully edited entry newtitle.\n")); auto db = readTestDatabase(); auto* entry = db->rootGroup()->findEntryByPath("/newtitle"); @@ -305,6 +334,13 @@ void TestCli::testEdit() QCOMPARE(entry->url(), QString("https://otherurl.example.com/")); QCOMPARE(entry->password(), QString("Password")); + // Quiet option + qint64 pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + editCmd.execute({"edit", m_dbFile->fileName(), "-q", "-t", "newtitle", "/Sample Entry"}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + Utils::Test::setNextPassword("a"); editCmd.execute({"edit", "-g", m_dbFile->fileName(), "/newtitle"}); db = readTestDatabase(); @@ -430,7 +466,7 @@ void TestCli::testEstimate() QVERIFY(result.startsWith("Length " + length)); QVERIFY(result.contains("Entropy " + entropy)); QVERIFY(result.contains("Log10 " + log10)); - for (const auto& string: asConst(searchStrings)) { + for (const auto& string : asConst(searchStrings)) { QVERIFY2(result.contains(string), qPrintable("String " + string + " missing")); } } @@ -455,6 +491,18 @@ void TestCli::testExtract() auto* entry = db->rootGroup()->findEntryByPath("/Sample Entry"); QVERIFY(entry); QCOMPARE(entry->password(), QString("Password")); + + m_stdoutFile->reset(); + + // Quiet option + QScopedPointer dbQuiet(new Database()); + qint64 pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + extractCmd.execute({"extract", "-q", m_dbFile->fileName()}); + m_stdoutFile->seek(pos); + reader.readDatabase(m_stdoutFile.data(), dbQuiet.data()); + QVERIFY(!reader.hasError()); + QVERIFY(db.data()); } void TestCli::testGenerate_data() @@ -533,8 +581,22 @@ void TestCli::testList() "eMail/\n" "Homebanking/\n")); + // Quiet option qint64 pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); + listCmd.execute({"ls", "-q", m_dbFile->fileName()}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n" + "General/\n" + "Windows/\n" + "Network/\n" + "Internet/\n" + "eMail/\n" + "Homebanking/\n")); + + + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); listCmd.execute({"ls", "-R", m_dbFile->fileName()}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -581,8 +643,15 @@ void TestCli::testLocate() m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("/Sample Entry\n")); + // Quiet option qint64 pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); + locateCmd.execute({"locate", m_dbFile->fileName(), "-q", "Sample"}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("/Sample Entry\n")); + + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", m_dbFile->fileName(), "Does Not Exist"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt @@ -709,6 +778,13 @@ void TestCli::testMerge() m_stdoutFile->seek(pos); m_stdoutFile->readLine(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("Database was not modified by merge operation.\n")); + + // Quiet option + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); + mergeCmd.execute({"merge", "-q", "-s", sourceFile.fileName(), sourceFile.fileName()}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); } void TestCli::testRemove() @@ -779,6 +855,52 @@ void TestCli::testRemove() QCOMPARE(m_stderrFile->readAll(), QByteArray("Entry /Sample Entry not found.\n")); } +void TestCli::testRemoveQuiet() +{ + Remove removeCmd; + QVERIFY(!removeCmd.name.isEmpty()); + QVERIFY(removeCmd.getDescriptionLine().contains(removeCmd.name)); + + Kdbx3Reader reader; + Kdbx3Writer writer; + + qint64 pos = m_stdoutFile->pos(); + + // delete entry and verify + Utils::Test::setNextPassword("a"); + removeCmd.execute({"rm", "-q", m_dbFile->fileName(), "/Sample Entry"}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + + auto key = QSharedPointer::create(); + key->addKey(QSharedPointer::create("a")); + QFile readBack(m_dbFile->fileName()); + readBack.open(QIODevice::ReadOnly); + auto readBackDb = QSharedPointer::create(); + reader.readDatabase(&readBack, key, readBackDb.data()); + readBack.close(); + QVERIFY(readBackDb); + QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry")); + QVERIFY(readBackDb->rootGroup()->findEntryByPath("/Recycle Bin/Sample Entry")); + + pos = m_stdoutFile->pos(); + + // remove the entry completely + Utils::Test::setNextPassword("a"); + removeCmd.execute({"rm", "-q", m_dbFile->fileName(), "/Recycle Bin/Sample Entry"}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + + readBack.setFileName(m_dbFile->fileName()); + readBack.open(QIODevice::ReadOnly); + readBackDb = QSharedPointer::create(); + reader.readDatabase(&readBack, key, readBackDb.data()); + readBack.close(); + QVERIFY(readBackDb); + QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry")); + QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Recycle Bin/Sample Entry")); +} + void TestCli::testShow() { Show showCmd; @@ -797,6 +919,16 @@ void TestCli::testShow() qint64 pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); + showCmd.execute({"show", m_dbFile->fileName(), "-q", "/Sample Entry"}); + m_stdoutFile->seek(pos); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("Title: Sample Entry\n" + "UserName: User Name\n" + "Password: Password\n" + "URL: http://www.somesite.com/\n" + "Notes: Notes\n")); + + pos = m_stdoutFile->pos(); + Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); m_stdoutFile->readLine(); // skip password prompt diff --git a/tests/TestCli.h b/tests/TestCli.h index 2c411f2ca..061055dcd 100644 --- a/tests/TestCli.h +++ b/tests/TestCli.h @@ -54,6 +54,7 @@ private slots: void testLocate(); void testMerge(); void testRemove(); + void testRemoveQuiet(); void testShow(); private: From d84ba23c81fc386817198bd188bdd4d61cbd2048 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Wed, 28 Nov 2018 16:13:56 -0500 Subject: [PATCH 69/84] Correct refactor issues with entry selection and search (#2518) * Align entryview selection change signals with groupview * Eliminate redundent and confusing signals/slots * Correct group selection canceling search --- src/gui/DatabaseWidget.cpp | 46 +++++++++++++++---------------------- src/gui/DatabaseWidget.h | 7 ++---- src/gui/entry/EntryView.cpp | 13 +++++++---- src/gui/entry/EntryView.h | 3 ++- src/gui/group/GroupView.cpp | 19 +++------------ src/gui/group/GroupView.h | 5 +--- tests/gui/TestGui.cpp | 14 +++++++++-- 7 files changed, 48 insertions(+), 59 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 35beb5368..389d6fc4c 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -157,16 +157,14 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) 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_groupView, SIGNAL(groupSelectionChanged(Group*)), SLOT(onGroupChanged(Group*))); + connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SIGNAL(groupChanged())); connect(m_entryView, SIGNAL(entryActivated(Entry*,EntryModel::ModelColumn)), SLOT(entryActivationSignalReceived(Entry*,EntryModel::ModelColumn))); - connect(m_entryView, SIGNAL(entrySelectionChanged()), SIGNAL(entrySelectionChanged())); + connect(m_entryView, SIGNAL(entrySelectionChanged(Entry*)), SLOT(onEntryChanged(Entry*))); connect(m_editEntryWidget, SIGNAL(editFinished(bool)), SLOT(switchToMainView(bool))); connect(m_editEntryWidget, SIGNAL(historyEntryActivated(Entry*)), SLOT(switchToHistoryView(Entry*))); connect(m_historyEditEntryWidget, SIGNAL(editFinished(bool)), SLOT(switchBackToEntryEdit())); @@ -180,10 +178,6 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) connect(&m_fileWatchUnblockTimer, SIGNAL(timeout()), this, SLOT(unblockAutoReload())); connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged())); - connect(m_groupView, SIGNAL(groupPressed(Group*)), SLOT(emitPressedGroup(Group*))); - connect(m_groupView, SIGNAL(groupChanged(Group*)), SLOT(emitPressedGroup(Group*))); - connect(m_editEntryWidget, SIGNAL(editFinished(bool)), SLOT(emitEntrySelectionChanged())); - connectDatabaseSignals(); m_fileWatchTimer.setSingleShot(true); @@ -700,6 +694,12 @@ void DatabaseWidget::switchToMainView(bool previousDialogAccepted) } setCurrentWidget(m_mainWidget); + + if (sender() == m_entryView) { + onEntryChanged(m_entryView->currentEntry()); + } else if (sender() == m_groupView) { + onGroupChanged(m_groupView->currentGroup()); + } } void DatabaseWidget::switchToHistoryView(Entry* entry) @@ -879,7 +879,6 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod // Call this first to clear out of search mode, otherwise // the desired entry is not properly selected endSearch(); - emit clearSearch(); m_groupView->setCurrentGroup(entry->group()); m_entryView->setCurrentEntry(entry); break; @@ -1030,14 +1029,15 @@ void DatabaseWidget::setSearchLimitGroup(bool state) void DatabaseWidget::onGroupChanged(Group* group) { // Intercept group changes if in search mode - if (isSearchActive()) { + if (isSearchActive() && m_searchLimitGroup) { search(m_lastSearchText); } else if (isSearchActive()) { - // Otherwise cancel search - emit clearSearch(); + endSearch(); } else { m_entryView->displayGroup(group); } + + m_previewView->setGroup(group); } QString DatabaseWidget::getCurrentSearch() @@ -1060,6 +1060,9 @@ void DatabaseWidget::endSearch() m_searchingLabel->setText(tr("Searching...")); m_lastSearchText.clear(); + + // Tell the search widget to clear + emit clearSearch(); } void DatabaseWidget::emitGroupContextMenuRequested(const QPoint& pos) @@ -1072,26 +1075,15 @@ void DatabaseWidget::emitEntryContextMenuRequested(const QPoint& pos) emit entryContextMenuRequested(m_entryView->viewport()->mapToGlobal(pos)); } -void DatabaseWidget::emitEntrySelectionChanged() +void DatabaseWidget::onEntryChanged(Entry* entry) { - Entry* currentEntry = m_entryView->currentEntry(); - if (currentEntry) { - m_previewView->setEntry(currentEntry); + if (entry) { + m_previewView->setEntry(entry); } emit entrySelectionChanged(); } -void DatabaseWidget::emitPressedGroup(Group* currentGroup) -{ - if (!currentGroup) { - // if no group is pressed, leave in details the last group - return; - } - - emit pressedGroup(currentGroup); -} - bool DatabaseWidget::canDeleteCurrentGroup() const { bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup(); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 078ae9d23..1c190558c 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -132,8 +132,6 @@ signals: void databaseMerged(QSharedPointer mergedDb); void groupContextMenuRequested(const QPoint& globalPos); void entryContextMenuRequested(const QPoint& globalPos); - void pressedEntry(Entry* selectedEntry); - void pressedGroup(Group* selectedGroup); void listModeAboutToActivate(); void listModeActivated(); void searchModeAboutToActivate(); @@ -168,7 +166,6 @@ public slots: void openUrlForEntry(Entry* entry); void createGroup(); void deleteGroup(); - void onGroupChanged(Group* group); void switchToMainView(bool previousDialogAccepted = false); void switchToEntryEdit(); void switchToGroupEdit(); @@ -210,8 +207,8 @@ private slots: void switchToGroupEdit(Group* entry, bool create); void emitGroupContextMenuRequested(const QPoint& pos); void emitEntryContextMenuRequested(const QPoint& pos); - void emitPressedGroup(Group* currentGroup); - void emitEntrySelectionChanged(); + void onEntryChanged(Entry* entry); + void onGroupChanged(Group* group); void connectDatabaseSignals(); void loadDatabase(bool accepted); void unlockDatabase(bool accepted); diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 64eca5ee3..fa237163e 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -50,7 +50,7 @@ EntryView::EntryView(QWidget* parent) setDefaultDropAction(Qt::MoveAction); connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex))); - connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); + connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(emitEntrySelectionChanged())); connect(m_model, SIGNAL(usernamesHiddenChanged()), SIGNAL(viewStateChanged())); connect(m_model, SIGNAL(passwordsHiddenChanged()), SIGNAL(viewStateChanged())); @@ -144,13 +144,13 @@ void EntryView::keyPressEvent(QKeyEvent* event) void EntryView::focusInEvent(QFocusEvent* event) { - emit entrySelectionChanged(); + emit entrySelectionChanged(currentEntry()); QTreeView::focusInEvent(event); } void EntryView::focusOutEvent(QFocusEvent* event) { - emit entrySelectionChanged(); + emit entrySelectionChanged(nullptr); QTreeView::focusOutEvent(event); } @@ -181,7 +181,7 @@ void EntryView::setFirstEntryActive() QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); setCurrentEntry(m_model->entryFromIndex(index)); } else { - emit entrySelectionChanged(); + emit entrySelectionChanged(currentEntry()); } } @@ -196,6 +196,11 @@ void EntryView::emitEntryActivated(const QModelIndex& index) emit entryActivated(entry, static_cast(m_sortModel->mapToSource(index).column())); } +void EntryView::emitEntrySelectionChanged() +{ + emit entrySelectionChanged(currentEntry()); +} + void EntryView::setModel(QAbstractItemModel* model) { Q_UNUSED(model); diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 766699599..09dfd8dde 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -52,7 +52,7 @@ public: signals: void entryActivated(Entry* entry, EntryModel::ModelColumn column); - void entrySelectionChanged(); + void entrySelectionChanged(Entry* entry); void viewStateChanged(); public slots: @@ -66,6 +66,7 @@ protected: private slots: void emitEntryActivated(const QModelIndex& index); + void emitEntrySelectionChanged(); void showHeaderMenu(const QPoint& position); void toggleColumnVisibility(QAction* action); void fitColumnsToWindow(); diff --git a/src/gui/group/GroupView.cpp b/src/gui/group/GroupView.cpp index e98a7385e..5d7cba96a 100644 --- a/src/gui/group/GroupView.cpp +++ b/src/gui/group/GroupView.cpp @@ -34,15 +34,12 @@ GroupView::GroupView(Database* db, QWidget* parent) setHeaderHidden(true); setUniformRowHeights(true); - connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(expandedChanged(QModelIndex))); - connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(expandedChanged(QModelIndex))); + connect(this, SIGNAL(expanded(QModelIndex)), SLOT(expandedChanged(QModelIndex))); + connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(expandedChanged(QModelIndex))); connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(syncExpandedState(QModelIndex,int,int))); connect(m_model, SIGNAL(modelReset()), SLOT(modelReset())); - connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(emitGroupChanged())); - connect(this, SIGNAL(clicked(QModelIndex)), SLOT(emitGroupPressed(QModelIndex))); - modelReset(); setDragEnabled(true); @@ -110,11 +107,6 @@ void GroupView::expandGroup(Group* group, bool expand) setExpanded(index, expand); } -void GroupView::emitGroupChanged(const QModelIndex& index) -{ - emit groupChanged(m_model->groupFromIndex(index)); -} - void GroupView::setModel(QAbstractItemModel* model) { Q_UNUSED(model); @@ -123,12 +115,7 @@ void GroupView::setModel(QAbstractItemModel* model) void GroupView::emitGroupChanged() { - emit groupChanged(currentGroup()); -} - -void GroupView::emitGroupPressed(const QModelIndex& index) -{ - emit groupPressed(m_model->groupFromIndex(index)); + emit groupSelectionChanged(currentGroup()); } void GroupView::syncExpandedState(const QModelIndex& parent, int start, int end) diff --git a/src/gui/group/GroupView.h b/src/gui/group/GroupView.h index 0c27b1a12..141706b07 100644 --- a/src/gui/group/GroupView.h +++ b/src/gui/group/GroupView.h @@ -37,14 +37,11 @@ public: void expandGroup(Group* group, bool expand = true); signals: - void groupChanged(Group* group); - void groupPressed(Group* group); + void groupSelectionChanged(Group* group); private slots: void expandedChanged(const QModelIndex& index); - void emitGroupChanged(const QModelIndex& index); void emitGroupChanged(); - void emitGroupPressed(const QModelIndex& index); void syncExpandedState(const QModelIndex& parent, int start, int end); void modelReset(); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 1964b0e0f..48c308243 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -875,18 +875,28 @@ void TestGui::testSearch() QTRY_COMPARE(entryView->model()->rowCount(), 2); // Test group search + searchWidget->setLimitGroup(false); GroupView* groupView = m_dbWidget->findChild("groupView"); QCOMPARE(groupView->currentGroup(), m_db->rootGroup()); QModelIndex rootGroupIndex = groupView->model()->index(0, 0); clickIndex(groupView->model()->index(0, 0, rootGroupIndex), groupView, Qt::LeftButton); QCOMPARE(groupView->currentGroup()->name(), QString("General")); - - searchWidget->setLimitGroup(false); + // Selecting a group should cancel search + QTRY_COMPARE(entryView->model()->rowCount(), 0); + // Restore search + QTest::keyClick(m_mainWindow.data(), Qt::Key_F, Qt::ControlModifier); + QTest::keyClicks(searchTextEdit, "someTHING"); QTRY_COMPARE(entryView->model()->rowCount(), 2); + // Enable group limiting searchWidget->setLimitGroup(true); QTRY_COMPARE(entryView->model()->rowCount(), 0); + // Selecting another group should NOT cancel search + clickIndex(rootGroupIndex, groupView, Qt::LeftButton); + QCOMPARE(groupView->currentGroup(), m_db->rootGroup()); + QTRY_COMPARE(entryView->model()->rowCount(), 2); // reset + searchWidget->setLimitGroup(false); clickIndex(rootGroupIndex, groupView, Qt::LeftButton); QCOMPARE(groupView->currentGroup(), m_db->rootGroup()); From 1a2721529d6356125895d5b54a8ffa04800843d5 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Tue, 30 Oct 2018 17:17:52 -0400 Subject: [PATCH 70/84] Correct simultaneous saving with Yubikey * Move mutex lock to right before challenge call and wait for up to 1 second for unlock * Fix bug where ALREADY_RUNNING was interpreted as success and causing database corruption --- src/keys/YkChallengeResponseKey.cpp | 24 ++++++------------------ src/keys/YkChallengeResponseKey.h | 2 +- src/keys/drivers/YubiKey.cpp | 18 ++++++++---------- src/keys/drivers/YubiKey.h | 12 +----------- 4 files changed, 16 insertions(+), 40 deletions(-) diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index ade1b6324..830e0c0e0 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -18,6 +18,7 @@ #include "keys/YkChallengeResponseKey.h" #include "keys/drivers/YubiKey.h" +#include "core/AsyncTask.h" #include "core/Tools.h" #include "crypto/CryptoHash.h" #include "crypto/Random.h" @@ -56,10 +57,8 @@ bool YkChallengeResponseKey::challenge(const QByteArray& challenge) return this->challenge(challenge, 2); } -bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned retries) +bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned int retries) { - Q_ASSERT(retries > 0); - do { --retries; @@ -67,28 +66,17 @@ bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned ret emit userInteractionRequired(); } - QFuture future = QtConcurrent::run( - [this, challenge]() { return YubiKey::instance()->challenge(m_slot, true, challenge, m_key); }); - - QEventLoop loop; - QFutureWatcher watcher; - connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit())); - watcher.setFuture(future); - loop.exec(); + auto result = AsyncTask::runAndWaitForFuture([this, challenge]() { + return YubiKey::instance()->challenge(m_slot, true, challenge, m_key); + }); if (m_blocking) { emit userConfirmed(); } - if (future.result() != YubiKey::ERROR) { + if (result == YubiKey::SUCCESS) { return true; } - - // if challenge failed, retry to detect YubiKeys in the event the YubiKey was un-plugged and re-plugged - if (retries > 0 && !YubiKey::instance()->init()) { - continue; - } - } while (retries > 0); return false; diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h index 0ce915271..fccd90811 100644 --- a/src/keys/YkChallengeResponseKey.h +++ b/src/keys/YkChallengeResponseKey.h @@ -35,7 +35,7 @@ public: QByteArray rawKey() const override; bool challenge(const QByteArray& challenge) override; - bool challenge(const QByteArray& challenge, unsigned retries); + bool challenge(const QByteArray& challenge, unsigned int retries); QString getName() const; bool isBlocking() const; diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index 38ea818e5..bd09fb9f6 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -161,19 +161,14 @@ bool YubiKey::getSerial(unsigned int& serial) YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response) { - if (!m_mutex.tryLock()) { - return ALREADY_RUNNING; + // ensure that YubiKey::init() succeeded + if (!init()) { + return ERROR; } int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2; QByteArray paddedChallenge = challenge; - // ensure that YubiKey::init() succeeded - if (!init()) { - m_mutex.unlock(); - return ERROR; - } - // yk_challenge_response() insists on 64 byte response buffer */ response.clear(); response.resize(64); @@ -194,9 +189,12 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte c = reinterpret_cast(paddedChallenge.constData()); r = reinterpret_cast(response.data()); - int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChallenge.size(), c, response.size(), r); - emit challenged(); + // Try to grab a lock for 1 second, fail out if not possible + if (!m_mutex.tryLock(1000)) { + return ALREADY_RUNNING; + } + int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChallenge.size(), c, response.size(), r); m_mutex.unlock(); if (!ret) { diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index 39085546b..14cb43394 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -68,7 +68,7 @@ public: * @param mayBlock operation is allowed to block * @param challenge challenge input to YubiKey * @param response response output from YubiKey - * @return true on success + * @return challenge result */ ChallengeResult challenge(int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response); @@ -98,21 +98,11 @@ signals: */ void detectComplete(); - /** - * Emitted when the YubiKey was challenged and has returned a response. - */ - void challenged(); - /** * Emitted when no Yubikey could be found. */ void notFound(); - /** - * Emitted when detection is already running. - */ - void alreadyRunning(); - private: explicit YubiKey(); static YubiKey* m_instance; From 49b82ea6a2486d2ca087d5c2ab2c37ffa4f2305f Mon Sep 17 00:00:00 2001 From: louib Date: Wed, 28 Nov 2018 16:15:15 -0500 Subject: [PATCH 71/84] Global const for KeyFileOption (CLI) (#2505) --- src/cli/Add.cpp | 8 ++------ src/cli/Clip.cpp | 8 +++----- src/cli/Command.cpp | 7 +++++++ src/cli/Command.h | 1 + src/cli/Edit.cpp | 8 ++------ src/cli/Extract.cpp | 7 ++----- src/cli/List.cpp | 8 +++----- src/cli/Locate.cpp | 7 ++----- src/cli/Merge.cpp | 21 ++++++++------------- src/cli/Remove.cpp | 7 ++----- src/cli/Show.cpp | 7 ++----- 11 files changed, 34 insertions(+), 55 deletions(-) diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index 68240adb6..e51803614 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -49,11 +49,7 @@ int Add::execute(const QStringList& arguments) parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); - - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); + parser.addOption(Command::KeyFileOption); QCommandLineOption username(QStringList() << "u" << "username", QObject::tr("Username for the entry."), @@ -91,7 +87,7 @@ int Add::execute(const QStringList& arguments) const QString& entryPath = args.at(1); auto db = Database::unlockFromStdin(databasePath, - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db) { diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 224841f60..6417c8c4b 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -47,11 +47,9 @@ int Clip::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); parser.addOption(Command::QuietOption); + parser.addOption(Command::KeyFileOption); + QCommandLineOption totp(QStringList() << "t" << "totp", QObject::tr("Copy the current TOTP to the clipboard.")); parser.addOption(totp); @@ -68,7 +66,7 @@ int Clip::execute(const QStringList& arguments) } auto db = Database::unlockFromStdin(args.at(0), - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db) { diff --git a/src/cli/Command.cpp b/src/cli/Command.cpp index a93452bcb..ff74089af 100644 --- a/src/cli/Command.cpp +++ b/src/cli/Command.cpp @@ -39,6 +39,13 @@ const QCommandLineOption Command::QuietOption = QCommandLineOption(QStringList() << "q" << "quiet", QObject::tr("Silence password prompt and other secondary outputs.")); + +const QCommandLineOption Command::KeyFileOption = + QCommandLineOption(QStringList() << "k" + << "key-file", + QObject::tr("Key file of the database."), + QObject::tr("path")); + QMap commands; Command::~Command() diff --git a/src/cli/Command.h b/src/cli/Command.h index beb9d4087..b74a312df 100644 --- a/src/cli/Command.h +++ b/src/cli/Command.h @@ -39,6 +39,7 @@ public: static Command* getCommand(const QString& commandName); static const QCommandLineOption QuietOption; + static const QCommandLineOption KeyFileOption; }; #endif // KEEPASSXC_COMMAND_H diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index bbb1473c8..d8d89833a 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -49,11 +49,7 @@ int Edit::execute(const QStringList& arguments) parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); - - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); + parser.addOption(Command::KeyFileOption); QCommandLineOption username(QStringList() << "u" << "username", QObject::tr("Username for the entry."), @@ -95,7 +91,7 @@ int Edit::execute(const QStringList& arguments) const QString& entryPath = args.at(1); auto db = Database::unlockFromStdin(databasePath, - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db) { diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index e6afdfbdb..38ef09db9 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -50,10 +50,7 @@ int Extract::execute(const QStringList& arguments) parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database to extract.")); parser.addOption(Command::QuietOption); - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); + parser.addOption(Command::KeyFileOption); parser.addHelpOption(); parser.process(arguments); @@ -74,7 +71,7 @@ int Extract::execute(const QStringList& arguments) passwordKey->setPassword(line); compositeKey->addKey(passwordKey); - QString keyFilePath = parser.value(keyFile); + QString keyFilePath = parser.value(Command::KeyFileOption); if (!keyFilePath.isEmpty()) { // LCOV_EXCL_START auto fileKey = QSharedPointer::create(); diff --git a/src/cli/List.cpp b/src/cli/List.cpp index ca96799f0..f7be0ac72 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -47,10 +47,8 @@ int List::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), "[group]"); parser.addOption(Command::QuietOption); - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); + parser.addOption(Command::KeyFileOption); + QCommandLineOption recursiveOption(QStringList() << "R" << "recursive", QObject::tr("Recursively list the elements of the group.")); parser.addOption(recursiveOption); @@ -66,7 +64,7 @@ int List::execute(const QStringList& arguments) bool recursive = parser.isSet(recursiveOption); auto db = Database::unlockFromStdin(args.at(0), - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db) { diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index ab45115db..0ccf5825c 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -49,10 +49,7 @@ int Locate::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addPositionalArgument("term", QObject::tr("Search term.")); parser.addOption(Command::QuietOption); - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); + parser.addOption(Command::KeyFileOption); parser.addHelpOption(); parser.process(arguments); @@ -63,7 +60,7 @@ int Locate::execute(const QStringList& arguments) } auto db = Database::unlockFromStdin(args.at(0), - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db) { diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index b17d27f7e..17431d62e 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -50,14 +50,12 @@ int Merge::execute(const QStringList& arguments) QCommandLineOption samePasswordOption(QStringList() << "s" << "same-credentials", QObject::tr("Use the same credentials for both database files.")); parser.addOption(samePasswordOption); - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); - QCommandLineOption keyFileFrom(QStringList() << "f" << "key-file-from", - QObject::tr("Key file of the database to merge from."), - QObject::tr("path")); - parser.addOption(keyFileFrom); + parser.addOption(Command::KeyFileOption); + + QCommandLineOption keyFileFromOption(QStringList() << "f" << "key-file-from", + QObject::tr("Key file of the database to merge from."), + QObject::tr("path")); + parser.addOption(keyFileFromOption); parser.addHelpOption(); parser.process(arguments); @@ -69,7 +67,7 @@ int Merge::execute(const QStringList& arguments) } auto db1 = Database::unlockFromStdin(args.at(0), - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db1) { @@ -78,10 +76,7 @@ int Merge::execute(const QStringList& arguments) QSharedPointer db2; if (!parser.isSet("same-credentials")) { - db2 = Database::unlockFromStdin(args.at(1), - parser.value(keyFileFrom), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFromOption), Utils::STDOUT, Utils::STDERR); } else { db2 = QSharedPointer::create(); QString errorMessage; diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 7e36d1e4a..1341fc6d1 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -50,10 +50,7 @@ int Remove::execute(const QStringList& arguments) parser.setApplicationDescription(QCoreApplication::tr("main", "Remove an entry from the database.")); parser.addPositionalArgument("database", QCoreApplication::tr("main", "Path of the database.")); parser.addOption(Command::QuietOption); - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); + parser.addOption(Command::KeyFileOption); parser.addPositionalArgument("entry", QCoreApplication::tr("main", "Path of the entry to remove.")); parser.addHelpOption(); parser.process(arguments); @@ -65,7 +62,7 @@ int Remove::execute(const QStringList& arguments) } auto db = Database::unlockFromStdin(args.at(0), - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db) { diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index cf8dc895d..5efae70c5 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -46,11 +46,8 @@ int Show::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); parser.addPositionalArgument("database", QObject::tr("Path of the database.")); - QCommandLineOption keyFile(QStringList() << "k" << "key-file", - QObject::tr("Key file of the database."), - QObject::tr("path")); - parser.addOption(keyFile); parser.addOption(Command::QuietOption); + parser.addOption(Command::KeyFileOption); QCommandLineOption totp(QStringList() << "t" << "totp", QObject::tr("Show the entry's current TOTP.")); parser.addOption(totp); @@ -73,7 +70,7 @@ int Show::execute(const QStringList& arguments) } auto db = Database::unlockFromStdin(args.at(0), - parser.value(keyFile), + parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); if (!db) { From b39522f874f6a1c493eb1633808baa1e3e20ae50 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Wed, 31 Oct 2018 18:23:42 +0100 Subject: [PATCH 72/84] Avoid allocating temporary containers --- src/core/Group.cpp | 2 +- src/core/Merger.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 68c2bcf47..2051fe437 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -983,7 +983,7 @@ QStringList Group::locate(const QString& locateTerm, const QString& currentPath) for (const Entry* entry : asConst(m_entries)) { QString entryPath = currentPath + entry->title(); - if (entryPath.toLower().contains(locateTerm.toLower())) { + if (entryPath.contains(locateTerm, Qt::CaseInsensitive)) { response << entryPath; } } diff --git a/src/core/Merger.cpp b/src/core/Merger.cpp index 2bdff7377..0da7458ea 100644 --- a/src/core/Merger.cpp +++ b/src/core/Merger.cpp @@ -480,7 +480,7 @@ bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::M bool updateTimeInfo = targetEntry->canUpdateTimeinfo(); targetEntry->setUpdateTimeinfo(false); targetEntry->removeHistoryItems(targetHistoryItems); - for (Entry* historyItem : merged.values()) { + for (Entry* historyItem : merged) { Q_ASSERT(!historyItem->parent()); targetEntry->addHistoryItem(historyItem); } From 9d106d6bfcabbb623055def8a6941d399b5a3b0f Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Wed, 31 Oct 2018 18:31:01 +0100 Subject: [PATCH 73/84] Remove unused variables --- src/format/Kdbx4Writer.cpp | 1 - src/gui/KMessageWidget.cpp | 1 - src/gui/entry/AutoTypeMatchModel.cpp | 1 - src/gui/entry/EditEntryWidget.cpp | 1 - 4 files changed, 4 deletions(-) diff --git a/src/format/Kdbx4Writer.cpp b/src/format/Kdbx4Writer.cpp index 8439fb1db..a73cbd83a 100644 --- a/src/format/Kdbx4Writer.cpp +++ b/src/format/Kdbx4Writer.cpp @@ -50,7 +50,6 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db) QByteArray masterSeed = randomGen()->randomArray(32); QByteArray encryptionIV = randomGen()->randomArray(ivSize); QByteArray protectedStreamKey = randomGen()->randomArray(64); - QByteArray startBytes; QByteArray endOfHeader = "\r\n\r\n"; if (!db->setKey(db->key(), false, true)) { diff --git a/src/gui/KMessageWidget.cpp b/src/gui/KMessageWidget.cpp index 3de1a15b6..fabc52952 100644 --- a/src/gui/KMessageWidget.cpp +++ b/src/gui/KMessageWidget.cpp @@ -287,7 +287,6 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type) // Tint close icon auto closeButtonPixmap = d->closeButtonPixmap; - QPixmap mask(closeButtonPixmap); QPainter painter; painter.begin(&closeButtonPixmap); painter.setRenderHints(QPainter::HighQualityAntialiasing); diff --git a/src/gui/entry/AutoTypeMatchModel.cpp b/src/gui/entry/AutoTypeMatchModel.cpp index 197b3cd96..4148ccc66 100644 --- a/src/gui/entry/AutoTypeMatchModel.cpp +++ b/src/gui/entry/AutoTypeMatchModel.cpp @@ -101,7 +101,6 @@ QVariant AutoTypeMatchModel::data(const QModelIndex& index, int role) const AutoTypeMatch match = matchFromIndex(index); if (role == Qt::DisplayRole) { - QString result; switch (index.column()) { case ParentGroup: if (match.entry->group()) { diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 72d09c9ff..75e5294ba 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -463,7 +463,6 @@ void EditEntryWidget::updateSSHAgentKeyInfo() void EditEntryWidget::saveSSHAgentConfig() { KeeAgentSettings settings; - QString privateKeyPath = m_sshAgentUi->attachmentComboBox->currentText(); settings.setAddAtDatabaseOpen(m_sshAgentUi->addKeyToAgentCheckBox->isChecked()); settings.setRemoveAtDatabaseClose(m_sshAgentUi->removeKeyFromAgentCheckBox->isChecked()); From a9fa8915d99010e69f6ea1fa6cd5095e4470028f Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Wed, 31 Oct 2018 18:37:00 +0100 Subject: [PATCH 74/84] Use QVariant::toUrl() instead of QVariant::value() --- src/gui/EditWidgetIcons.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 09aafbf11..5d1a5f901 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -173,7 +173,7 @@ namespace { { QUrl url; if (var.canConvert()) - url = var.value(); + url = var.toUrl(); return url; } From f9625189cb7ddf9d1353a6dab079830778623d48 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Wed, 31 Oct 2018 18:44:25 +0100 Subject: [PATCH 75/84] Fix typo in parameter name A typo in a parameter name caused an inconsistency between declaration and definition of a function. --- src/streams/qtiocompressor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/streams/qtiocompressor.cpp b/src/streams/qtiocompressor.cpp index 6e3c69ffc..22b2997cc 100644 --- a/src/streams/qtiocompressor.cpp +++ b/src/streams/qtiocompressor.cpp @@ -71,7 +71,7 @@ public: ~QtIOCompressorPrivate(); void flushZlib(int flushMode); bool writeBytes(ZlibByte *buffer, ZlibSize outputSize); - void setZlibError(const QString &erroMessage, int zlibErrorCode); + void setZlibError(const QString &errorMessage, int zlibErrorCode); QIODevice *device; bool manageDevice; From fc930bae69be516cb2ad72e83dad1cf6d8631956 Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Thu, 1 Nov 2018 04:27:38 +0100 Subject: [PATCH 76/84] Restore correct formatting Many lines were not conformant with the project's formatting rules. This patch should fix all formatting and whitespace issues in the code base. A clang-format directive was put around the connect() calls containing SIGNALs and SLOTs whose signatures would be denormalized because of the formatting rules. --- src/autotype/AutoType.cpp | 10 +- src/autotype/AutoTypeSelectDialog.cpp | 3 + src/autotype/WildcardMatcher.cpp | 2 +- src/autotype/WildcardMatcher.h | 2 +- src/autotype/xcb/AutoTypeXCB.cpp | 3 +- src/browser/BrowserAccessControlDialog.cpp | 39 +-- src/browser/BrowserAccessControlDialog.h | 32 +- src/browser/BrowserAction.cpp | 37 +- src/browser/BrowserAction.h | 32 +- src/browser/BrowserClients.cpp | 32 +- src/browser/BrowserClients.h | 32 +- src/browser/BrowserEntryConfig.cpp | 32 +- src/browser/BrowserEntryConfig.h | 32 +- src/browser/BrowserEntrySaveDialog.cpp | 42 +-- src/browser/BrowserEntrySaveDialog.h | 41 +-- src/browser/BrowserOptionDialog.cpp | 59 ++-- src/browser/BrowserOptionDialog.h | 36 +- src/browser/BrowserService.cpp | 89 +++-- src/browser/BrowserService.h | 34 +- src/browser/BrowserSettings.cpp | 42 +-- src/browser/BrowserSettings.h | 36 +- src/browser/HostInstaller.cpp | 40 +-- src/browser/HostInstaller.h | 32 +- src/browser/NativeMessagingBase.cpp | 35 +- src/browser/NativeMessagingBase.h | 36 +- src/browser/NativeMessagingHost.cpp | 36 +- src/browser/NativeMessagingHost.h | 32 +- src/browser/Variant.cpp | 30 +- src/browser/Variant.h | 30 +- src/cli/Add.cpp | 12 +- src/cli/Clip.cpp | 7 +- src/cli/Diceware.cpp | 6 +- src/cli/Edit.cpp | 18 +- src/cli/Estimate.cpp | 13 +- src/cli/Extract.cpp | 3 +- src/cli/Generate.cpp | 21 +- src/cli/List.cpp | 3 +- src/cli/Merge.cpp | 6 +- src/cli/Show.cpp | 6 +- src/cli/Utils.cpp | 203 ++++++----- src/cli/Utils.h | 26 +- src/core/AsyncTask.h | 2 +- src/core/Bootstrap.cpp | 310 ++++++++--------- src/core/Bootstrap.h | 16 +- src/core/Config.cpp | 2 +- src/core/Database.cpp | 5 +- src/core/Entry.cpp | 9 +- src/core/Entry.h | 3 +- src/core/EntryAttributes.cpp | 4 +- src/core/Group.cpp | 6 + src/core/InactivityTimer.cpp | 3 +- src/core/Merger.cpp | 68 ++-- src/core/Merger.h | 20 +- src/core/Metadata.cpp | 2 +- src/core/OSEventFilter.h | 1 + src/core/PasswordGenerator.cpp | 1 - src/core/Tools.cpp | 232 +++++++------ src/core/Tools.h | 38 +-- src/crypto/Crypto.cpp | 5 +- src/crypto/SymmetricCipher.cpp | 30 +- src/crypto/SymmetricCipher.h | 30 +- src/crypto/SymmetricCipherBackend.h | 30 +- src/crypto/SymmetricCipherGcrypt.cpp | 30 +- src/crypto/SymmetricCipherGcrypt.h | 30 +- src/crypto/kdf/Argon2Kdf.h | 12 +- src/crypto/kdf/Kdf.h | 2 +- src/format/Kdbx3Reader.cpp | 3 +- src/format/Kdbx3Writer.cpp | 7 +- src/format/Kdbx4Writer.cpp | 10 +- src/format/KdbxXmlReader.cpp | 2 +- src/format/KdbxXmlReader.h | 3 +- src/format/KdbxXmlWriter.cpp | 2 + src/format/KeePass1.h | 32 +- src/format/KeePass2.cpp | 18 +- src/format/KeePass2.h | 30 +- src/gui/AboutDialog.cpp | 7 +- src/gui/ApplicationSettingsWidget.h | 2 +- src/gui/CategoryListWidget.cpp | 5 +- src/gui/DatabaseOpenWidget.cpp | 4 +- src/gui/DatabaseTabWidget.cpp | 12 +- src/gui/DatabaseWidget.cpp | 6 +- src/gui/EditWidget.cpp | 1 + src/gui/EditWidgetIcons.cpp | 37 +- src/gui/EditWidgetIcons.h | 8 +- src/gui/EditWidgetProperties.cpp | 25 +- src/gui/KeePass1OpenWidget.cpp | 2 +- src/gui/MainWindow.cpp | 29 +- src/gui/PasswordEdit.cpp | 5 +- src/gui/PasswordGeneratorWidget.cpp | 6 +- src/gui/TotpSetupDialog.cpp | 3 +- src/gui/csvImport/CsvImportWidget.cpp | 8 +- src/gui/csvImport/CsvImportWizard.cpp | 1 - src/gui/dbsettings/DatabaseSettingsDialog.cpp | 10 +- src/gui/dbsettings/DatabaseSettingsDialog.h | 2 +- src/gui/dbsettings/DatabaseSettingsWidget.cpp | 2 +- src/gui/dbsettings/DatabaseSettingsWidget.h | 2 +- .../DatabaseSettingsWidgetBrowser.cpp | 74 ++-- .../DatabaseSettingsWidgetBrowser.h | 18 +- .../DatabaseSettingsWidgetEncryption.cpp | 13 +- .../DatabaseSettingsWidgetEncryption.h | 17 +- .../DatabaseSettingsWidgetGeneral.cpp | 8 +- .../DatabaseSettingsWidgetGeneral.h | 9 +- .../DatabaseSettingsWidgetMasterKey.cpp | 49 +-- .../DatabaseSettingsWidgetMasterKey.h | 15 +- .../DatabaseSettingsWidgetMetaDataSimple.cpp | 1 + .../DatabaseSettingsWidgetMetaDataSimple.h | 11 +- src/gui/entry/AutoTypeMatchView.cpp | 6 +- src/gui/entry/EditEntryWidget.cpp | 49 +-- src/gui/entry/EditEntryWidget.h | 3 +- src/gui/entry/EntryAttachmentsWidget.cpp | 3 + src/gui/entry/EntryAttributesModel.cpp | 6 + src/gui/entry/EntryView.cpp | 13 + src/gui/group/EditGroupWidget.cpp | 3 + src/gui/group/EditGroupWidget.h | 2 +- src/gui/group/GroupModel.cpp | 2 + src/gui/group/GroupView.cpp | 2 + src/gui/masterkey/KeyComponentWidget.cpp | 4 +- src/gui/masterkey/KeyComponentWidget.h | 8 +- src/gui/masterkey/KeyFileEditWidget.cpp | 9 +- src/gui/masterkey/KeyFileEditWidget.h | 6 +- src/gui/masterkey/PasswordEditWidget.cpp | 8 +- src/gui/masterkey/PasswordEditWidget.h | 4 +- src/gui/masterkey/YubiKeyEditWidget.cpp | 17 +- src/gui/masterkey/YubiKeyEditWidget.h | 4 +- src/gui/settings/SettingsWidget.h | 4 +- src/gui/wizard/NewDatabaseWizard.cpp | 10 +- src/gui/wizard/NewDatabaseWizard.h | 4 +- src/gui/wizard/NewDatabaseWizardPage.cpp | 1 + src/gui/wizard/NewDatabaseWizardPage.h | 6 +- .../wizard/NewDatabaseWizardPageEncryption.h | 4 +- .../wizard/NewDatabaseWizardPageMasterKey.h | 4 +- .../wizard/NewDatabaseWizardPageMetaData.h | 4 +- src/keys/ChallengeResponseKey.h | 41 ++- src/keys/CompositeKey.cpp | 32 +- src/keys/CompositeKey.h | 34 +- src/keys/FileKey.cpp | 32 +- src/keys/FileKey.h | 32 +- src/keys/Key.h | 35 +- src/keys/PasswordKey.cpp | 30 +- src/keys/PasswordKey.h | 33 +- src/keys/YkChallengeResponseKey.cpp | 33 +- src/keys/YkChallengeResponseKey.h | 2 +- src/keys/drivers/YubiKey.cpp | 32 +- src/keys/drivers/YubiKey.h | 32 +- src/keys/drivers/YubiKeyStub.cpp | 32 +- src/main.cpp | 31 +- src/proxy/NativeMessagingHost.cpp | 37 +- src/proxy/NativeMessagingHost.h | 30 +- src/proxy/keepassxc-proxy.cpp | 32 +- src/sshagent/ASN1Key.cpp | 2 +- src/sshagent/ASN1Key.h | 2 +- src/sshagent/AgentSettingsPage.h | 2 +- src/sshagent/AgentSettingsWidget.cpp | 3 +- src/sshagent/KeeAgentSettings.cpp | 2 + src/sshagent/OpenSSHKey.cpp | 2 +- src/streams/HashedBlockStream.cpp | 30 +- src/streams/HashedBlockStream.h | 30 +- src/streams/HmacBlockStream.cpp | 30 +- src/streams/HmacBlockStream.h | 30 +- src/streams/LayeredStream.cpp | 30 +- src/streams/LayeredStream.h | 30 +- src/streams/StoreDataStream.cpp | 30 +- src/streams/StoreDataStream.h | 30 +- src/totp/totp.cpp | 26 +- src/totp/totp.h | 79 ++--- src/touchid/TouchID.h | 8 +- tests/TestCli.cpp | 251 ++++++++------ tests/TestCli.h | 6 +- tests/TestCryptoHash.cpp | 5 +- tests/TestCsvExporter.cpp | 7 +- tests/TestEntry.cpp | 57 ++-- tests/TestGlobal.h | 2 +- tests/TestKdbx4.cpp | 8 +- tests/TestKeePass2Format.cpp | 3 +- tests/TestMerge.cpp | 61 ++-- tests/TestMerge.h | 19 +- tests/TestModified.cpp | 1 - tests/TestOpenSSHKey.cpp | 316 +++++++++--------- tests/TestPasswordGenerator.cpp | 19 +- tests/TestPasswordGenerator.h | 2 +- tests/TestSymmetricCipher.cpp | 37 +- tests/TestTools.cpp | 2 +- tests/gui/TestGui.cpp | 23 +- tests/mock/MockChallengeResponseKey.cpp | 2 +- tests/mock/MockChallengeResponseKey.h | 2 +- tests/util/TemporaryFile.h | 2 +- 186 files changed, 2437 insertions(+), 2122 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index c2ddc9752..012dee62c 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -672,7 +672,7 @@ bool AutoType::checkSyntax(const QString& string) QString shortcutKeys = "[\\^\\%~\\+@]"; // a normal string not in parentheses QString fixedStrings = "[^\\^\\%~\\+@\\{\\}]*"; - + // clang-format off QRegularExpression autoTypeSyntax( "^(?:" + shortcutKeys + "|" + fixedStrings + "|\\{(?:" + normalCommands + "|" + specialLiterals + "|" + functionKeys @@ -688,6 +688,7 @@ bool AutoType::checkSyntax(const QString& string) + customAttributes + "\\})*$", QRegularExpression::CaseInsensitiveOption); + // clang-format on QRegularExpressionMatch match = autoTypeSyntax.match(string); return match.hasMatch(); } @@ -755,9 +756,10 @@ bool AutoType::verifyAutoTypeSyntax(const QString& sequence) } } else if (AutoType::checkHighRepetition(sequence)) { QMessageBox::StandardButton reply; - reply = - QMessageBox::question(nullptr, tr("Auto-Type"), tr("This Auto-Type command contains arguments which are " - "repeated very often. Do you really want to proceed?")); + reply = QMessageBox::question(nullptr, + tr("Auto-Type"), + tr("This Auto-Type command contains arguments which are " + "repeated very often. Do you really want to proceed?")); if (reply == QMessageBox::No) { return false; diff --git a/src/autotype/AutoTypeSelectDialog.cpp b/src/autotype/AutoTypeSelectDialog.cpp index fe0ba811e..1449f9e02 100644 --- a/src/autotype/AutoTypeSelectDialog.cpp +++ b/src/autotype/AutoTypeSelectDialog.cpp @@ -67,10 +67,13 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent) QLabel* descriptionLabel = new QLabel(tr("Select entry to Auto-Type:"), this); layout->addWidget(descriptionLabel); + // clang-format off connect(m_view, SIGNAL(activated(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); connect(m_view, SIGNAL(clicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(matchRemoved())); connect(m_view, SIGNAL(rejected()), SLOT(reject())); + // clang-format on + layout->addWidget(m_view); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, Qt::Horizontal, this); diff --git a/src/autotype/WildcardMatcher.cpp b/src/autotype/WildcardMatcher.cpp index 6afd10cee..b69425ee1 100644 --- a/src/autotype/WildcardMatcher.cpp +++ b/src/autotype/WildcardMatcher.cpp @@ -23,7 +23,7 @@ const QChar WildcardMatcher::Wildcard = '*'; const Qt::CaseSensitivity WildcardMatcher::Sensitivity = Qt::CaseInsensitive; -WildcardMatcher::WildcardMatcher(QString text) +WildcardMatcher::WildcardMatcher(QString text) : m_text(std::move(text)) { } diff --git a/src/autotype/WildcardMatcher.h b/src/autotype/WildcardMatcher.h index 5be6f5e40..d8ee1dc79 100644 --- a/src/autotype/WildcardMatcher.h +++ b/src/autotype/WildcardMatcher.h @@ -23,7 +23,7 @@ class WildcardMatcher { public: - explicit WildcardMatcher(QString text); + explicit WildcardMatcher(QString text); bool match(const QString& pattern); static const QChar Wildcard; diff --git a/src/autotype/xcb/AutoTypeXCB.cpp b/src/autotype/xcb/AutoTypeXCB.cpp index 5198468cf..94a132d40 100644 --- a/src/autotype/xcb/AutoTypeXCB.cpp +++ b/src/autotype/xcb/AutoTypeXCB.cpp @@ -211,8 +211,7 @@ int AutoTypePlatformX11::platformEventFilter(void* event) xcb_key_press_event_t* keyPressEvent = static_cast(event); if (keyPressEvent->detail == m_currentGlobalKeycode && (keyPressEvent->state & m_modifierMask) == m_currentGlobalNativeModifiers - && (!QApplication::activeWindow() || QApplication::activeWindow()->isMinimized()) - && m_loaded) { + && (!QApplication::activeWindow() || QApplication::activeWindow()->isMinimized()) && m_loaded) { if (type == XCB_KEY_PRESS) { emit globalShortcutTriggered(); } diff --git a/src/browser/BrowserAccessControlDialog.cpp b/src/browser/BrowserAccessControlDialog.cpp index e4205f811..9a10e555f 100644 --- a/src/browser/BrowserAccessControlDialog.cpp +++ b/src/browser/BrowserAccessControlDialog.cpp @@ -1,25 +1,26 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "BrowserAccessControlDialog.h" -#include "core/Entry.h" #include "ui_BrowserAccessControlDialog.h" +#include "core/Entry.h" + BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent) : QDialog(parent) , m_ui(new Ui::BrowserAccessControlDialog()) @@ -38,8 +39,8 @@ BrowserAccessControlDialog::~BrowserAccessControlDialog() void BrowserAccessControlDialog::setUrl(const QString& url) { m_ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n" - "Please select whether you want to allow access.")) - .arg(QUrl(url).host())); + "Please select whether you want to allow access.")) + .arg(QUrl(url).host())); } void BrowserAccessControlDialog::setItems(const QList& items) diff --git a/src/browser/BrowserAccessControlDialog.h b/src/browser/BrowserAccessControlDialog.h index 65cfe5e97..4c93a54fc 100644 --- a/src/browser/BrowserAccessControlDialog.h +++ b/src/browser/BrowserAccessControlDialog.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSERACCESSCONTROLDIALOG_H #define BROWSERACCESSCONTROLDIALOG_H diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index ccd17c5fd..97ead6a34 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "BrowserAction.h" #include "BrowserSettings.h" @@ -167,9 +167,10 @@ QJsonObject BrowserAction::handleAssociate(const QJsonObject& json, const QStrin QMutexLocker locker(&m_mutex); if (key.compare(m_clientPublicKey, Qt::CaseSensitive) == 0) { - // Check for identification key. If it's not found, ensure backwards compatibility and use the current public key + // Check for identification key. If it's not found, ensure backwards compatibility and use the current public + // key const QString idKey = decrypted.value("idKey").toString(); - const QString id = m_browserService.storeKey((idKey.isEmpty() ? key: idKey)); + const QString id = m_browserService.storeKey((idKey.isEmpty() ? key : idKey)); if (id.isEmpty()) { return getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED); } diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index b7a60938f..c82af2c17 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSERACTION_H #define BROWSERACTION_H diff --git a/src/browser/BrowserClients.cpp b/src/browser/BrowserClients.cpp index c994ef1d0..083df3945 100644 --- a/src/browser/BrowserClients.cpp +++ b/src/browser/BrowserClients.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "BrowserClients.h" #include diff --git a/src/browser/BrowserClients.h b/src/browser/BrowserClients.h index b725154a5..1fa3dfe17 100644 --- a/src/browser/BrowserClients.h +++ b/src/browser/BrowserClients.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSERCLIENTS_H #define BROWSERCLIENTS_H diff --git a/src/browser/BrowserEntryConfig.cpp b/src/browser/BrowserEntryConfig.cpp index 47e8762a8..a058b6a05 100644 --- a/src/browser/BrowserEntryConfig.cpp +++ b/src/browser/BrowserEntryConfig.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "BrowserEntryConfig.h" #include "core/Entry.h" diff --git a/src/browser/BrowserEntryConfig.h b/src/browser/BrowserEntryConfig.h index f1363f421..183c492aa 100644 --- a/src/browser/BrowserEntryConfig.h +++ b/src/browser/BrowserEntryConfig.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSERENTRYCONFIG_H #define BROWSERENTRYCONFIG_H diff --git a/src/browser/BrowserEntrySaveDialog.cpp b/src/browser/BrowserEntrySaveDialog.cpp index 305c46c6d..43d6c0c62 100644 --- a/src/browser/BrowserEntrySaveDialog.cpp +++ b/src/browser/BrowserEntrySaveDialog.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2018 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "BrowserEntrySaveDialog.h" #include "ui_BrowserEntrySaveDialog.h" @@ -34,7 +34,7 @@ BrowserEntrySaveDialog::BrowserEntrySaveDialog(QWidget* parent) m_ui->itemsList->setSelectionMode(QAbstractItemView::SingleSelection); m_ui->label->setText(QString(tr("You have multiple databases open.\n" - "Please select the correct database for saving credentials."))); + "Please select the correct database for saving credentials."))); } BrowserEntrySaveDialog::~BrowserEntrySaveDialog() @@ -58,7 +58,7 @@ int BrowserEntrySaveDialog::setItems(QList& databaseWidgets, Da } else { item->setText(QString("%1 (%2)").arg(databaseName, databaseFileName)); } - + if (currentWidget == dbWidget) { activeIndex = counter; } @@ -76,7 +76,7 @@ int BrowserEntrySaveDialog::setItems(QList& databaseWidgets, Da return databaseWidgets.length(); } - QList BrowserEntrySaveDialog::getSelected() const - { +QList BrowserEntrySaveDialog::getSelected() const +{ return m_ui->itemsList->selectedItems(); - } +} diff --git a/src/browser/BrowserEntrySaveDialog.h b/src/browser/BrowserEntrySaveDialog.h index 0b1db7271..2e2378635 100644 --- a/src/browser/BrowserEntrySaveDialog.h +++ b/src/browser/BrowserEntrySaveDialog.h @@ -1,29 +1,30 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2018 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2018 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSERENTRYSAVEDIALOG_H #define BROWSERENTRYSAVEDIALOG_H -#include -#include -#include #include "gui/DatabaseTabWidget.h" +#include +#include +#include + class Entry; namespace Ui @@ -40,7 +41,7 @@ public: ~BrowserEntrySaveDialog() override; int setItems(QList& databaseWidgets, DatabaseWidget* currentWidget) const; - QList getSelected() const; + QList getSelected() const; private: QScopedPointer m_ui; diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index 6dcbd3263..27dce7994 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -1,27 +1,28 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "BrowserOptionDialog.h" +#include "ui_BrowserOptionDialog.h" + #include "BrowserSettings.h" #include "config-keepassx.h" #include "core/FilePath.h" -#include "ui_BrowserOptionDialog.h" #include @@ -32,18 +33,24 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) m_ui->setupUi(this); m_ui->extensionLabel->setOpenExternalLinks(true); - m_ui->extensionLabel->setText(tr("KeePassXC-Browser is needed for the browser integration to work.
Download it for %1 and %2.").arg( - "Firefox", - "Google Chrome / Chromium / Vivaldi")); + m_ui->extensionLabel->setText( + tr("KeePassXC-Browser is needed for the browser integration to work.
Download it for %1 and %2.") + .arg("Firefox", + "Google Chrome / Chromium / Vivaldi")); m_ui->scriptWarningWidget->setVisible(false); m_ui->scriptWarningWidget->setAutoHideTimeout(-1); - m_ui->scriptWarningWidget->showMessage(tr("Warning, the keepassxc-proxy application was not found!" - "
Please check the KeePassXC installation directory or confirm the custom path in advanced options." - "
Browser integration WILL NOT WORK without the proxy application." - "
Expected Path: "), MessageWidget::Warning); + m_ui->scriptWarningWidget->showMessage( + tr("Warning, the keepassxc-proxy application was not found!" + "
Please check the KeePassXC installation directory or confirm the custom path in advanced options." + "
Browser integration WILL NOT WORK without the proxy application." + "
Expected Path: "), + MessageWidget::Warning); - m_ui->warningWidget->showMessage(tr("Warning: The following options can be dangerous!"), MessageWidget::Warning); + m_ui->warningWidget->showMessage(tr("Warning: The following options can be dangerous!"), + MessageWidget::Warning); m_ui->warningWidget->setCloseButtonVisible(false); m_ui->warningWidget->setAutoHideTimeout(-1); diff --git a/src/browser/BrowserOptionDialog.h b/src/browser/BrowserOptionDialog.h index 5423f010f..5efb808e5 100644 --- a/src/browser/BrowserOptionDialog.h +++ b/src/browser/BrowserOptionDialog.h @@ -1,27 +1,27 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSEROPTIONDIALOG_H #define BROWSEROPTIONDIALOG_H -#include #include +#include #include namespace Ui diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 1f616bd85..945f1303a 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -1,32 +1,32 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -#include #include -#include +#include #include +#include #include -#include "BrowserService.h" #include "BrowserAccessControlDialog.h" #include "BrowserEntryConfig.h" #include "BrowserEntrySaveDialog.h" +#include "BrowserService.h" #include "BrowserSettings.h" #include "core/Database.h" #include "core/EntrySearcher.h" @@ -54,7 +54,8 @@ BrowserService::BrowserService(DatabaseTabWidget* parent) // Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr) if (m_dbTabWidget) { connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*))); - connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*))); + connect( + m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*))); connect(m_dbTabWidget, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), this, @@ -355,7 +356,8 @@ void BrowserService::updateEntry(const QString& id, return; } - if (username.compare(login, Qt::CaseSensitive) != 0 || entry->password().compare(password, Qt::CaseSensitive) != 0) { + if (username.compare(login, Qt::CaseSensitive) != 0 + || entry->password().compare(password, Qt::CaseSensitive) != 0) { int dialogResult = QMessageBox::No; if (!browserSettings()->alwaysAllowUpdate()) { QMessageBox msgBox; @@ -393,16 +395,17 @@ QList BrowserService::searchEntries(QSharedPointer db, const Q QString entryScheme = entryQUrl.scheme(); QUrl qUrl(url); - // Ignore entry if port or scheme defined in the URL doesn't match - if ((entryQUrl.port() > 0 && entryQUrl.port() != qUrl.port()) || - (browserSettings()->matchUrlScheme() && !entryScheme.isEmpty() && entryScheme.compare(qUrl.scheme()) != 0)) { + // Ignore entry if port or scheme defined in the URL doesn't match + if ((entryQUrl.port() > 0 && entryQUrl.port() != qUrl.port()) + || (browserSettings()->matchUrlScheme() && !entryScheme.isEmpty() + && entryScheme.compare(qUrl.scheme()) != 0)) { continue; } // Filter to match hostname in URL field - if ((!entryUrl.isEmpty() && hostname.contains(entryUrl)) + if ((!entryUrl.isEmpty() && hostname.contains(entryUrl)) || (matchUrlScheme(entryUrl) && hostname.endsWith(entryQUrl.host()))) { - entries.append(entry); + entries.append(entry); } } @@ -420,7 +423,8 @@ QList BrowserService::searchEntries(const QString& url, const StringPair 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); + QString key = + db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first); if (!key.isEmpty() && keyPair.second == key) { databases << db; } @@ -484,16 +488,22 @@ void BrowserService::convertAttributesToCustomData(QSharedPointer curr progress.reset(); if (counter > 0) { - QMessageBox::information(nullptr, tr("KeePassXC: Converted KeePassHTTP attributes"), + QMessageBox::information(nullptr, + tr("KeePassXC: Converted KeePassHTTP attributes"), tr("Successfully converted attributes from %1 entry(s).\n" - "Moved %2 keys to custom data.", "").arg(counter).arg(keyCounter), + "Moved %2 keys to custom data.", + "") + .arg(counter) + .arg(keyCounter), QMessageBox::Ok); } else if (counter == 0 && keyCounter > 0) { - QMessageBox::information(nullptr, tr("KeePassXC: Converted KeePassHTTP attributes"), + QMessageBox::information(nullptr, + tr("KeePassXC: Converted KeePassHTTP attributes"), tr("Successfully moved %n keys to custom data.", "", keyCounter), QMessageBox::Ok); } else { - QMessageBox::information(nullptr, tr("KeePassXC: No entry with KeePassHTTP attributes found!"), + QMessageBox::information(nullptr, + tr("KeePassXC: No entry with KeePassHTTP attributes found!"), tr("The active database does not contain an entry with KeePassHTTP attributes."), QMessageBox::Ok); } @@ -539,9 +549,14 @@ QList BrowserService::sortEntries(QList& pwEntries, const QStrin // Sort same priority entries by Title or UserName auto entries = priorities.values(i); std::sort(entries.begin(), entries.end(), [&field](Entry* left, Entry* right) { - return (QString::localeAwareCompare(left->attributes()->value(field), right->attributes()->value(field)) < 0) || - ((QString::localeAwareCompare(left->attributes()->value(field), right->attributes()->value(field)) == 0) && - (QString::localeAwareCompare(left->attributes()->value("UserName"), right->attributes()->value("UserName")) < 0)); + return (QString::localeAwareCompare(left->attributes()->value(field), right->attributes()->value(field)) + < 0) + || ((QString::localeAwareCompare(left->attributes()->value(field), + right->attributes()->value(field)) + == 0) + && (QString::localeAwareCompare(left->attributes()->value("UserName"), + right->attributes()->value("UserName")) + < 0)); }); results << entries; if (browserSettings()->bestMatchOnly() && !pwEntries.isEmpty()) { @@ -843,7 +858,8 @@ int BrowserService::moveKeysToCustomData(Entry* entry, QSharedPointer // Add key to database custom data if (db && !db->metadata()->customData()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey)) { - db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey, entry->attributes()->value(key)); + db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey, + entry->attributes()->value(key)); ++keyCounter; } } @@ -879,7 +895,6 @@ 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); - return dialogResult == QMessageBox::Yes; } diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 612b55eec..6c84696f9 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -1,21 +1,21 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSERSERVICE_H #define BROWSERSERVICE_H diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index 646c6c4d3..f1f9667f8 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -1,21 +1,21 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "BrowserSettings.h" #include "core/Config.h" @@ -192,7 +192,7 @@ bool BrowserSettings::chromeSupport() void BrowserSettings::setChromeSupport(bool enabled) { m_hostInstaller.installBrowser( - HostInstaller::SupportedBrowsers::CHROME, enabled, supportBrowserProxy(), customProxyLocation()); + HostInstaller::SupportedBrowsers::CHROME, enabled, supportBrowserProxy(), customProxyLocation()); } bool BrowserSettings::chromiumSupport() @@ -203,7 +203,7 @@ bool BrowserSettings::chromiumSupport() void BrowserSettings::setChromiumSupport(bool enabled) { m_hostInstaller.installBrowser( - HostInstaller::SupportedBrowsers::CHROMIUM, enabled, supportBrowserProxy(), customProxyLocation()); + HostInstaller::SupportedBrowsers::CHROMIUM, enabled, supportBrowserProxy(), customProxyLocation()); } bool BrowserSettings::firefoxSupport() @@ -214,7 +214,7 @@ bool BrowserSettings::firefoxSupport() void BrowserSettings::setFirefoxSupport(bool enabled) { m_hostInstaller.installBrowser( - HostInstaller::SupportedBrowsers::FIREFOX, enabled, supportBrowserProxy(), customProxyLocation()); + HostInstaller::SupportedBrowsers::FIREFOX, enabled, supportBrowserProxy(), customProxyLocation()); } bool BrowserSettings::vivaldiSupport() @@ -225,7 +225,7 @@ bool BrowserSettings::vivaldiSupport() void BrowserSettings::setVivaldiSupport(bool enabled) { m_hostInstaller.installBrowser( - HostInstaller::SupportedBrowsers::VIVALDI, enabled, supportBrowserProxy(), customProxyLocation()); + HostInstaller::SupportedBrowsers::VIVALDI, enabled, supportBrowserProxy(), customProxyLocation()); } bool BrowserSettings::torBrowserSupport() diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index 92cdcd16d..0a9dc8261 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -1,21 +1,21 @@ /* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2013 Francois Ferrand + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef BROWSERSETTINGS_H #define BROWSERSETTINGS_H @@ -72,7 +72,7 @@ public: void setVivaldiSupport(bool enabled); bool torBrowserSupport(); void setTorBrowserSupport(bool enabled); - + bool passwordUseNumbers(); void setPasswordUseNumbers(bool useNumbers); bool passwordUseLowercase(); diff --git a/src/browser/HostInstaller.cpp b/src/browser/HostInstaller.cpp index f4585febe..08782fa16 100644 --- a/src/browser/HostInstaller.cpp +++ b/src/browser/HostInstaller.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "HostInstaller.h" #include "config-keepassx.h" @@ -46,8 +46,10 @@ HostInstaller::HostInstaller() , TARGET_DIR_VIVALDI("/.config/vivaldi/NativeMessagingHosts") , TARGET_DIR_TOR_BROWSER("/.tor-browser/app/Browser/TorBrowser/Data/Browser/.mozilla/native-messaging-hosts") #elif defined(Q_OS_WIN) + // clang-format off , TARGET_DIR_CHROME("HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser") , TARGET_DIR_CHROMIUM("HKEY_CURRENT_USER\\Software\\Chromium\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser") + // clang-format on , TARGET_DIR_FIREFOX("HKEY_CURRENT_USER\\Software\\Mozilla\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser") , TARGET_DIR_VIVALDI(TARGET_DIR_CHROME) , TARGET_DIR_TOR_BROWSER(TARGET_DIR_FIREFOX) @@ -163,14 +165,14 @@ QString HostInstaller::getTargetPath(SupportedBrowsers browser) const case SupportedBrowsers::VIVALDI: return TARGET_DIR_VIVALDI; case SupportedBrowsers::TOR_BROWSER: - return TARGET_DIR_TOR_BROWSER; + return TARGET_DIR_TOR_BROWSER; default: return QString(); } } /** - * Returns the browser name + * Returns the browser name * Needed for Windows to separate Chromium- or Firefox-based scripts * * @param browser Selected browser @@ -188,7 +190,7 @@ QString HostInstaller::getBrowserName(SupportedBrowsers browser) const case SupportedBrowsers::VIVALDI: return "vivaldi"; case SupportedBrowsers::TOR_BROWSER: - return "tor-browser"; + return "tor-browser"; default: return QString(); } diff --git a/src/browser/HostInstaller.h b/src/browser/HostInstaller.h index 77d1ffca5..ea0c4bd2f 100644 --- a/src/browser/HostInstaller.h +++ b/src/browser/HostInstaller.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef HOSTINSTALLER_H #define HOSTINSTALLER_H diff --git a/src/browser/NativeMessagingBase.cpp b/src/browser/NativeMessagingBase.cpp index 87c44b42c..fa88c015f 100644 --- a/src/browser/NativeMessagingBase.cpp +++ b/src/browser/NativeMessagingBase.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "NativeMessagingBase.h" #include @@ -138,7 +138,8 @@ QString NativeMessagingBase::getLocalServerPath() const #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) // Use XDG_RUNTIME_DIR instead of /tmp if it's available QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); - return path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverPath : path + serverPath; + return path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverPath + : path + serverPath; #else // Q_OS_MACOS, Q_OS_WIN and others return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverPath; #endif diff --git a/src/browser/NativeMessagingBase.h b/src/browser/NativeMessagingBase.h index f27edbfc1..12e551665 100644 --- a/src/browser/NativeMessagingBase.h +++ b/src/browser/NativeMessagingBase.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef NATIVEMESSAGINGBASE_H #define NATIVEMESSAGINGBASE_H @@ -33,11 +33,11 @@ #include #ifndef Q_OS_WIN +#include #include -#include #endif -static const int NATIVE_MSG_MAX_LENGTH = 1024*1024; +static const int NATIVE_MSG_MAX_LENGTH = 1024 * 1024; class NativeMessagingBase : public QObject { diff --git a/src/browser/NativeMessagingHost.cpp b/src/browser/NativeMessagingHost.cpp index 03f742020..5d2383b2b 100644 --- a/src/browser/NativeMessagingHost.cpp +++ b/src/browser/NativeMessagingHost.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "NativeMessagingHost.h" #include "BrowserSettings.h" @@ -65,8 +65,8 @@ void NativeMessagingHost::run() // Update KeePassXC/keepassxc-proxy binary paths to Native Messaging scripts if (browserSettings()->updateBinaryPath()) { - browserSettings()->updateBinaryPaths(browserSettings()->useCustomProxy() ? browserSettings()->customProxyLocation() - : ""); + browserSettings()->updateBinaryPaths( + browserSettings()->useCustomProxy() ? browserSettings()->customProxyLocation() : ""); } m_running.store(true); diff --git a/src/browser/NativeMessagingHost.h b/src/browser/NativeMessagingHost.h index 774306d30..30a67378a 100644 --- a/src/browser/NativeMessagingHost.h +++ b/src/browser/NativeMessagingHost.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef NATIVEMESSAGINGHOST_H #define NATIVEMESSAGINGHOST_H diff --git a/src/browser/Variant.cpp b/src/browser/Variant.cpp index 2d42ac4ec..64231736f 100644 --- a/src/browser/Variant.cpp +++ b/src/browser/Variant.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "Variant.h" diff --git a/src/browser/Variant.h b/src/browser/Variant.h index 8acdbaa56..76211cdc9 100644 --- a/src/browser/Variant.h +++ b/src/browser/Variant.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef VARIANT_H #define VARIANT_H diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index e51803614..9c78db2fb 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -51,7 +51,8 @@ int Add::execute(const QStringList& arguments) parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); - QCommandLineOption username(QStringList() << "u" << "username", + QCommandLineOption username(QStringList() << "u" + << "username", QObject::tr("Username for the entry."), QObject::tr("username")); parser.addOption(username); @@ -59,15 +60,18 @@ int Add::execute(const QStringList& arguments) QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL")); parser.addOption(url); - QCommandLineOption prompt(QStringList() << "p" << "password-prompt", + QCommandLineOption prompt(QStringList() << "p" + << "password-prompt", QObject::tr("Prompt for the entry's password.")); parser.addOption(prompt); - QCommandLineOption generate(QStringList() << "g" << "generate", + QCommandLineOption generate(QStringList() << "g" + << "generate", QObject::tr("Generate a password for the entry.")); parser.addOption(generate); - QCommandLineOption length(QStringList() << "l" << "password-length", + QCommandLineOption length(QStringList() << "l" + << "password-length", QObject::tr("Length for the generated password."), QObject::tr("length")); parser.addOption(length); diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 6417c8c4b..7e81bcf70 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -50,12 +50,13 @@ int Clip::execute(const QStringList& arguments) parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); - QCommandLineOption totp(QStringList() << "t" << "totp", + QCommandLineOption totp(QStringList() << "t" + << "totp", QObject::tr("Copy the current TOTP to the clipboard.")); parser.addOption(totp); parser.addPositionalArgument("entry", QObject::tr("Path of the entry to clip.", "clip = copy to clipboard")); - parser.addPositionalArgument("timeout", - QObject::tr("Timeout in seconds before clearing the clipboard."), "[timeout]"); + parser.addPositionalArgument( + "timeout", QObject::tr("Timeout in seconds before clearing the clipboard."), "[timeout]"); parser.addHelpOption(); parser.process(arguments); diff --git a/src/cli/Diceware.cpp b/src/cli/Diceware.cpp index bb7931352..be1ac084f 100644 --- a/src/cli/Diceware.cpp +++ b/src/cli/Diceware.cpp @@ -43,11 +43,13 @@ int Diceware::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); - QCommandLineOption words(QStringList() << "W" << "words", + QCommandLineOption words(QStringList() << "W" + << "words", QObject::tr("Word count for the diceware passphrase."), QObject::tr("count", "CLI parameter")); parser.addOption(words); - QCommandLineOption wordlistFile(QStringList() << "w" << "word-list", + QCommandLineOption wordlistFile(QStringList() << "w" + << "word-list", QObject::tr("Wordlist for the diceware generator.\n[Default: EFF English]"), QObject::tr("path")); parser.addOption(wordlistFile); diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index d8d89833a..81c1e3223 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -51,7 +51,8 @@ int Edit::execute(const QStringList& arguments) parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); - QCommandLineOption username(QStringList() << "u" << "username", + QCommandLineOption username(QStringList() << "u" + << "username", QObject::tr("Username for the entry."), QObject::tr("username")); parser.addOption(username); @@ -59,20 +60,24 @@ int Edit::execute(const QStringList& arguments) QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL")); parser.addOption(url); - QCommandLineOption title(QStringList() << "t" << "title", + QCommandLineOption title(QStringList() << "t" + << "title", QObject::tr("Title for the entry."), QObject::tr("title")); parser.addOption(title); - QCommandLineOption prompt(QStringList() << "p" << "password-prompt", + QCommandLineOption prompt(QStringList() << "p" + << "password-prompt", QObject::tr("Prompt for the entry's password.")); parser.addOption(prompt); - QCommandLineOption generate(QStringList() << "g" << "generate", + QCommandLineOption generate(QStringList() << "g" + << "generate", QObject::tr("Generate a password for the entry.")); parser.addOption(generate); - QCommandLineOption length(QStringList() << "l" << "password-length", + QCommandLineOption length(QStringList() << "l" + << "password-length", QObject::tr("Length for the generated password."), QObject::tr("length")); parser.addOption(length); @@ -111,8 +116,7 @@ int Edit::execute(const QStringList& arguments) } if (parser.value("username").isEmpty() && parser.value("url").isEmpty() && parser.value("title").isEmpty() - && !parser.isSet(prompt) - && !parser.isSet(generate)) { + && !parser.isSet(prompt) && !parser.isSet(generate)) { err << QObject::tr("Not changing any field for entry %1.").arg(entryPath) << endl; return EXIT_FAILURE; } diff --git a/src/cli/Estimate.cpp b/src/cli/Estimate.cpp index db32f8dc7..556ff4435 100644 --- a/src/cli/Estimate.cpp +++ b/src/cli/Estimate.cpp @@ -51,9 +51,11 @@ static void estimate(const char* pwd, bool advanced) int len = static_cast(strlen(pwd)); if (!advanced) { e = ZxcvbnMatch(pwd, nullptr, nullptr); + // clang-format off out << QObject::tr("Length %1").arg(len, 0) << '\t' << QObject::tr("Entropy %1").arg(e, 0, 'f', 3) << '\t' << QObject::tr("Log10 %1").arg(e * 0.301029996, 0, 'f', 3) << endl; + // clang-format on } else { int ChkLen = 0; ZxcMatch_t *info, *p; @@ -63,10 +65,12 @@ static void estimate(const char* pwd, bool advanced) m += p->Entrpy; } m = e - m; + // clang-format off out << QObject::tr("Length %1").arg(len) << '\t' << QObject::tr("Entropy %1").arg(e, 0, 'f', 3) << '\t' << QObject::tr("Log10 %1").arg(e * 0.301029996, 0, 'f', 3) << "\n " << QObject::tr("Multi-word extra bits %1").arg(m, 0, 'f', 1) << endl; + // clang-format on p = info; ChkLen = 0; while (p) { @@ -132,9 +136,10 @@ static void estimate(const char* pwd, bool advanced) break; } ChkLen += p->Length; - + // clang-format off out << QObject::tr("Length %1").arg(p->Length) << '\t' << QObject::tr("Entropy %1 (%2)").arg(p->Entrpy, 6, 'f', 3).arg(p->Entrpy * 0.301029996, 0, 'f', 2) << '\t'; + // clang-format on for (n = 0; n < p->Length; ++n, ++pwd) { out << *pwd; } @@ -143,7 +148,8 @@ static void estimate(const char* pwd, bool advanced) } ZxcvbnFreeInfo(info); if (ChkLen != len) { - out << QObject::tr("*** Password length (%1) != sum of length of parts (%2) ***").arg(len).arg(ChkLen) << endl; + out << QObject::tr("*** Password length (%1) != sum of length of parts (%2) ***").arg(len).arg(ChkLen) + << endl; } } } @@ -156,7 +162,8 @@ int Estimate::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); parser.addPositionalArgument("password", QObject::tr("Password for which to estimate the entropy."), "[password]"); - QCommandLineOption advancedOption(QStringList() << "a" << "advanced", + QCommandLineOption advancedOption(QStringList() << "a" + << "advanced", QObject::tr("Perform advanced analysis on the password.")); parser.addOption(advancedOption); parser.addHelpOption(); diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 38ef09db9..0b20b9528 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -84,7 +84,8 @@ int Extract::execute(const QStringList& arguments) if (fileKey->type() != FileKey::Hashed) { err << QObject::tr("WARNING: You are using a legacy key file format which may become\n" "unsupported in the future.\n\n" - "Please consider generating a new key file.") << endl; + "Please consider generating a new key file.") + << endl; } // LCOV_EXCL_STOP diff --git a/src/cli/Generate.cpp b/src/cli/Generate.cpp index da2deb8d8..adbfc0cc0 100644 --- a/src/cli/Generate.cpp +++ b/src/cli/Generate.cpp @@ -43,26 +43,33 @@ int Generate::execute(const QStringList& arguments) QCommandLineParser parser; parser.setApplicationDescription(description); - QCommandLineOption len(QStringList() << "L" << "length", + QCommandLineOption len(QStringList() << "L" + << "length", QObject::tr("Length of the generated password"), QObject::tr("length")); parser.addOption(len); - QCommandLineOption lower(QStringList() << "l" << "lower", + QCommandLineOption lower(QStringList() << "l" + << "lower", QObject::tr("Use lowercase characters")); parser.addOption(lower); - QCommandLineOption upper(QStringList() << "u" << "upper", + QCommandLineOption upper(QStringList() << "u" + << "upper", QObject::tr("Use uppercase characters")); parser.addOption(upper); - QCommandLineOption numeric(QStringList() << "n" << "numeric", + QCommandLineOption numeric(QStringList() << "n" + << "numeric", QObject::tr("Use numbers.")); parser.addOption(numeric); - QCommandLineOption special(QStringList() << "s" << "special", + QCommandLineOption special(QStringList() << "s" + << "special", QObject::tr("Use special characters")); parser.addOption(special); - QCommandLineOption extended(QStringList() << "e" << "extended", + QCommandLineOption extended(QStringList() << "e" + << "extended", QObject::tr("Use extended ASCII")); parser.addOption(extended); - QCommandLineOption exclude(QStringList() << "x" << "exclude", + QCommandLineOption exclude(QStringList() << "x" + << "exclude", QObject::tr("Exclude character set"), QObject::tr("chars")); parser.addOption(exclude); diff --git a/src/cli/List.cpp b/src/cli/List.cpp index f7be0ac72..e4b06f2cc 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -49,7 +49,8 @@ int List::execute(const QStringList& arguments) parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); - QCommandLineOption recursiveOption(QStringList() << "R" << "recursive", + QCommandLineOption recursiveOption(QStringList() << "R" + << "recursive", QObject::tr("Recursively list the elements of the group.")); parser.addOption(recursiveOption); parser.addHelpOption(); diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index 17431d62e..58c7965dc 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -47,12 +47,14 @@ int Merge::execute(const QStringList& arguments) parser.addPositionalArgument("database2", QObject::tr("Path of the database to merge from.")); parser.addOption(Command::QuietOption); - QCommandLineOption samePasswordOption(QStringList() << "s" << "same-credentials", + QCommandLineOption samePasswordOption(QStringList() << "s" + << "same-credentials", QObject::tr("Use the same credentials for both database files.")); parser.addOption(samePasswordOption); parser.addOption(Command::KeyFileOption); - QCommandLineOption keyFileFromOption(QStringList() << "f" << "key-file-from", + QCommandLineOption keyFileFromOption(QStringList() << "f" + << "key-file-from", QObject::tr("Key file of the database to merge from."), QObject::tr("path")); parser.addOption(keyFileFromOption); diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index 5efae70c5..e900a07b9 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -48,11 +48,13 @@ int Show::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); - QCommandLineOption totp(QStringList() << "t" << "totp", + QCommandLineOption totp(QStringList() << "t" + << "totp", QObject::tr("Show the entry's current TOTP.")); parser.addOption(totp); QCommandLineOption attributes( - QStringList() << "a" << "attributes", + QStringList() << "a" + << "attributes", QObject::tr( "Names of the attributes to show. " "This option can be specified more than once, with each attribute shown one-per-line in the given order. " diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index 7359f139c..d78c06074 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -28,20 +28,20 @@ namespace Utils { -/** - * STDOUT file handle for the CLI. - */ -FILE* STDOUT = stdout; + /** + * STDOUT file handle for the CLI. + */ + FILE* STDOUT = stdout; -/** - * STDERR file handle for the CLI. - */ -FILE* STDERR = stderr; + /** + * STDERR file handle for the CLI. + */ + FILE* STDERR = stderr; -/** - * STDIN file handle for the CLI. - */ -FILE* STDIN = stdin; + /** + * STDIN file handle for the CLI. + */ + FILE* STDIN = stdin; /** * DEVNULL file handle for the CLI. @@ -56,48 +56,47 @@ FILE* DEVNULL = fopen("/dev/null", "w"); void setStdinEcho(bool enable = true) { #ifdef Q_OS_WIN - HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); - DWORD mode; - GetConsoleMode(hIn, &mode); + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + DWORD mode; + GetConsoleMode(hIn, &mode); - if (enable) { - mode |= ENABLE_ECHO_INPUT; - } else { - mode &= ~ENABLE_ECHO_INPUT; - } - - SetConsoleMode(hIn, mode); + if (enable) { + mode |= ENABLE_ECHO_INPUT; + } else { + mode &= ~ENABLE_ECHO_INPUT; + } + SetConsoleMode(hIn, mode); #else - struct termios t; - tcgetattr(STDIN_FILENO, &t); + struct termios t; + tcgetattr(STDIN_FILENO, &t); - if (enable) { - t.c_lflag |= ECHO; - } else { - t.c_lflag &= ~ECHO; + if (enable) { + t.c_lflag |= ECHO; + } else { + t.c_lflag &= ~ECHO; + } + + tcsetattr(STDIN_FILENO, TCSANOW, &t); +#endif } - tcsetattr(STDIN_FILENO, TCSANOW, &t); -#endif -} + namespace Test + { + QStringList nextPasswords = {}; -namespace Test -{ -QStringList nextPasswords = {}; - -/** - * Set the next password returned by \link getPassword() instead of reading it from STDIN. - * Multiple calls to this method will fill a queue of passwords. - * This function is intended for testing purposes. - * - * @param password password to return next - */ -void setNextPassword(const QString& password) -{ - nextPasswords.append(password); -} -} // namespace Test + /** + * Set the next password returned by \link getPassword() instead of reading it from STDIN. + * Multiple calls to this method will fill a queue of passwords. + * This function is intended for testing purposes. + * + * @param password password to return next + */ + void setNextPassword(const QString& password) + { + nextPasswords.append(password); + } + } // namespace Test /** * Read a user password from STDIN or return a password previously @@ -109,74 +108,74 @@ QString getPassword(FILE* outputDescriptor) { TextStream out(outputDescriptor, QIODevice::WriteOnly); - // return preset password if one is set - if (!Test::nextPasswords.isEmpty()) { - auto password = Test::nextPasswords.takeFirst(); - // simulate user entering newline + // return preset password if one is set + if (!Test::nextPasswords.isEmpty()) { + auto password = Test::nextPasswords.takeFirst(); + // simulate user entering newline + out << endl; + return password; + } + + TextStream in(STDIN, QIODevice::ReadOnly); + + setStdinEcho(false); + QString line = in.readLine(); + setStdinEcho(true); out << endl; - return password; + + return line; } - TextStream in(STDIN, QIODevice::ReadOnly); + /** + * A valid and running event loop is needed to use the global QClipboard, + * so we need to use this from the CLI. + */ + int clipText(const QString& text) + { + TextStream err(Utils::STDERR); - setStdinEcho(false); - QString line = in.readLine(); - setStdinEcho(true); - out << endl; - - return line; -} - -/** - * A valid and running event loop is needed to use the global QClipboard, - * so we need to use this from the CLI. - */ -int clipText(const QString& text) -{ - TextStream err(Utils::STDERR); - - QString programName = ""; - QStringList arguments; + QString programName = ""; + QStringList arguments; #ifdef Q_OS_UNIX - programName = "xclip"; - arguments << "-i" - << "-selection" - << "clipboard"; + programName = "xclip"; + arguments << "-i" + << "-selection" + << "clipboard"; #endif #ifdef Q_OS_MACOS - programName = "pbcopy"; + programName = "pbcopy"; #endif #ifdef Q_OS_WIN - programName = "clip"; + programName = "clip"; #endif - if (programName.isEmpty()) { - err << QObject::tr("No program defined for clipboard manipulation"); - err.flush(); - return EXIT_FAILURE; + if (programName.isEmpty()) { + err << QObject::tr("No program defined for clipboard manipulation"); + err.flush(); + return EXIT_FAILURE; + } + + auto* clipProcess = new QProcess(nullptr); + clipProcess->start(programName, arguments); + clipProcess->waitForStarted(); + + if (clipProcess->state() != QProcess::Running) { + err << QObject::tr("Unable to start program %1").arg(programName); + err.flush(); + return EXIT_FAILURE; + } + + if (clipProcess->write(text.toLatin1()) == -1) { + qDebug("Unable to write to process : %s", qPrintable(clipProcess->errorString())); + } + clipProcess->waitForBytesWritten(); + clipProcess->closeWriteChannel(); + clipProcess->waitForFinished(); + + return clipProcess->exitCode(); } - auto* clipProcess = new QProcess(nullptr); - clipProcess->start(programName, arguments); - clipProcess->waitForStarted(); - - if (clipProcess->state() != QProcess::Running) { - err << QObject::tr("Unable to start program %1").arg(programName); - err.flush(); - return EXIT_FAILURE; - } - - if (clipProcess->write(text.toLatin1()) == -1) { - qDebug("Unable to write to process : %s", qPrintable(clipProcess->errorString())); - } - clipProcess->waitForBytesWritten(); - clipProcess->closeWriteChannel(); - clipProcess->waitForFinished(); - - return clipProcess->exitCode(); -} - -} // namespace Utils +} // namespace Utils diff --git a/src/cli/Utils.h b/src/cli/Utils.h index 8ea55da1e..28f260381 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -18,24 +18,24 @@ #ifndef KEEPASSXC_UTILS_H #define KEEPASSXC_UTILS_H -#include #include "cli/TextStream.h" +#include namespace Utils { -extern FILE* STDOUT; -extern FILE* STDERR; -extern FILE* STDIN; -extern FILE* DEVNULL; + extern FILE* STDOUT; + extern FILE* STDERR; + extern FILE* STDIN; + extern FILE* DEVNULL; -void setStdinEcho(bool enable); -QString getPassword(FILE* outputDescriptor = STDOUT); -int clipText(const QString& text); + void setStdinEcho(bool enable); + QString getPassword(FILE* outputDescriptor = STDOUT); + int clipText(const QString& text); -namespace Test -{ -void setNextPassword(const QString& password); -} -}; + namespace Test + { + void setNextPassword(const QString& password); + } +}; // namespace Utils #endif // KEEPASSXC_UTILS_H diff --git a/src/core/AsyncTask.h b/src/core/AsyncTask.h index 45bb191bc..f74d7c738 100644 --- a/src/core/AsyncTask.h +++ b/src/core/AsyncTask.h @@ -13,7 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ #ifndef KEEPASSXC_ASYNCTASK_HPP #define KEEPASSXC_ASYNCTASK_HPP diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp index 0610def8b..a62cc5a9b 100644 --- a/src/core/Bootstrap.cpp +++ b/src/core/Bootstrap.cpp @@ -26,201 +26,201 @@ namespace Bootstrap { -/** - * When QNetworkAccessManager is instantiated it regularly starts polling - * all network interfaces to see if anything changes and if so, what. This - * creates a latency spike every 10 seconds on Mac OS 10.12+ and Windows 7 >= - * when on a wifi connection. - * So here we disable it for lack of better measure. - * This will also cause this message: QObject::startTimer: Timers cannot - * have negative intervals - * For more info see: - * - https://bugreports.qt.io/browse/QTBUG-40332 - * - https://bugreports.qt.io/browse/QTBUG-46015 - */ -static inline void applyEarlyQNetworkAccessManagerWorkaround() -{ - qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); -} - -/** - * Perform early application bootstrapping such as setting up search paths, - * configuration OS security properties, and loading translators. - * A QApplication object has to be instantiated before calling this function. - */ -void bootstrapApplication() -{ -#ifdef QT_NO_DEBUG - disableCoreDumps(); -#endif - setupSearchPaths(); - applyEarlyQNetworkAccessManagerWorkaround(); - Translator::installTranslators(); - -#ifdef Q_OS_MACOS - // Don't show menu icons on OSX - QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); -#endif -} - -/** - * Restore the main window's state after launch - * - * @param mainWindow the main window whose state to restore - */ -void restoreMainWindowState(MainWindow& mainWindow) -{ - // start minimized if configured - if (config()->get("GUI/MinimizeOnStartup").toBool()) { - mainWindow.showMinimized(); - } else { - mainWindow.bringToFront(); + /** + * When QNetworkAccessManager is instantiated it regularly starts polling + * all network interfaces to see if anything changes and if so, what. This + * creates a latency spike every 10 seconds on Mac OS 10.12+ and Windows 7 >= + * when on a wifi connection. + * So here we disable it for lack of better measure. + * This will also cause this message: QObject::startTimer: Timers cannot + * have negative intervals + * For more info see: + * - https://bugreports.qt.io/browse/QTBUG-40332 + * - https://bugreports.qt.io/browse/QTBUG-46015 + */ + static inline void applyEarlyQNetworkAccessManagerWorkaround() + { + qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); } - if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) { - const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList(); - for (const QString& filename : fileNames) { - if (!filename.isEmpty() && QFile::exists(filename)) { - mainWindow.openDatabase(filename); + /** + * Perform early application bootstrapping such as setting up search paths, + * configuration OS security properties, and loading translators. + * A QApplication object has to be instantiated before calling this function. + */ + void bootstrapApplication() + { +#ifdef QT_NO_DEBUG + disableCoreDumps(); +#endif + setupSearchPaths(); + applyEarlyQNetworkAccessManagerWorkaround(); + Translator::installTranslators(); + +#ifdef Q_OS_MACOS + // Don't show menu icons on OSX + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif + } + + /** + * Restore the main window's state after launch + * + * @param mainWindow the main window whose state to restore + */ + void restoreMainWindowState(MainWindow& mainWindow) + { + // start minimized if configured + if (config()->get("GUI/MinimizeOnStartup").toBool()) { + mainWindow.showMinimized(); + } else { + mainWindow.bringToFront(); + } + + if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) { + const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList(); + for (const QString& filename : fileNames) { + if (!filename.isEmpty() && QFile::exists(filename)) { + mainWindow.openDatabase(filename); + } } } } -} -// LCOV_EXCL_START -void disableCoreDumps() -{ - // default to true - // there is no point in printing a warning if this is not implemented on the platform - bool success = true; + // LCOV_EXCL_START + void disableCoreDumps() + { + // default to true + // there is no point in printing a warning if this is not implemented on the platform + bool success = true; #if defined(HAVE_RLIMIT_CORE) - struct rlimit limit; - limit.rlim_cur = 0; - limit.rlim_max = 0; - success = success && (setrlimit(RLIMIT_CORE, &limit) == 0); + struct rlimit limit; + limit.rlim_cur = 0; + limit.rlim_max = 0; + success = success && (setrlimit(RLIMIT_CORE, &limit) == 0); #endif #if defined(HAVE_PR_SET_DUMPABLE) - success = success && (prctl(PR_SET_DUMPABLE, 0) == 0); + success = success && (prctl(PR_SET_DUMPABLE, 0) == 0); #endif // Mac OS X #ifdef HAVE_PT_DENY_ATTACH - success = success && (ptrace(PT_DENY_ATTACH, 0, 0, 0) == 0); + success = success && (ptrace(PT_DENY_ATTACH, 0, 0, 0) == 0); #endif #ifdef Q_OS_WIN - success = success && createWindowsDACL(); + success = success && createWindowsDACL(); #endif - if (!success) { - qWarning("Unable to disable core dumps."); + if (!success) { + qWarning("Unable to disable core dumps."); + } } -} -// -// This function grants the user associated with the process token minimal access rights and -// denies everything else on Windows. This includes PROCESS_QUERY_INFORMATION and -// PROCESS_VM_READ access rights that are required for MiniDumpWriteDump() or ReadProcessMemory(). -// We do this using a discretionary access control list (DACL). Effectively this prevents -// crash dumps and disallows other processes from accessing our memory. This works as long -// as you do not have admin privileges, since then you are able to grant yourself the -// SeDebugPrivilege or SeTakeOwnershipPrivilege and circumvent the DACL. -// -bool createWindowsDACL() -{ - bool bSuccess = false; + // + // This function grants the user associated with the process token minimal access rights and + // denies everything else on Windows. This includes PROCESS_QUERY_INFORMATION and + // PROCESS_VM_READ access rights that are required for MiniDumpWriteDump() or ReadProcessMemory(). + // We do this using a discretionary access control list (DACL). Effectively this prevents + // crash dumps and disallows other processes from accessing our memory. This works as long + // as you do not have admin privileges, since then you are able to grant yourself the + // SeDebugPrivilege or SeTakeOwnershipPrivilege and circumvent the DACL. + // + bool createWindowsDACL() + { + bool bSuccess = false; #ifdef Q_OS_WIN - // Process token and user - HANDLE hToken = nullptr; - PTOKEN_USER pTokenUser = nullptr; - DWORD cbBufferSize = 0; + // Process token and user + HANDLE hToken = nullptr; + PTOKEN_USER pTokenUser = nullptr; + DWORD cbBufferSize = 0; - // Access control list - PACL pACL = nullptr; - DWORD cbACL = 0; + // Access control list + PACL pACL = nullptr; + DWORD cbACL = 0; - // Open the access token associated with the calling process - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { - goto Cleanup; - } + // Open the access token associated with the calling process + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + goto Cleanup; + } - // Retrieve the token information in a TOKEN_USER structure - GetTokenInformation(hToken, TokenUser, nullptr, 0, &cbBufferSize); + // Retrieve the token information in a TOKEN_USER structure + GetTokenInformation(hToken, TokenUser, nullptr, 0, &cbBufferSize); - pTokenUser = static_cast(HeapAlloc(GetProcessHeap(), 0, cbBufferSize)); - if (pTokenUser == nullptr) { - goto Cleanup; - } + pTokenUser = static_cast(HeapAlloc(GetProcessHeap(), 0, cbBufferSize)); + if (pTokenUser == nullptr) { + goto Cleanup; + } - if (!GetTokenInformation(hToken, TokenUser, pTokenUser, cbBufferSize, &cbBufferSize)) { - goto Cleanup; - } + if (!GetTokenInformation(hToken, TokenUser, pTokenUser, cbBufferSize, &cbBufferSize)) { + goto Cleanup; + } - if (!IsValidSid(pTokenUser->User.Sid)) { - goto Cleanup; - } + if (!IsValidSid(pTokenUser->User.Sid)) { + goto Cleanup; + } - // Calculate the amount of memory that must be allocated for the DACL - cbACL = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pTokenUser->User.Sid); + // Calculate the amount of memory that must be allocated for the DACL + cbACL = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pTokenUser->User.Sid); - // Create and initialize an ACL - pACL = static_cast(HeapAlloc(GetProcessHeap(), 0, cbACL)); - if (pACL == nullptr) { - goto Cleanup; - } + // Create and initialize an ACL + pACL = static_cast(HeapAlloc(GetProcessHeap(), 0, cbACL)); + if (pACL == nullptr) { + goto Cleanup; + } - if (!InitializeAcl(pACL, cbACL, ACL_REVISION)) { - goto Cleanup; - } + if (!InitializeAcl(pACL, cbACL, ACL_REVISION)) { + goto Cleanup; + } - // Add allowed access control entries, everything else is denied - if (!AddAccessAllowedAce( - pACL, - ACL_REVISION, - SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, // same as protected process - pTokenUser->User.Sid // pointer to the trustee's SID - )) { - goto Cleanup; - } + // Add allowed access control entries, everything else is denied + if (!AddAccessAllowedAce( + pACL, + ACL_REVISION, + SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, // same as protected process + pTokenUser->User.Sid // pointer to the trustee's SID + )) { + goto Cleanup; + } - // Set discretionary access control list - bSuccess = ERROR_SUCCESS - == SetSecurityInfo(GetCurrentProcess(), // object handle - SE_KERNEL_OBJECT, // type of object - DACL_SECURITY_INFORMATION, // change only the objects DACL - nullptr, - nullptr, // do not change owner or group - pACL, // DACL specified - nullptr // do not change SACL - ); + // Set discretionary access control list + bSuccess = ERROR_SUCCESS + == SetSecurityInfo(GetCurrentProcess(), // object handle + SE_KERNEL_OBJECT, // type of object + DACL_SECURITY_INFORMATION, // change only the objects DACL + nullptr, + nullptr, // do not change owner or group + pACL, // DACL specified + nullptr // do not change SACL + ); -Cleanup: + Cleanup: - if (pACL != nullptr) { - HeapFree(GetProcessHeap(), 0, pACL); - } - if (pTokenUser != nullptr) { - HeapFree(GetProcessHeap(), 0, pTokenUser); - } - if (hToken != nullptr) { - CloseHandle(hToken); - } + if (pACL != nullptr) { + HeapFree(GetProcessHeap(), 0, pACL); + } + if (pTokenUser != nullptr) { + HeapFree(GetProcessHeap(), 0, pTokenUser); + } + if (hToken != nullptr) { + CloseHandle(hToken); + } #endif - return bSuccess; -} -// LCOV_EXCL_STOP + return bSuccess; + } + // LCOV_EXCL_STOP -void setupSearchPaths() -{ + void setupSearchPaths() + { #ifdef Q_OS_WIN - // Make sure Windows doesn't load DLLs from the current working directory - SetDllDirectoryA(""); - SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE); + // Make sure Windows doesn't load DLLs from the current working directory + SetDllDirectoryA(""); + SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE); #endif -} + } -} // namespace Bootstrap +} // namespace Bootstrap diff --git a/src/core/Bootstrap.h b/src/core/Bootstrap.h index 0e9db155a..95158fb8c 100644 --- a/src/core/Bootstrap.h +++ b/src/core/Bootstrap.h @@ -15,7 +15,6 @@ * along with this program. If not, see . */ - #ifndef KEEPASSXC_BOOTSTRAP_H #define KEEPASSXC_BOOTSTRAP_H @@ -23,12 +22,11 @@ namespace Bootstrap { -void bootstrapApplication(); -void restoreMainWindowState(MainWindow& mainWindow); -void disableCoreDumps(); -bool createWindowsDACL(); -void setupSearchPaths(); -}; + void bootstrapApplication(); + void restoreMainWindowState(MainWindow& mainWindow); + void disableCoreDumps(); + bool createWindowsDACL(); + void setupSearchPaths(); +}; // namespace Bootstrap - -#endif //KEEPASSXC_BOOTSTRAP_H +#endif // KEEPASSXC_BOOTSTRAP_H diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 01497caf9..66591a851 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -84,7 +84,7 @@ void Config::upgrade() { for (const auto& setting : deprecationMap.keys()) { if (m_settings->contains(setting)) { - if(!deprecationMap.value(setting).isEmpty()) { + if (!deprecationMap.value(setting).isEmpty()) { // Add entry with new name and old entry's value m_settings->setValue(deprecationMap.value(setting), m_settings->value(setting)); } diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 6d475acf9..ff345c84e 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -720,14 +720,15 @@ QSharedPointer Database::unlockFromStdin(const QString& databaseFilena QString errorMessage; // LCOV_EXCL_START if (!fileKey->load(keyFilename, &errorMessage)) { - err << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage)<< endl; + err << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage) << endl; return {}; } if (fileKey->type() != FileKey::Hashed) { err << QObject::tr("WARNING: You are using a legacy key file format which may become\n" "unsupported in the future.\n\n" - "Please consider generating a new key file.") << endl; + "Please consider generating a new key file.") + << endl; } // LCOV_EXCL_STOP diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 793a373bb..55f56f583 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -657,12 +657,14 @@ Entry* Entry::clone(CloneFlags flags) const if (flags & CloneUserAsRef) { // Build the username reference QString username = "{REF:U@I:" + uuidToHex() + "}"; - entry->m_attributes->set(EntryAttributes::UserNameKey, username.toUpper(), m_attributes->isProtected(EntryAttributes::UserNameKey)); + entry->m_attributes->set( + EntryAttributes::UserNameKey, username.toUpper(), m_attributes->isProtected(EntryAttributes::UserNameKey)); } if (flags & ClonePassAsRef) { QString password = "{REF:P@I:" + uuidToHex() + "}"; - entry->m_attributes->set(EntryAttributes::PasswordKey, password.toUpper(), m_attributes->isProtected(EntryAttributes::PasswordKey)); + entry->m_attributes->set( + EntryAttributes::PasswordKey, password.toUpper(), m_attributes->isProtected(EntryAttributes::PasswordKey)); } entry->m_autoTypeAssociations->copyDataFrom(m_autoTypeAssociations); @@ -1065,7 +1067,8 @@ QString Entry::resolveUrl(const QString& url) const // Validate the URL QUrl tempUrl = QUrl(newUrl); - if (tempUrl.isValid() && (tempUrl.scheme() == "http" || tempUrl.scheme() == "https" || tempUrl.scheme() == "file")) { + if (tempUrl.isValid() + && (tempUrl.scheme() == "http" || tempUrl.scheme() == "https" || tempUrl.scheme() == "file")) { return tempUrl.url(); } diff --git a/src/core/Entry.h b/src/core/Entry.h index f6518bdd3..655cc3621 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -36,7 +36,8 @@ class Database; class Group; -namespace Totp { +namespace Totp +{ struct Settings; } diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp index b9fcd9ac2..fc0793ce8 100644 --- a/src/core/EntryAttributes.cpp +++ b/src/core/EntryAttributes.cpp @@ -23,8 +23,8 @@ const QString EntryAttributes::UserNameKey = "UserName"; const QString EntryAttributes::PasswordKey = "Password"; const QString EntryAttributes::URLKey = "URL"; const QString EntryAttributes::NotesKey = "Notes"; -const QStringList EntryAttributes::DefaultAttributes(QStringList() << TitleKey << UserNameKey << PasswordKey << URLKey - << NotesKey); +const QStringList EntryAttributes::DefaultAttributes(QStringList() + << TitleKey << UserNameKey << PasswordKey << URLKey << NotesKey); const QString EntryAttributes::WantedFieldGroupName = "WantedField"; const QString EntryAttributes::SearchInGroupName = "SearchIn"; diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 2051fe437..ec7633b88 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -630,10 +630,12 @@ Entry* Group::findEntryByPathRecursive(const QString& entryPath, const QString& // Return the first entry that matches the full path OR if there is no leading // slash, return the first entry title that matches for (Entry* entry : entries()) { + // clang-format off if (entryPath == (basePath + entry->title()) || (!entryPath.startsWith("/") && entry->title() == entryPath)) { return entry; } + // clang-format on } for (Group* group : children()) { @@ -654,9 +656,11 @@ Group* Group::findGroupByPath(const QString& groupPath) if (groupPath.isEmpty()) { normalizedGroupPath = QString("/"); // root group } else { + // clang-format off normalizedGroupPath = (groupPath.startsWith("/") ? "" : "/") + groupPath + (groupPath.endsWith("/") ? "" : "/"); + // clang-format on } return findGroupByPathRecursive(normalizedGroupPath, "/"); } @@ -894,6 +898,7 @@ void Group::connectDatabaseSignalsRecursive(Database* db) } if (db) { + // clang-format off 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())); @@ -902,6 +907,7 @@ void Group::connectDatabaseSignalsRecursive(Database* db) connect(this, SIGNAL(aboutToMove(Group*,Group*,int)), db, SIGNAL(groupAboutToMove(Group*,Group*,int))); connect(this, SIGNAL(groupMoved()), db, SIGNAL(groupMoved())); connect(this, SIGNAL(groupModified()), db, SLOT(markAsModified())); + // clang-format on } m_db = db; diff --git a/src/core/InactivityTimer.cpp b/src/core/InactivityTimer.cpp index 405970cc6..85c58d269 100644 --- a/src/core/InactivityTimer.cpp +++ b/src/core/InactivityTimer.cpp @@ -55,12 +55,13 @@ void InactivityTimer::deactivate() bool InactivityTimer::eventFilter(QObject* watched, QEvent* event) { const QEvent::Type type = event->type(); - + // clang-format off if ((type >= QEvent::MouseButtonPress && type <= QEvent::KeyRelease) || (type >= QEvent::HoverEnter && type <= QEvent::HoverMove) || (type == QEvent::Wheel)) { m_timer->start(); } + // clang-format on return QObject::eventFilter(watched, event); } diff --git a/src/core/Merger.cpp b/src/core/Merger.cpp index 0da7458ea..cc57ae3b2 100644 --- a/src/core/Merger.cpp +++ b/src/core/Merger.cpp @@ -93,7 +93,8 @@ Merger::ChangeList Merger::mergeGroup(const MergeContext& context) moveEntry(targetEntry, context.m_targetGroup); } else { // Entry is already present in the database. Update it. - const bool locationChanged = targetEntry->timeInfo().locationChanged() < sourceEntry->timeInfo().locationChanged(); + const bool locationChanged = + targetEntry->timeInfo().locationChanged() < sourceEntry->timeInfo().locationChanged(); if (locationChanged && targetEntry->group() != context.m_targetGroup) { changes << tr("Relocating %1 [%2]").arg(sourceEntry->title(), sourceEntry->uuidToHex()); moveEntry(targetEntry, context.m_targetGroup); @@ -136,7 +137,8 @@ Merger::ChangeList Merger::mergeGroup(const MergeContext& context) return changes; } -Merger::ChangeList Merger::resolveGroupConflict(const MergeContext& context, const Group* sourceChildGroup, Group* targetChildGroup) +Merger::ChangeList +Merger::resolveGroupConflict(const MergeContext& context, const Group* sourceChildGroup, Group* targetChildGroup) { Q_UNUSED(context); ChangeList changes; @@ -261,10 +263,13 @@ void Merger::eraseGroup(Group* group) database->setDeletedObjects(deletions); } -Merger::ChangeList Merger::resolveEntryConflict_Duplicate(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) +Merger::ChangeList +Merger::resolveEntryConflict_Duplicate(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) { ChangeList changes; - const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), sourceEntry->timeInfo().lastModificationTime(), CompareItemIgnoreMilliseconds); + const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), + sourceEntry->timeInfo().lastModificationTime(), + CompareItemIgnoreMilliseconds); // if one entry is newer, create a clone and add it to the group if (comparison < 0) { Entry* clonedEntry = sourceEntry->clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory); @@ -280,11 +285,14 @@ Merger::ChangeList Merger::resolveEntryConflict_Duplicate(const MergeContext& co return changes; } -Merger::ChangeList Merger::resolveEntryConflict_KeepLocal(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) +Merger::ChangeList +Merger::resolveEntryConflict_KeepLocal(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) { Q_UNUSED(context); ChangeList changes; - const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), sourceEntry->timeInfo().lastModificationTime(), CompareItemIgnoreMilliseconds); + const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), + sourceEntry->timeInfo().lastModificationTime(), + CompareItemIgnoreMilliseconds); if (comparison < 0) { // we need to make our older entry "newer" than the new entry - therefore // we just create a new history entry without any changes - this preserves @@ -293,18 +301,21 @@ Merger::ChangeList Merger::resolveEntryConflict_KeepLocal(const MergeContext& co // this type of merge changes the database timestamp since reapplying the // old entry is an active change of the database! changes << tr("Reapplying older target entry on top of newer source %1 [%2]") - .arg(targetEntry->title(), targetEntry->uuidToHex()); + .arg(targetEntry->title(), targetEntry->uuidToHex()); Entry* agedTargetEntry = targetEntry->clone(Entry::CloneNoFlags); targetEntry->addHistoryItem(agedTargetEntry); } return changes; } -Merger::ChangeList Merger::resolveEntryConflict_KeepRemote(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) +Merger::ChangeList +Merger::resolveEntryConflict_KeepRemote(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) { Q_UNUSED(context); ChangeList changes; - const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), sourceEntry->timeInfo().lastModificationTime(), CompareItemIgnoreMilliseconds); + const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), + sourceEntry->timeInfo().lastModificationTime(), + CompareItemIgnoreMilliseconds); if (comparison > 0) { // we need to make our older entry "newer" than the new entry - therefore // we just create a new history entry without any changes - this preserves @@ -313,7 +324,7 @@ Merger::ChangeList Merger::resolveEntryConflict_KeepRemote(const MergeContext& c // this type of merge changes the database timestamp since reapplying the // old entry is an active change of the database! changes << tr("Reapplying older source entry on top of newer target %1 [%2]") - .arg(targetEntry->title(), targetEntry->uuidToHex()); + .arg(targetEntry->title(), targetEntry->uuidToHex()); targetEntry->beginUpdate(); targetEntry->copyDataFrom(sourceEntry); targetEntry->endUpdate(); @@ -322,13 +333,17 @@ Merger::ChangeList Merger::resolveEntryConflict_KeepRemote(const MergeContext& c return changes; } - -Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod) +Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContext& context, + const Entry* sourceEntry, + Entry* targetEntry, + Group::MergeMode mergeMethod) { Q_UNUSED(context); ChangeList changes; - const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), sourceEntry->timeInfo().lastModificationTime(), CompareItemIgnoreMilliseconds); + const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), + sourceEntry->timeInfo().lastModificationTime(), + CompareItemIgnoreMilliseconds); if (comparison < 0) { Group* currentGroup = targetEntry->group(); Entry* clonedEntry = sourceEntry->clone(Entry::CloneIncludeHistory); @@ -347,14 +362,15 @@ Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContex qPrintable(targetEntry->group()->name())); const bool changed = mergeHistory(sourceEntry, targetEntry, mergeMethod); if (changed) { - changes << tr("Synchronizing from older source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex()); + changes + << tr("Synchronizing from older source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex()); } } return changes; } - -Merger::ChangeList Merger::resolveEntryConflict(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) +Merger::ChangeList +Merger::resolveEntryConflict(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry) { ChangeList changes; // We need to cut off the milliseconds since the persistent format only supports times down to seconds @@ -395,15 +411,19 @@ bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::M Q_UNUSED(mergeMethod); const auto targetHistoryItems = targetEntry->historyItems(); const auto sourceHistoryItems = sourceEntry->historyItems(); - const int comparison = compare(sourceEntry->timeInfo().lastModificationTime(), targetEntry->timeInfo().lastModificationTime(), CompareItemIgnoreMilliseconds); + const int comparison = compare(sourceEntry->timeInfo().lastModificationTime(), + targetEntry->timeInfo().lastModificationTime(), + CompareItemIgnoreMilliseconds); const bool preferLocal = mergeMethod == Group::KeepLocal || comparison < 0; const bool preferRemote = mergeMethod == Group::KeepRemote || comparison > 0; QMap merged; for (Entry* historyItem : targetHistoryItems) { const QDateTime modificationTime = Clock::serialized(historyItem->timeInfo().lastModificationTime()); - if (merged.contains(modificationTime) && !merged[modificationTime]->equals(historyItem, CompareItemIgnoreMilliseconds)) { - ::qWarning("Inconsistent history entry of %s[%s] at %s contains conflicting changes - conflict resolution may lose data!", + if (merged.contains(modificationTime) + && !merged[modificationTime]->equals(historyItem, CompareItemIgnoreMilliseconds)) { + ::qWarning("Inconsistent history entry of %s[%s] at %s contains conflicting changes - conflict resolution " + "may lose data!", qPrintable(sourceEntry->title()), qPrintable(sourceEntry->uuidToHex()), qPrintable(modificationTime.toString("yyyy-MM-dd HH-mm-ss-zzz"))); @@ -413,8 +433,10 @@ bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::M for (Entry* historyItem : sourceHistoryItems) { // Items with same modification-time changes will be regarded as same (like KeePass2) const QDateTime modificationTime = Clock::serialized(historyItem->timeInfo().lastModificationTime()); - if (merged.contains(modificationTime) && !merged[modificationTime]->equals(historyItem, CompareItemIgnoreMilliseconds)) { - ::qWarning("History entry of %s[%s] at %s contains conflicting changes - conflict resolution may lose data!", + if (merged.contains(modificationTime) + && !merged[modificationTime]->equals(historyItem, CompareItemIgnoreMilliseconds)) { + ::qWarning( + "History entry of %s[%s] at %s contains conflicting changes - conflict resolution may lose data!", qPrintable(sourceEntry->title()), qPrintable(sourceEntry->uuidToHex()), qPrintable(modificationTime.toString("yyyy-MM-dd HH-mm-ss-zzz"))); @@ -430,7 +452,9 @@ bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::M const QDateTime targetModificationTime = Clock::serialized(targetEntry->timeInfo().lastModificationTime()); const QDateTime sourceModificationTime = Clock::serialized(sourceEntry->timeInfo().lastModificationTime()); - if (targetModificationTime == sourceModificationTime && !targetEntry->equals(sourceEntry, CompareItemIgnoreMilliseconds | CompareItemIgnoreHistory | CompareItemIgnoreLocation)) { + if (targetModificationTime == sourceModificationTime + && !targetEntry->equals(sourceEntry, + CompareItemIgnoreMilliseconds | CompareItemIgnoreHistory | CompareItemIgnoreLocation)) { ::qWarning("Entry of %s[%s] contains conflicting changes - conflict resolution may lose data!", qPrintable(sourceEntry->title()), qPrintable(sourceEntry->uuidToHex())); diff --git a/src/core/Merger.h b/src/core/Merger.h index 1f16fe026..03a47a27f 100644 --- a/src/core/Merger.h +++ b/src/core/Merger.h @@ -55,14 +55,22 @@ private: bool mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod); void moveEntry(Entry* entry, Group* targetGroup); void moveGroup(Group* group, Group* targetGroup); - void eraseEntry(Entry* entry); // remove an entry without a trace in the deletedObjects - needed for elemination cloned entries - void eraseGroup(Group* group); // remove an entry without a trace in the deletedObjects - needed for elemination cloned entries + // remove an entry without a trace in the deletedObjects - needed for elemination cloned entries + void eraseEntry(Entry* entry); + // remove an entry without a trace in the deletedObjects - needed for elemination cloned entries + void eraseGroup(Group* group); ChangeList resolveEntryConflict(const MergeContext& context, const Entry* existingEntry, Entry* otherEntry); ChangeList resolveGroupConflict(const MergeContext& context, const Group* existingGroup, Group* otherGroup); - Merger::ChangeList resolveEntryConflict_Duplicate(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry); - Merger::ChangeList resolveEntryConflict_KeepLocal(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry); - Merger::ChangeList resolveEntryConflict_KeepRemote(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry); - Merger::ChangeList resolveEntryConflict_MergeHistories(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod); + Merger::ChangeList + resolveEntryConflict_Duplicate(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry); + Merger::ChangeList + resolveEntryConflict_KeepLocal(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry); + Merger::ChangeList + resolveEntryConflict_KeepRemote(const MergeContext& context, const Entry* sourceEntry, Entry* targetEntry); + Merger::ChangeList resolveEntryConflict_MergeHistories(const MergeContext& context, + const Entry* sourceEntry, + Entry* targetEntry, + Group::MergeMode mergeMethod); private: MergeContext m_context; diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp index 40bf52775..45010a4ff 100644 --- a/src/core/Metadata.cpp +++ b/src/core/Metadata.cpp @@ -429,7 +429,7 @@ void Metadata::removeCustomIcon(const QUuid& uuid) emit metadataModified(); } -QUuid Metadata::findCustomIcon(const QImage &candidate) +QUuid Metadata::findCustomIcon(const QImage& candidate) { QByteArray hash = hashImage(candidate); return m_customIconsHashes.value(hash, QUuid()); diff --git a/src/core/OSEventFilter.h b/src/core/OSEventFilter.h index 1dca5392e..a27ade713 100644 --- a/src/core/OSEventFilter.h +++ b/src/core/OSEventFilter.h @@ -9,6 +9,7 @@ class OSEventFilter : public QAbstractNativeEventFilter public: OSEventFilter(); bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; + private: Q_DISABLE_COPY(OSEventFilter) }; diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp index 69a0dfb3e..1132582d6 100644 --- a/src/core/PasswordGenerator.cpp +++ b/src/core/PasswordGenerator.cpp @@ -124,7 +124,6 @@ bool PasswordGenerator::isValid() const } return !passwordGroups().isEmpty(); - } QVector PasswordGenerator::passwordGroups() const diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 362cfa937..673e6a604 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -22,14 +22,12 @@ #include "core/Translator.h" #include +#include #include #include #include -#include #include - -#include - +#include #include #ifdef Q_OS_WIN @@ -59,147 +57,145 @@ namespace Tools { -QString humanReadableFileSize(qint64 bytes, quint32 precision) -{ - constexpr auto kibibyte = 1024; - double size = bytes; + QString humanReadableFileSize(qint64 bytes, quint32 precision) + { + constexpr auto kibibyte = 1024; + double size = bytes; - QStringList units = QStringList() << "B" - << "KiB" - << "MiB" - << "GiB"; - int i = 0; - int maxI = units.size() - 1; + QStringList units = QStringList() << "B" + << "KiB" + << "MiB" + << "GiB"; + int i = 0; + int maxI = units.size() - 1; - while ((size >= kibibyte) && (i < maxI)) { - size /= kibibyte; - i++; + while ((size >= kibibyte) && (i < maxI)) { + size /= kibibyte; + i++; + } + + return QString("%1 %2").arg(QLocale().toString(size, 'f', precision), units.at(i)); } - return QString("%1 %2").arg(QLocale().toString(size, 'f', precision), units.at(i)); -} + bool readFromDevice(QIODevice* device, QByteArray& data, int size) + { + QByteArray buffer; + buffer.resize(size); -bool readFromDevice(QIODevice* device, QByteArray& data, int size) -{ - QByteArray buffer; - buffer.resize(size); - - qint64 readResult = device->read(buffer.data(), size); - if (readResult == -1) { - return false; - } else { - buffer.resize(readResult); - data = buffer; - return true; - } -} - -bool readAllFromDevice(QIODevice* device, QByteArray& data) -{ - QByteArray result; - qint64 readBytes = 0; - qint64 readResult; - do { - result.resize(result.size() + 16384); - readResult = device->read(result.data() + readBytes, result.size() - readBytes); - if (readResult > 0) { - readBytes += readResult; + qint64 readResult = device->read(buffer.data(), size); + if (readResult == -1) { + return false; + } else { + buffer.resize(readResult); + data = buffer; + return true; } } - while (readResult > 0); - if (readResult == -1) { - return false; - } else { - result.resize(static_cast(readBytes)); - data = result; - return true; + bool readAllFromDevice(QIODevice* device, QByteArray& data) + { + QByteArray result; + qint64 readBytes = 0; + qint64 readResult; + do { + result.resize(result.size() + 16384); + readResult = device->read(result.data() + readBytes, result.size() - readBytes); + if (readResult > 0) { + readBytes += readResult; + } + } while (readResult > 0); + + if (readResult == -1) { + return false; + } else { + result.resize(static_cast(readBytes)); + data = result; + return true; + } } -} -QString imageReaderFilter() -{ - const QList formats = QImageReader::supportedImageFormats(); - QStringList formatsStringList; + QString imageReaderFilter() + { + const QList formats = QImageReader::supportedImageFormats(); + QStringList formatsStringList; - for (const QByteArray& format : formats) { - for (char codePoint : format) { - if (!QChar(codePoint).isLetterOrNumber()) { - continue; + for (const QByteArray& format : formats) { + for (char codePoint : format) { + if (!QChar(codePoint).isLetterOrNumber()) { + continue; + } + } + + formatsStringList.append("*." + QString::fromLatin1(format).toLower()); + } + + return formatsStringList.join(" "); + } + + bool isHex(const QByteArray& ba) + { + for (const unsigned char c : ba) { + if (!std::isxdigit(c)) { + return false; } } - formatsStringList.append("*." + QString::fromLatin1(format).toLower()); + return true; } - return formatsStringList.join(" "); -} + bool isBase64(const QByteArray& ba) + { + constexpr auto pattern = R"(^(?:[a-z0-9+]{4})*(?:[a-z0-9+]{3}=|[a-z0-9+]{2}==)?$)"; + QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::RegExp2); -bool isHex(const QByteArray& ba) -{ - for (const unsigned char c : ba) { - if (!std::isxdigit(c)) { - return false; + QString base64 = QString::fromLatin1(ba.constData(), ba.size()); + + return regexp.exactMatch(base64); + } + + void sleep(int ms) + { + Q_ASSERT(ms >= 0); + + if (ms == 0) { + return; } - } - - return true; -} - -bool isBase64(const QByteArray& ba) -{ - constexpr auto pattern = R"(^(?:[a-z0-9+]{4})*(?:[a-z0-9+]{3}=|[a-z0-9+]{2}==)?$)"; - QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::RegExp2); - - QString base64 = QString::fromLatin1(ba.constData(), ba.size()); - - return regexp.exactMatch(base64); -} - -void sleep(int ms) -{ - Q_ASSERT(ms >= 0); - - if (ms == 0) { - return; - } #ifdef Q_OS_WIN - Sleep(uint(ms)); + Sleep(uint(ms)); #else - timespec ts; - ts.tv_sec = ms/1000; - ts.tv_nsec = (ms%1000)*1000*1000; - nanosleep(&ts, nullptr); + timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000 * 1000; + nanosleep(&ts, nullptr); #endif -} - -void wait(int ms) -{ - Q_ASSERT(ms >= 0); - - if (ms == 0) { - return; } - QElapsedTimer timer; - timer.start(); + void wait(int ms) + { + Q_ASSERT(ms >= 0); - if (ms <= 50) { - QCoreApplication::processEvents(QEventLoop::AllEvents, ms); - sleep(qMax(ms - static_cast(timer.elapsed()), 0)); - } else { - int timeLeft; - do { - timeLeft = ms - timer.elapsed(); - if (timeLeft > 0) { - QCoreApplication::processEvents(QEventLoop::AllEvents, timeLeft); - sleep(10); - } + if (ms == 0) { + return; + } + + QElapsedTimer timer; + timer.start(); + + if (ms <= 50) { + QCoreApplication::processEvents(QEventLoop::AllEvents, ms); + sleep(qMax(ms - static_cast(timer.elapsed()), 0)); + } else { + int timeLeft; + do { + timeLeft = ms - timer.elapsed(); + if (timeLeft > 0) { + QCoreApplication::processEvents(QEventLoop::AllEvents, timeLeft); + sleep(10); + } + } while (!timer.hasExpired(ms)); } - while (!timer.hasExpired(ms)); } -} // Escape common regex symbols except for *, ?, and | auto regexEscape = QRegularExpression(R"re(([-[\]{}()+.,\\\/^$#]))re"); diff --git a/src/core/Tools.h b/src/core/Tools.h index 37214f069..984bab491 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -31,28 +31,28 @@ class QRegularExpression; namespace Tools { -QString humanReadableFileSize(qint64 bytes, quint32 precision = 2); -bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384); -bool readAllFromDevice(QIODevice* device, QByteArray& data); -QString imageReaderFilter(); -bool isHex(const QByteArray& ba); -bool isBase64(const QByteArray& ba); -void sleep(int ms); -void wait(int ms); -QRegularExpression convertToRegex(const QString& string, bool useWildcards = false, bool exactMatch = false, - bool caseSensitive = false); + QString humanReadableFileSize(qint64 bytes, quint32 precision = 2); + bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384); + bool readAllFromDevice(QIODevice* device, QByteArray& data); + QString imageReaderFilter(); + bool isHex(const QByteArray& ba); + bool isBase64(const QByteArray& ba); + void sleep(int ms); + void wait(int ms); + QRegularExpression convertToRegex(const QString& string, bool useWildcards = false, + bool exactMatch = false, bool caseSensitive = false); -template -RandomAccessIterator binaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T& value) -{ - RandomAccessIterator it = std::lower_bound(begin, end, value); + template + RandomAccessIterator binaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T& value) + { + RandomAccessIterator it = std::lower_bound(begin, end, value); - if ((it == end) || (value < *it)) { - return end; - } else { - return it; + if ((it == end) || (value < *it)) { + return end; + } else { + return it; + } } -} } // namespace Tools diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index fffcddfbd..ab97322ca 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -144,8 +144,9 @@ bool Crypto::testSha512() QByteArray sha512Test = CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", CryptoHash::Sha512); - if (sha512Test != QByteArray::fromHex("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b" - "07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")) { + if (sha512Test + != QByteArray::fromHex("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b" + "07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")) { raiseError("SHA-512 mismatch."); return false; } diff --git a/src/crypto/SymmetricCipher.cpp b/src/crypto/SymmetricCipher.cpp index 10077e7e5..3637161ee 100644 --- a/src/crypto/SymmetricCipher.cpp +++ b/src/crypto/SymmetricCipher.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "SymmetricCipher.h" diff --git a/src/crypto/SymmetricCipher.h b/src/crypto/SymmetricCipher.h index ede5ab1a4..e0e91aff1 100644 --- a/src/crypto/SymmetricCipher.h +++ b/src/crypto/SymmetricCipher.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_SYMMETRICCIPHER_H #define KEEPASSX_SYMMETRICCIPHER_H diff --git a/src/crypto/SymmetricCipherBackend.h b/src/crypto/SymmetricCipherBackend.h index 27a39177e..781e33484 100644 --- a/src/crypto/SymmetricCipherBackend.h +++ b/src/crypto/SymmetricCipherBackend.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_SYMMETRICCIPHERBACKEND_H #define KEEPASSX_SYMMETRICCIPHERBACKEND_H diff --git a/src/crypto/SymmetricCipherGcrypt.cpp b/src/crypto/SymmetricCipherGcrypt.cpp index e3bc88cbf..8466954d6 100644 --- a/src/crypto/SymmetricCipherGcrypt.cpp +++ b/src/crypto/SymmetricCipherGcrypt.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "SymmetricCipherGcrypt.h" diff --git a/src/crypto/SymmetricCipherGcrypt.h b/src/crypto/SymmetricCipherGcrypt.h index 6f806b90b..20feca10a 100644 --- a/src/crypto/SymmetricCipherGcrypt.h +++ b/src/crypto/SymmetricCipherGcrypt.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_SYMMETRICCIPHERGCRYPT_H #define KEEPASSX_SYMMETRICCIPHERGCRYPT_H diff --git a/src/crypto/kdf/Argon2Kdf.h b/src/crypto/kdf/Argon2Kdf.h index 2f029a570..73b7f8529 100644 --- a/src/crypto/kdf/Argon2Kdf.h +++ b/src/crypto/kdf/Argon2Kdf.h @@ -46,12 +46,12 @@ protected: private: Q_REQUIRED_RESULT static bool transformKeyRaw(const QByteArray& key, - const QByteArray& seed, - quint32 version, - quint32 rounds, - quint64 memory, - quint32 parallelism, - QByteArray& result); + const QByteArray& seed, + quint32 version, + quint32 rounds, + quint64 memory, + quint32 parallelism, + QByteArray& result); }; #endif // KEEPASSX_ARGON2KDF_H diff --git a/src/crypto/kdf/Kdf.h b/src/crypto/kdf/Kdf.h index 1dff11067..36b4772f2 100644 --- a/src/crypto/kdf/Kdf.h +++ b/src/crypto/kdf/Kdf.h @@ -18,8 +18,8 @@ #ifndef KEEPASSX_KDF_H #define KEEPASSX_KDF_H -#include #include +#include #define KDF_DEFAULT_SEED_SIZE 32 #define KDF_DEFAULT_ROUNDS 1000000ull diff --git a/src/format/Kdbx3Reader.cpp b/src/format/Kdbx3Reader.cpp index 2ef6820ca..4fec74718 100644 --- a/src/format/Kdbx3Reader.cpp +++ b/src/format/Kdbx3Reader.cpp @@ -42,8 +42,7 @@ bool Kdbx3Reader::readDatabaseImpl(QIODevice* device, // check if all required headers were present if (m_masterSeed.isEmpty() || m_encryptionIV.isEmpty() || m_streamStartBytes.isEmpty() - || m_protectedStreamKey.isEmpty() - || db->cipher().isNull()) { + || m_protectedStreamKey.isEmpty() || db->cipher().isNull()) { raiseError(tr("missing database headers")); return false; } diff --git a/src/format/Kdbx3Writer.cpp b/src/format/Kdbx3Writer.cpp index 20af01406..b2de41f44 100644 --- a/src/format/Kdbx3Writer.cpp +++ b/src/format/Kdbx3Writer.cpp @@ -66,9 +66,10 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db) writeMagicNumbers(&header, KeePass2::SIGNATURE_1, KeePass2::SIGNATURE_2, KeePass2::FILE_VERSION_3_1); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toRfc4122())); - CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::CompressionFlags, - Endian::sizedIntToBytes(db->compressionAlgorithm(), - KeePass2::BYTEORDER))); + CHECK_RETURN_FALSE( + writeHeaderField(&header, + KeePass2::HeaderFieldID::CompressionFlags, + Endian::sizedIntToBytes(db->compressionAlgorithm(), KeePass2::BYTEORDER))); auto kdf = db->kdf(); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::MasterSeed, masterSeed)); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::TransformSeed, kdf->seed())); diff --git a/src/format/Kdbx4Writer.cpp b/src/format/Kdbx4Writer.cpp index a73cbd83a..33c0024ed 100644 --- a/src/format/Kdbx4Writer.cpp +++ b/src/format/Kdbx4Writer.cpp @@ -72,10 +72,12 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db) writeMagicNumbers(&header, KeePass2::SIGNATURE_1, KeePass2::SIGNATURE_2, KeePass2::FILE_VERSION_4); - CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toRfc4122())); - CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::CompressionFlags, - Endian::sizedIntToBytes(static_cast(db->compressionAlgorithm()), - KeePass2::BYTEORDER))); + CHECK_RETURN_FALSE( + writeHeaderField(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toRfc4122())); + CHECK_RETURN_FALSE(writeHeaderField( + &header, + KeePass2::HeaderFieldID::CompressionFlags, + Endian::sizedIntToBytes(static_cast(db->compressionAlgorithm()), KeePass2::BYTEORDER))); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::MasterSeed, masterSeed)); CHECK_RETURN_FALSE(writeHeaderField(&header, KeePass2::HeaderFieldID::EncryptionIV, encryptionIV)); diff --git a/src/format/KdbxXmlReader.cpp b/src/format/KdbxXmlReader.cpp index b64c28a22..163a9fa9d 100644 --- a/src/format/KdbxXmlReader.cpp +++ b/src/format/KdbxXmlReader.cpp @@ -44,7 +44,7 @@ KdbxXmlReader::KdbxXmlReader(quint32 version) * @param version KDBX version * @param binaryPool binary pool */ -KdbxXmlReader::KdbxXmlReader(quint32 version, QHash binaryPool) +KdbxXmlReader::KdbxXmlReader(quint32 version, QHash binaryPool) : m_kdbxVersion(version) , m_binaryPool(std::move(binaryPool)) { diff --git a/src/format/KdbxXmlReader.h b/src/format/KdbxXmlReader.h index dab6fc639..2ec9c9f66 100644 --- a/src/format/KdbxXmlReader.h +++ b/src/format/KdbxXmlReader.h @@ -21,7 +21,6 @@ #include "core/Database.h" #include "core/Metadata.h" #include "core/TimeInfo.h" -#include "core/Database.h" #include #include @@ -42,7 +41,7 @@ class KdbxXmlReader public: explicit KdbxXmlReader(quint32 version); - explicit KdbxXmlReader(quint32 version, QHash binaryPool); + explicit KdbxXmlReader(quint32 version, QHash binaryPool); virtual ~KdbxXmlReader() = default; virtual QSharedPointer readDatabase(const QString& filename); diff --git a/src/format/KdbxXmlWriter.cpp b/src/format/KdbxXmlWriter.cpp index 6bc4be51e..a95a60091 100644 --- a/src/format/KdbxXmlWriter.cpp +++ b/src/format/KdbxXmlWriter.cpp @@ -356,12 +356,14 @@ void KdbxXmlWriter::writeEntry(const Entry* entry) for (const QString& key : attributesKeyList) { m_xml.writeStartElement("String"); + // clang-format off bool protect = (((key == "Title") && m_meta->protectTitle()) || ((key == "UserName") && m_meta->protectUsername()) || ((key == "Password") && m_meta->protectPassword()) || ((key == "URL") && m_meta->protectUrl()) || ((key == "Notes") && m_meta->protectNotes()) || entry->attributes()->isProtected(key)); + // clang-format on writeString("Key", key); diff --git a/src/format/KeePass1.h b/src/format/KeePass1.h index fa220da03..caddee441 100644 --- a/src/format/KeePass1.h +++ b/src/format/KeePass1.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2012 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2012 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_KEEPASS1_H #define KEEPASSX_KEEPASS1_H @@ -35,6 +35,6 @@ namespace KeePass1 Rijndael = 2, Twofish = 8 }; -} +} // namespace KeePass1 #endif // KEEPASSX_KEEPASS1_H diff --git a/src/format/KeePass2.cpp b/src/format/KeePass2.cpp index 639255d27..fbc393030 100644 --- a/src/format/KeePass2.cpp +++ b/src/format/KeePass2.cpp @@ -23,14 +23,14 @@ #define UUID_LENGTH 16 -const QUuid KeePass2::CIPHER_AES128 = QUuid("61ab05a1-9464-41c3-8d74-3a563df8dd35"); -const QUuid KeePass2::CIPHER_AES256 = QUuid("31c1f2e6-bf71-4350-be58-05216afc5aff"); -const QUuid KeePass2::CIPHER_TWOFISH = QUuid("ad68f29f-576f-4bb9-a36a-d47af965346c"); +const QUuid KeePass2::CIPHER_AES128 = QUuid("61ab05a1-9464-41c3-8d74-3a563df8dd35"); +const QUuid KeePass2::CIPHER_AES256 = QUuid("31c1f2e6-bf71-4350-be58-05216afc5aff"); +const QUuid KeePass2::CIPHER_TWOFISH = QUuid("ad68f29f-576f-4bb9-a36a-d47af965346c"); const QUuid KeePass2::CIPHER_CHACHA20 = QUuid("d6038a2b-8b6f-4cb5-a524-339a31dbb59a"); -const QUuid KeePass2::KDF_AES_KDBX3 = QUuid("c9d9f39a-628a-4460-bf74-0d08c18a4fea"); -const QUuid KeePass2::KDF_AES_KDBX4 = QUuid("7c02bb82-79a7-4ac0-927d-114a00648238"); -const QUuid KeePass2::KDF_ARGON2 = QUuid("ef636ddf-8c29-444b-91f7-a9a403e30a0c"); +const QUuid KeePass2::KDF_AES_KDBX3 = QUuid("c9d9f39a-628a-4460-bf74-0d08c18a4fea"); +const QUuid KeePass2::KDF_AES_KDBX4 = QUuid("7c02bb82-79a7-4ac0-927d-114a00648238"); +const QUuid KeePass2::KDF_ARGON2 = QUuid("ef636ddf-8c29-444b-91f7-a9a403e30a0c"); const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xe8\x30\x09\x4b\x97\x20\x5d\x2a"); @@ -50,14 +50,12 @@ const QString KeePass2::KDFPARAM_ARGON2_ASSOCDATA("A"); const QList> KeePass2::CIPHERS{ qMakePair(KeePass2::CIPHER_AES256, QObject::tr("AES: 256-bit")), qMakePair(KeePass2::CIPHER_TWOFISH, QObject::tr("Twofish: 256-bit")), - qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20: 256-bit")) -}; + qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20: 256-bit"))}; const QList> KeePass2::KDFS{ qMakePair(KeePass2::KDF_ARGON2, QObject::tr("Argon2 (KDBX 4 – recommended)")), qMakePair(KeePass2::KDF_AES_KDBX4, QObject::tr("AES-KDF (KDBX 4)")), - qMakePair(KeePass2::KDF_AES_KDBX3, QObject::tr("AES-KDF (KDBX 3.1)")) -}; + qMakePair(KeePass2::KDF_AES_KDBX3, QObject::tr("AES-KDF (KDBX 3.1)"))}; QByteArray KeePass2::hmacKey(const QByteArray& masterSeed, const QByteArray& transformedMasterKey) { diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h index 195ce8c2b..d18db3578 100644 --- a/src/format/KeePass2.h +++ b/src/format/KeePass2.h @@ -21,9 +21,9 @@ #include #include #include +#include #include #include -#include #include "crypto/SymmetricCipher.h" #include "crypto/kdf/Kdf.h" @@ -46,14 +46,14 @@ namespace KeePass2 const QSysInfo::Endian BYTEORDER = QSysInfo::LittleEndian; -extern const QUuid CIPHER_AES128; -extern const QUuid CIPHER_AES256; -extern const QUuid CIPHER_TWOFISH; -extern const QUuid CIPHER_CHACHA20; + extern const QUuid CIPHER_AES128; + extern const QUuid CIPHER_AES256; + extern const QUuid CIPHER_TWOFISH; + extern const QUuid CIPHER_CHACHA20; -extern const QUuid KDF_AES_KDBX3; -extern const QUuid KDF_AES_KDBX4; -extern const QUuid KDF_ARGON2; + extern const QUuid KDF_AES_KDBX3; + extern const QUuid KDF_AES_KDBX4; + extern const QUuid KDF_ARGON2; extern const QByteArray INNER_STREAM_SALSA20_IV; @@ -68,8 +68,8 @@ extern const QUuid KDF_ARGON2; extern const QString KDFPARAM_ARGON2_SECRET; extern const QString KDFPARAM_ARGON2_ASSOCDATA; -extern const QList> CIPHERS; -extern const QList> KDFS; + extern const QList> CIPHERS; + extern const QList> KDFS; enum class HeaderFieldID { @@ -126,11 +126,11 @@ extern const QList> KDFS; ByteArray = 0x42 }; -QByteArray hmacKey(const QByteArray& masterSeed, const QByteArray& transformedMasterKey); -QSharedPointer kdfFromParameters(const QVariantMap& p); -QVariantMap kdfToParameters(const QSharedPointer& kdf); -QSharedPointer uuidToKdf(const QUuid& uuid); -ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id); + QByteArray hmacKey(const QByteArray& masterSeed, const QByteArray& transformedMasterKey); + QSharedPointer kdfFromParameters(const QVariantMap& p); + QVariantMap kdfToParameters(const QSharedPointer& kdf); + QSharedPointer uuidToKdf(const QUuid& uuid); + ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id); } // namespace KeePass2 diff --git a/src/gui/AboutDialog.cpp b/src/gui/AboutDialog.cpp index 483e4dd2b..48546edb4 100644 --- a/src/gui/AboutDialog.cpp +++ b/src/gui/AboutDialog.cpp @@ -64,10 +64,9 @@ AboutDialog::AboutDialog(QWidget* parent) debugInfo.append(tr("Distribution: %1").arg(KEEPASSXC_DIST_TYPE).append("\n")); #endif - debugInfo.append("\n").append(QString("%1\n- Qt %2\n- %3\n\n") - .arg(tr("Libraries:"), - QString::fromLocal8Bit(qVersion()), - Crypto::backendVersion())); + debugInfo.append("\n").append( + QString("%1\n- Qt %2\n- %3\n\n") + .arg(tr("Libraries:"), QString::fromLocal8Bit(qVersion()), Crypto::backendVersion())); #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) debugInfo.append(tr("Operating system: %1\nCPU architecture: %2\nKernel: %3 %4") diff --git a/src/gui/ApplicationSettingsWidget.h b/src/gui/ApplicationSettingsWidget.h index 5fedabfcd..f596b52c5 100644 --- a/src/gui/ApplicationSettingsWidget.h +++ b/src/gui/ApplicationSettingsWidget.h @@ -25,7 +25,7 @@ namespace Ui { class ApplicationSettingsWidgetGeneral; class ApplicationSettingsWidgetSecurity; -} +} // namespace Ui class ISettingsPage { diff --git a/src/gui/CategoryListWidget.cpp b/src/gui/CategoryListWidget.cpp index 50b02da81..c57b19bc0 100644 --- a/src/gui/CategoryListWidget.cpp +++ b/src/gui/CategoryListWidget.cpp @@ -38,8 +38,9 @@ CategoryListWidget::CategoryListWidget(QWidget* parent) connect(m_ui->scrollUp, SIGNAL(clicked()), SLOT(scrollCategoriesUp())); connect(m_ui->scrollDown, SIGNAL(clicked()), SLOT(scrollCategoriesDown())); connect(m_ui->categoryList->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(updateCategoryScrollButtons())); - connect( - m_ui->categoryList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), SLOT(updateCategoryScrollButtons())); + // clang-format off + connect(m_ui->categoryList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), SLOT(updateCategoryScrollButtons())); + // clang-format on } CategoryListWidget::~CategoryListWidget() diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index b3427dc49..9c496aa31 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -105,9 +105,11 @@ 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) { + // clang-format off 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); + // clang-format on pollYubikey(); m_yubiKeyBeingPolled = true; @@ -190,7 +192,7 @@ void DatabaseOpenWidget::openDatabase() } if (!m_ui->editPassword->isPasswordVisible()) { - m_ui->editPassword->setShowPassword(false); + m_ui->editPassword->setShowPassword(false); } QCoreApplication::processEvents(); diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index c7d752bd2..b45c5a8a5 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -94,10 +94,12 @@ QSharedPointer DatabaseTabWidget::execNewDatabaseWizard() Q_ASSERT(db->key()); Q_ASSERT(db->kdf()); if (!db->key() || !db->kdf()) { - MessageBox::critical(this, tr("Database creation error"), - 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); + MessageBox::critical(this, + tr("Database creation error"), + 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 {}; } @@ -216,7 +218,7 @@ void DatabaseTabWidget::mergeDatabase() auto dbWidget = currentDatabaseWidget(); 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"), "", filter); + const QString fileName = fileDialog()->getOpenFileName(this, tr("Merge database"), QString(), filter); if (!fileName.isEmpty()) { mergeDatabase(fileName); } diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 389d6fc4c..234f7e042 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -50,8 +50,8 @@ #include "gui/EntryPreviewWidget.h" #include "gui/KeePass1OpenWidget.h" #include "gui/MessageBox.h" -#include "gui/TotpSetupDialog.h" #include "gui/TotpDialog.h" +#include "gui/TotpSetupDialog.h" #include "gui/TotpExportSettingsDialog.h" #include "gui/entry/EditEntryWidget.h" #include "gui/entry/EntryView.h" @@ -155,6 +155,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) addChildWidget(m_csvImportWizard); addChildWidget(m_keepass1OpenWidget); + // clang-format off connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged())); connect(m_previewSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(previewSplitterSizesChanged())); connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode))); @@ -177,7 +178,8 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) connect(&m_fileWatchTimer, SIGNAL(timeout()), this, SLOT(reloadDatabaseFile())); connect(&m_fileWatchUnblockTimer, SIGNAL(timeout()), this, SLOT(unblockAutoReload())); connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged())); - + // clang-format on + connectDatabaseSignals(); m_fileWatchTimer.setSingleShot(true); diff --git a/src/gui/EditWidget.cpp b/src/gui/EditWidget.cpp index 66038282d..6a5765933 100644 --- a/src/gui/EditWidget.cpp +++ b/src/gui/EditWidget.cpp @@ -18,6 +18,7 @@ #include "EditWidget.h" #include "ui_EditWidget.h" + #include #include diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 5d1a5f901..abb65c00d 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -31,6 +31,7 @@ #ifdef WITH_XC_NETWORKING #include +#include #endif IconStruct::IconStruct() @@ -44,6 +45,7 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent) , m_ui(new Ui::EditWidgetIcons()) , m_db(nullptr) #ifdef WITH_XC_NETWORKING + , m_netMgr(new QNetworkAccessManager(this)) , m_reply(nullptr) #endif , m_defaultIconModel(new DefaultIconModel(this)) @@ -54,6 +56,7 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent) m_ui->defaultIconsView->setModel(m_defaultIconModel); m_ui->customIconsView->setModel(m_customIconModel); + // clang-format off connect(m_ui->defaultIconsView, SIGNAL(clicked(QModelIndex)), this, SLOT(updateRadioButtonDefaultIcons())); connect(m_ui->customIconsView, SIGNAL(clicked(QModelIndex)), this, SLOT(updateRadioButtonCustomIcons())); connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SLOT(updateWidgetsDefaultIcons(bool))); @@ -64,14 +67,11 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent) connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated())); connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated())); - connect(m_ui->defaultIconsView->selectionModel(), - SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, - SIGNAL(widgetUpdated())); - connect(m_ui->customIconsView->selectionModel(), - SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, - SIGNAL(widgetUpdated())); + connect(m_ui->defaultIconsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SIGNAL(widgetUpdated())); + connect(m_ui->customIconsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SIGNAL(widgetUpdated())); + // clang-format on m_ui->faviconButton->setVisible(false); m_ui->addButton->setEnabled(true); @@ -156,7 +156,8 @@ void EditWidgetIcons::setUrl(const QString& url) } #ifdef WITH_XC_NETWORKING -namespace { +namespace +{ // Try to get the 2nd level domain of the host part of a QUrl. For example, // "foo.bar.example.com" would become "example.com", and "foo.bar.example.co.uk" // would become "example.co.uk". @@ -177,13 +178,13 @@ namespace { return url; } - QUrl getRedirectTarget(QNetworkReply *reply) + QUrl getRedirectTarget(QNetworkReply* reply) { QVariant var = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); QUrl url = convertVariantToUrl(var); return url; } -} +} // namespace #endif void EditWidgetIcons::downloadFavicon() @@ -264,9 +265,10 @@ void EditWidgetIcons::fetchFinished() return; } else { if (!fallbackEnabled) { - emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" + - tr("Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security"), - MessageWidget::Error); + emit messageEditEntry( + tr("Unable to fetch favicon.") + "\n" + + tr("Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security"), + MessageWidget::Error); } else { emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error); } @@ -294,7 +296,7 @@ void EditWidgetIcons::startFetchFavicon(const QUrl& url) QNetworkRequest request(url); - m_reply = m_netMgr.get(request); + m_reply = m_netMgr->get(request); connect(m_reply, &QNetworkReply::finished, this, &EditWidgetIcons::fetchFinished); connect(m_reply, &QIODevice::readyRead, this, &EditWidgetIcons::fetchReadyRead); #else @@ -339,8 +341,9 @@ void EditWidgetIcons::addCustomIconFromFile() if (!errornames.empty()) { // Show the first 8 icons that failed to load errornames = errornames.mid(0, 8); - emit messageEditEntry(msg + "\n" + tr("The following icon(s) failed:", "", errornames.size()) + - "\n" + errornames.join("\n"), MessageWidget::Error); + emit messageEditEntry(msg + "\n" + tr("The following icon(s) failed:", "", errornames.size()) + "\n" + + errornames.join("\n"), + MessageWidget::Error); } else if (numloaded > 0) { emit messageEditEntry(msg, MessageWidget::Positive); } else { diff --git a/src/gui/EditWidgetIcons.h b/src/gui/EditWidgetIcons.h index 677cbebef..5c02f31a1 100644 --- a/src/gui/EditWidgetIcons.h +++ b/src/gui/EditWidgetIcons.h @@ -21,9 +21,8 @@ #include #include -#include -#include #include +#include #include "config-keepassx.h" #include "core/Global.h" @@ -33,6 +32,7 @@ class Database; class DefaultIconModel; class CustomIconModel; #ifdef WITH_XC_NETWORKING +class QNetworkAccessManager; class QNetworkReply; #endif @@ -95,8 +95,8 @@ private: QUrl m_fetchUrl; QList m_urlsToTry; QByteArray m_bytesReceived; - QNetworkAccessManager m_netMgr; - QNetworkReply *m_reply; + QNetworkAccessManager* m_netMgr; + QNetworkReply* m_reply; int m_redirects; #endif DefaultIconModel* const m_defaultIconModel; diff --git a/src/gui/EditWidgetProperties.cpp b/src/gui/EditWidgetProperties.cpp index b2de9d778..bc0f6e164 100644 --- a/src/gui/EditWidgetProperties.cpp +++ b/src/gui/EditWidgetProperties.cpp @@ -16,11 +16,11 @@ */ #include "EditWidgetProperties.h" - -#include +#include "ui_EditWidgetProperties.h" #include "MessageBox.h" -#include "ui_EditWidgetProperties.h" + +#include EditWidgetProperties::EditWidgetProperties(QWidget* parent) : QWidget(parent) @@ -32,9 +32,11 @@ EditWidgetProperties::EditWidgetProperties(QWidget* parent) m_ui->removeCustomDataButton->setEnabled(false); m_ui->customDataTable->setModel(m_customDataModel); + // clang-format off connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(toggleRemoveButton(QItemSelection))); + // clang-format on connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData())); } @@ -66,12 +68,13 @@ const CustomData* EditWidgetProperties::customData() const void EditWidgetProperties::removeSelectedPluginData() { - if (QMessageBox::Yes != MessageBox::question(this, - tr("Delete plugin data?"), - tr("Do you really want to delete the selected plugin data?\n" - "This may cause the affected plugins to malfunction."), - QMessageBox::Yes | QMessageBox::Cancel, - QMessageBox::Cancel)) { + if (QMessageBox::Yes + != MessageBox::question(this, + tr("Delete plugin data?"), + tr("Do you really want to delete the selected plugin data?\n" + "This may cause the affected plugins to malfunction."), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel)) { return; } @@ -97,8 +100,8 @@ void EditWidgetProperties::updateModel() m_customDataModel->setHorizontalHeaderLabels({tr("Key"), tr("Value")}); for (const QString& key : m_customData->keys()) { - m_customDataModel->appendRow(QList() << new QStandardItem(key) - << new QStandardItem(m_customData->value(key))); + m_customDataModel->appendRow(QList() + << new QStandardItem(key) << new QStandardItem(m_customData->value(key))); } m_ui->removeCustomDataButton->setEnabled(false); diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp index d033979b8..834425ec1 100644 --- a/src/gui/KeePass1OpenWidget.cpp +++ b/src/gui/KeePass1OpenWidget.cpp @@ -16,6 +16,7 @@ */ #include "KeePass1OpenWidget.h" +#include "ui_DatabaseOpenWidget.h" #include #include @@ -24,7 +25,6 @@ #include "core/Metadata.h" #include "format/KeePass1Reader.h" #include "gui/MessageBox.h" -#include "ui_DatabaseOpenWidget.h" KeePass1OpenWidget::KeePass1OpenWidget(QWidget* parent) : DatabaseOpenWidget(parent) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 1cd700b43..55c261858 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -20,10 +20,10 @@ #include "ui_MainWindow.h" #include +#include #include #include #include -#include #include "config-keepassx.h" @@ -56,8 +56,8 @@ #include #endif -#include "gui/PasswordGeneratorWidget.h" #include "gui/ApplicationSettingsWidget.h" +#include "gui/PasswordGeneratorWidget.h" #include "touchid/TouchID.h" @@ -67,7 +67,8 @@ class BrowserPlugin : public ISettingsPage public: BrowserPlugin(DatabaseTabWidget* tabWidget) { - m_nativeMessagingHost = QSharedPointer(new NativeMessagingHost(tabWidget, browserSettings()->isEnabled())); + m_nativeMessagingHost = + QSharedPointer(new NativeMessagingHost(tabWidget, browserSettings()->isEnabled())); } ~BrowserPlugin() @@ -105,8 +106,8 @@ public: } } - private: - QSharedPointer m_nativeMessagingHost; +private: + QSharedPointer m_nativeMessagingHost; }; #endif @@ -346,11 +347,13 @@ MainWindow::MainWindow() #ifdef Q_OS_MACOS setUnifiedTitleAndToolBarOnMac(true); #endif - + // clang-format off connect(m_ui->tabWidget, SIGNAL(messageGlobal(QString,MessageWidget::MessageType)), this, SLOT(displayGlobalMessage(QString,MessageWidget::MessageType))); + // clang-format on + connect(m_ui->tabWidget, SIGNAL(messageDismissGlobal()), this, SLOT(hideGlobalMessage())); m_screenLockListener = new ScreenLockListener(this); @@ -368,13 +371,15 @@ MainWindow::MainWindow() tr("WARNING: You are using an unstable build of KeePassXC!\n" "There is a high risk of corruption, maintain a backup of your databases.\n" "This version is not meant for production use."), - MessageWidget::Warning, -1); + MessageWidget::Warning, + -1); #elif (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) && QT_VERSION < QT_VERSION_CHECK(5, 6, 0)) if (!config()->get("QtErrorMessageShown", false).toBool()) { m_ui->globalMessageWidget->showMessage( tr("WARNING: Your Qt version may cause KeePassXC to crash with an On-Screen Keyboard!\n" "We recommend you use the AppImage available on our downloads page."), - MessageWidget::Warning, -1); + MessageWidget::Warning, + -1); config()->set("QtErrorMessageShown", true); } #endif @@ -671,10 +676,10 @@ void MainWindow::switchToSettings(bool enabled) void MainWindow::switchToPasswordGen(bool enabled) { if (enabled) { - m_ui->passwordGeneratorWidget->loadSettings(); - m_ui->passwordGeneratorWidget->regeneratePassword(); - m_ui->passwordGeneratorWidget->setStandaloneMode(true); - m_ui->stackedWidget->setCurrentIndex(PasswordGeneratorScreen); + m_ui->passwordGeneratorWidget->loadSettings(); + m_ui->passwordGeneratorWidget->regeneratePassword(); + m_ui->passwordGeneratorWidget->setStandaloneMode(true); + m_ui->stackedWidget->setCurrentIndex(PasswordGeneratorScreen); } else { m_ui->passwordGeneratorWidget->saveSettings(); switchToDatabases(); diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 6ec044f9e..e341eddd4 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -71,8 +71,9 @@ void PasswordEdit::setShowPassword(bool show) emit showPasswordChanged(show); } -bool PasswordEdit::isPasswordVisible() const { - return isEnabled(); +bool PasswordEdit::isPasswordVisible() const +{ + return isEnabled(); } bool PasswordEdit::passwordsEqual() const diff --git a/src/gui/PasswordGeneratorWidget.cpp b/src/gui/PasswordGeneratorWidget.cpp index 048c39542..80ea007aa 100644 --- a/src/gui/PasswordGeneratorWidget.cpp +++ b/src/gui/PasswordGeneratorWidget.cpp @@ -338,10 +338,8 @@ void PasswordGeneratorWidget::selectSimpleMode() m_ui->checkBoxLower->setChecked(m_ui->checkBoxLowerAdv->isChecked()); m_ui->checkBoxNumbers->setChecked(m_ui->checkBoxNumbersAdv->isChecked()); m_ui->checkBoxSpecialChars->setChecked(m_ui->checkBoxBraces->isChecked() | m_ui->checkBoxPunctuation->isChecked() - | m_ui->checkBoxQuotes->isChecked() - | m_ui->checkBoxMath->isChecked() - | m_ui->checkBoxDashes->isChecked() - | m_ui->checkBoxLogograms->isChecked()); + | m_ui->checkBoxQuotes->isChecked() | m_ui->checkBoxMath->isChecked() + | m_ui->checkBoxDashes->isChecked() | m_ui->checkBoxLogograms->isChecked()); m_ui->checkBoxExtASCII->setChecked(m_ui->checkBoxExtASCIIAdv->isChecked()); m_ui->simpleBar->show(); } diff --git a/src/gui/TotpSetupDialog.cpp b/src/gui/TotpSetupDialog.cpp index babeb7840..ebbcbc9fe 100644 --- a/src/gui/TotpSetupDialog.cpp +++ b/src/gui/TotpSetupDialog.cpp @@ -17,9 +17,10 @@ */ #include "TotpSetupDialog.h" -#include "totp/totp.h" #include "ui_TotpSetupDialog.h" +#include "totp/totp.h" + TotpSetupDialog::TotpSetupDialog(QWidget* parent, Entry* entry) : QDialog(parent) , m_ui(new Ui::TotpSetupDialog()) diff --git a/src/gui/csvImport/CsvImportWidget.cpp b/src/gui/csvImport/CsvImportWidget.cpp index 3b623f73d..19b5732bc 100644 --- a/src/gui/csvImport/CsvImportWidget.cpp +++ b/src/gui/csvImport/CsvImportWidget.cpp @@ -40,7 +40,7 @@ CsvImportWidget::CsvImportWidget(QWidget* parent) , m_columnHeader(QStringList() << QObject::tr("Group") << QObject::tr("Title") << QObject::tr("Username") << QObject::tr("Password") << QObject::tr("URL") << QObject::tr("Notes") << QObject::tr("Last Modified") << QObject::tr("Created") - /* << QObject::tr("Future field1") */ ) + /* << QObject::tr("Future field1") */) { m_ui->setupUi(this); @@ -87,9 +87,11 @@ CsvImportWidget::CsvImportWidget(QWidget* parent) m_combos.append(combo); combo->setModel(m_comboModel); #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) - connect(combo, QOverload::of(&QComboBox::currentIndexChanged), [=]{ comboChanged(combo, i); }); + connect(combo, QOverload::of(&QComboBox::currentIndexChanged), [=] { comboChanged(combo, i); }); #else - connect(combo, static_cast(&QComboBox::currentIndexChanged), [=]{ comboChanged(combo, i); }); + connect(combo, static_cast(&QComboBox::currentIndexChanged), [=] { + comboChanged(combo, i); + }); #endif // layout labels and combo fields in column-first order diff --git a/src/gui/csvImport/CsvImportWizard.cpp b/src/gui/csvImport/CsvImportWizard.cpp index 9b255f941..356435ba9 100644 --- a/src/gui/csvImport/CsvImportWizard.cpp +++ b/src/gui/csvImport/CsvImportWizard.cpp @@ -42,7 +42,6 @@ void CsvImportWizard::load(const QString& filename, Database* database) m_parse->load(filename, database); } - void CsvImportWizard::parseFinished(bool accepted) { emit importFinished(accepted); diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp index 75a3fb5ef..b35059ee1 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp +++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp @@ -18,16 +18,17 @@ #include "DatabaseSettingsDialog.h" #include "ui_DatabaseSettingsDialog.h" -#include "DatabaseSettingsWidgetGeneral.h" + #include "DatabaseSettingsWidgetEncryption.h" +#include "DatabaseSettingsWidgetGeneral.h" #include "DatabaseSettingsWidgetMasterKey.h" #ifdef WITH_XC_BROWSER #include "DatabaseSettingsWidgetBrowser.h" #endif #include "core/Config.h" -#include "core/FilePath.h" #include "core/Database.h" +#include "core/FilePath.h" #include "touchid/TouchID.h" DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent) @@ -57,12 +58,13 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent) m_ui->stackedWidget->setCurrentIndex(0); m_securityTabWidget->setCurrentIndex(0); - connect(m_securityTabWidget, SIGNAL(currentChanged(int)), SLOT(pageChanged())); + connect(m_securityTabWidget, SIGNAL(currentChanged(int)), SLOT(pageChanged())); connect(m_ui->categoryList, SIGNAL(categoryChanged(int)), m_ui->stackedWidget, SLOT(setCurrentIndex(int))); connect(m_ui->advancedSettingsToggle, SIGNAL(toggled(bool)), SLOT(toggleAdvancedMode(bool))); #ifdef WITH_XC_BROWSER - m_ui->categoryList->addCategory(tr("Browser Integration"), FilePath::instance()->icon("apps", "internet-web-browser")); + m_ui->categoryList->addCategory(tr("Browser Integration"), + FilePath::instance()->icon("apps", "internet-web-browser")); m_ui->stackedWidget->addWidget(m_browserWidget); #endif diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.h b/src/gui/dbsettings/DatabaseSettingsDialog.h index 41fc508a9..dff8c0e3a 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.h +++ b/src/gui/dbsettings/DatabaseSettingsDialog.h @@ -18,8 +18,8 @@ #ifndef KEEPASSX_DATABASESETTINGSWIDGET_H #define KEEPASSX_DATABASESETTINGSWIDGET_H -#include "gui/DialogyWidget.h" #include "config-keepassx.h" +#include "gui/DialogyWidget.h" #include #include diff --git a/src/gui/dbsettings/DatabaseSettingsWidget.cpp b/src/gui/dbsettings/DatabaseSettingsWidget.cpp index 67b3ef375..7b438cc33 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidget.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidget.cpp @@ -18,8 +18,8 @@ #include "DatabaseSettingsWidget.h" #include "core/Database.h" -#include #include +#include DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* parent) : SettingsWidget(parent) diff --git a/src/gui/dbsettings/DatabaseSettingsWidget.h b/src/gui/dbsettings/DatabaseSettingsWidget.h index 6d58ddeb7..5a6e5c069 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidget.h +++ b/src/gui/dbsettings/DatabaseSettingsWidget.h @@ -48,4 +48,4 @@ protected: QSharedPointer m_db; }; -#endif //KEEPASSXC_DATABASESETTINGSWIDGET_H +#endif // KEEPASSXC_DATABASESETTINGSWIDGET_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp index a91744cdb..9f2714ea3 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp @@ -17,18 +17,21 @@ */ #include "DatabaseSettingsWidgetBrowser.h" +#include "ui_DatabaseSettingsWidgetBrowser.h" + #include + +#include "browser/BrowserSettings.h" #include "core/Clock.h" #include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" #include "core/Metadata.h" #include "gui/MessageBox.h" -#include "browser/BrowserSettings.h" -#include "ui_DatabaseSettingsWidgetBrowser.h" DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent) - : DatabaseSettingsWidget(parent), m_ui(new Ui::DatabaseSettingsWidgetBrowser()) + : DatabaseSettingsWidget(parent) + , m_ui(new Ui::DatabaseSettingsWidgetBrowser()) , m_customData(new CustomData(this)) , m_customDataModel(new QStandardItemModel(this)) , m_browserService(nullptr) @@ -38,8 +41,13 @@ DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent) m_ui->customDataTable->setModel(m_customDataModel); settingsWarning(); - connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + + // clang-format off + connect(m_ui->customDataTable->selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(toggleRemoveButton(QItemSelection))); + // clang-format on + connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedKey())); connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SLOT(convertAttributesToCustomData())); connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SLOT(updateSharedKeyList())); @@ -83,11 +91,13 @@ bool DatabaseSettingsWidgetBrowser::save() void DatabaseSettingsWidgetBrowser::removeSelectedKey() { - if (QMessageBox::Yes != MessageBox::question(this, - tr("Delete the selected key?"), - tr("Do you really want to delete the selected key?\n" - "This may prevent connection to the browser plugin."), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + if (QMessageBox::Yes + != MessageBox::question(this, + tr("Delete the selected key?"), + tr("Do you really want to delete the selected key?\n" + "This may prevent connection to the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel)) { return; } @@ -116,9 +126,8 @@ void DatabaseSettingsWidgetBrowser::updateModel() if (key.startsWith(BrowserService::ASSOCIATE_KEY_PREFIX)) { QString strippedKey = key; strippedKey.remove(BrowserService::ASSOCIATE_KEY_PREFIX); - m_customDataModel->appendRow(QList() - << new QStandardItem(strippedKey) - << new QStandardItem(customData()->value(key))); + m_customDataModel->appendRow(QList() << new QStandardItem(strippedKey) + << new QStandardItem(customData()->value(key))); } } @@ -132,7 +141,8 @@ void DatabaseSettingsWidgetBrowser::settingsWarning() m_ui->removeSharedEncryptionKeys->setEnabled(false); m_ui->removeStoredPermissions->setEnabled(false); m_ui->customDataTable->setEnabled(false); - m_ui->warningWidget->showMessage(tr("Enable Browser Integration to access these settings."), MessageWidget::Warning); + m_ui->warningWidget->showMessage(tr("Enable Browser Integration to access these settings."), + MessageWidget::Warning); m_ui->warningWidget->setCloseButtonVisible(false); m_ui->warningWidget->setAutoHideTimeout(-1); } else { @@ -146,11 +156,13 @@ void DatabaseSettingsWidgetBrowser::settingsWarning() void DatabaseSettingsWidgetBrowser::removeSharedEncryptionKeys() { - if (QMessageBox::Yes != MessageBox::question(this, - tr("Disconnect all browsers"), - tr("Do you really want to disconnect all browsers?\n" - "This may prevent connection to the browser plugin."), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + if (QMessageBox::Yes + != MessageBox::question(this, + tr("Disconnect all browsers"), + tr("Do you really want to disconnect all browsers?\n" + "This may prevent connection to the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel)) { return; } @@ -182,11 +194,13 @@ void DatabaseSettingsWidgetBrowser::removeSharedEncryptionKeys() void DatabaseSettingsWidgetBrowser::removeStoredPermissions() { - if (QMessageBox::Yes != MessageBox::question(this, - tr("Forget all site-specific settings on entries"), - tr("Do you really want forget all site-specific settings on every entry?\n" - "Permissions to access entries will be revoked."), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + if (QMessageBox::Yes + != MessageBox::question(this, + tr("Forget all site-specific settings on entries"), + tr("Do you really want forget all site-specific settings on every entry?\n" + "Permissions to access entries will be revoked."), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel)) { return; } @@ -226,11 +240,14 @@ void DatabaseSettingsWidgetBrowser::removeStoredPermissions() void DatabaseSettingsWidgetBrowser::convertAttributesToCustomData() { - if (QMessageBox::Yes != MessageBox::question(this, - tr("Move KeePassHTTP attributes to custom data"), - tr("Do you really want to move all legacy browser integration data to the latest standard?\n" - "This is necessary to maintain compatibility with the browser plugin."), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + if (QMessageBox::Yes + != MessageBox::question( + this, + tr("Move KeePassHTTP attributes to custom data"), + tr("Do you really want to move all legacy browser integration data to the latest standard?\n" + "This is necessary to maintain compatibility with the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel)) { return; } @@ -242,4 +259,3 @@ void DatabaseSettingsWidgetBrowser::updateSharedKeyList() { updateModel(); } - diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h index 0c56ede57..d9d9885ca 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h @@ -21,18 +21,19 @@ #include "DatabaseSettingsWidget.h" +#include "browser/BrowserService.h" +#include "core/CustomData.h" +#include "gui/DatabaseTabWidget.h" + +#include #include #include #include -#include -#include "core/CustomData.h" -#include "gui/DatabaseTabWidget.h" -#include "browser/BrowserService.h" class Database; namespace Ui { -class DatabaseSettingsWidgetBrowser; + class DatabaseSettingsWidgetBrowser; } class DatabaseSettingsWidgetBrowser : public DatabaseSettingsWidget @@ -45,7 +46,10 @@ public: ~DatabaseSettingsWidgetBrowser() override; CustomData* customData() const; - inline bool hasAdvancedMode() const override { return false; } + inline bool hasAdvancedMode() const override + { + return false; + } public slots: void initialize() override; @@ -75,4 +79,4 @@ private: BrowserService m_browserService; }; -#endif //KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H +#endif // KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp index 63a1ccef8..f6ed2f577 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp @@ -17,13 +17,14 @@ #include "DatabaseSettingsWidgetEncryption.h" #include "ui_DatabaseSettingsWidgetEncryption.h" -#include "core/Database.h" -#include "core/Metadata.h" -#include "core/Global.h" + #include "core/AsyncTask.h" -#include "gui/MessageBox.h" +#include "core/Database.h" +#include "core/Global.h" +#include "core/Metadata.h" #include "crypto/kdf/Argon2Kdf.h" #include "format/KeePass2.h" +#include "gui/MessageBox.h" #include #include @@ -260,7 +261,7 @@ bool DatabaseSettingsWidgetEncryption::save() return false; } } else if ((kdf->uuid() == KeePass2::KDF_AES_KDBX3 || kdf->uuid() == KeePass2::KDF_AES_KDBX4) - && m_ui->transformRoundsSpinBox->value() < 100000) { + && m_ui->transformRoundsSpinBox->value() < 100000) { QMessageBox warning; warning.setIcon(QMessageBox::Warning); warning.setWindowTitle(tr("Number of rounds too low", "Key transformation rounds")); @@ -394,7 +395,7 @@ void DatabaseSettingsWidgetEncryption::updateFormatCompatibility(int index, bool m_ui->compatibilitySelection->blockSignals(block); } - if (retransform) { + if (retransform) { QUuid kdfUuid(m_ui->compatibilitySelection->itemData(index).toByteArray()); auto kdf = KeePass2::uuidToKdf(kdfUuid); m_db->setKdf(kdf); diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h index 8b1e42e13..986a33b6a 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h +++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h @@ -26,10 +26,10 @@ class Database; namespace Ui { -class DatabaseSettingsWidgetEncryption; + class DatabaseSettingsWidgetEncryption; } -class DatabaseSettingsWidgetEncryption: public DatabaseSettingsWidget +class DatabaseSettingsWidgetEncryption : public DatabaseSettingsWidget { Q_OBJECT @@ -38,7 +38,10 @@ public: Q_DISABLE_COPY(DatabaseSettingsWidgetEncryption); ~DatabaseSettingsWidgetEncryption() override; - inline bool hasAdvancedMode() const override { return true; } + inline bool hasAdvancedMode() const override + { + return true; + } void setAdvancedMode(bool advanced) override; public slots: @@ -64,7 +67,11 @@ private slots: void markDirty(); private: - enum FormatSelection { KDBX4, KDBX3 }; + enum FormatSelection + { + KDBX4, + KDBX3 + }; static const char* CD_DECRYPTION_TIME_PREFERENCE_KEY; bool m_isDirty = false; @@ -72,4 +79,4 @@ private: const QScopedPointer m_ui; }; -#endif //KEEPASSXC_DATABASESETTINGSWIDGETENCRYPTION_H +#endif // KEEPASSXC_DATABASESETTINGSWIDGETENCRYPTION_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp index c8d71762d..b96016fdc 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp @@ -17,6 +17,7 @@ #include "DatabaseSettingsWidgetGeneral.h" #include "ui_DatabaseSettingsWidgetGeneral.h" + #include "core/Clock.h" #include "core/Database.h" #include "core/Entry.h" @@ -24,7 +25,8 @@ #include "core/Metadata.h" DatabaseSettingsWidgetGeneral::DatabaseSettingsWidgetGeneral(QWidget* parent) - : DatabaseSettingsWidget(parent), m_ui(new Ui::DatabaseSettingsWidgetGeneral()) + : DatabaseSettingsWidget(parent) + , m_ui(new Ui::DatabaseSettingsWidgetGeneral()) { m_ui->setupUi(this); @@ -53,7 +55,7 @@ void DatabaseSettingsWidgetGeneral::initialize() m_ui->historyMaxItemsSpinBox->setValue(Metadata::DefaultHistoryMaxItems); m_ui->historyMaxItemsCheckBox->setChecked(false); } - int historyMaxSizeMiB = qRound(meta->historyMaxSize()/qreal(1048576)); + int historyMaxSizeMiB = qRound(meta->historyMaxSize() / qreal(1048576)); if (historyMaxSizeMiB > 0) { m_ui->historyMaxSizeSpinBox->setValue(historyMaxSizeMiB); m_ui->historyMaxSizeCheckBox->setChecked(true); @@ -100,7 +102,7 @@ bool DatabaseSettingsWidgetGeneral::save() int historyMaxSize; if (m_ui->historyMaxSizeCheckBox->isChecked()) { - historyMaxSize = m_ui->historyMaxSizeSpinBox->value()*1048576; + historyMaxSize = m_ui->historyMaxSizeSpinBox->value() * 1048576; } else { historyMaxSize = -1; } diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h index 009b6b643..7fe65d9ab 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h +++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h @@ -26,7 +26,7 @@ class Database; namespace Ui { -class DatabaseSettingsWidgetGeneral; + class DatabaseSettingsWidgetGeneral; } class DatabaseSettingsWidgetGeneral : public DatabaseSettingsWidget @@ -38,7 +38,10 @@ public: Q_DISABLE_COPY(DatabaseSettingsWidgetGeneral); ~DatabaseSettingsWidgetGeneral() override; - inline bool hasAdvancedMode() const override { return false; } + inline bool hasAdvancedMode() const override + { + return false; + } public slots: void initialize() override; @@ -51,4 +54,4 @@ protected: const QScopedPointer m_ui; }; -#endif //KEEPASSXC_DATABASESETTINGSWIDGETGENERAL_H +#endif // KEEPASSXC_DATABASESETTINGSWIDGETGENERAL_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp index 3cc37accb..b1f07211e 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp @@ -16,18 +16,19 @@ */ #include "DatabaseSettingsWidgetMasterKey.h" -#include "core/Database.h" -#include "keys/PasswordKey.h" -#include "keys/FileKey.h" -#include "keys/YkChallengeResponseKey.h" -#include "gui/MessageBox.h" -#include "gui/masterkey/PasswordEditWidget.h" -#include "gui/masterkey/KeyFileEditWidget.h" -#include "gui/masterkey/YubiKeyEditWidget.h" -#include -#include +#include "core/Database.h" +#include "gui/MessageBox.h" +#include "gui/masterkey/KeyFileEditWidget.h" +#include "gui/masterkey/PasswordEditWidget.h" +#include "gui/masterkey/YubiKeyEditWidget.h" +#include "keys/FileKey.h" +#include "keys/PasswordKey.h" +#include "keys/YkChallengeResponseKey.h" + #include +#include +#include DatabaseSettingsWidgetMasterKey::DatabaseSettingsWidgetMasterKey(QWidget* parent) : DatabaseSettingsWidget(parent) @@ -82,7 +83,7 @@ void DatabaseSettingsWidgetMasterKey::load(QSharedPointer db) bool isDirty = false; bool hasAdditionalKeys = false; - for (const auto& key: m_db->key()->keys()) { + for (const auto& key : m_db->key()->keys()) { if (key->uuid() == PasswordKey::UUID) { m_passwordEditWidget->setComponentAdded(true); } else if (key->uuid() == FileKey::UUID) { @@ -92,7 +93,7 @@ void DatabaseSettingsWidgetMasterKey::load(QSharedPointer db) } #ifdef WITH_XC_YUBIKEY - for (const auto& key: m_db->key()->challengeResponseKeys()) { + for (const auto& key : m_db->key()->challengeResponseKeys()) { if (key->uuid() == YkChallengeResponseKey::UUID) { m_yubiKeyEditWidget->setComponentAdded(true); hasAdditionalKeys = true; @@ -128,7 +129,7 @@ bool DatabaseSettingsWidgetMasterKey::save() m_isDirty |= (m_yubiKeyEditWidget->visiblePage() == KeyComponentWidget::Page::Edit); #endif - if (m_db->key() && ! m_db->key()->keys().isEmpty() && !m_isDirty) { + if (m_db->key() && !m_db->key()->keys().isEmpty() && !m_isDirty) { // key unchanged return true; } @@ -139,7 +140,7 @@ bool DatabaseSettingsWidgetMasterKey::save() QSharedPointer fileKey; QSharedPointer ykCrKey; - for (const auto& key: m_db->key()->keys()) { + for (const auto& key : m_db->key()->keys()) { if (key->uuid() == PasswordKey::UUID) { passwordKey = key; } else if (key->uuid() == FileKey::UUID) { @@ -147,7 +148,7 @@ bool DatabaseSettingsWidgetMasterKey::save() } } - for (const auto& key: m_db->key()->challengeResponseKeys()) { + for (const auto& key : m_db->key()->challengeResponseKeys()) { if (key->uuid() == YkChallengeResponseKey::UUID) { ykCrKey = key; } @@ -168,18 +169,22 @@ bool DatabaseSettingsWidgetMasterKey::save() #endif if (newKey->keys().isEmpty() && newKey->challengeResponseKeys().isEmpty()) { - MessageBox::critical(this, tr("No encryption key added"), + MessageBox::critical(this, + tr("No encryption key added"), tr("You must add at least one encryption key to secure your database!"), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::Ok, + QMessageBox::Ok); return false; } if (m_passwordEditWidget->visiblePage() == KeyComponentWidget::AddNew) { - auto answer = MessageBox::warning(this, tr("No password set"), + auto answer = MessageBox::warning(this, + tr("No password set"), tr("WARNING! You have not set a password. Using a database without " "a password is strongly discouraged!\n\n" "Are you sure you want to continue without a password?"), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); if (answer != QMessageBox::Yes) { return false; } @@ -210,7 +215,8 @@ void DatabaseSettingsWidgetMasterKey::setAdditionalKeyOptionsVisible(bool show) } bool DatabaseSettingsWidgetMasterKey::addToCompositeKey(KeyComponentWidget* widget, - QSharedPointer& newKey, QSharedPointer& oldKey) + QSharedPointer& newKey, + QSharedPointer& oldKey) { if (widget->visiblePage() == KeyComponentWidget::Edit) { QString error = tr("Unknown error"); @@ -226,7 +232,8 @@ bool DatabaseSettingsWidgetMasterKey::addToCompositeKey(KeyComponentWidget* widg } bool DatabaseSettingsWidgetMasterKey::addToCompositeKey(KeyComponentWidget* widget, - QSharedPointer& newKey, QSharedPointer& oldKey) + QSharedPointer& newKey, + QSharedPointer& oldKey) { if (widget->visiblePage() == KeyComponentWidget::Edit) { QString error = tr("Unknown error"); diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h index 7ab0b085f..1a3d6ed3f 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h +++ b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h @@ -18,8 +18,9 @@ #ifndef KEEPASSXC_DATABASESETTINGSPAGECHANGEMASTERKEY_H #define KEEPASSXC_DATABASESETTINGSPAGECHANGEMASTERKEY_H -#include "config-keepassx.h" #include "DatabaseSettingsWidget.h" +#include "config-keepassx.h" + #include class Database; @@ -32,7 +33,7 @@ class KeyFileEditWidget; class YubiKeyEditWidget; class QPushButton; -class DatabaseSettingsWidgetMasterKey: public DatabaseSettingsWidget +class DatabaseSettingsWidgetMasterKey : public DatabaseSettingsWidget { Q_OBJECT @@ -43,7 +44,10 @@ public: void load(QSharedPointer db) override; - inline bool hasAdvancedMode() const override { return false; } + inline bool hasAdvancedMode() const override + { + return false; + } public slots: void initialize() override; @@ -58,9 +62,12 @@ private slots: private: void setAdditionalKeyOptionsVisible(bool show); + // clang-format off bool addToCompositeKey(KeyComponentWidget* widget, QSharedPointer& newKey, QSharedPointer& oldKey); + // clang-format on + bool addToCompositeKey(KeyComponentWidget* widget, QSharedPointer& newKey, QSharedPointer& oldKey); @@ -75,4 +82,4 @@ private: #endif }; -#endif //KEEPASSXC_DATABASESETTINGSPAGECHANGEMASTERKEY_H +#endif // KEEPASSXC_DATABASESETTINGSPAGECHANGEMASTERKEY_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp index 6805d7b65..0a1dccb5f 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp @@ -17,6 +17,7 @@ #include "DatabaseSettingsWidgetMetaDataSimple.h" #include "ui_DatabaseSettingsWidgetMetaDataSimple.h" + #include "core/Database.h" #include "core/Metadata.h" diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h index bc32c1ee0..c357305d8 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h +++ b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h @@ -26,19 +26,22 @@ class Database; namespace Ui { -class DatabaseSettingsWidgetMetaDataSimple; + class DatabaseSettingsWidgetMetaDataSimple; } class DatabaseSettingWidgetMetaData : public DatabaseSettingsWidget { -Q_OBJECT + Q_OBJECT public: explicit DatabaseSettingWidgetMetaData(QWidget* parent = nullptr); Q_DISABLE_COPY(DatabaseSettingWidgetMetaData); ~DatabaseSettingWidgetMetaData() override; - inline bool hasAdvancedMode() const override { return false; } + inline bool hasAdvancedMode() const override + { + return false; + } public slots: void initialize() override; @@ -52,4 +55,4 @@ private: const QScopedPointer m_ui; }; -#endif //KEEPASSXC_DATABASESETTINGSWIDGEMETADATA_H +#endif // KEEPASSXC_DATABASESETTINGSWIDGEMETADATA_H diff --git a/src/gui/entry/AutoTypeMatchView.cpp b/src/gui/entry/AutoTypeMatchView.cpp index 087f2cc64..9c4e81d92 100644 --- a/src/gui/entry/AutoTypeMatchView.cpp +++ b/src/gui/entry/AutoTypeMatchView.cpp @@ -18,11 +18,11 @@ #include "AutoTypeMatchView.h" +#include "gui/SortFilterHideProxyModel.h" + #include #include -#include "gui/SortFilterHideProxyModel.h" - AutoTypeMatchView::AutoTypeMatchView(QWidget* parent) : QTreeView(parent) , m_model(new AutoTypeMatchModel(this)) @@ -43,8 +43,10 @@ AutoTypeMatchView::AutoTypeMatchView(QWidget* parent) header()->setDefaultSectionSize(150); connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitMatchActivated(QModelIndex))); + // clang-format off connect( selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(matchSelectionChanged())); + // clang-format on } void AutoTypeMatchView::keyPressEvent(QKeyEvent* event) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 75e5294ba..335ac1aa4 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -102,9 +102,12 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) connect(this, SIGNAL(accepted()), SLOT(acceptEntry())); connect(this, SIGNAL(rejected()), SLOT(cancel())); connect(this, SIGNAL(apply()), SLOT(commitEntry())); + // clang-format off connect(m_iconsWidget, SIGNAL(messageEditEntry(QString,MessageWidget::MessageType)), SLOT(showMessage(QString,MessageWidget::MessageType))); + // clang-format on + connect(m_iconsWidget, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); m_mainUi->passwordGenerator->layout()->setContentsMargins(0, 0, 0, 0); @@ -166,6 +169,8 @@ void EditEntryWidget::setupAdvanced() m_attributesModel->setEntryAttributes(m_entryAttributes); m_advancedUi->attributesView->setModel(m_attributesModel); + + // clang-format off connect(m_advancedUi->addAttributeButton, SIGNAL(clicked()), SLOT(insertAttribute())); connect(m_advancedUi->editAttributeButton, SIGNAL(clicked()), SLOT(editCurrentAttribute())); connect(m_advancedUi->removeAttributeButton, SIGNAL(clicked()), SLOT(removeCurrentAttribute())); @@ -176,6 +181,7 @@ void EditEntryWidget::setupAdvanced() SLOT(updateCurrentAttribute())); connect(m_advancedUi->fgColorButton, SIGNAL(clicked()), SLOT(pickColor())); connect(m_advancedUi->bgColorButton, SIGNAL(clicked()), SLOT(pickColor())); + // clang-format on } void EditEntryWidget::setupIcon() @@ -193,26 +199,27 @@ void EditEntryWidget::setupAutoType() m_autoTypeAssocModel->setAutoTypeAssociations(m_autoTypeAssoc); m_autoTypeUi->assocView->setModel(m_autoTypeAssocModel); m_autoTypeUi->assocView->setColumnHidden(1, true); + + // clang-format off connect(m_autoTypeUi->enableButton, SIGNAL(toggled(bool)), SLOT(updateAutoTypeEnabled())); - connect( - m_autoTypeUi->customSequenceButton, SIGNAL(toggled(bool)), m_autoTypeUi->sequenceEdit, SLOT(setEnabled(bool))); - connect(m_autoTypeUi->customWindowSequenceButton, - SIGNAL(toggled(bool)), - m_autoTypeUi->windowSequenceEdit, - SLOT(setEnabled(bool))); + connect(m_autoTypeUi->customSequenceButton, SIGNAL(toggled(bool)), + m_autoTypeUi->sequenceEdit, SLOT(setEnabled(bool))); + connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(toggled(bool)), + m_autoTypeUi->windowSequenceEdit, SLOT(setEnabled(bool))); connect(m_autoTypeUi->assocAddButton, SIGNAL(clicked()), SLOT(insertAutoTypeAssoc())); connect(m_autoTypeUi->assocRemoveButton, SIGNAL(clicked()), SLOT(removeAutoTypeAssoc())); connect(m_autoTypeUi->assocView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(updateAutoTypeEnabled())); - connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(updateAutoTypeEnabled())); connect(m_autoTypeUi->assocView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(loadCurrentAssoc(QModelIndex))); + connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(updateAutoTypeEnabled())); connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc())); connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)), SLOT(applyCurrentAssoc())); connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(toggled(bool)), SLOT(applyCurrentAssoc())); connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)), SLOT(applyCurrentAssoc())); + // clang-format on } void EditEntryWidget::setupProperties() @@ -234,14 +241,17 @@ void EditEntryWidget::setupHistory() m_historyUi->historyView->setModel(m_sortModel); m_historyUi->historyView->setRootIsDecorated(false); + // clang-format off connect(m_historyUi->historyView, SIGNAL(activated(QModelIndex)), SLOT(histEntryActivated(QModelIndex))); connect(m_historyUi->historyView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(updateHistoryButtons(QModelIndex,QModelIndex))); + connect(m_historyUi->showButton, SIGNAL(clicked()), SLOT(showHistoryEntry())); connect(m_historyUi->restoreButton, SIGNAL(clicked()), SLOT(restoreHistoryEntry())); connect(m_historyUi->deleteButton, SIGNAL(clicked()), SLOT(deleteHistoryEntry())); connect(m_historyUi->deleteAllButton, SIGNAL(clicked()), SLOT(deleteAllHistoryEntries())); + // clang-format on } void EditEntryWidget::setupEntryUpdate() @@ -253,7 +263,7 @@ void EditEntryWidget::setupEntryUpdate() connect(m_mainUi->passwordRepeatEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); #ifdef WITH_XC_NETWORKING - connect(m_mainUi->urlEdit, SIGNAL(textChanged(const QString&)), this, SLOT(updateFaviconButtonEnable(const QString&))); + connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString&)), this, SLOT(updateFaviconButtonEnable(QString&))); #endif connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->notesEnabled, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); @@ -280,7 +290,7 @@ void EditEntryWidget::setupEntryUpdate() connect(m_autoTypeUi->windowTitleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)), this, SLOT(setUnsavedChanges())); -// Properties and History tabs don't need extra connections + // Properties and History tabs don't need extra connections #ifdef WITH_XC_SSHAGENT // SSH Agent tab @@ -288,13 +298,12 @@ void EditEntryWidget::setupEntryUpdate() connect(m_sshAgentUi->attachmentRadioButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->externalFileRadioButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->attachmentComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setUnsavedChanges())); - connect( - m_sshAgentUi->attachmentComboBox, SIGNAL(editTextChanged(QString)), this, SLOT(setUnsavedChanges())); + connect(m_sshAgentUi->attachmentComboBox, SIGNAL(editTextChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->externalFileEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->addKeyToAgentCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->removeKeyFromAgentCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); - connect( - m_sshAgentUi->requireUserConfirmationCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); + connect(m_sshAgentUi->requireUserConfirmationCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->lifetimeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_sshAgentUi->lifetimeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setUnsavedChanges())); } @@ -427,11 +436,8 @@ void EditEntryWidget::updateSSHAgentKeyInfo() } if (!key.fingerprint().isEmpty()) { - m_sshAgentUi->fingerprintTextLabel->setText( - key.fingerprint(QCryptographicHash::Md5) + - "\n" + - key.fingerprint(QCryptographicHash::Sha256) - ); + m_sshAgentUi->fingerprintTextLabel->setText(key.fingerprint(QCryptographicHash::Md5) + "\n" + + key.fingerprint(QCryptographicHash::Sha256)); } else { m_sshAgentUi->fingerprintTextLabel->setText(tr("(encrypted)")); } @@ -613,11 +619,8 @@ void EditEntryWidget::decryptPrivateKey() m_sshAgentUi->commentTextLabel->setText(tr("n/a")); } - m_sshAgentUi->fingerprintTextLabel->setText( - key.fingerprint(QCryptographicHash::Md5) + - "\n" + - key.fingerprint(QCryptographicHash::Sha256) - ); + m_sshAgentUi->fingerprintTextLabel->setText(key.fingerprint(QCryptographicHash::Md5) + "\n" + + key.fingerprint(QCryptographicHash::Sha256)); m_sshAgentUi->publicKeyEdit->document()->setPlainText(key.publicKey()); m_sshAgentUi->copyToClipboardButton->setEnabled(true); } diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index b0f5d8c94..9473f76ac 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -19,6 +19,7 @@ #ifndef KEEPASSX_EDITENTRYWIDGET_H #define KEEPASSX_EDITENTRYWIDGET_H +#include #include #include #include @@ -52,7 +53,7 @@ namespace Ui class EditEntryWidgetMain; class EditEntryWidgetHistory; class EditWidget; -} +} // namespace Ui class EditEntryWidget : public EditWidget { diff --git a/src/gui/entry/EntryAttachmentsWidget.cpp b/src/gui/entry/EntryAttachmentsWidget.cpp index 6f42a1994..e7a72c51e 100644 --- a/src/gui/entry/EntryAttachmentsWidget.cpp +++ b/src/gui/entry/EntryAttachmentsWidget.cpp @@ -43,9 +43,12 @@ EntryAttachmentsWidget::EntryAttachmentsWidget(QWidget* parent) connect(this, SIGNAL(readOnlyChanged(bool)), SLOT(updateButtonsEnabled())); connect(m_attachmentsModel, SIGNAL(modelReset()), SLOT(updateButtonsEnabled())); + + // clang-format off connect(m_ui->attachmentsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(updateButtonsEnabled())); + // clang-format on connect(m_ui->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex))); connect(m_ui->saveAttachmentButton, SIGNAL(clicked()), SLOT(saveSelectedAttachments())); diff --git a/src/gui/entry/EntryAttributesModel.cpp b/src/gui/entry/EntryAttributesModel.cpp index 373fcc6c7..bfdb592cd 100644 --- a/src/gui/entry/EntryAttributesModel.cpp +++ b/src/gui/entry/EntryAttributesModel.cpp @@ -46,9 +46,15 @@ void EntryAttributesModel::setEntryAttributes(EntryAttributes* entryAttributes) connect(m_entryAttributes, SIGNAL(added(QString)), SLOT(attributeAdd())); connect(m_entryAttributes, SIGNAL(aboutToBeRemoved(QString)), SLOT(attributeAboutToRemove(QString))); connect(m_entryAttributes, SIGNAL(removed(QString)), SLOT(attributeRemove())); + // clang-format off connect( m_entryAttributes, SIGNAL(aboutToRename(QString,QString)), SLOT(attributeAboutToRename(QString,QString))); + // clang-format on + + // clang-format off connect(m_entryAttributes, SIGNAL(renamed(QString,QString)), SLOT(attributeRename(QString,QString))); + // clang-format on + connect(m_entryAttributes, SIGNAL(aboutToBeReset()), SLOT(aboutToReset())); connect(m_entryAttributes, SIGNAL(reset()), SLOT(reset())); } diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index fa237163e..0a620a687 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -49,10 +49,12 @@ EntryView::EntryView(QWidget* parent) // QAbstractItemView::startDrag() uses this property as the default drag action setDefaultDropAction(Qt::MoveAction); + // clang-format off connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex))); connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(emitEntrySelectionChanged())); connect(m_model, SIGNAL(usernamesHiddenChanged()), SIGNAL(viewStateChanged())); connect(m_model, SIGNAL(passwordsHiddenChanged()), SIGNAL(viewStateChanged())); + // clang-format on m_headerMenu = new QMenu(this); m_headerMenu->setTitle(tr("Customize View")); @@ -93,10 +95,21 @@ EntryView::EntryView(QWidget* parent) header()->setContextMenuPolicy(Qt::CustomContextMenu); connect(header(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(showHeaderMenu(QPoint))); + // clang-format off connect(header(), SIGNAL(sectionCountChanged(int,int)), SIGNAL(viewStateChanged())); + // clang-format on + + // clang-format off connect(header(), SIGNAL(sectionMoved(int,int,int)), SIGNAL(viewStateChanged())); + // clang-format on + + // clang-format off connect(header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(viewStateChanged())); + // clang-format on + + // clang-format off connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), SIGNAL(viewStateChanged())); + // clang-format on resetFixedColumns(); diff --git a/src/gui/group/EditGroupWidget.cpp b/src/gui/group/EditGroupWidget.cpp index 5143f4fc1..ec0b604a4 100644 --- a/src/gui/group/EditGroupWidget.cpp +++ b/src/gui/group/EditGroupWidget.cpp @@ -47,9 +47,12 @@ EditGroupWidget::EditGroupWidget(QWidget* parent) connect(this, SIGNAL(accepted()), SLOT(save())); connect(this, SIGNAL(rejected()), SLOT(cancel())); + // clang-format off connect(m_editGroupWidgetIcons, SIGNAL(messageEditEntry(QString,MessageWidget::MessageType)), SLOT(showMessage(QString,MessageWidget::MessageType))); + // clang-format on + connect(m_editGroupWidgetIcons, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); } diff --git a/src/gui/group/EditGroupWidget.h b/src/gui/group/EditGroupWidget.h index 992af0072..f229e4594 100644 --- a/src/gui/group/EditGroupWidget.h +++ b/src/gui/group/EditGroupWidget.h @@ -31,7 +31,7 @@ namespace Ui { class EditGroupWidgetMain; class EditWidget; -} +} // namespace Ui class EditGroupWidget : public EditWidget { diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp index bb579be03..da84e3d69 100644 --- a/src/gui/group/GroupModel.cpp +++ b/src/gui/group/GroupModel.cpp @@ -39,6 +39,7 @@ void GroupModel::changeDatabase(Database* newDb) m_db = newDb; + // clang-format off connect(m_db, SIGNAL(groupDataChanged(Group*)), SLOT(groupDataChanged(Group*))); connect(m_db, SIGNAL(groupAboutToAdd(Group*,int)), SLOT(groupAboutToAdd(Group*,int))); connect(m_db, SIGNAL(groupAdded()), SLOT(groupAdded())); @@ -46,6 +47,7 @@ void GroupModel::changeDatabase(Database* newDb) connect(m_db, SIGNAL(groupRemoved()), SLOT(groupRemoved())); connect(m_db, SIGNAL(groupAboutToMove(Group*,Group*,int)), SLOT(groupAboutToMove(Group*,Group*,int))); connect(m_db, SIGNAL(groupMoved()), SLOT(groupMoved())); + // clang-format on endResetModel(); } diff --git a/src/gui/group/GroupView.cpp b/src/gui/group/GroupView.cpp index 5d7cba96a..567048631 100644 --- a/src/gui/group/GroupView.cpp +++ b/src/gui/group/GroupView.cpp @@ -34,11 +34,13 @@ GroupView::GroupView(Database* db, QWidget* parent) setHeaderHidden(true); setUniformRowHeights(true); + // clang-format off connect(this, SIGNAL(expanded(QModelIndex)), SLOT(expandedChanged(QModelIndex))); connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(expandedChanged(QModelIndex))); connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(syncExpandedState(QModelIndex,int,int))); connect(m_model, SIGNAL(modelReset()), SLOT(modelReset())); connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(emitGroupChanged())); + // clang-format on modelReset(); diff --git a/src/gui/masterkey/KeyComponentWidget.cpp b/src/gui/masterkey/KeyComponentWidget.cpp index 72367ecbe..7d795aca1 100644 --- a/src/gui/masterkey/KeyComponentWidget.cpp +++ b/src/gui/masterkey/KeyComponentWidget.cpp @@ -17,6 +17,7 @@ #include "KeyComponentWidget.h" #include "ui_KeyComponentWidget.h" + #include #include @@ -124,7 +125,8 @@ void KeyComponentWidget::updateComponentName(const QString& name) m_ui->addButton->setText(tr("Add %1", "Add a key component").arg(name)); m_ui->changeButton->setText(tr("Change %1", "Change a key component").arg(name)); m_ui->removeButton->setText(tr("Remove %1", "Remove a key component").arg(name)); - m_ui->changeOrRemoveLabel->setText(tr("%1 set, click to change or remove", "Change or remove a key component").arg(name)); + m_ui->changeOrRemoveLabel->setText( + tr("%1 set, click to change or remove", "Change or remove a key component").arg(name)); } void KeyComponentWidget::updateComponentDescription(const QString& description) diff --git a/src/gui/masterkey/KeyComponentWidget.h b/src/gui/masterkey/KeyComponentWidget.h index 5d5188d6e..cf2ae4947 100644 --- a/src/gui/masterkey/KeyComponentWidget.h +++ b/src/gui/masterkey/KeyComponentWidget.h @@ -18,12 +18,12 @@ #ifndef KEEPASSXC_KEYCOMPONENTWIDGET_H #define KEEPASSXC_KEYCOMPONENTWIDGET_H -#include #include +#include namespace Ui { -class KeyComponentWidget; + class KeyComponentWidget; } class CompositeKey; class QStackedWidget; @@ -31,12 +31,14 @@ class QStackedWidget; class KeyComponentWidget : public QWidget { Q_OBJECT + // clang-format off Q_PROPERTY(QString componentName READ m_componentName READ componentName WRITE setComponentName NOTIFY nameChanged) Q_PROPERTY(QString componentDescription READ m_componentDescription READ componentDescription WRITE setComponentDescription NOTIFY descriptionChanged) Q_PROPERTY(bool componentAdded READ m_isComponentAdded READ componentAdded WRITE setComponentAdded NOTIFY componentAddChanged) + // clang-format on public: enum Page @@ -127,4 +129,4 @@ private: const QScopedPointer m_ui; }; -#endif //KEEPASSXC_KEYCOMPONENTWIDGET_H +#endif // KEEPASSXC_KEYCOMPONENTWIDGET_H diff --git a/src/gui/masterkey/KeyFileEditWidget.cpp b/src/gui/masterkey/KeyFileEditWidget.cpp index c694e2c5a..ff56f2cb4 100644 --- a/src/gui/masterkey/KeyFileEditWidget.cpp +++ b/src/gui/masterkey/KeyFileEditWidget.cpp @@ -17,8 +17,9 @@ #include "KeyFileEditWidget.h" #include "ui_KeyFileEditWidget.h" -#include "gui/MainWindow.h" + #include "gui/FileDialog.h" +#include "gui/MainWindow.h" #include "gui/MessageBox.h" #include "keys/CompositeKey.h" #include "keys/FileKey.h" @@ -100,8 +101,10 @@ void KeyFileEditWidget::createKeyFile() QString errorMsg; bool created = FileKey::create(fileName, &errorMsg); if (!created) { - MessageBox::critical(getMainWindow(), tr("Error creating key file"), - tr("Unable to create key file: %1").arg(errorMsg), QMessageBox::Button::Ok); + MessageBox::critical(getMainWindow(), + tr("Error creating key file"), + tr("Unable to create key file: %1").arg(errorMsg), + QMessageBox::Button::Ok); } else { m_compUi->keyFileCombo->setEditText(fileName); } diff --git a/src/gui/masterkey/KeyFileEditWidget.h b/src/gui/masterkey/KeyFileEditWidget.h index aa6c71a58..76cb31fa4 100644 --- a/src/gui/masterkey/KeyFileEditWidget.h +++ b/src/gui/masterkey/KeyFileEditWidget.h @@ -23,12 +23,12 @@ namespace Ui { -class KeyFileEditWidget; + class KeyFileEditWidget; } class KeyFileEditWidget : public KeyComponentWidget { -Q_OBJECT + Q_OBJECT public: explicit KeyFileEditWidget(QWidget* parent = nullptr); @@ -51,4 +51,4 @@ private: QPointer m_compEditWidget; }; -#endif //KEEPASSXC_KEYFILEEDITWIDGET_H +#endif // KEEPASSXC_KEYFILEEDITWIDGET_H diff --git a/src/gui/masterkey/PasswordEditWidget.cpp b/src/gui/masterkey/PasswordEditWidget.cpp index 4006a0b1e..d9d293c43 100644 --- a/src/gui/masterkey/PasswordEditWidget.cpp +++ b/src/gui/masterkey/PasswordEditWidget.cpp @@ -17,10 +17,11 @@ #include "PasswordEditWidget.h" #include "ui_PasswordEditWidget.h" + #include "core/FilePath.h" #include "gui/PasswordGeneratorWidget.h" -#include "keys/PasswordKey.h" #include "keys/CompositeKey.h" +#include "keys/PasswordKey.h" #include @@ -67,7 +68,10 @@ QWidget* PasswordEditWidget::componentEditWidget() m_compUi->passwordGeneratorButton->setIcon(filePath()->icon("actions", "password-generator", false)); m_compUi->repeatPasswordEdit->enableVerifyMode(m_compUi->enterPasswordEdit); - connect(m_compUi->togglePasswordButton, SIGNAL(toggled(bool)), m_compUi->enterPasswordEdit, SLOT(setShowPassword(bool))); + connect(m_compUi->togglePasswordButton, + SIGNAL(toggled(bool)), + m_compUi->enterPasswordEdit, + SLOT(setShowPassword(bool))); connect(m_compUi->passwordGeneratorButton, SIGNAL(clicked(bool)), SLOT(showPasswordGenerator())); return m_compEditWidget; diff --git a/src/gui/masterkey/PasswordEditWidget.h b/src/gui/masterkey/PasswordEditWidget.h index ac10c5122..eefe8855e 100644 --- a/src/gui/masterkey/PasswordEditWidget.h +++ b/src/gui/masterkey/PasswordEditWidget.h @@ -23,7 +23,7 @@ namespace Ui { -class PasswordEditWidget; + class PasswordEditWidget; } class PasswordEditWidget : public KeyComponentWidget @@ -53,4 +53,4 @@ private: QPointer m_compEditWidget; }; -#endif //KEEPASSXC_PASSWORDEDITWIDGET_H +#endif // KEEPASSXC_PASSWORDEDITWIDGET_H diff --git a/src/gui/masterkey/YubiKeyEditWidget.cpp b/src/gui/masterkey/YubiKeyEditWidget.cpp index 27e0a4cf0..96b06833d 100644 --- a/src/gui/masterkey/YubiKeyEditWidget.cpp +++ b/src/gui/masterkey/YubiKeyEditWidget.cpp @@ -17,11 +17,12 @@ #include "YubiKeyEditWidget.h" #include "ui_YubiKeyEditWidget.h" -#include "gui/MessageBox.h" + +#include "config-keepassx.h" #include "gui/MainWindow.h" +#include "gui/MessageBox.h" #include "keys/CompositeKey.h" #include "keys/YkChallengeResponseKey.h" -#include "config-keepassx.h" #include @@ -30,10 +31,11 @@ YubiKeyEditWidget::YubiKeyEditWidget(QWidget* parent) , m_compUi(new Ui::YubiKeyEditWidget()) { setComponentName(tr("YubiKey Challenge-Response")); - setComponentDescription(tr("

If you own a YubiKey, you can use it " - "for additional security.

The YubiKey requires one of its slots to be programmed as " - "" - "HMAC-SHA1 Challenge-Response.

")); + setComponentDescription( + tr("

If you own a YubiKey, you can use it " + "for additional security.

The YubiKey requires one of its slots to be programmed as " + "" + "HMAC-SHA1 Challenge-Response.

")); } YubiKeyEditWidget::~YubiKeyEditWidget() @@ -75,7 +77,10 @@ QWidget* YubiKeyEditWidget::componentEditWidget() #ifdef WITH_XC_YUBIKEY connect(m_compUi->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); + // clang-format off connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); + // clang-format on + connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); pollYubikey(); diff --git a/src/gui/masterkey/YubiKeyEditWidget.h b/src/gui/masterkey/YubiKeyEditWidget.h index 82fc8b35a..8d7498034 100644 --- a/src/gui/masterkey/YubiKeyEditWidget.h +++ b/src/gui/masterkey/YubiKeyEditWidget.h @@ -23,7 +23,7 @@ namespace Ui { -class YubiKeyEditWidget; + class YubiKeyEditWidget; } class YkChallengeResponseKey; @@ -57,4 +57,4 @@ private: bool m_isDetected = false; }; -#endif //KEEPASSXC_YUBIKEYEDITWIDGET_H +#endif // KEEPASSXC_YUBIKEYEDITWIDGET_H diff --git a/src/gui/settings/SettingsWidget.h b/src/gui/settings/SettingsWidget.h index 4630a776e..8c55c5221 100644 --- a/src/gui/settings/SettingsWidget.h +++ b/src/gui/settings/SettingsWidget.h @@ -62,7 +62,7 @@ public slots: /** * Discard settings. */ - virtual void discard() {}; + virtual void discard(){}; signals: void editFinished(bool saved); @@ -72,4 +72,4 @@ private: bool m_advancedMode = false; }; -#endif //KEEPASSXC_SETTINGSWIDGET_H +#endif // KEEPASSXC_SETTINGSWIDGET_H diff --git a/src/gui/wizard/NewDatabaseWizard.cpp b/src/gui/wizard/NewDatabaseWizard.cpp index 004d92f31..be5277ac5 100644 --- a/src/gui/wizard/NewDatabaseWizard.cpp +++ b/src/gui/wizard/NewDatabaseWizard.cpp @@ -16,14 +16,14 @@ */ #include "NewDatabaseWizard.h" -#include "NewDatabaseWizardPageMetaData.h" #include "NewDatabaseWizardPageEncryption.h" #include "NewDatabaseWizardPageMasterKey.h" +#include "NewDatabaseWizardPageMetaData.h" -#include "core/Global.h" #include "core/Database.h" -#include "core/Group.h" #include "core/FilePath.h" +#include "core/Global.h" +#include "core/Group.h" #include "format/KeePass2.h" #include @@ -35,11 +35,13 @@ NewDatabaseWizard::NewDatabaseWizard(QWidget* parent) setWizardStyle(QWizard::MacStyle); setOption(QWizard::WizardOption::HaveHelpButton, false); + // clang-format off m_pages << new NewDatabaseWizardPageMetaData() << new NewDatabaseWizardPageEncryption() << new NewDatabaseWizardPageMasterKey(); + // clang-format on - for (const auto& page: asConst(m_pages)) { + for (const auto& page : asConst(m_pages)) { addPage(page); } diff --git a/src/gui/wizard/NewDatabaseWizard.h b/src/gui/wizard/NewDatabaseWizard.h index 802dc061e..fd18f7b22 100644 --- a/src/gui/wizard/NewDatabaseWizard.h +++ b/src/gui/wizard/NewDatabaseWizard.h @@ -30,7 +30,7 @@ class NewDatabaseWizardPage; */ class NewDatabaseWizard : public QWizard { -Q_OBJECT + Q_OBJECT public: explicit NewDatabaseWizard(QWidget* parent = nullptr); @@ -47,4 +47,4 @@ private: QList> m_pages; }; -#endif //KEEPASSXC_NEWDATABASEWIZARD_H +#endif // KEEPASSXC_NEWDATABASEWIZARD_H diff --git a/src/gui/wizard/NewDatabaseWizardPage.cpp b/src/gui/wizard/NewDatabaseWizardPage.cpp index b49b7b384..5b64a6b3d 100644 --- a/src/gui/wizard/NewDatabaseWizardPage.cpp +++ b/src/gui/wizard/NewDatabaseWizardPage.cpp @@ -19,6 +19,7 @@ #include "NewDatabaseWizardPage.h" #include "ui_NewDatabaseWizardPage.h" + #include "core/Database.h" #include "gui/dbsettings/DatabaseSettingsWidget.h" diff --git a/src/gui/wizard/NewDatabaseWizardPage.h b/src/gui/wizard/NewDatabaseWizardPage.h index 785527e23..ea5158a00 100644 --- a/src/gui/wizard/NewDatabaseWizardPage.h +++ b/src/gui/wizard/NewDatabaseWizardPage.h @@ -26,7 +26,7 @@ class Database; class DatabaseSettingsWidget; namespace Ui { -class NewDatabaseWizardPage; + class NewDatabaseWizardPage; } /** @@ -34,7 +34,7 @@ class NewDatabaseWizardPage; */ class NewDatabaseWizardPage : public QWizardPage { -Q_OBJECT + Q_OBJECT public: explicit NewDatabaseWizardPage(QWidget* parent = nullptr); @@ -58,4 +58,4 @@ protected: const QScopedPointer m_ui; }; -#endif //KEEPASSXC_NEWDATABASEWIZARDPAGE_H +#endif // KEEPASSXC_NEWDATABASEWIZARDPAGE_H diff --git a/src/gui/wizard/NewDatabaseWizardPageEncryption.h b/src/gui/wizard/NewDatabaseWizardPageEncryption.h index c10e84dba..af22851ff 100644 --- a/src/gui/wizard/NewDatabaseWizardPageEncryption.h +++ b/src/gui/wizard/NewDatabaseWizardPageEncryption.h @@ -22,7 +22,7 @@ class NewDatabaseWizardPageEncryption : public NewDatabaseWizardPage { -Q_OBJECT + Q_OBJECT public: explicit NewDatabaseWizardPageEncryption(QWidget* parent = nullptr); @@ -30,4 +30,4 @@ public: ~NewDatabaseWizardPageEncryption() override; }; -#endif //KEEPASSXC_NEWDATABASEWIZARDPAGEENCRYPTION_H +#endif // KEEPASSXC_NEWDATABASEWIZARDPAGEENCRYPTION_H diff --git a/src/gui/wizard/NewDatabaseWizardPageMasterKey.h b/src/gui/wizard/NewDatabaseWizardPageMasterKey.h index c6fa53cea..3b5072846 100644 --- a/src/gui/wizard/NewDatabaseWizardPageMasterKey.h +++ b/src/gui/wizard/NewDatabaseWizardPageMasterKey.h @@ -22,7 +22,7 @@ class NewDatabaseWizardPageMasterKey : public NewDatabaseWizardPage { -Q_OBJECT + Q_OBJECT public: explicit NewDatabaseWizardPageMasterKey(QWidget* parent = nullptr); @@ -33,4 +33,4 @@ private slots: void updateWindowSize(); }; -#endif //KEEPASSXC_NEWDATABASEWIZARDPAGEMASTERKEY_H +#endif // KEEPASSXC_NEWDATABASEWIZARDPAGEMASTERKEY_H diff --git a/src/gui/wizard/NewDatabaseWizardPageMetaData.h b/src/gui/wizard/NewDatabaseWizardPageMetaData.h index 44e8f1941..01b6fc47c 100644 --- a/src/gui/wizard/NewDatabaseWizardPageMetaData.h +++ b/src/gui/wizard/NewDatabaseWizardPageMetaData.h @@ -27,7 +27,7 @@ class Database; class NewDatabaseWizardPageMetaData : public NewDatabaseWizardPage { -Q_OBJECT + Q_OBJECT public: explicit NewDatabaseWizardPageMetaData(QWidget* parent = nullptr); @@ -35,4 +35,4 @@ public: ~NewDatabaseWizardPageMetaData() override; }; -#endif //KEEPASSXC_NEWDATABASEWIZARDPAGEMETADATA_H +#endif // KEEPASSXC_NEWDATABASEWIZARDPAGEMETADATA_H diff --git a/src/keys/ChallengeResponseKey.h b/src/keys/ChallengeResponseKey.h index 2f7e7f4ca..8d1fa5774 100644 --- a/src/keys/ChallengeResponseKey.h +++ b/src/keys/ChallengeResponseKey.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2014 Kyle Manna -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2014 Kyle Manna + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_CHALLENGE_RESPONSE_KEY_H #define KEEPASSX_CHALLENGE_RESPONSE_KEY_H @@ -25,9 +25,14 @@ class ChallengeResponseKey { public: - explicit ChallengeResponseKey(const QUuid& uuid) : m_uuid(uuid) {} + explicit ChallengeResponseKey(const QUuid& uuid) + : m_uuid(uuid) + { + } Q_DISABLE_COPY(ChallengeResponseKey); - virtual ~ChallengeResponseKey() {} + virtual ~ChallengeResponseKey() + { + } virtual QByteArray rawKey() const = 0; virtual bool challenge(const QByteArray& challenge) = 0; virtual QUuid uuid() const diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index 10e86318b..3eb4691cd 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2018 KeePassXC Team -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "CompositeKey.h" #include diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h index f32f3a1a1..c0c77f2f4 100644 --- a/src/keys/CompositeKey.h +++ b/src/keys/CompositeKey.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2018 KeePassXC Team -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_COMPOSITEKEY_H #define KEEPASSX_COMPOSITEKEY_H @@ -45,7 +45,7 @@ public: void addKey(const QSharedPointer& key); const QList>& keys() const; - void addChallengeResponseKey(const QSharedPointer& key);\ + void addChallengeResponseKey(const QSharedPointer& key); const QList>& challengeResponseKeys() const; private: diff --git a/src/keys/FileKey.cpp b/src/keys/FileKey.cpp index 6751b7877..9d1e8f50f 100644 --- a/src/keys/FileKey.cpp +++ b/src/keys/FileKey.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 KeePassXC Team -* Copyright (C) 2011 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2011 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "FileKey.h" diff --git a/src/keys/FileKey.h b/src/keys/FileKey.h index 295fe5565..d7486467b 100644 --- a/src/keys/FileKey.h +++ b/src/keys/FileKey.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 KeePassXC Team -* Copyright (C) 2011 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * Copyright (C) 2011 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_FILEKEY_H #define KEEPASSX_FILEKEY_H diff --git a/src/keys/Key.h b/src/keys/Key.h index db7864ec3..985f383d2 100644 --- a/src/keys/Key.h +++ b/src/keys/Key.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2018 KeePassXC Team -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_KEY_H #define KEEPASSX_KEY_H @@ -25,7 +25,8 @@ class Key { public: - explicit Key(const QUuid& uuid) : m_uuid(uuid) {}; + explicit Key(const QUuid& uuid) + : m_uuid(uuid){}; Q_DISABLE_COPY(Key); virtual ~Key() = default; virtual QByteArray rawKey() const = 0; diff --git a/src/keys/PasswordKey.cpp b/src/keys/PasswordKey.cpp index 9fecc7637..35ecb9989 100644 --- a/src/keys/PasswordKey.cpp +++ b/src/keys/PasswordKey.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "PasswordKey.h" diff --git a/src/keys/PasswordKey.h b/src/keys/PasswordKey.h index 46c47e6d3..68ab79895 100644 --- a/src/keys/PasswordKey.h +++ b/src/keys/PasswordKey.h @@ -1,25 +1,25 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_PASSWORDKEY_H #define KEEPASSX_PASSWORDKEY_H -#include #include +#include #include "keys/Key.h" @@ -34,6 +34,7 @@ public: void setPassword(const QString& password); static QSharedPointer fromRawKey(const QByteArray& rawKey); + private: QByteArray m_key; }; diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 830e0c0e0..ee413aeac 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -1,20 +1,21 @@ /* -* Copyright (C) 2014 Kyle Manna -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2014 Kyle Manna + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "keys/YkChallengeResponseKey.h" #include "keys/drivers/YubiKey.h" diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h index fccd90811..b8467e7a6 100644 --- a/src/keys/YkChallengeResponseKey.h +++ b/src/keys/YkChallengeResponseKey.h @@ -13,7 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ #ifndef KEEPASSX_YK_CHALLENGERESPONSEKEY_H #define KEEPASSX_YK_CHALLENGERESPONSEKEY_H diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index bd09fb9f6..6a9843b44 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2014 Kyle Manna -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2014 Kyle Manna + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index 14cb43394..420e650b5 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -1,20 +1,20 @@ /* -* Copyright (C) 2014 Kyle Manna -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2014 Kyle Manna + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_YUBIKEY_H #define KEEPASSX_YUBIKEY_H diff --git a/src/keys/drivers/YubiKeyStub.cpp b/src/keys/drivers/YubiKeyStub.cpp index 3cff04965..f0647ab8a 100644 --- a/src/keys/drivers/YubiKeyStub.cpp +++ b/src/keys/drivers/YubiKeyStub.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2014 Kyle Manna -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2014 Kyle Manna + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include diff --git a/src/main.cpp b/src/main.cpp index c811fe62c..81192f977 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,19 +16,19 @@ * along with this program. If not, see . */ +#include #include #include -#include +#include "cli/Utils.h" #include "config-keepassx.h" #include "core/Bootstrap.h" -#include "core/Tools.h" #include "core/Config.h" +#include "core/Tools.h" #include "crypto/Crypto.h" #include "gui/Application.h" #include "gui/MainWindow.h" #include "gui/MessageBox.h" -#include "cli/Utils.h" #if defined(WITH_ASAN) && defined(WITH_LSAN) #include @@ -58,19 +58,24 @@ int main(int argc, char** argv) Bootstrap::bootstrapApplication(); QCommandLineParser parser; - parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC - cross-platform password manager")); - parser.addPositionalArgument("filename", - QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), "[filename(s)]"); + parser.setApplicationDescription( + QCoreApplication::translate("main", "KeePassXC - cross-platform password manager")); + parser.addPositionalArgument( + "filename", + QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), + "[filename(s)]"); QCommandLineOption configOption( "config", QCoreApplication::translate("main", "path to a custom config file"), "config"); QCommandLineOption keyfileOption( "keyfile", QCoreApplication::translate("main", "key file of the database"), "keyfile"); QCommandLineOption pwstdinOption("pw-stdin", - QCoreApplication::translate("main", "read password of the database from stdin")); + QCoreApplication::translate("main", "read password of the database from stdin")); // This is needed under Windows where clients send --parent-window parameter with Native Messaging connect method - QCommandLineOption parentWindowOption( - QStringList() << "pw" << "parent-window", QCoreApplication::translate("main", "Parent window handle"), "handle"); + QCommandLineOption parentWindowOption(QStringList() << "pw" + << "parent-window", + QCoreApplication::translate("main", "Parent window handle"), + "handle"); QCommandLineOption helpOption = parser.addHelpOption(); QCommandLineOption versionOption = parser.addVersionOption(); @@ -80,19 +85,21 @@ int main(int argc, char** argv) parser.addOption(parentWindowOption); parser.process(app); - + // Don't try and do anything with the application if we're only showing the help / version if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } - + const QStringList fileNames = parser.positionalArguments(); if (app.isAlreadyRunning()) { if (!fileNames.isEmpty()) { app.sendFileNamesToRunningInstance(fileNames); } - qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.").toUtf8().constData(); + qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.") + .toUtf8() + .constData(); return 0; } diff --git a/src/proxy/NativeMessagingHost.cpp b/src/proxy/NativeMessagingHost.cpp index 0f9d82eaa..60f5d79ed 100644 --- a/src/proxy/NativeMessagingHost.cpp +++ b/src/proxy/NativeMessagingHost.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "NativeMessagingHost.h" #include @@ -22,12 +22,13 @@ #include #endif -NativeMessagingHost::NativeMessagingHost() : NativeMessagingBase(true) +NativeMessagingHost::NativeMessagingHost() + : NativeMessagingBase(true) { m_localSocket = new QLocalSocket(); m_localSocket->connectToServer(getLocalServerPath()); m_localSocket->setReadBufferSize(NATIVE_MSG_MAX_LENGTH); - + int socketDesc = m_localSocket->socketDescriptor(); if (socketDesc) { int max = NATIVE_MSG_MAX_LENGTH; @@ -101,7 +102,7 @@ bool NativeMessagingHost::readStdIn(const quint32 length) m_localSocket->write(arr.constData(), arr.length()); m_localSocket->flush(); } - + return true; } diff --git a/src/proxy/NativeMessagingHost.h b/src/proxy/NativeMessagingHost.h index 67fd5e531..083e12d48 100644 --- a/src/proxy/NativeMessagingHost.h +++ b/src/proxy/NativeMessagingHost.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef NATIVEMESSAGINGHOST_H #define NATIVEMESSAGINGHOST_H diff --git a/src/proxy/keepassxc-proxy.cpp b/src/proxy/keepassxc-proxy.cpp index d75f841c0..0d0fbfb23 100644 --- a/src/proxy/keepassxc-proxy.cpp +++ b/src/proxy/keepassxc-proxy.cpp @@ -1,20 +1,20 @@ /* -* Copyright (C) 2017 Sami Vänttinen -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 Sami Vänttinen + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "NativeMessagingHost.h" #include diff --git a/src/sshagent/ASN1Key.cpp b/src/sshagent/ASN1Key.cpp index dc6da2adc..7d71d81a1 100644 --- a/src/sshagent/ASN1Key.cpp +++ b/src/sshagent/ASN1Key.cpp @@ -112,7 +112,7 @@ namespace return QByteArray::fromHex(QString(iqmp_hex).toLatin1()); } -} +} // namespace bool ASN1Key::parseDSA(QByteArray& ba, OpenSSHKey& key) { diff --git a/src/sshagent/ASN1Key.h b/src/sshagent/ASN1Key.h index 59f8d4e81..5e235fb0d 100644 --- a/src/sshagent/ASN1Key.h +++ b/src/sshagent/ASN1Key.h @@ -26,6 +26,6 @@ namespace ASN1Key { bool parseDSA(QByteArray& ba, OpenSSHKey& key); bool parseRSA(QByteArray& ba, OpenSSHKey& key); -} +} // namespace ASN1Key #endif // ASN1KEY_H diff --git a/src/sshagent/AgentSettingsPage.h b/src/sshagent/AgentSettingsPage.h index 09d898048..015dfb9ac 100644 --- a/src/sshagent/AgentSettingsPage.h +++ b/src/sshagent/AgentSettingsPage.h @@ -19,8 +19,8 @@ #ifndef AGENTSETTINGSPAGE_H #define AGENTSETTINGSPAGE_H -#include "gui/DatabaseTabWidget.h" #include "gui/ApplicationSettingsWidget.h" +#include "gui/DatabaseTabWidget.h" class AgentSettingsPage : public ISettingsPage { diff --git a/src/sshagent/AgentSettingsWidget.cpp b/src/sshagent/AgentSettingsWidget.cpp index 10a09f9da..6d69e4b61 100644 --- a/src/sshagent/AgentSettingsWidget.cpp +++ b/src/sshagent/AgentSettingsWidget.cpp @@ -17,9 +17,10 @@ */ #include "AgentSettingsWidget.h" -#include "core/Config.h" #include "ui_AgentSettingsWidget.h" +#include "core/Config.h" + AgentSettingsWidget::AgentSettingsWidget(QWidget* parent) : QWidget(parent) , m_ui(new Ui::AgentSettingsWidget()) diff --git a/src/sshagent/KeeAgentSettings.cpp b/src/sshagent/KeeAgentSettings.cpp index ac6a381bf..8c2278005 100644 --- a/src/sshagent/KeeAgentSettings.cpp +++ b/src/sshagent/KeeAgentSettings.cpp @@ -34,6 +34,7 @@ KeeAgentSettings::KeeAgentSettings() bool KeeAgentSettings::operator==(KeeAgentSettings& other) { + // clang-format off return (m_allowUseOfSshKey == other.m_allowUseOfSshKey && m_addAtDatabaseOpen == other.m_addAtDatabaseOpen && m_removeAtDatabaseClose == other.m_removeAtDatabaseClose && m_useConfirmConstraintWhenAdding == other.m_useConfirmConstraintWhenAdding @@ -43,6 +44,7 @@ bool KeeAgentSettings::operator==(KeeAgentSettings& other) && m_attachmentName == other.m_attachmentName && m_saveAttachmentToTempFile == other.m_saveAttachmentToTempFile && m_fileName == other.m_fileName); + // clang-format on } bool KeeAgentSettings::operator!=(KeeAgentSettings& other) diff --git a/src/sshagent/OpenSSHKey.cpp b/src/sshagent/OpenSSHKey.cpp index 9d1301a05..8abbee367 100644 --- a/src/sshagent/OpenSSHKey.cpp +++ b/src/sshagent/OpenSSHKey.cpp @@ -390,7 +390,7 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase) hash.addData(m_cipherIV.data(), 8); mdBuf = hash.result(); keyData.append(mdBuf); - } while(keyData.size() < cipher->keySize()); + } while (keyData.size() < cipher->keySize()); if (keyData.size() > cipher->keySize()) { // If our key size isn't a multiple of 16 (e.g. AES-192 or something), diff --git a/src/streams/HashedBlockStream.cpp b/src/streams/HashedBlockStream.cpp index 753bdecc6..879960223 100644 --- a/src/streams/HashedBlockStream.cpp +++ b/src/streams/HashedBlockStream.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "HashedBlockStream.h" diff --git a/src/streams/HashedBlockStream.h b/src/streams/HashedBlockStream.h index 60a15a8c9..e107c2cce 100644 --- a/src/streams/HashedBlockStream.h +++ b/src/streams/HashedBlockStream.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_HASHEDBLOCKSTREAM_H #define KEEPASSX_HASHEDBLOCKSTREAM_H diff --git a/src/streams/HmacBlockStream.cpp b/src/streams/HmacBlockStream.cpp index 30c7bac5e..ccfefcba7 100644 --- a/src/streams/HmacBlockStream.cpp +++ b/src/streams/HmacBlockStream.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "HmacBlockStream.h" diff --git a/src/streams/HmacBlockStream.h b/src/streams/HmacBlockStream.h index a2ad062e3..18f5e5301 100644 --- a/src/streams/HmacBlockStream.h +++ b/src/streams/HmacBlockStream.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2017 KeePassXC Team -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_HMACBLOCKSTREAM_H #define KEEPASSX_HMACBLOCKSTREAM_H diff --git a/src/streams/LayeredStream.cpp b/src/streams/LayeredStream.cpp index 9b1545850..af62dae95 100644 --- a/src/streams/LayeredStream.cpp +++ b/src/streams/LayeredStream.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "LayeredStream.h" diff --git a/src/streams/LayeredStream.h b/src/streams/LayeredStream.h index 4ca7aba9a..0e1156d70 100644 --- a/src/streams/LayeredStream.h +++ b/src/streams/LayeredStream.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2010 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2010 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_LAYEREDSTREAM_H #define KEEPASSX_LAYEREDSTREAM_H diff --git a/src/streams/StoreDataStream.cpp b/src/streams/StoreDataStream.cpp index 18d7db980..428592e50 100644 --- a/src/streams/StoreDataStream.cpp +++ b/src/streams/StoreDataStream.cpp @@ -1,19 +1,19 @@ /* -* Copyright (C) 2012 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2012 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "StoreDataStream.h" diff --git a/src/streams/StoreDataStream.h b/src/streams/StoreDataStream.h index 9f39d6a7c..e08f46c61 100644 --- a/src/streams/StoreDataStream.h +++ b/src/streams/StoreDataStream.h @@ -1,19 +1,19 @@ /* -* Copyright (C) 2012 Felix Geyer -* -* 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 -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2012 Felix Geyer + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #ifndef KEEPASSX_STOREDATASTREAM_H #define KEEPASSX_STOREDATASTREAM_H diff --git a/src/totp/totp.cpp b/src/totp/totp.cpp index f1146441a..fef8e0b7e 100644 --- a/src/totp/totp.cpp +++ b/src/totp/totp.cpp @@ -30,7 +30,7 @@ #include #include -static QList encoders { +static QList encoders{ {"", "", "0123456789", Totp::DEFAULT_DIGITS, Totp::DEFAULT_STEP, false}, {"steam", Totp::STEAM_SHORTNAME, "23456789BCDFGHJKMNPQRTVWXY", Totp::STEAM_DIGITS, Totp::DEFAULT_STEP, true}, }; @@ -88,16 +88,18 @@ QSharedPointer Totp::parseSettings(const QString& rawSettings, c return settings; } -QSharedPointer Totp::createSettings(const QString& key, const uint digits, const uint step, - const QString& encoderShortName) +QSharedPointer +Totp::createSettings(const QString& key, const uint digits, const uint step, const QString& encoderShortName) { bool isCustom = digits != DEFAULT_DIGITS || step != DEFAULT_STEP; - return QSharedPointer(new Totp::Settings { - getEncoderByShortName(encoderShortName), key, false, isCustom, digits, step - }); + return QSharedPointer( + new Totp::Settings{getEncoderByShortName(encoderShortName), key, false, isCustom, digits, step}); } -QString Totp::writeSettings(const QSharedPointer& settings, const QString& title, const QString& username, bool forceOtp) +QString Totp::writeSettings(const QSharedPointer& settings, + const QString& title, + const QString& username, + bool forceOtp) { if (settings.isNull()) { return {}; @@ -106,11 +108,11 @@ QString Totp::writeSettings(const QSharedPointer& settings, cons // OTP Url output if (settings->otpUrl || forceOtp) { auto urlstring = QString("otpauth://totp/%1:%2?secret=%3&period=%4&digits=%5&issuer=%1") - .arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title)), - username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username)), - QString(Base32::sanitizeInput(settings->key.toLatin1()))) - .arg(settings->step) - .arg(settings->digits); + .arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title)), + username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username)), + QString(Base32::sanitizeInput(settings->key.toLatin1()))) + .arg(settings->step) + .arg(settings->digits); if (!settings->encoder.name.isEmpty()) { urlstring.append("&encoder=").append(settings->encoder.name); diff --git a/src/totp/totp.h b/src/totp/totp.h index 0697281bf..9c90ec5a9 100644 --- a/src/totp/totp.h +++ b/src/totp/totp.h @@ -21,54 +21,57 @@ #include #include -#include #include +#include class QUrl; -namespace Totp { - -struct Encoder +namespace Totp { - QString name; - QString shortName; - QString alphabet; - uint digits; - uint step; - bool reverse; -}; -struct Settings -{ - Totp::Encoder encoder; - QString key; - bool otpUrl; - bool custom; - uint digits; - uint step; -}; + struct Encoder + { + QString name; + QString shortName; + QString alphabet; + uint digits; + uint step; + bool reverse; + }; -constexpr uint DEFAULT_STEP = 30u; -constexpr uint DEFAULT_DIGITS = 6u; -constexpr uint STEAM_DIGITS = 5u; -static const QString STEAM_SHORTNAME = "S"; + struct Settings + { + Totp::Encoder encoder; + QString key; + bool otpUrl; + bool custom; + uint digits; + uint step; + }; -static const QString ATTRIBUTE_OTP = "otp"; -static const QString ATTRIBUTE_SEED = "TOTP Seed"; -static const QString ATTRIBUTE_SETTINGS = "TOTP Settings"; + constexpr uint DEFAULT_STEP = 30u; + constexpr uint DEFAULT_DIGITS = 6u; + constexpr uint STEAM_DIGITS = 5u; + static const QString STEAM_SHORTNAME = "S"; -QSharedPointer parseSettings(const QString& rawSettings, const QString& key = {}); -QSharedPointer createSettings(const QString& key, const uint digits, const uint step, - const QString& encoderShortName = {}); -QString writeSettings(const QSharedPointer& settings, const QString& title = {}, - const QString& username = {}, bool forceOtp = false); + static const QString ATTRIBUTE_OTP = "otp"; + static const QString ATTRIBUTE_SEED = "TOTP Seed"; + static const QString ATTRIBUTE_SETTINGS = "TOTP Settings"; -QString generateTotp(const QSharedPointer& settings, const quint64 time = 0ull); + QSharedPointer parseSettings(const QString& rawSettings, const QString& key = {}); + QSharedPointer + createSettings(const QString& key, const uint digits, const uint step, const QString& encoderShortName = {}); + QString writeSettings(const QSharedPointer& settings, + const QString& title = {}, + const QString& username = {}, + bool forceOtp = false); -Encoder& defaultEncoder(); -Encoder& steamEncoder(); -Encoder& getEncoderByShortName(const QString& shortName); -Encoder& getEncoderByName(const QString& name); -} + QString generateTotp(const QSharedPointer& settings, const quint64 time = 0ull); + + Encoder& defaultEncoder(); + Encoder& steamEncoder(); + Encoder& getEncoderByShortName(const QString& shortName); + Encoder& getEncoderByName(const QString& name); +} // namespace Totp #endif // QTOTP_H diff --git a/src/touchid/TouchID.h b/src/touchid/TouchID.h index 115943a27..d37522054 100644 --- a/src/touchid/TouchID.h +++ b/src/touchid/TouchID.h @@ -5,10 +5,10 @@ #define TOUCHID_AVAILABLE 1 #define TOUCHID_NOT_AVAILABLE 0 -#include -#include #include +#include #include +#include class TouchID { @@ -16,7 +16,9 @@ public: static TouchID& getInstance(); private: - TouchID() {} + TouchID() + { + } // TouchID(TouchID const&); // Don't Implement // void operator=(TouchID const&); // Don't implement diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index d25201e2e..435c2f6e2 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -16,24 +16,24 @@ */ #include "TestCli.h" + #include "config-keepassx-tests.h" -#include "core/Global.h" -#include "core/Config.h" #include "core/Bootstrap.h" -#include "core/Tools.h" +#include "core/Config.h" +#include "core/Global.h" #include "core/PasswordGenerator.h" +#include "core/Tools.h" #include "crypto/Crypto.h" -#include "format/KeePass2.h" #include "format/Kdbx3Reader.h" +#include "format/Kdbx3Writer.h" #include "format/Kdbx4Reader.h" #include "format/Kdbx4Writer.h" -#include "format/Kdbx3Writer.h" #include "format/KdbxXmlReader.h" +#include "format/KeePass2.h" -#include "cli/Command.h" -#include "cli/Utils.h" #include "cli/Add.h" #include "cli/Clip.h" +#include "cli/Command.h" #include "cli/Diceware.h" #include "cli/Edit.h" #include "cli/Estimate.h" @@ -44,12 +44,13 @@ #include "cli/Merge.h" #include "cli/Remove.h" #include "cli/Show.h" +#include "cli/Utils.h" -#include #include +#include #include -#include #include +#include #include @@ -130,7 +131,7 @@ QSharedPointer TestCli::readTestDatabase() const { Utils::Test::setNextPassword("a"); auto db = QSharedPointer(Database::unlockFromStdin(m_dbFile->fileName(), "", m_stdoutHandle)); - m_stdoutFile->seek(ftell(m_stdoutHandle)); // re-synchronize handles + m_stdoutFile->seek(ftell(m_stdoutHandle)); // re-synchronize handles return db; } @@ -159,7 +160,16 @@ void TestCli::testAdd() QVERIFY(addCmd.getDescriptionLine().contains(addCmd.name)); Utils::Test::setNextPassword("a"); - addCmd.execute({"add", "-u", "newuser", "--url", "https://example.com/", "-g", "-l", "20", m_dbFile->fileName(), "/newuser-entry"}); + addCmd.execute({"add", + "-u", + "newuser", + "--url", + "https://example.com/", + "-g", + "-l", + "20", + m_dbFile->fileName(), + "/newuser-entry"}); m_stderrFile->reset(); m_stdoutFile->reset(); m_stdoutFile->readLine(); // skip password prompt @@ -184,7 +194,17 @@ void TestCli::testAdd() Utils::Test::setNextPassword("a"); Utils::Test::setNextPassword("newpassword"); - addCmd.execute({"add", "-u", "newuser2", "--url", "https://example.net/", "-g", "-l", "20", "-p", m_dbFile->fileName(), "/newuser-entry2"}); + addCmd.execute({"add", + "-u", + "newuser2", + "--url", + "https://example.net/", + "-g", + "-l", + "20", + "-p", + m_dbFile->fileName(), + "/newuser-entry2"}); db = readTestDatabase(); entry = db->rootGroup()->findEntryByPath("/newuser-entry2"); @@ -251,8 +271,10 @@ void TestCli::testClip() // Password with timeout Utils::Test::setNextPassword("a"); + // clang-format off QFuture future = QtConcurrent::run(&clipCmd, &Clip::execute, QStringList{"clip", m_dbFile->fileName(), "/Sample Entry", "1"}); - + // clang-format on + QTRY_COMPARE_WITH_TIMEOUT(clipboard->text(), QString("Password"), 500); QTRY_COMPARE_WITH_TIMEOUT(clipboard->text(), QString(""), 1500); @@ -322,7 +344,9 @@ void TestCli::testEdit() QVERIFY(editCmd.getDescriptionLine().contains(editCmd.name)); Utils::Test::setNextPassword("a"); + // clang-format off editCmd.execute({"edit", "-u", "newuser", "--url", "https://otherurl.example.com/", "-t", "newtitle", m_dbFile->fileName(), "/Sample Entry"}); + // clang-format on m_stdoutFile->reset(); m_stdoutFile->readLine(); // skip prompt line QCOMPARE(m_stdoutFile->readLine(), QByteArray("Successfully edited entry newtitle.\n")); @@ -378,54 +402,74 @@ void TestCli::testEstimate_data() QTest::addColumn("log10"); QTest::addColumn("searchStrings"); - QTest::newRow("Dictionary") - << "password" << "8" << "1.0" << "0.3" - << QStringList{"Type: Dictionary", "\tpassword"}; + QTest::newRow("Dictionary") << "password" + << "8" + << "1.0" + << "0.3" << QStringList{"Type: Dictionary", "\tpassword"}; - QTest::newRow("Spatial") - << "zxcv" << "4" << "10.3" << "3.1" - << QStringList{"Type: Spatial", "\tzxcv"}; + QTest::newRow("Spatial") << "zxcv" + << "4" + << "10.3" + << "3.1" << QStringList{"Type: Spatial", "\tzxcv"}; - QTest::newRow("Spatial(Rep)") - << "sdfgsdfg" << "8" << "11.3" << "3.4" - << QStringList{"Type: Spatial(Rep)", "\tsdfgsdfg"}; + QTest::newRow("Spatial(Rep)") << "sdfgsdfg" + << "8" + << "11.3" + << "3.4" << QStringList{"Type: Spatial(Rep)", "\tsdfgsdfg"}; QTest::newRow("Dictionary / Sequence") - << "password123" << "11" << "4.5" << "1.3" - << QStringList{"Type: Dictionary", "Type: Sequence", "\tpassword", "\t123"}; + << "password123" + << "11" + << "4.5" + << "1.3" << QStringList{"Type: Dictionary", "Type: Sequence", "\tpassword", "\t123"}; - QTest::newRow("Dict+Leet") - << "p455w0rd" << "8" << "2.5" << "0.7" - << QStringList{"Type: Dict+Leet", "\tp455w0rd"}; + QTest::newRow("Dict+Leet") << "p455w0rd" + << "8" + << "2.5" + << "0.7" << QStringList{"Type: Dict+Leet", "\tp455w0rd"}; - QTest::newRow("Dictionary(Rep)") - << "hellohello" << "10" << "7.3" << "2.2" - << QStringList{"Type: Dictionary(Rep)", "\thellohello"}; + QTest::newRow("Dictionary(Rep)") << "hellohello" + << "10" + << "7.3" + << "2.2" << QStringList{"Type: Dictionary(Rep)", "\thellohello"}; QTest::newRow("Sequence(Rep) / Dictionary") - << "456456foobar" << "12" << "16.7" << "5.0" - << QStringList{"Type: Sequence(Rep)", "Type: Dictionary", "\t456456", "\tfoobar"}; + << "456456foobar" + << "12" + << "16.7" + << "5.0" << QStringList{"Type: Sequence(Rep)", "Type: Dictionary", "\t456456", "\tfoobar"}; QTest::newRow("Bruteforce(Rep) / Bruteforce") - << "xzxzy" << "5" << "16.1" << "4.8" - << QStringList{"Type: Bruteforce(Rep)", "Type: Bruteforce", "\txzxz", "\ty"}; + << "xzxzy" + << "5" + << "16.1" + << "4.8" << QStringList{"Type: Bruteforce(Rep)", "Type: Bruteforce", "\txzxz", "\ty"}; QTest::newRow("Dictionary / Date(Rep)") - << "pass20182018" << "12" << "15.1" << "4.56" - << QStringList{"Type: Dictionary", "Type: Date(Rep)", "\tpass", "\t20182018"}; + << "pass20182018" + << "12" + << "15.1" + << "4.56" << QStringList{"Type: Dictionary", "Type: Date(Rep)", "\tpass", "\t20182018"}; QTest::newRow("Dictionary / Date / Bruteforce") - << "mypass2018-2" << "12" << "32.9" << "9.9" - << QStringList{"Type: Dictionary", "Type: Date", "Type: Bruteforce", "\tmypass", "\t2018", "\t-2"}; + << "mypass2018-2" + << "12" + << "32.9" + << "9.9" << QStringList{"Type: Dictionary", "Type: Date", "Type: Bruteforce", "\tmypass", "\t2018", "\t-2"}; - QTest::newRow("Strong Password") - << "E*!%.Qw{t.X,&bafw)\"Q!ah$%;U/" << "28" << "165.7" << "49.8" - << QStringList{"Type: Bruteforce", "\tE*"}; + QTest::newRow("Strong Password") << "E*!%.Qw{t.X,&bafw)\"Q!ah$%;U/" + << "28" + << "165.7" + << "49.8" << QStringList{"Type: Bruteforce", "\tE*"}; // TODO: detect passphrases and adjust entropy calculation accordingly (issue #2347) QTest::newRow("Strong Passphrase") - << "squint wooing resupply dangle isolation axis headsman" << "53" << "151.2" << "45.5" - << QStringList{"Type: Dictionary", "Type: Bruteforce", "Multi-word extra bits 22.0", "\tsquint", "\t ", "\twooing"}; + << "squint wooing resupply dangle isolation axis headsman" + << "53" + << "151.2" + << "45.5" + << QStringList{ + "Type: Dictionary", "Type: Bruteforce", "Multi-word extra bits 22.0", "\tsquint", "\t ", "\twooing"}; } void TestCli::testEstimate() @@ -514,31 +558,23 @@ void TestCli::testGenerate_data() QTest::newRow("length") << QStringList{"generate", "-L", "13"} << "^.{13}$"; QTest::newRow("lowercase") << QStringList{"generate", "-L", "14", "-l"} << "^[a-z]{14}$"; QTest::newRow("uppercase") << QStringList{"generate", "-L", "15", "-u"} << "^[A-Z]{15}$"; - QTest::newRow("numbers")<< QStringList{"generate", "-L", "16", "-n"} << "^[0-9]{16}$"; - QTest::newRow("special") - << QStringList{"generate", "-L", "200", "-s"} - << R"(^[\(\)\[\]\{\}\.\-*|\\,:;"'\/\_!+-<=>?#$%&^`@~]{200}$)"; - QTest::newRow("special (exclude)") - << QStringList{"generate", "-L", "200", "-s" , "-x", "+.?@&"} - << R"(^[\(\)\[\]\{\}\.\-*|\\,:;"'\/\_!-<=>#$%^`~]{200}$)"; - QTest::newRow("extended") - << QStringList{"generate", "-L", "50", "-e"} - << R"(^[^a-zA-Z0-9\(\)\[\]\{\}\.\-\*\|\\,:;"'\/\_!+-<=>?#$%&^`@~]{50}$)"; + QTest::newRow("numbers") << QStringList{"generate", "-L", "16", "-n"} << "^[0-9]{16}$"; + QTest::newRow("special") << QStringList{"generate", "-L", "200", "-s"} + << R"(^[\(\)\[\]\{\}\.\-*|\\,:;"'\/\_!+-<=>?#$%&^`@~]{200}$)"; + QTest::newRow("special (exclude)") << QStringList{"generate", "-L", "200", "-s", "-x", "+.?@&"} + << R"(^[\(\)\[\]\{\}\.\-*|\\,:;"'\/\_!-<=>#$%^`~]{200}$)"; + QTest::newRow("extended") << QStringList{"generate", "-L", "50", "-e"} + << R"(^[^a-zA-Z0-9\(\)\[\]\{\}\.\-\*\|\\,:;"'\/\_!+-<=>?#$%&^`@~]{50}$)"; QTest::newRow("numbers + lowercase + uppercase") - << QStringList{"generate", "-L", "16", "-n", "-u", "-l"} - << "^[0-9a-zA-Z]{16}$"; + << QStringList{"generate", "-L", "16", "-n", "-u", "-l"} << "^[0-9a-zA-Z]{16}$"; QTest::newRow("numbers + lowercase + uppercase (exclude)") - << QStringList{"generate", "-L", "500", "-n", "-u", "-l", "-x", "abcdefg0123@"} - << "^[^abcdefg0123@]{500}$"; + << QStringList{"generate", "-L", "500", "-n", "-u", "-l", "-x", "abcdefg0123@"} << "^[^abcdefg0123@]{500}$"; QTest::newRow("numbers + lowercase + uppercase (exclude similar)") - << QStringList{"generate", "-L", "200", "-n", "-u", "-l", "--exclude-similar"} - << "^[^l1IO0]{200}$"; + << QStringList{"generate", "-L", "200", "-n", "-u", "-l", "--exclude-similar"} << "^[^l1IO0]{200}$"; QTest::newRow("uppercase + lowercase (every)") - << QStringList{"generate", "-L", "2", "-u", "-l", "--every-group"} - << "^[a-z][A-Z]|[A-Z][a-z]$"; + << QStringList{"generate", "-L", "2", "-u", "-l", "--every-group"} << "^[a-z][A-Z]|[A-Z][a-z]$"; QTest::newRow("numbers + lowercase (every)") - << QStringList{"generate", "-L", "2", "-n", "-l", "--every-group"} - << "^[a-z][0-9]|[0-9][a-z]$"; + << QStringList{"generate", "-L", "2", "-n", "-l", "--every-group"} << "^[a-z][0-9]|[0-9][a-z]$"; } void TestCli::testGenerate() @@ -559,7 +595,8 @@ void TestCli::testGenerate() QRegularExpression regex(pattern); QString password = stream.readLine(); pos = stream.pos(); - QVERIFY2(regex.match(password).hasMatch(), qPrintable("Password " + password + " does not match pattern " + pattern)); + QVERIFY2(regex.match(password).hasMatch(), + qPrintable("Password " + password + " does not match pattern " + pattern)); } } @@ -572,14 +609,15 @@ void TestCli::testList() Utils::Test::setNextPassword("a"); listCmd.execute({"ls", m_dbFile->fileName()}); m_stdoutFile->reset(); - m_stdoutFile->readLine(); // skip password prompt - QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n" - "General/\n" - "Windows/\n" - "Network/\n" - "Internet/\n" - "eMail/\n" - "Homebanking/\n")); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), + QByteArray("Sample Entry\n" + "General/\n" + "Windows/\n" + "Network/\n" + "Internet/\n" + "eMail/\n" + "Homebanking/\n")); // Quiet option qint64 pos = m_stdoutFile->pos(); @@ -599,20 +637,21 @@ void TestCli::testList() Utils::Test::setNextPassword("a"); listCmd.execute({"ls", "-R", m_dbFile->fileName()}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt - QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n" - "General/\n" - " [empty]\n" - "Windows/\n" - " [empty]\n" - "Network/\n" - " [empty]\n" - "Internet/\n" - " [empty]\n" - "eMail/\n" - " [empty]\n" - "Homebanking/\n" - " [empty]\n")); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), + QByteArray("Sample Entry\n" + "General/\n" + " [empty]\n" + "Windows/\n" + " [empty]\n" + "Network/\n" + " [empty]\n" + "Internet/\n" + " [empty]\n" + "eMail/\n" + " [empty]\n" + "Homebanking/\n" + " [empty]\n")); pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); @@ -625,7 +664,7 @@ void TestCli::testList() Utils::Test::setNextPassword("a"); listCmd.execute({"ls", m_dbFile->fileName(), "/DoesNotExist/"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt m_stderrFile->reset(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); QCOMPARE(m_stderrFile->readAll(), QByteArray("Cannot find group /DoesNotExist/.\n")); @@ -640,7 +679,7 @@ void TestCli::testLocate() Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", m_dbFile->fileName(), "Sample"}); m_stdoutFile->reset(); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("/Sample Entry\n")); // Quiet option @@ -654,7 +693,7 @@ void TestCli::testLocate() Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", m_dbFile->fileName(), "Does Not Exist"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt m_stderrFile->reset(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); QCOMPARE(m_stderrFile->readAll(), QByteArray("No results for that search term.\n")); @@ -678,14 +717,14 @@ void TestCli::testLocate() Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", tmpFile.fileName(), "New"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("/General/New Entry\n")); pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); locateCmd.execute({"locate", tmpFile.fileName(), "Entry"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("/Sample Entry\n/General/New Entry\n")); } @@ -811,7 +850,7 @@ void TestCli::testRemove() Utils::Test::setNextPassword("a"); removeCmd.execute({"rm", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully recycled entry Sample Entry.\n")); auto key = QSharedPointer::create(); @@ -831,7 +870,7 @@ void TestCli::testRemove() Utils::Test::setNextPassword("a"); removeCmd.execute({"rm", fileCopy.fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully deleted entry Sample Entry.\n")); readBack.setFileName(fileCopy.fileName()); @@ -849,7 +888,7 @@ void TestCli::testRemove() Utils::Test::setNextPassword("a"); removeCmd.execute({"rm", fileCopy.fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt m_stderrFile->reset(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); QCOMPARE(m_stderrFile->readAll(), QByteArray("Entry /Sample Entry not found.\n")); @@ -910,12 +949,13 @@ void TestCli::testShow() Utils::Test::setNextPassword("a"); showCmd.execute({"show", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->reset(); - m_stdoutFile->readLine(); // skip password prompt - QCOMPARE(m_stdoutFile->readAll(), QByteArray("Title: Sample Entry\n" - "UserName: User Name\n" - "Password: Password\n" - "URL: http://www.somesite.com/\n" - "Notes: Notes\n")); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), + QByteArray("Title: Sample Entry\n" + "UserName: User Name\n" + "Password: Password\n" + "URL: http://www.somesite.com/\n" + "Notes: Notes\n")); qint64 pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); @@ -931,22 +971,23 @@ void TestCli::testShow() Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n")); pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt - QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n" - "http://www.somesite.com/\n")); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), + QByteArray("Sample Entry\n" + "http://www.somesite.com/\n")); pos = m_stdoutFile->pos(); Utils::Test::setNextPassword("a"); showCmd.execute({"show", "-a", "DoesNotExist", m_dbFile->fileName(), "/Sample Entry"}); m_stdoutFile->seek(pos); - m_stdoutFile->readLine(); // skip password prompt + m_stdoutFile->readLine(); // skip password prompt m_stderrFile->reset(); QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); QCOMPARE(m_stderrFile->readAll(), QByteArray("ERROR: unknown attribute DoesNotExist.\n")); diff --git a/tests/TestCli.h b/tests/TestCli.h index 061055dcd..63cd1f55e 100644 --- a/tests/TestCli.h +++ b/tests/TestCli.h @@ -21,11 +21,11 @@ #include "core/Database.h" #include "util/TemporaryFile.h" -#include -#include #include #include #include +#include +#include class TestCli : public QObject { @@ -70,4 +70,4 @@ private: FILE* m_stdinHandle = stdin; }; -#endif //KEEPASSXC_TESTCLI_H +#endif // KEEPASSXC_TESTCLI_H diff --git a/tests/TestCryptoHash.cpp b/tests/TestCryptoHash.cpp index ac6cd7c7b..7cc6f7c17 100644 --- a/tests/TestCryptoHash.cpp +++ b/tests/TestCryptoHash.cpp @@ -50,8 +50,9 @@ void TestCryptoHash::test() "8d2877eec2f63b931bd47417a81a538327af927da3e")); QByteArray result3 = CryptoHash::hash(source2, CryptoHash::Sha512); - QCOMPARE(result3, QByteArray::fromHex("0d41b612584ed39ff72944c29494573e40f4bb95283455fae2e0be1e3565aa9f48057d59e6ff" - "d777970e282871c25a549a2763e5b724794f312c97021c42f91d")); + QCOMPARE(result3, + QByteArray::fromHex("0d41b612584ed39ff72944c29494573e40f4bb95283455fae2e0be1e3565aa9f48057d59e6ff" + "d777970e282871c25a549a2763e5b724794f312c97021c42f91d")); CryptoHash cryptoHash4(CryptoHash::Sha512); cryptoHash4.addData(QString("KeePa").toLatin1()); diff --git a/tests/TestCsvExporter.cpp b/tests/TestCsvExporter.cpp index b71cf9ca7..07208c380 100644 --- a/tests/TestCsvExporter.cpp +++ b/tests/TestCsvExporter.cpp @@ -62,9 +62,10 @@ void TestCsvExporter::testExport() QVERIFY(buffer.open(QIODevice::ReadWrite)); m_csvExporter->exportDatabase(&buffer, m_db); - QString expectedResult = - QString().append(ExpectedHeaderLine).append("\"Root/Test Group Name\",\"Test Entry Title\",\"Test Username\",\"Test " - "Password\",\"http://test.url\",\"Test Notes\"\n"); + QString expectedResult = 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); } diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp index 8109f9bd9..23af41e6a 100644 --- a/tests/TestEntry.cpp +++ b/tests/TestEntry.cpp @@ -343,8 +343,7 @@ void TestEntry::testResolveReferencePlaceholders() tstEntry->setGroup(root); tstEntry->setUuid(QUuid::createUuid()); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), - entry1->title()); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), entry1->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry1->username())), entry1->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry1->password())), entry1->title()); @@ -354,8 +353,7 @@ void TestEntry::testResolveReferencePlaceholders() QString("{REF:T@O:%1}").arg(entry1->attributes()->value("CustomAttribute1"))), entry1->title()); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), - entry1->title()); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), entry1->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@U:%1}").arg(entry1->username())), entry1->username()); @@ -364,8 +362,7 @@ void TestEntry::testResolveReferencePlaceholders() QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry1->url())), entry1->url()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry1->notes())), entry1->notes()); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry2->uuidToHex())), - entry2->title()); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry2->uuidToHex())), entry2->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry2->title())), entry2->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry2->username())), entry2->title()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry2->password())), entry2->title()); @@ -383,23 +380,38 @@ void TestEntry::testResolveReferencePlaceholders() QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry2->url())), entry2->url()); QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry2->notes())), entry2->notes()); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex())), entry3->attributes()->value("AttributeTitle")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex())), entry3->attributes()->value("AttributeUsername")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex())), entry3->attributes()->value("AttributePassword")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex())), entry3->attributes()->value("AttributeUrl")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex())), entry3->attributes()->value("AttributeNotes")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex())), + entry3->attributes()->value("AttributeTitle")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex())), + entry3->attributes()->value("AttributeUsername")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex())), + entry3->attributes()->value("AttributePassword")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex())), + entry3->attributes()->value("AttributeUrl")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex())), + entry3->attributes()->value("AttributeNotes")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex().toUpper())), entry3->attributes()->value("AttributeTitle")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex().toUpper())), entry3->attributes()->value("AttributeUsername")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex().toUpper())), entry3->attributes()->value("AttributePassword")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex().toUpper())), entry3->attributes()->value("AttributeUrl")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex().toUpper())), entry3->attributes()->value("AttributeNotes")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex().toUpper())), + entry3->attributes()->value("AttributeTitle")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex().toUpper())), + entry3->attributes()->value("AttributeUsername")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex().toUpper())), + entry3->attributes()->value("AttributePassword")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex().toUpper())), + entry3->attributes()->value("AttributeUrl")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex().toUpper())), + entry3->attributes()->value("AttributeNotes")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:t@i:%1}").arg(entry3->uuidToHex().toLower())), entry3->attributes()->value("AttributeTitle")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:u@i:%1}").arg(entry3->uuidToHex().toLower())), entry3->attributes()->value("AttributeUsername")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:p@i:%1}").arg(entry3->uuidToHex().toLower())), entry3->attributes()->value("AttributePassword")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:a@i:%1}").arg(entry3->uuidToHex().toLower())), entry3->attributes()->value("AttributeUrl")); - QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:n@i:%1}").arg(entry3->uuidToHex().toLower())), entry3->attributes()->value("AttributeNotes")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:t@i:%1}").arg(entry3->uuidToHex().toLower())), + entry3->attributes()->value("AttributeTitle")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:u@i:%1}").arg(entry3->uuidToHex().toLower())), + entry3->attributes()->value("AttributeUsername")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:p@i:%1}").arg(entry3->uuidToHex().toLower())), + entry3->attributes()->value("AttributePassword")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:a@i:%1}").arg(entry3->uuidToHex().toLower())), + entry3->attributes()->value("AttributeUrl")); + QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:n@i:%1}").arg(entry3->uuidToHex().toLower())), + entry3->attributes()->value("AttributeNotes")); } void TestEntry::testResolveNonIdPlaceholdersToUuid() @@ -466,8 +478,7 @@ void TestEntry::testResolveNonIdPlaceholdersToUuid() newEntry->setGroup(root); newEntry->setNotes(newEntryNotesRaw); - const QString newEntryNotesResolved = - newEntry->resolveMultiplePlaceholders(newEntry->notes()); + const QString newEntryNotesResolved = newEntry->resolveMultiplePlaceholders(newEntry->notes()); QCOMPARE(newEntryNotesResolved, referencedEntry->uuidToHex()); } } diff --git a/tests/TestGlobal.h b/tests/TestGlobal.h index 958034293..c85e60faf 100644 --- a/tests/TestGlobal.h +++ b/tests/TestGlobal.h @@ -25,7 +25,7 @@ namespace QTest { - + template <> inline char* toString(const Group::TriState& triState) { QString value; diff --git a/tests/TestKdbx4.cpp b/tests/TestKdbx4.cpp index a7fd52e0e..fe4679da5 100644 --- a/tests/TestKdbx4.cpp +++ b/tests/TestKdbx4.cpp @@ -320,11 +320,11 @@ void TestKdbx4::testUpgradeMasterKeyIntegrity_data() QTest::newRow("Upgrade (explicit): kdf-argon2") << QString("kdf-argon2") << KeePass2::FILE_VERSION_4; QTest::newRow("Upgrade (explicit): kdf-aes-kdbx4") << QString("kdf-aes-kdbx4") << KeePass2::FILE_VERSION_4; QTest::newRow("Upgrade (implicit): public-customdata") << QString("public-customdata") << KeePass2::FILE_VERSION_4; - QTest::newRow("Upgrade (implicit): rootgroup-customdata") << QString("rootgroup-customdata") - << KeePass2::FILE_VERSION_4; + QTest::newRow("Upgrade (implicit): rootgroup-customdata") + << QString("rootgroup-customdata") << KeePass2::FILE_VERSION_4; QTest::newRow("Upgrade (implicit): group-customdata") << QString("group-customdata") << KeePass2::FILE_VERSION_4; - QTest::newRow("Upgrade (implicit): rootentry-customdata") << QString("rootentry-customdata") - << KeePass2::FILE_VERSION_4; + QTest::newRow("Upgrade (implicit): rootentry-customdata") + << QString("rootentry-customdata") << KeePass2::FILE_VERSION_4; QTest::newRow("Upgrade (implicit): entry-customdata") << QString("entry-customdata") << KeePass2::FILE_VERSION_4; } diff --git a/tests/TestKeePass2Format.cpp b/tests/TestKeePass2Format.cpp index 35bbfed3e..012a1b11a 100644 --- a/tests/TestKeePass2Format.cpp +++ b/tests/TestKeePass2Format.cpp @@ -146,7 +146,8 @@ void TestKeePass2Format::testXmlGroupRoot() QCOMPARE(group->defaultAutoTypeSequence(), QString("")); QCOMPARE(group->autoTypeEnabled(), Group::Inherit); QCOMPARE(group->searchingEnabled(), Group::Inherit); - QCOMPARE(group->lastTopVisibleEntry()->uuid(), QUuid::fromRfc4122(QByteArray::fromBase64("+wSUOv6qf0OzW8/ZHAs2sA=="))); + QCOMPARE(group->lastTopVisibleEntry()->uuid(), + QUuid::fromRfc4122(QByteArray::fromBase64("+wSUOv6qf0OzW8/ZHAs2sA=="))); QCOMPARE(group->children().size(), 3); QVERIFY(m_xmlDb->metadata()->recycleBin() == m_xmlDb->rootGroup()->children().at(2)); diff --git a/tests/TestMerge.cpp b/tests/TestMerge.cpp index 3e41d7d50..a09eb32e6 100644 --- a/tests/TestMerge.cpp +++ b/tests/TestMerge.cpp @@ -37,7 +37,7 @@ namespace } MockClock* m_clock = nullptr; -} +} // namespace void TestMerge::initTestCase() { @@ -302,7 +302,9 @@ void TestMerge::testResolveConflictDuplicate() "KeepBoth should not reuse the UUIDs when cloning."); } -void TestMerge::testResolveConflictTemplate(int mergeMode, std::function&)> verification) +void TestMerge::testResolveConflictTemplate( + int mergeMode, + std::function&)> verification) { QMap timestamps; timestamps["initialTime"] = m_clock->currentDateTimeUtc(); @@ -434,7 +436,8 @@ void TestMerge::testResolveConflictTemplate(int mergeMode, std::functionrootGroup()->findEntryByPath("entrySource")); } -void TestMerge::testDeletionConflictTemplate(int mergeMode, std::function&)> verification) +void TestMerge::testDeletionConflictTemplate(int mergeMode, + std::function&)> verification) { QMap identifiers; m_clock->currentDateTimeUtc(); @@ -609,7 +612,7 @@ void TestMerge::assertDeletionNewerOnly(Database* db, const QMap QVERIFY(db->containsDeletedObject(identifiers["EntryDeletedInTargetAfterEntryUpdatedInSource"])); } -void TestMerge::assertDeletionLocalOnly(Database* db, const QMap &identifiers) +void TestMerge::assertDeletionLocalOnly(Database* db, const QMap& identifiers) { QPointer mergedRootGroup = db->rootGroup(); @@ -647,66 +650,80 @@ void TestMerge::assertDeletionLocalOnly(Database* db, const QMap QVERIFY(db->containsDeletedObject(identifiers["EntryDeletedInTargetAfterEntryUpdatedInSource"])); } -void TestMerge::assertUpdateMergedEntry1(Entry *mergedEntry1, const QMap ×tamps) +void TestMerge::assertUpdateMergedEntry1(Entry* mergedEntry1, const QMap& timestamps) { QCOMPARE(mergedEntry1->historyItems().count(), 4); QCOMPARE(mergedEntry1->historyItems().at(0)->notes(), QString("")); QCOMPARE(mergedEntry1->historyItems().at(0)->timeInfo().lastModificationTime(), timestamps["initialTime"]); QCOMPARE(mergedEntry1->historyItems().at(1)->notes(), QString("")); - QCOMPARE(mergedEntry1->historyItems().at(1)->timeInfo().lastModificationTime(), timestamps["oldestCommonHistoryTime"]); + QCOMPARE(mergedEntry1->historyItems().at(1)->timeInfo().lastModificationTime(), + timestamps["oldestCommonHistoryTime"]); QCOMPARE(mergedEntry1->historyItems().at(2)->notes(), QString("1 Common")); - QCOMPARE(mergedEntry1->historyItems().at(2)->timeInfo().lastModificationTime(), timestamps["newestCommonHistoryTime"]); + QCOMPARE(mergedEntry1->historyItems().at(2)->timeInfo().lastModificationTime(), + timestamps["newestCommonHistoryTime"]); QCOMPARE(mergedEntry1->historyItems().at(3)->notes(), QString("2 Source")); - QCOMPARE(mergedEntry1->historyItems().at(3)->timeInfo().lastModificationTime(), timestamps["oldestDivergingHistoryTime"]); + QCOMPARE(mergedEntry1->historyItems().at(3)->timeInfo().lastModificationTime(), + timestamps["oldestDivergingHistoryTime"]); QCOMPARE(mergedEntry1->notes(), QString("3 Destination")); QCOMPARE(mergedEntry1->timeInfo().lastModificationTime(), timestamps["newestDivergingHistoryTime"]); } -void TestMerge::assertUpdateReappliedEntry2(Entry *mergedEntry2, const QMap ×tamps) +void TestMerge::assertUpdateReappliedEntry2(Entry* mergedEntry2, const QMap& timestamps) { QCOMPARE(mergedEntry2->historyItems().count(), 5); QCOMPARE(mergedEntry2->historyItems().at(0)->notes(), QString("")); QCOMPARE(mergedEntry2->historyItems().at(0)->timeInfo().lastModificationTime(), timestamps["initialTime"]); QCOMPARE(mergedEntry2->historyItems().at(1)->notes(), QString("")); - QCOMPARE(mergedEntry2->historyItems().at(1)->timeInfo().lastModificationTime(), timestamps["oldestCommonHistoryTime"]); + QCOMPARE(mergedEntry2->historyItems().at(1)->timeInfo().lastModificationTime(), + timestamps["oldestCommonHistoryTime"]); QCOMPARE(mergedEntry2->historyItems().at(2)->notes(), QString("1 Common")); - QCOMPARE(mergedEntry2->historyItems().at(2)->timeInfo().lastModificationTime(), timestamps["newestCommonHistoryTime"]); + QCOMPARE(mergedEntry2->historyItems().at(2)->timeInfo().lastModificationTime(), + timestamps["newestCommonHistoryTime"]); QCOMPARE(mergedEntry2->historyItems().at(3)->notes(), QString("2 Destination")); - QCOMPARE(mergedEntry2->historyItems().at(3)->timeInfo().lastModificationTime(), timestamps["oldestDivergingHistoryTime"]); + QCOMPARE(mergedEntry2->historyItems().at(3)->timeInfo().lastModificationTime(), + timestamps["oldestDivergingHistoryTime"]); QCOMPARE(mergedEntry2->historyItems().at(4)->notes(), QString("3 Source")); - QCOMPARE(mergedEntry2->historyItems().at(4)->timeInfo().lastModificationTime(), timestamps["newestDivergingHistoryTime"]); + QCOMPARE(mergedEntry2->historyItems().at(4)->timeInfo().lastModificationTime(), + timestamps["newestDivergingHistoryTime"]); QCOMPARE(mergedEntry2->notes(), QString("2 Destination")); QCOMPARE(mergedEntry2->timeInfo().lastModificationTime(), timestamps["mergeTime"]); } -void TestMerge::assertUpdateReappliedEntry1(Entry *mergedEntry1, const QMap ×tamps) +void TestMerge::assertUpdateReappliedEntry1(Entry* mergedEntry1, const QMap& timestamps) { QCOMPARE(mergedEntry1->historyItems().count(), 5); QCOMPARE(mergedEntry1->historyItems().at(0)->notes(), QString("")); QCOMPARE(mergedEntry1->historyItems().at(0)->timeInfo().lastModificationTime(), timestamps["initialTime"]); QCOMPARE(mergedEntry1->historyItems().at(1)->notes(), QString("")); - QCOMPARE(mergedEntry1->historyItems().at(1)->timeInfo().lastModificationTime(), timestamps["oldestCommonHistoryTime"]); + QCOMPARE(mergedEntry1->historyItems().at(1)->timeInfo().lastModificationTime(), + timestamps["oldestCommonHistoryTime"]); QCOMPARE(mergedEntry1->historyItems().at(2)->notes(), QString("1 Common")); - QCOMPARE(mergedEntry1->historyItems().at(2)->timeInfo().lastModificationTime(), timestamps["newestCommonHistoryTime"]); + QCOMPARE(mergedEntry1->historyItems().at(2)->timeInfo().lastModificationTime(), + timestamps["newestCommonHistoryTime"]); QCOMPARE(mergedEntry1->historyItems().at(3)->notes(), QString("2 Source")); - QCOMPARE(mergedEntry1->historyItems().at(3)->timeInfo().lastModificationTime(), timestamps["oldestDivergingHistoryTime"]); + QCOMPARE(mergedEntry1->historyItems().at(3)->timeInfo().lastModificationTime(), + timestamps["oldestDivergingHistoryTime"]); QCOMPARE(mergedEntry1->historyItems().at(4)->notes(), QString("3 Destination")); - QCOMPARE(mergedEntry1->historyItems().at(4)->timeInfo().lastModificationTime(), timestamps["newestDivergingHistoryTime"]); + QCOMPARE(mergedEntry1->historyItems().at(4)->timeInfo().lastModificationTime(), + timestamps["newestDivergingHistoryTime"]); QCOMPARE(mergedEntry1->notes(), QString("2 Source")); QCOMPARE(mergedEntry1->timeInfo().lastModificationTime(), timestamps["mergeTime"]); } -void TestMerge::assertUpdateMergedEntry2(Entry *mergedEntry2, const QMap ×tamps) +void TestMerge::assertUpdateMergedEntry2(Entry* mergedEntry2, const QMap& timestamps) { QCOMPARE(mergedEntry2->historyItems().count(), 4); QCOMPARE(mergedEntry2->historyItems().at(0)->notes(), QString("")); QCOMPARE(mergedEntry2->historyItems().at(0)->timeInfo().lastModificationTime(), timestamps["initialTime"]); QCOMPARE(mergedEntry2->historyItems().at(1)->notes(), QString("")); - QCOMPARE(mergedEntry2->historyItems().at(1)->timeInfo().lastModificationTime(), timestamps["oldestCommonHistoryTime"]); + QCOMPARE(mergedEntry2->historyItems().at(1)->timeInfo().lastModificationTime(), + timestamps["oldestCommonHistoryTime"]); QCOMPARE(mergedEntry2->historyItems().at(2)->notes(), QString("1 Common")); - QCOMPARE(mergedEntry2->historyItems().at(2)->timeInfo().lastModificationTime(), timestamps["newestCommonHistoryTime"]); + QCOMPARE(mergedEntry2->historyItems().at(2)->timeInfo().lastModificationTime(), + timestamps["newestCommonHistoryTime"]); QCOMPARE(mergedEntry2->historyItems().at(3)->notes(), QString("2 Destination")); - QCOMPARE(mergedEntry2->historyItems().at(3)->timeInfo().lastModificationTime(), timestamps["oldestDivergingHistoryTime"]); + QCOMPARE(mergedEntry2->historyItems().at(3)->timeInfo().lastModificationTime(), + timestamps["oldestDivergingHistoryTime"]); QCOMPARE(mergedEntry2->notes(), QString("3 Source")); QCOMPARE(mergedEntry2->timeInfo().lastModificationTime(), timestamps["newestDivergingHistoryTime"]); } diff --git a/tests/TestMerge.h b/tests/TestMerge.h index 7a18d2a39..159256f2b 100644 --- a/tests/TestMerge.h +++ b/tests/TestMerge.h @@ -66,15 +66,16 @@ private slots: private: Database* createTestDatabase(); Database* createTestDatabaseStructureClone(Database* source, int entryFlags, int groupFlags); - void testResolveConflictTemplate(int mergeMode, std::function&)> verification); - void testDeletionConflictTemplate(int mergeMode, std::function&)> verification); - static void assertDeletionNewerOnly(Database *db, const QMap &identifiers); - static void assertDeletionLocalOnly(Database *db, const QMap &identifiers); - static void assertUpdateMergedEntry1(Entry *entry, const QMap ×tamps); - static void assertUpdateReappliedEntry2(Entry *entry, const QMap ×tamps); - static void assertUpdateReappliedEntry1(Entry *entry, const QMap ×tamps); - static void assertUpdateMergedEntry2(Entry *entry, const QMap ×tamps); - + void testResolveConflictTemplate(int mergeMode, + std::function&)> verification); + void testDeletionConflictTemplate(int mergeMode, + std::function&)> verification); + static void assertDeletionNewerOnly(Database* db, const QMap& identifiers); + static void assertDeletionLocalOnly(Database* db, const QMap& identifiers); + static void assertUpdateMergedEntry1(Entry* entry, const QMap& timestamps); + static void assertUpdateReappliedEntry2(Entry* entry, const QMap& timestamps); + static void assertUpdateReappliedEntry1(Entry* entry, const QMap& timestamps); + static void assertUpdateMergedEntry2(Entry* entry, const QMap& timestamps); }; #endif // KEEPASSX_TESTMERGE_H diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp index fff558b22..254db3796 100644 --- a/tests/TestModified.cpp +++ b/tests/TestModified.cpp @@ -181,7 +181,6 @@ void TestModified::testGroupSets() root->setIcon(root->iconUuid()); QTRY_COMPARE(spyModified.count(), spyCount); - group->setUuid(QUuid::createUuid()); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); diff --git a/tests/TestOpenSSHKey.cpp b/tests/TestOpenSSHKey.cpp index 40fb9c302..63a1455d9 100644 --- a/tests/TestOpenSSHKey.cpp +++ b/tests/TestOpenSSHKey.cpp @@ -89,38 +89,36 @@ void TestOpenSSHKey::testParseDSA() void TestOpenSSHKey::testDecryptRSAAES128CBC() { - const QString keyString = QString( - "-----BEGIN RSA PRIVATE KEY-----\n" - "Proc-Type: 4,ENCRYPTED\n" - "DEK-Info: AES-128-CBC,804E4D214D1263FF94E3743FE799DBB4\n" - "\n" - "lM9TDfOTbiRhaGGDh7Hn+rqw8CCWcYBZYu7smyYLdnWKXKPmbne8CQFZBAS1FJwZ\n" - "6Mj6n075yFGyzN9/OfeqKiUA4adlbwLbGwB+yyKsC2FlsvRIEr4hup02WWM47vHj\n" - "DS4TRmNkE7MKFLhpNCyt5OGGM45s+/lwVTw51K0Hm99TBd72IrX4jfY9ZxAVbL3l\n" - "aTohL8x6oOTe7q318QgJoFi+DjJhDWLGLLJ7fBqD2imz2fmrY4j8Jpw2sDe1rj82\n" - "gMqqNG3FrfN0S4uYlWYH5pAh+BUcB1UdmTU/rV5wJMK1oUytmZv/J2+X/0k3Y93F\n" - "aw6JWOy28OizW+TQXvv8gREWsp5PEclqUZhhGQbVbCQCiDOxg+xiXNySdRH1IqjR\n" - "zQiKgD4SPzkxQekExPaIQT/KutWZdMNYybEqooCx8YyeDoN31z7Wa2rv6OulOn/j\n" - "wJFvyd2PT/6brHKI4ky8RYroDf4FbVYKfyEW5CSAg2OyL/tY/kSPgy/k0WT7fDwq\n" - "dPSuYM9yeWNL6kAhDqDOv8+s3xvOVEljktBvQvItQwVLmHszC3E2AcnaxzdblKPu\n" - "e3+mBT80NXHjERK2ht+/9JYseK1ujNbNAaG8SbKfU3FF0VlyJ0QW6TuIEdpNnymT\n" - "0fm0cDfKNaoeJIFnBRZhgIOJAic9DM0cTe/vSG69DaUYsaQPp36al7Fbux3GpFHS\n" - "OtJEySYGro/6zvJ9dDIEfIGZjA3RaMt6+DuyJZXQdT2RNXa9j60xW7dXh0En4n82\n" - "JUKTxYhDPLS5c8BzpJqoopxpKwElmrJ7Y3xpd6z2vIlD8ftuZrkk6siTMNQ2s7MI\n" - "Xl332O+0H4k7uSfczHPOOw36TFhNjGQAP0b7O+0/RVG0ttOIoAn7ZkX3nfdbtG5B\n" - "DWKvDaopvrcC2/scQ5uLUnqnBiGw1XiYpdg5ang7knHNzHZAIekVaYYZigpCAKp+\n" - "OtoaDeUEzqFhYVmF8ad1fgvC9ZUsuxS4XUHCKl0H6CJcvW9MJPVbveqYoK+j9qKd\n" - "iMIkQBP1kE2rzGZVGUkZTpM9LVD9nP0nsbr6E8BatFcNgRirsg2BTJglNpXlCmY6\n" - "ldzJ/ELBbzoXIn+0wTGai0o4eBPx55baef69JfPuZqEB9pLNE+mHstrqIwcfqYu4\n" - "M+Vzun1QshRMj9a1PVkIHfs1fLeebI4QCHO0vJlc9K4iYPM4rsDNO3YaAgGRuARS\n" - "f3McGiGFxkv5zxe8i05ZBnn+exE77jpRKxd223jAMe2wu4WiFB7ZVo4Db6b5Oo2T\n" - "TPh3VuY7TNMEKkcUi+mGLKjroocQ5j8WQYlfnyOaTalUVQDzOTNb67QIIoiszR0U\n" - "+AXGyxHj0QtotZFoPME+AbS9Zqy3SgSOuIzPBPU5zS4uoKNdD5NPE5YAuafCjsDy\n" - "MT4DVy+cPOQYUK022S7T2nsA1btmvUvD5LL2Mc8VuKsWOn/7FKZua6OCfipt6oX0\n" - "1tzYrw0/ALK+CIdVdYIiPPfxGZkr+JSLOOg7u50tpmen9GzxgNTv63miygwUAIDF\n" - "u0GbQwOueoA453/N75FcXOgrbqTdivyadUbRP+l7YJk/SfIytyJMOigejp+Z1lzF\n" - "-----END RSA PRIVATE KEY-----\n" - ); + const QString keyString = QString("-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-128-CBC,804E4D214D1263FF94E3743FE799DBB4\n" + "\n" + "lM9TDfOTbiRhaGGDh7Hn+rqw8CCWcYBZYu7smyYLdnWKXKPmbne8CQFZBAS1FJwZ\n" + "6Mj6n075yFGyzN9/OfeqKiUA4adlbwLbGwB+yyKsC2FlsvRIEr4hup02WWM47vHj\n" + "DS4TRmNkE7MKFLhpNCyt5OGGM45s+/lwVTw51K0Hm99TBd72IrX4jfY9ZxAVbL3l\n" + "aTohL8x6oOTe7q318QgJoFi+DjJhDWLGLLJ7fBqD2imz2fmrY4j8Jpw2sDe1rj82\n" + "gMqqNG3FrfN0S4uYlWYH5pAh+BUcB1UdmTU/rV5wJMK1oUytmZv/J2+X/0k3Y93F\n" + "aw6JWOy28OizW+TQXvv8gREWsp5PEclqUZhhGQbVbCQCiDOxg+xiXNySdRH1IqjR\n" + "zQiKgD4SPzkxQekExPaIQT/KutWZdMNYybEqooCx8YyeDoN31z7Wa2rv6OulOn/j\n" + "wJFvyd2PT/6brHKI4ky8RYroDf4FbVYKfyEW5CSAg2OyL/tY/kSPgy/k0WT7fDwq\n" + "dPSuYM9yeWNL6kAhDqDOv8+s3xvOVEljktBvQvItQwVLmHszC3E2AcnaxzdblKPu\n" + "e3+mBT80NXHjERK2ht+/9JYseK1ujNbNAaG8SbKfU3FF0VlyJ0QW6TuIEdpNnymT\n" + "0fm0cDfKNaoeJIFnBRZhgIOJAic9DM0cTe/vSG69DaUYsaQPp36al7Fbux3GpFHS\n" + "OtJEySYGro/6zvJ9dDIEfIGZjA3RaMt6+DuyJZXQdT2RNXa9j60xW7dXh0En4n82\n" + "JUKTxYhDPLS5c8BzpJqoopxpKwElmrJ7Y3xpd6z2vIlD8ftuZrkk6siTMNQ2s7MI\n" + "Xl332O+0H4k7uSfczHPOOw36TFhNjGQAP0b7O+0/RVG0ttOIoAn7ZkX3nfdbtG5B\n" + "DWKvDaopvrcC2/scQ5uLUnqnBiGw1XiYpdg5ang7knHNzHZAIekVaYYZigpCAKp+\n" + "OtoaDeUEzqFhYVmF8ad1fgvC9ZUsuxS4XUHCKl0H6CJcvW9MJPVbveqYoK+j9qKd\n" + "iMIkQBP1kE2rzGZVGUkZTpM9LVD9nP0nsbr6E8BatFcNgRirsg2BTJglNpXlCmY6\n" + "ldzJ/ELBbzoXIn+0wTGai0o4eBPx55baef69JfPuZqEB9pLNE+mHstrqIwcfqYu4\n" + "M+Vzun1QshRMj9a1PVkIHfs1fLeebI4QCHO0vJlc9K4iYPM4rsDNO3YaAgGRuARS\n" + "f3McGiGFxkv5zxe8i05ZBnn+exE77jpRKxd223jAMe2wu4WiFB7ZVo4Db6b5Oo2T\n" + "TPh3VuY7TNMEKkcUi+mGLKjroocQ5j8WQYlfnyOaTalUVQDzOTNb67QIIoiszR0U\n" + "+AXGyxHj0QtotZFoPME+AbS9Zqy3SgSOuIzPBPU5zS4uoKNdD5NPE5YAuafCjsDy\n" + "MT4DVy+cPOQYUK022S7T2nsA1btmvUvD5LL2Mc8VuKsWOn/7FKZua6OCfipt6oX0\n" + "1tzYrw0/ALK+CIdVdYIiPPfxGZkr+JSLOOg7u50tpmen9GzxgNTv63miygwUAIDF\n" + "u0GbQwOueoA453/N75FcXOgrbqTdivyadUbRP+l7YJk/SfIytyJMOigejp+Z1lzF\n" + "-----END RSA PRIVATE KEY-----\n"); const QByteArray keyData = keyString.toLatin1(); @@ -179,65 +177,61 @@ void TestOpenSSHKey::testParseRSA() void TestOpenSSHKey::testParseRSACompare() { - const QString oldKeyString = QString( - "-----BEGIN RSA PRIVATE KEY-----\n" - "MIIEpAIBAAKCAQEAsCHtJicDPWnvHSIKbnTZaJkIB9vgE0pmLdK580JUqBuonVbB\n" - "y1QTy0ZQ7/TtqvLPgwPK88TR46OLO/QGCzo2+XxgJ85uy0xfuyUYRmSuw0drsErN\n" - "mH8vU91lSBxsGDp9LtBbgHKoR23vMWZ34IxFRc55XphrIH48ijsMaL6bXBwF/3tD\n" - "9T3lm2MpP1huyVNnIY9+GRRWCy4f9LMj/UGu/n4RtwwfpOZBBRwYkq5QkzA9lPm/\n" - "VzF3MP1rKTMkvAw+Nfb383mkmc6MRnsa6uh6iDa9aVB7naegM13UJQX/PY1Ks6pO\n" - "XDpy/MQ7iCh+HmYNq5dRmARyaNl9xIXJNhz1cQIDAQABAoIBAQCnEUc1LUQxeM5K\n" - "wANNCqE+SgoIClPdeHC7fmrLh1ttqe6ib6ybBUFRS31yXs0hnfefunVEDKlaV8K2\n" - "N52UAMAsngFHQNRvGh6kEWeZPd9Xc+N98TZbNCjcT+DGKc+Om8wqH5DrodZlCq4c\n" - "GaoT4HnE4TjWtZTH2XXrWF9I66PKFWf070R44nvyVcvaZi4pC2YmURRPuGF6K1iK\n" - "dH8zM6HHG1UGu2W6hLNn+K01IulG0Lb8eWNaNYMmtQWaxyp7I2IWkkecUs3nCuiR\n" - "byFOoomCjdh8r9yZFvwxjGUhgtkALN9GCU0Mwve+s11IB2gevruN+q9/Qejbyfdm\n" - "IlgLAeTRAoGBANRcVzW9CYeobCf+U9hKJFEOur8XO+J2mTMaELA0EjWpTJFAeIT7\n" - "KeRpCRG4/vOSklxxRF6vP1EACA4Z+5BlN+FTipHHs+bSEgqkPZiiANDH7Zot5Iqv\n" - "1q0fRyldNRZNZK7DWp08BPNVWGA/EnEuKJiURxnxBaxNXbUyMCdjxvMvAoGBANRT\n" - "utbrqS/bAa/DcHKn3V6DRqBl3TDOfvCNjiKC84a67F2uXgzLIdMktr4d1NyCZVJd\n" - "7/zVgWORLIdg1eAi6rYGoOvNV39wwga7CF+m9sBY0wAaKYCELe6L26r4aQHVCX6n\n" - "rnIgUv+4o4itmU2iP0r3wlmDC9pDRQP82vfvQPlfAoGASwhleANW/quvq2HdViq8\n" - "Mje2HBalfhrRfpDTHK8JUBSFjTzuWG42GxJRtgVbb8x2ElujAKGDCaetMO5VSGu7\n" - "Fs5hw6iAFCpdXY0yhl+XUi2R8kwM2EPQ4lKO3jqkq0ClNmqn9a5jQWcCVt9yMLNS\n" - "fLbHeI8EpiCf34ngIcrLXNkCgYEAzlcEZuKkC46xB+dNew8pMTUwSKZVm53BfPKD\n" - "44QRN6imFbBjU9mAaJnwQbfp6dWKs834cGPolyM4++MeVfB42iZ88ksesgmZdUMD\n" - "szkl6O0pOJs0I+HQZVdjRbadDZvD22MHQ3+oST1dJ3FVXz3Cdo9qPuT8esMO6f4r\n" - "qfDH2s8CgYAXC/lWWHQ//PGP0pH4oiEXisx1K0X1u0xMGgrChxBRGRiKZUwNMIvJ\n" - "TqUu7IKizK19cLHF/NBvxHYHFw+m7puNjn6T1RtRCUjRZT7Dx1VHfVosL9ih5DA8\n" - "tpbZA5KGKcvHtB5DDgT0MHwzBZnb4Q//Rhovzn+HXZPsJTTgHHy3NQ==\n" - "-----END RSA PRIVATE KEY-----\n" - ); + const QString oldKeyString = QString("-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpAIBAAKCAQEAsCHtJicDPWnvHSIKbnTZaJkIB9vgE0pmLdK580JUqBuonVbB\n" + "y1QTy0ZQ7/TtqvLPgwPK88TR46OLO/QGCzo2+XxgJ85uy0xfuyUYRmSuw0drsErN\n" + "mH8vU91lSBxsGDp9LtBbgHKoR23vMWZ34IxFRc55XphrIH48ijsMaL6bXBwF/3tD\n" + "9T3lm2MpP1huyVNnIY9+GRRWCy4f9LMj/UGu/n4RtwwfpOZBBRwYkq5QkzA9lPm/\n" + "VzF3MP1rKTMkvAw+Nfb383mkmc6MRnsa6uh6iDa9aVB7naegM13UJQX/PY1Ks6pO\n" + "XDpy/MQ7iCh+HmYNq5dRmARyaNl9xIXJNhz1cQIDAQABAoIBAQCnEUc1LUQxeM5K\n" + "wANNCqE+SgoIClPdeHC7fmrLh1ttqe6ib6ybBUFRS31yXs0hnfefunVEDKlaV8K2\n" + "N52UAMAsngFHQNRvGh6kEWeZPd9Xc+N98TZbNCjcT+DGKc+Om8wqH5DrodZlCq4c\n" + "GaoT4HnE4TjWtZTH2XXrWF9I66PKFWf070R44nvyVcvaZi4pC2YmURRPuGF6K1iK\n" + "dH8zM6HHG1UGu2W6hLNn+K01IulG0Lb8eWNaNYMmtQWaxyp7I2IWkkecUs3nCuiR\n" + "byFOoomCjdh8r9yZFvwxjGUhgtkALN9GCU0Mwve+s11IB2gevruN+q9/Qejbyfdm\n" + "IlgLAeTRAoGBANRcVzW9CYeobCf+U9hKJFEOur8XO+J2mTMaELA0EjWpTJFAeIT7\n" + "KeRpCRG4/vOSklxxRF6vP1EACA4Z+5BlN+FTipHHs+bSEgqkPZiiANDH7Zot5Iqv\n" + "1q0fRyldNRZNZK7DWp08BPNVWGA/EnEuKJiURxnxBaxNXbUyMCdjxvMvAoGBANRT\n" + "utbrqS/bAa/DcHKn3V6DRqBl3TDOfvCNjiKC84a67F2uXgzLIdMktr4d1NyCZVJd\n" + "7/zVgWORLIdg1eAi6rYGoOvNV39wwga7CF+m9sBY0wAaKYCELe6L26r4aQHVCX6n\n" + "rnIgUv+4o4itmU2iP0r3wlmDC9pDRQP82vfvQPlfAoGASwhleANW/quvq2HdViq8\n" + "Mje2HBalfhrRfpDTHK8JUBSFjTzuWG42GxJRtgVbb8x2ElujAKGDCaetMO5VSGu7\n" + "Fs5hw6iAFCpdXY0yhl+XUi2R8kwM2EPQ4lKO3jqkq0ClNmqn9a5jQWcCVt9yMLNS\n" + "fLbHeI8EpiCf34ngIcrLXNkCgYEAzlcEZuKkC46xB+dNew8pMTUwSKZVm53BfPKD\n" + "44QRN6imFbBjU9mAaJnwQbfp6dWKs834cGPolyM4++MeVfB42iZ88ksesgmZdUMD\n" + "szkl6O0pOJs0I+HQZVdjRbadDZvD22MHQ3+oST1dJ3FVXz3Cdo9qPuT8esMO6f4r\n" + "qfDH2s8CgYAXC/lWWHQ//PGP0pH4oiEXisx1K0X1u0xMGgrChxBRGRiKZUwNMIvJ\n" + "TqUu7IKizK19cLHF/NBvxHYHFw+m7puNjn6T1RtRCUjRZT7Dx1VHfVosL9ih5DA8\n" + "tpbZA5KGKcvHtB5DDgT0MHwzBZnb4Q//Rhovzn+HXZPsJTTgHHy3NQ==\n" + "-----END RSA PRIVATE KEY-----\n"); - const QString newKeyString = QString( - "-----BEGIN OPENSSH PRIVATE KEY-----\n" - "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\n" - "NhAAAAAwEAAQAAAQEAsCHtJicDPWnvHSIKbnTZaJkIB9vgE0pmLdK580JUqBuonVbBy1QT\n" - "y0ZQ7/TtqvLPgwPK88TR46OLO/QGCzo2+XxgJ85uy0xfuyUYRmSuw0drsErNmH8vU91lSB\n" - "xsGDp9LtBbgHKoR23vMWZ34IxFRc55XphrIH48ijsMaL6bXBwF/3tD9T3lm2MpP1huyVNn\n" - "IY9+GRRWCy4f9LMj/UGu/n4RtwwfpOZBBRwYkq5QkzA9lPm/VzF3MP1rKTMkvAw+Nfb383\n" - "mkmc6MRnsa6uh6iDa9aVB7naegM13UJQX/PY1Ks6pOXDpy/MQ7iCh+HmYNq5dRmARyaNl9\n" - "xIXJNhz1cQAAA8DLsKINy7CiDQAAAAdzc2gtcnNhAAABAQCwIe0mJwM9ae8dIgpudNlomQ\n" - "gH2+ATSmYt0rnzQlSoG6idVsHLVBPLRlDv9O2q8s+DA8rzxNHjo4s79AYLOjb5fGAnzm7L\n" - "TF+7JRhGZK7DR2uwSs2Yfy9T3WVIHGwYOn0u0FuAcqhHbe8xZnfgjEVFznlemGsgfjyKOw\n" - "xovptcHAX/e0P1PeWbYyk/WG7JU2chj34ZFFYLLh/0syP9Qa7+fhG3DB+k5kEFHBiSrlCT\n" - "MD2U+b9XMXcw/WspMyS8DD419vfzeaSZzoxGexrq6HqINr1pUHudp6AzXdQlBf89jUqzqk\n" - "5cOnL8xDuIKH4eZg2rl1GYBHJo2X3Ehck2HPVxAAAAAwEAAQAAAQEApxFHNS1EMXjOSsAD\n" - "TQqhPkoKCApT3Xhwu35qy4dbbanuom+smwVBUUt9cl7NIZ33n7p1RAypWlfCtjedlADALJ\n" - "4BR0DUbxoepBFnmT3fV3PjffE2WzQo3E/gxinPjpvMKh+Q66HWZQquHBmqE+B5xOE41rWU\n" - "x9l161hfSOujyhVn9O9EeOJ78lXL2mYuKQtmJlEUT7hheitYinR/MzOhxxtVBrtluoSzZ/\n" - "itNSLpRtC2/HljWjWDJrUFmscqeyNiFpJHnFLN5wrokW8hTqKJgo3YfK/cmRb8MYxlIYLZ\n" - "ACzfRglNDML3vrNdSAdoHr67jfqvf0Ho28n3ZiJYCwHk0QAAAIAXC/lWWHQ//PGP0pH4oi\n" - "EXisx1K0X1u0xMGgrChxBRGRiKZUwNMIvJTqUu7IKizK19cLHF/NBvxHYHFw+m7puNjn6T\n" - "1RtRCUjRZT7Dx1VHfVosL9ih5DA8tpbZA5KGKcvHtB5DDgT0MHwzBZnb4Q//Rhovzn+HXZ\n" - "PsJTTgHHy3NQAAAIEA1FxXNb0Jh6hsJ/5T2EokUQ66vxc74naZMxoQsDQSNalMkUB4hPsp\n" - "5GkJEbj+85KSXHFEXq8/UQAIDhn7kGU34VOKkcez5tISCqQ9mKIA0Mftmi3kiq/WrR9HKV\n" - "01Fk1krsNanTwE81VYYD8ScS4omJRHGfEFrE1dtTIwJ2PG8y8AAACBANRTutbrqS/bAa/D\n" - "cHKn3V6DRqBl3TDOfvCNjiKC84a67F2uXgzLIdMktr4d1NyCZVJd7/zVgWORLIdg1eAi6r\n" - "YGoOvNV39wwga7CF+m9sBY0wAaKYCELe6L26r4aQHVCX6nrnIgUv+4o4itmU2iP0r3wlmD\n" - "C9pDRQP82vfvQPlfAAAABmlkX3JzYQECAwQ=\n" - "-----END OPENSSH PRIVATE KEY-----\n" - ); + const QString newKeyString = QString("-----BEGIN OPENSSH PRIVATE KEY-----\n" + "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\n" + "NhAAAAAwEAAQAAAQEAsCHtJicDPWnvHSIKbnTZaJkIB9vgE0pmLdK580JUqBuonVbBy1QT\n" + "y0ZQ7/TtqvLPgwPK88TR46OLO/QGCzo2+XxgJ85uy0xfuyUYRmSuw0drsErNmH8vU91lSB\n" + "xsGDp9LtBbgHKoR23vMWZ34IxFRc55XphrIH48ijsMaL6bXBwF/3tD9T3lm2MpP1huyVNn\n" + "IY9+GRRWCy4f9LMj/UGu/n4RtwwfpOZBBRwYkq5QkzA9lPm/VzF3MP1rKTMkvAw+Nfb383\n" + "mkmc6MRnsa6uh6iDa9aVB7naegM13UJQX/PY1Ks6pOXDpy/MQ7iCh+HmYNq5dRmARyaNl9\n" + "xIXJNhz1cQAAA8DLsKINy7CiDQAAAAdzc2gtcnNhAAABAQCwIe0mJwM9ae8dIgpudNlomQ\n" + "gH2+ATSmYt0rnzQlSoG6idVsHLVBPLRlDv9O2q8s+DA8rzxNHjo4s79AYLOjb5fGAnzm7L\n" + "TF+7JRhGZK7DR2uwSs2Yfy9T3WVIHGwYOn0u0FuAcqhHbe8xZnfgjEVFznlemGsgfjyKOw\n" + "xovptcHAX/e0P1PeWbYyk/WG7JU2chj34ZFFYLLh/0syP9Qa7+fhG3DB+k5kEFHBiSrlCT\n" + "MD2U+b9XMXcw/WspMyS8DD419vfzeaSZzoxGexrq6HqINr1pUHudp6AzXdQlBf89jUqzqk\n" + "5cOnL8xDuIKH4eZg2rl1GYBHJo2X3Ehck2HPVxAAAAAwEAAQAAAQEApxFHNS1EMXjOSsAD\n" + "TQqhPkoKCApT3Xhwu35qy4dbbanuom+smwVBUUt9cl7NIZ33n7p1RAypWlfCtjedlADALJ\n" + "4BR0DUbxoepBFnmT3fV3PjffE2WzQo3E/gxinPjpvMKh+Q66HWZQquHBmqE+B5xOE41rWU\n" + "x9l161hfSOujyhVn9O9EeOJ78lXL2mYuKQtmJlEUT7hheitYinR/MzOhxxtVBrtluoSzZ/\n" + "itNSLpRtC2/HljWjWDJrUFmscqeyNiFpJHnFLN5wrokW8hTqKJgo3YfK/cmRb8MYxlIYLZ\n" + "ACzfRglNDML3vrNdSAdoHr67jfqvf0Ho28n3ZiJYCwHk0QAAAIAXC/lWWHQ//PGP0pH4oi\n" + "EXisx1K0X1u0xMGgrChxBRGRiKZUwNMIvJTqUu7IKizK19cLHF/NBvxHYHFw+m7puNjn6T\n" + "1RtRCUjRZT7Dx1VHfVosL9ih5DA8tpbZA5KGKcvHtB5DDgT0MHwzBZnb4Q//Rhovzn+HXZ\n" + "PsJTTgHHy3NQAAAIEA1FxXNb0Jh6hsJ/5T2EokUQ66vxc74naZMxoQsDQSNalMkUB4hPsp\n" + "5GkJEbj+85KSXHFEXq8/UQAIDhn7kGU34VOKkcez5tISCqQ9mKIA0Mftmi3kiq/WrR9HKV\n" + "01Fk1krsNanTwE81VYYD8ScS4omJRHGfEFrE1dtTIwJ2PG8y8AAACBANRTutbrqS/bAa/D\n" + "cHKn3V6DRqBl3TDOfvCNjiKC84a67F2uXgzLIdMktr4d1NyCZVJd7/zVgWORLIdg1eAi6r\n" + "YGoOvNV39wwga7CF+m9sBY0wAaKYCELe6L26r4aQHVCX6nrnIgUv+4o4itmU2iP0r3wlmD\n" + "C9pDRQP82vfvQPlfAAAABmlkX3JzYQECAwQ=\n" + "-----END OPENSSH PRIVATE KEY-----\n"); const QByteArray oldKeyData = oldKeyString.toLatin1(); const QByteArray newKeyData = newKeyString.toLatin1(); @@ -294,38 +288,36 @@ void TestOpenSSHKey::testDecryptOpenSSHAES256CBC() void TestOpenSSHKey::testDecryptRSAAES256CBC() { - const QString keyString = QString( - "-----BEGIN RSA PRIVATE KEY-----\n" - "Proc-Type: 4,ENCRYPTED\n" - "DEK-Info: AES-256-CBC,D51E3F558B621BD9384627762CBD16AC\n" - "\n" - "b6nr/06Gj8/Nw3ZFMePFyZeuBodExvZZtZPSH3t/2ArcxXOkoqUhLmlcY/JrvnBF\n" - "JHc34wx/6Yng7mqtUMuk2iMemTzOj3JRx8zHUhwPLnjM/tmeOm0wBUb3WB4+rFZW\n" - "s1PaIgeKywKgFK0UkcSRpMuSaxheWmHrtJkBsHTF7Tg3ogPL8Dc+nhQlbe/ZGaQb\n" - "vMdSYcBMaXngS5ZiOafXeY8+l+IMMOZwy5vPTFQGqKHIzOxFhShs1hSExnwOXy69\n" - "wxrA/QftjNEy5ixIeGT7iQfRB04tEVg0DjYphTPmI2ophzFlwJVBjhj2cmmnsMZg\n" - "a2TdT/78KZsw2cA5ieMcU6d7Yz5p5nu5dyTbZonn6qWficdZRJwZnVb5ikPnJYbZ\n" - "1YJRHHND+RWtpanxz7WhStscTCLeI9w9j2gqBJSjKDPgJaoMiA+tyEoakNlPYg+9\n" - "DadJkBGP0g5E9zw0n4niqQ7eCxk7qolmW6Wtn2zL4UyeJKGi9NWFSGW9x/PmAIse\n" - "E2KVodiJMRNa8/qUZcW58ZG2uRnFTsW4BXdmzOy/Zp53TVGWStBVLDcldSD03ItD\n" - "JIWQWDgWp5xyVqPl+8mkW7xDY0GRVSJCyRkctQeGTGysy0BcNjgQQtiA3lPC0rY5\n" - "m2VxrCYU1KuyHsAjs/V8THcW4a1UdPcVBg1QbCh29bMoM6u4MuXVt7rkwxAV9HJa\n" - "VbwPsKy7V6G60KaAFIiOs0wdOzBZBoPGd9vBQOEzATh2FYJruDo2OfzEnhv25RxE\n" - "1q+C/Jds9cWqaNY8kNtUG799XIKkjrC6KvnoV6UA4BkGs2DAcO9rnwtl/hToEoBe\n" - "ZVj72dlTuS6l9rHqKaz2GI0k0SEt/ZoakPHeDRgPNcDvEZWitV8MuD6Mwb47Y88u\n" - "sjBmS5k4sJOtB4bLg/UShcqYfkv2OTsK90qGQtba9vMk04Xh1FuxB4fHa5VoKrsX\n" - "Th/LB34xoYugd16NPmLuawhSo70o4bT70GYpxnb4brGfjWiuthRdegAG9ESSX+M6\n" - "rNKQPnn2GSroIpkoA4k0PaflcE5tpzeIiJdv0h65N3vw6MFnCaWy8sRSy9fMyRim\n" - "U8QZB2jcp+YjUU/eny3scuh0Vqt6g1tfFbI84pCC5bArBirf63MeMtwDU/IVImax\n" - "xzKOzl7k8ropA+rhAJ4Z9X35EmUncBXhf8g39w6nFuSlqjE6rMxCrsrehljQ1Iuz\n" - "bujaJ2PKpf98OejHDKnMDOfBBq0DdeERCYWlCcqWSgrEgHh4vB5dEQAPP5bAkdZj\n" - "m0Dq+gF99yadioxf3/MUZVTa1dHklBJJkXTYVPeyH07Th5j7bGCcVb9Zd2Ao/Dia\n" - "MPWf6xViCC6d0njCLQY2R8mOR5OMVsdlFrsKZMQ/lqjS/WSM6URDkuGb0Cq94TQd\n" - "7DoblcA50FTwYrVXMygWygbjzJxhcoJDHztzwoqLT/ghh+6zRg6R/fY222tHHbhz\n" - "nePf421NILzfxnuW+GOwRCM5+IHE3OBS/PYDGijjRFHU4ky0rRRDE64m9CeFzeBh\n" - "CnFvW6Yx3Hrh5tXBP7kRZ6KjyrPP7tI4ciVSJceSBLRzFmoBr10kRMm+VsUh2xZH\n" - "-----END RSA PRIVATE KEY-----\n" - ); + const QString keyString = QString("-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-256-CBC,D51E3F558B621BD9384627762CBD16AC\n" + "\n" + "b6nr/06Gj8/Nw3ZFMePFyZeuBodExvZZtZPSH3t/2ArcxXOkoqUhLmlcY/JrvnBF\n" + "JHc34wx/6Yng7mqtUMuk2iMemTzOj3JRx8zHUhwPLnjM/tmeOm0wBUb3WB4+rFZW\n" + "s1PaIgeKywKgFK0UkcSRpMuSaxheWmHrtJkBsHTF7Tg3ogPL8Dc+nhQlbe/ZGaQb\n" + "vMdSYcBMaXngS5ZiOafXeY8+l+IMMOZwy5vPTFQGqKHIzOxFhShs1hSExnwOXy69\n" + "wxrA/QftjNEy5ixIeGT7iQfRB04tEVg0DjYphTPmI2ophzFlwJVBjhj2cmmnsMZg\n" + "a2TdT/78KZsw2cA5ieMcU6d7Yz5p5nu5dyTbZonn6qWficdZRJwZnVb5ikPnJYbZ\n" + "1YJRHHND+RWtpanxz7WhStscTCLeI9w9j2gqBJSjKDPgJaoMiA+tyEoakNlPYg+9\n" + "DadJkBGP0g5E9zw0n4niqQ7eCxk7qolmW6Wtn2zL4UyeJKGi9NWFSGW9x/PmAIse\n" + "E2KVodiJMRNa8/qUZcW58ZG2uRnFTsW4BXdmzOy/Zp53TVGWStBVLDcldSD03ItD\n" + "JIWQWDgWp5xyVqPl+8mkW7xDY0GRVSJCyRkctQeGTGysy0BcNjgQQtiA3lPC0rY5\n" + "m2VxrCYU1KuyHsAjs/V8THcW4a1UdPcVBg1QbCh29bMoM6u4MuXVt7rkwxAV9HJa\n" + "VbwPsKy7V6G60KaAFIiOs0wdOzBZBoPGd9vBQOEzATh2FYJruDo2OfzEnhv25RxE\n" + "1q+C/Jds9cWqaNY8kNtUG799XIKkjrC6KvnoV6UA4BkGs2DAcO9rnwtl/hToEoBe\n" + "ZVj72dlTuS6l9rHqKaz2GI0k0SEt/ZoakPHeDRgPNcDvEZWitV8MuD6Mwb47Y88u\n" + "sjBmS5k4sJOtB4bLg/UShcqYfkv2OTsK90qGQtba9vMk04Xh1FuxB4fHa5VoKrsX\n" + "Th/LB34xoYugd16NPmLuawhSo70o4bT70GYpxnb4brGfjWiuthRdegAG9ESSX+M6\n" + "rNKQPnn2GSroIpkoA4k0PaflcE5tpzeIiJdv0h65N3vw6MFnCaWy8sRSy9fMyRim\n" + "U8QZB2jcp+YjUU/eny3scuh0Vqt6g1tfFbI84pCC5bArBirf63MeMtwDU/IVImax\n" + "xzKOzl7k8ropA+rhAJ4Z9X35EmUncBXhf8g39w6nFuSlqjE6rMxCrsrehljQ1Iuz\n" + "bujaJ2PKpf98OejHDKnMDOfBBq0DdeERCYWlCcqWSgrEgHh4vB5dEQAPP5bAkdZj\n" + "m0Dq+gF99yadioxf3/MUZVTa1dHklBJJkXTYVPeyH07Th5j7bGCcVb9Zd2Ao/Dia\n" + "MPWf6xViCC6d0njCLQY2R8mOR5OMVsdlFrsKZMQ/lqjS/WSM6URDkuGb0Cq94TQd\n" + "7DoblcA50FTwYrVXMygWygbjzJxhcoJDHztzwoqLT/ghh+6zRg6R/fY222tHHbhz\n" + "nePf421NILzfxnuW+GOwRCM5+IHE3OBS/PYDGijjRFHU4ky0rRRDE64m9CeFzeBh\n" + "CnFvW6Yx3Hrh5tXBP7kRZ6KjyrPP7tI4ciVSJceSBLRzFmoBr10kRMm+VsUh2xZH\n" + "-----END RSA PRIVATE KEY-----\n"); const QByteArray keyData = keyString.toLatin1(); @@ -374,38 +366,36 @@ void TestOpenSSHKey::testDecryptOpenSSHAES256CTR() void TestOpenSSHKey::testDecryptRSAAES256CTR() { - const QString keyString = QString( - "-----BEGIN RSA PRIVATE KEY-----\n" - "Proc-Type: 4,ENCRYPTED\n" - "DEK-Info: AES-256-CTR,192421854316290DFA8F469A1E8CB9BB\n" - "\n" - "3h7gUWua+jcvhYj1vUusbMdOG9j8SmNWFV5Hfersi8nF4ddsWEQDnMrRuhtIn4tU\n" - "GcLY+SXguim5XXwF8gG1tmvpvFMhudTfX+0cIAX7eAVmqLy2RTA18DWqDwWokVP0\n" - "RJPgRJJSorjMtu2F0YGVVjElW7pHIal7luNk3BDgYUMlgSg0BGOWb+9BkXcEnfc8\n" - "KEwsJw2onoR2eXo6rYnczGaqPhAPu+I+PfUn0J8PFiffWc1KebRntLdRWeNsBS4p\n" - "oxtqByzMYIu/WPSJJ5iFoNdKaWQPiZJB+juwI1wNLEtpzKkhpc7/6mOy87h+0eGV\n" - "fF7javrbHv37eE+k2iZXrcLfvRpiBqt5+uhhCaM8TivBeUho5J38ru/wt/dk+OvY\n" - "tzXboWA4zVnaYmqta1CkXYKOmb5a8TWEwtxmAuE6kCz/n3pPa6gwkwsyGI65IEyX\n" - "ycJsbwUilAzXTcz5bIruGx38Sa9fndAN9llOQMS/hdyNs5W5dO1XZ5gU+ARPce+j\n" - "+A2R8oCUv+2ciEu8z3F++U9aTRmTlD3xeIM0IWUFXKt8Y9fSRC5XoPCbZYNxnV6/\n" - "hn9NPKCb890Faxies3MABOB5IZ0aTPWkx9ntxFhMaXyfkX2YthNO0GzAENPP9Knt\n" - "DYhQePlKQ7sNi8+wzsHNeDxNuL/+Rib2MN3ankDtHIsqFz/Em+rD0+3ya8bLy3pP\n" - "eeUiNpezL+uxI5llq/pikzK4sOgvH1r5YEkMxt9I09grkBwxR7DMBo0vgRE2MLiL\n" - "nlri8TDwArC1+0gE8NspkkClsBOHXuVlGZo5lup2tn5MzERQcLvuFnAby/GnaVXQ\n" - "Hm76teb1wMdL58FrdZsKR6e80E+F6JpTsz0a3XJqptgAwGsoxqizkUNJG5hRP8bi\n" - "NBCFQZPeYi/GxgN5O2UkxhgRkKAcrHg+G87nhLk1ipsc214rb6iOspNizP6fGDuv\n" - "/bsNTpYRgMNxCLh5Nv0HSUqckoNKOcIVe/9nF5/LLFGfhz95agjKTbBygThFK28N\n" - "bnHq5fO9yKCMrGCRBQ6No1wwexyS4IAq17LcQP3k4w4n+Wt2GjO5HIldGOEyGqCE\n" - "zeHYrPpGXF/yf3XTm00XghdQtVtRJptdddXVGZN3EN2w7/ghOSIIlsJO9C4IRgU3\n" - "WkhX7oOpSE4wmXd5Ada+D1U46snW5nWANWko2NmQNVDeJcvuymL6t2ccNYeFWiA+\n" - "Hlv0avBnqng7ZWPxYacqZI3+vU0rN9usN1pzwY/4NsBa34o3M7u6KvzEkyewbyUT\n" - "VfXLJ8XRzb2u4NqQv0WiTBIRxvVB1sRPcrwB4HWKHwRFT8T7f1fefteROrKV7aKm\n" - "Q48pckidDM0ORh1yIET8u24Mreo5eeWXjVJ9MHoM0486VySYxMwk8yp4tnaHx5kd\n" - "fGlBbbwFOifhzjAk0u3KJRAG85t2GZhfTMo1IHS2kdu4Xs1N00ZmK0hjeGG+DCwy\n" - "06ZKHOF5BHWU3SpQEjCsPDwfIpOINWGAJJnp6NIVf7FkHwViV50GWWGSZal4NqZy\n" - "kR19buHiOb7KnGoPCw8MUmIym8v30FePhM4YQ7ASmRtsXlAhQNRX\n" - "-----END RSA PRIVATE KEY-----\n" - ); + const QString keyString = QString("-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-256-CTR,192421854316290DFA8F469A1E8CB9BB\n" + "\n" + "3h7gUWua+jcvhYj1vUusbMdOG9j8SmNWFV5Hfersi8nF4ddsWEQDnMrRuhtIn4tU\n" + "GcLY+SXguim5XXwF8gG1tmvpvFMhudTfX+0cIAX7eAVmqLy2RTA18DWqDwWokVP0\n" + "RJPgRJJSorjMtu2F0YGVVjElW7pHIal7luNk3BDgYUMlgSg0BGOWb+9BkXcEnfc8\n" + "KEwsJw2onoR2eXo6rYnczGaqPhAPu+I+PfUn0J8PFiffWc1KebRntLdRWeNsBS4p\n" + "oxtqByzMYIu/WPSJJ5iFoNdKaWQPiZJB+juwI1wNLEtpzKkhpc7/6mOy87h+0eGV\n" + "fF7javrbHv37eE+k2iZXrcLfvRpiBqt5+uhhCaM8TivBeUho5J38ru/wt/dk+OvY\n" + "tzXboWA4zVnaYmqta1CkXYKOmb5a8TWEwtxmAuE6kCz/n3pPa6gwkwsyGI65IEyX\n" + "ycJsbwUilAzXTcz5bIruGx38Sa9fndAN9llOQMS/hdyNs5W5dO1XZ5gU+ARPce+j\n" + "+A2R8oCUv+2ciEu8z3F++U9aTRmTlD3xeIM0IWUFXKt8Y9fSRC5XoPCbZYNxnV6/\n" + "hn9NPKCb890Faxies3MABOB5IZ0aTPWkx9ntxFhMaXyfkX2YthNO0GzAENPP9Knt\n" + "DYhQePlKQ7sNi8+wzsHNeDxNuL/+Rib2MN3ankDtHIsqFz/Em+rD0+3ya8bLy3pP\n" + "eeUiNpezL+uxI5llq/pikzK4sOgvH1r5YEkMxt9I09grkBwxR7DMBo0vgRE2MLiL\n" + "nlri8TDwArC1+0gE8NspkkClsBOHXuVlGZo5lup2tn5MzERQcLvuFnAby/GnaVXQ\n" + "Hm76teb1wMdL58FrdZsKR6e80E+F6JpTsz0a3XJqptgAwGsoxqizkUNJG5hRP8bi\n" + "NBCFQZPeYi/GxgN5O2UkxhgRkKAcrHg+G87nhLk1ipsc214rb6iOspNizP6fGDuv\n" + "/bsNTpYRgMNxCLh5Nv0HSUqckoNKOcIVe/9nF5/LLFGfhz95agjKTbBygThFK28N\n" + "bnHq5fO9yKCMrGCRBQ6No1wwexyS4IAq17LcQP3k4w4n+Wt2GjO5HIldGOEyGqCE\n" + "zeHYrPpGXF/yf3XTm00XghdQtVtRJptdddXVGZN3EN2w7/ghOSIIlsJO9C4IRgU3\n" + "WkhX7oOpSE4wmXd5Ada+D1U46snW5nWANWko2NmQNVDeJcvuymL6t2ccNYeFWiA+\n" + "Hlv0avBnqng7ZWPxYacqZI3+vU0rN9usN1pzwY/4NsBa34o3M7u6KvzEkyewbyUT\n" + "VfXLJ8XRzb2u4NqQv0WiTBIRxvVB1sRPcrwB4HWKHwRFT8T7f1fefteROrKV7aKm\n" + "Q48pckidDM0ORh1yIET8u24Mreo5eeWXjVJ9MHoM0486VySYxMwk8yp4tnaHx5kd\n" + "fGlBbbwFOifhzjAk0u3KJRAG85t2GZhfTMo1IHS2kdu4Xs1N00ZmK0hjeGG+DCwy\n" + "06ZKHOF5BHWU3SpQEjCsPDwfIpOINWGAJJnp6NIVf7FkHwViV50GWWGSZal4NqZy\n" + "kR19buHiOb7KnGoPCw8MUmIym8v30FePhM4YQ7ASmRtsXlAhQNRX\n" + "-----END RSA PRIVATE KEY-----\n"); const QByteArray keyData = keyString.toLatin1(); @@ -422,16 +412,14 @@ void TestOpenSSHKey::testDecryptRSAAES256CTR() void TestOpenSSHKey::testDecryptUTF8() { - const QString keyString = QString( - "-----BEGIN OPENSSH PRIVATE KEY-----\n" - "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDtSl4OvT\n" - "H/wHay2dvjOnpIAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIIhrBrn6rb+d3GwF\n" - "ifpJ6gYut95lXvwypiQmu9ZpA8H9AAAAsD85Gpn2mbVEWq3ygx11wBnN5mUQXnMuP48rLv\n" - "0qwm12IihOkrR925ledwN2Sa5mkkL0XjDz6SsKfIFhFa84hUHQdw5zPR8yVGRWLzkNDmo7\n" - "WXNpnoE4ebsX2j0TsBNjP80RUcJdjSXidkt3+aZjaCfquO8cBQn4GJJSDSPwFJYlJeSD/h\n" - "vpb72MEQchOD3NNMORYTJ5sOJ73RayhhmwjTVlrG+zYAw6fXW0YXX3+5LE\n" - "-----END OPENSSH PRIVATE KEY-----\n" - ); + const QString keyString = QString("-----BEGIN OPENSSH PRIVATE KEY-----\n" + "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDtSl4OvT\n" + "H/wHay2dvjOnpIAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIIhrBrn6rb+d3GwF\n" + "ifpJ6gYut95lXvwypiQmu9ZpA8H9AAAAsD85Gpn2mbVEWq3ygx11wBnN5mUQXnMuP48rLv\n" + "0qwm12IihOkrR925ledwN2Sa5mkkL0XjDz6SsKfIFhFa84hUHQdw5zPR8yVGRWLzkNDmo7\n" + "WXNpnoE4ebsX2j0TsBNjP80RUcJdjSXidkt3+aZjaCfquO8cBQn4GJJSDSPwFJYlJeSD/h\n" + "vpb72MEQchOD3NNMORYTJ5sOJ73RayhhmwjTVlrG+zYAw6fXW0YXX3+5LE\n" + "-----END OPENSSH PRIVATE KEY-----\n"); const QByteArray keyData = keyString.toLatin1(); diff --git a/tests/TestPasswordGenerator.cpp b/tests/TestPasswordGenerator.cpp index 53cf25c31..b043a7cd0 100644 --- a/tests/TestPasswordGenerator.cpp +++ b/tests/TestPasswordGenerator.cpp @@ -19,8 +19,8 @@ #include "core/PasswordGenerator.h" #include "crypto/Crypto.h" -#include #include +#include QTEST_GUILESS_MAIN(TestPasswordGenerator) @@ -89,14 +89,14 @@ void TestPasswordGenerator::testCharClasses() regex.setPattern(R"(^[^a-zA-Z0-9\.,:;"'\-/\\_|!\*\+\-<=>\?#`~%&^$@]+$)"); QVERIFY(regex.match(password).hasMatch()); - generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters - | PasswordGenerator::CharClass::UpperLetters | PasswordGenerator::CharClass::Braces); + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters + | PasswordGenerator::CharClass::Braces); password = generator.generatePassword(); regex.setPattern(R"(^[a-zA-Z\(\)\[\]\{\}]+$)"); QVERIFY(regex.match(password).hasMatch()); - generator.setCharClasses(PasswordGenerator::CharClass::Quotes - | PasswordGenerator::CharClass::Numbers | PasswordGenerator::CharClass::Dashes); + generator.setCharClasses(PasswordGenerator::CharClass::Quotes | PasswordGenerator::CharClass::Numbers + | PasswordGenerator::CharClass::Dashes); password = generator.generatePassword(); regex.setPattern(R"(^["'\d\-/\\_|]+$)"); QVERIFY(regex.match(password).hasMatch()); @@ -116,15 +116,14 @@ void TestPasswordGenerator::testLookalikeExclusion() QRegularExpression regex("^[^lI0]+$"); QVERIFY(regex.match(password).hasMatch()); - generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | - PasswordGenerator::CharClass::UpperLetters | PasswordGenerator::CharClass::Numbers); + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters + | PasswordGenerator::CharClass::Numbers); password = generator.generatePassword(); regex.setPattern("^[^lI01]+$"); QVERIFY(regex.match(password).hasMatch()); - generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters - | PasswordGenerator::CharClass::UpperLetters | PasswordGenerator::CharClass::Numbers - | PasswordGenerator::CharClass::EASCII); + generator.setCharClasses(PasswordGenerator::CharClass::LowerLetters | PasswordGenerator::CharClass::UpperLetters + | PasswordGenerator::CharClass::Numbers | PasswordGenerator::CharClass::EASCII); password = generator.generatePassword(); regex.setPattern("^[^lI01﹒]+$"); QVERIFY(regex.match(password).hasMatch()); diff --git a/tests/TestPasswordGenerator.h b/tests/TestPasswordGenerator.h index 5287e5bde..56c4d65a1 100644 --- a/tests/TestPasswordGenerator.h +++ b/tests/TestPasswordGenerator.h @@ -30,4 +30,4 @@ private slots: void testLookalikeExclusion(); }; -#endif //KEEPASSXC_TESTPASSWORDGENERATOR_H +#endif // KEEPASSXC_TESTPASSWORDGENERATOR_H diff --git a/tests/TestSymmetricCipher.cpp b/tests/TestSymmetricCipher.cpp index b69e463b1..752fc09df 100644 --- a/tests/TestSymmetricCipher.cpp +++ b/tests/TestSymmetricCipher.cpp @@ -44,6 +44,7 @@ void TestSymmetricCipher::testAlgorithmToCipher() QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::InvalidAlgorithm), QUuid()); } +// clang-format off void TestSymmetricCipher::testEncryptionDecryption_data() { QTest::addColumn("algorithm"); @@ -109,6 +110,7 @@ void TestSymmetricCipher::testEncryptionDecryption_data() << QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5") << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"); } +// clang-format on void TestSymmetricCipher::testEncryptionDecryption() { @@ -165,19 +167,17 @@ void TestSymmetricCipher::testAesCbcPadding_data() QTest::addColumn("plainText"); QTest::addColumn("padding"); - QTest::newRow("AES128") - << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") - << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") - << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") - << QByteArray::fromHex("55e21d7100b988ffec32feeafaf23538"); + QTest::newRow("AES128") << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("55e21d7100b988ffec32feeafaf23538"); - QTest::newRow("AES256") - << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") - << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") - << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") - << QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2"); + QTest::newRow("AES256") << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") + << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") + << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2"); } void TestSymmetricCipher::testAesCbcPadding() @@ -215,17 +215,16 @@ void TestSymmetricCipher::testInplaceEcb_data() QTest::addColumn("plainText"); QTest::addColumn("cipherText"); - QTest::newRow("AES128") - << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a") - << QByteArray::fromHex("3ad77bb40d7a3660a89ecaf32466ef97"); + QTest::newRow("AES128") << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a") + << QByteArray::fromHex("3ad77bb40d7a3660a89ecaf32466ef97"); } void TestSymmetricCipher::testInplaceEcb() { - QFETCH(QByteArray, key); - QFETCH(QByteArray, plainText); - QFETCH(QByteArray, cipherText); + QFETCH(QByteArray, key); + QFETCH(QByteArray, plainText); + QFETCH(QByteArray, cipherText); SymmetricCipher cipherInPlaceEnc(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); QVERIFY(cipherInPlaceEnc.init(key, QByteArray(16, 0))); diff --git a/tests/TestTools.cpp b/tests/TestTools.cpp index c3acd3c6d..de5a80c0a 100644 --- a/tests/TestTools.cpp +++ b/tests/TestTools.cpp @@ -28,7 +28,7 @@ namespace { return wholes + QLocale().decimalPoint() + fractions + " " + unit; } -} +} // namespace void TestTools::testHumanReadableFileSize() { diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 48c308243..3fa8df09a 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -49,12 +49,7 @@ #include "crypto/Crypto.h" #include "crypto/kdf/AesKdf.h" #include "format/KeePass2Reader.h" -#include "keys/PasswordKey.h" -#include "keys/FileKey.h" #include "gui/ApplicationSettingsWidget.h" -#include "gui/dbsettings/DatabaseSettingsDialog.h" -#include "gui/masterkey/PasswordEditWidget.h" -#include "gui/masterkey/KeyFileEditWidget.h" #include "gui/CategoryListWidget.h" #include "gui/CloneDialog.h" #include "gui/DatabaseTabWidget.h" @@ -63,15 +58,19 @@ #include "gui/MessageBox.h" #include "gui/PasswordEdit.h" #include "gui/SearchWidget.h" -#include "gui/TotpSetupDialog.h" #include "gui/TotpDialog.h" +#include "gui/TotpSetupDialog.h" +#include "gui/dbsettings/DatabaseSettingsDialog.h" #include "gui/entry/EditEntryWidget.h" #include "gui/entry/EntryView.h" #include "gui/group/EditGroupWidget.h" #include "gui/group/GroupModel.h" #include "gui/group/GroupView.h" -#include "gui/wizard/NewDatabaseWizard.h" #include "gui/masterkey/KeyComponentWidget.h" +#include "gui/masterkey/KeyFileEditWidget.h" +#include "gui/masterkey/PasswordEditWidget.h" +#include "gui/wizard/NewDatabaseWizard.h" +#include "keys/FileKey.h" #include "keys/PasswordKey.h" QTEST_MAIN(TestGui) @@ -159,7 +158,7 @@ void TestGui::testSettingsDefaultTabOrder() auto* settingsWidget = m_mainWindow->findChild(); QVERIFY(settingsWidget->isVisible()); QCOMPARE(settingsWidget->findChild("categoryList")->currentCategory(), 0); - for (auto* w: settingsWidget->findChildren()) { + for (auto* w : settingsWidget->findChildren()) { if (w->currentIndex() != 0) { QFAIL("Application settings contain QTabWidgets whose default index is not 0"); } @@ -171,7 +170,7 @@ void TestGui::testSettingsDefaultTabOrder() auto* dbSettingsWidget = m_mainWindow->findChild(); QVERIFY(dbSettingsWidget->isVisible()); QCOMPARE(dbSettingsWidget->findChild("categoryList")->currentCategory(), 0); - for (auto* w: dbSettingsWidget->findChildren()) { + for (auto* w : dbSettingsWidget->findChildren()) { if (w->currentIndex() != 0) { QFAIL("Database settings contain QTabWidgets whose default index is not 0"); } @@ -1091,7 +1090,11 @@ void TestGui::testDragAndDropGroup() // dropping parent on child is supposed to fail dragAndDropGroup(groupModel->index(0, 0, rootIndex), - groupModel->index(0, 0, groupModel->index(0, 0, rootIndex)), -1, false, "NewDatabase", 0); + groupModel->index(0, 0, groupModel->index(0, 0, rootIndex)), + -1, + false, + "NewDatabase", + 0); dragAndDropGroup(groupModel->index(1, 0, rootIndex), rootIndex, 0, true, "NewDatabase", 0); diff --git a/tests/mock/MockChallengeResponseKey.cpp b/tests/mock/MockChallengeResponseKey.cpp index 628504d85..1fa79dc84 100644 --- a/tests/mock/MockChallengeResponseKey.cpp +++ b/tests/mock/MockChallengeResponseKey.cpp @@ -13,7 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ #include "MockChallengeResponseKey.h" diff --git a/tests/mock/MockChallengeResponseKey.h b/tests/mock/MockChallengeResponseKey.h index 1ef11bf60..a9aeeb412 100644 --- a/tests/mock/MockChallengeResponseKey.h +++ b/tests/mock/MockChallengeResponseKey.h @@ -13,7 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ #ifndef KEEPASSXC_MOCKCHALLENGERESPONSEKEY_H #define KEEPASSXC_MOCKCHALLENGERESPONSEKEY_H diff --git a/tests/util/TemporaryFile.h b/tests/util/TemporaryFile.h index 4e39a9ae7..1fca110a5 100644 --- a/tests/util/TemporaryFile.h +++ b/tests/util/TemporaryFile.h @@ -47,4 +47,4 @@ public: #endif }; -#endif //KEEPASSXC_TEMPORARYFILE_H +#endif // KEEPASSXC_TEMPORARYFILE_H From e81841550b59749be2f12a66874f729cb9a8587c Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Fri, 2 Nov 2018 03:38:55 +0100 Subject: [PATCH 77/84] Correct checks for containers' emptiness --- src/core/CsvParser.cpp | 5 +++-- src/format/KdbxXmlReader.cpp | 2 +- src/gui/DatabaseOpenWidget.cpp | 2 +- src/gui/EntryPreviewWidget.cpp | 2 +- tests/TestKeePass1Reader.cpp | 2 +- tests/TestKeys.cpp | 4 ++-- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/CsvParser.cpp b/src/core/CsvParser.cpp index e545d1db4..7e4929481 100644 --- a/src/core/CsvParser.cpp +++ b/src/core/CsvParser.cpp @@ -403,10 +403,11 @@ QString CsvParser::getStatus() const int CsvParser::getCsvCols() const { - if ((m_table.size() > 0) && (m_table.at(0).size() > 0)) + if (!m_table.isEmpty() && !m_table.at(0).isEmpty()) { return m_table.at(0).size(); - else + } else { return 0; + } } int CsvParser::getCsvRows() const diff --git a/src/format/KdbxXmlReader.cpp b/src/format/KdbxXmlReader.cpp index 163a9fa9d..efef724d9 100644 --- a/src/format/KdbxXmlReader.cpp +++ b/src/format/KdbxXmlReader.cpp @@ -731,7 +731,7 @@ Entry* KdbxXmlReader::parseEntry(bool history) } if (m_xml.name() == "Binary") { QPair ref = parseEntryBinary(entry); - if (!ref.first.isNull() && !ref.second.isNull()) { + if (!ref.first.isEmpty() && !ref.second.isEmpty()) { binaryRefs.append(ref); } continue; diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 9c496aa31..0983ad855 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -174,7 +174,7 @@ QSharedPointer DatabaseOpenWidget::database() void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile) { - if (!pw.isNull()) { + if (!pw.isEmpty()) { m_ui->editPassword->setText(pw); } if (!keyFile.isEmpty()) { diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index 7c88d1fa5..359360879 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -220,7 +220,7 @@ void EntryPreviewWidget::updateEntryAttributesTab() m_ui->entryAttributesEdit->clear(); const EntryAttributes* attributes = m_currentEntry->attributes(); const QStringList customAttributes = attributes->customKeys(); - const bool haveAttributes = customAttributes.size() > 0; + const bool haveAttributes = !customAttributes.isEmpty(); setTabEnabled(m_ui->entryTabWidget, m_ui->entryAttributesTab, haveAttributes); if (haveAttributes) { QString attributesText; diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index bb9e07a42..078447acb 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -267,7 +267,7 @@ void TestKeePass1Reader::reopenDatabase(QSharedPointer db, QVERIFY(buffer.seek(0)); auto key = QSharedPointer::create(); - if (!password.isNull()) { + if (!password.isEmpty()) { key->addKey(QSharedPointer::create(password)); } if (!keyfileName.isEmpty()) { diff --git a/tests/TestKeys.cpp b/tests/TestKeys.cpp index c2e9aa196..d25a2bca8 100644 --- a/tests/TestKeys.cpp +++ b/tests/TestKeys.cpp @@ -227,7 +227,7 @@ void TestKeys::testCompositeKeyComponents() auto fileKeyEnc = QSharedPointer::create(); QString error; fileKeyEnc->load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key"), &error); - if (!error.isNull()) { + if (!error.isEmpty()) { QFAIL(qPrintable(error)); } auto challengeResponseKeyEnc = QSharedPointer::create(QByteArray(16, 0x10)); @@ -285,7 +285,7 @@ void TestKeys::testCompositeKeyComponents() compositeKeyDec3->addKey(passwordKeyEnc); auto fileKeyWrong = QSharedPointer::create(); fileKeyWrong->load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed2.key"), &error); - if (!error.isNull()) { + if (!error.isEmpty()) { QFAIL(qPrintable(error)); } compositeKeyDec3->addKey(fileKeyWrong); From 294188dea1b417f3f4c441dc0a07f0e9fd07f931 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Wed, 28 Nov 2018 21:51:57 -0500 Subject: [PATCH 78/84] Fix typo in EditEntryWidget after code cleanup #2 --- src/gui/entry/EditEntryWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 335ac1aa4..99200a1f9 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -263,7 +263,7 @@ void EditEntryWidget::setupEntryUpdate() connect(m_mainUi->passwordRepeatEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(setUnsavedChanges())); #ifdef WITH_XC_NETWORKING - connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString&)), this, SLOT(updateFaviconButtonEnable(QString&))); + connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), this, SLOT(updateFaviconButtonEnable(QString))); #endif connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); connect(m_mainUi->notesEnabled, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges())); From 9be538a5f648dc170dbc66caf0ba54d8f9ad33f0 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 1 Dec 2018 10:54:55 +0100 Subject: [PATCH 79/84] Update translations from Transifex --- share/translations/keepassx_ar.ts | 532 ++--- share/translations/keepassx_bn.ts | 156 +- share/translations/keepassx_ca.ts | 107 +- share/translations/keepassx_cs.ts | 223 +- share/translations/keepassx_da.ts | 126 +- share/translations/keepassx_de.ts | 149 +- share/translations/keepassx_el.ts | 103 +- share/translations/keepassx_en.ts | 2870 ++++++++++++++++---------- share/translations/keepassx_en_GB.ts | 96 +- share/translations/keepassx_en_US.ts | 71 +- share/translations/keepassx_es.ts | 185 +- share/translations/keepassx_eu.ts | 69 +- share/translations/keepassx_fi.ts | 233 ++- share/translations/keepassx_fr.ts | 557 +++-- share/translations/keepassx_hu.ts | 69 +- share/translations/keepassx_id.ts | 73 +- share/translations/keepassx_it.ts | 99 +- share/translations/keepassx_ja.ts | 91 +- share/translations/keepassx_ko.ts | 77 +- share/translations/keepassx_lt.ts | 89 +- share/translations/keepassx_nb.ts | 275 ++- share/translations/keepassx_nl_NL.ts | 75 +- share/translations/keepassx_pl.ts | 177 +- share/translations/keepassx_pt_BR.ts | 248 ++- share/translations/keepassx_pt_PT.ts | 235 ++- share/translations/keepassx_ro.ts | 87 +- share/translations/keepassx_ru.ts | 585 +++--- share/translations/keepassx_sk.ts | 121 +- share/translations/keepassx_sr.ts | 110 +- share/translations/keepassx_sv.ts | 162 +- share/translations/keepassx_th.ts | 77 +- share/translations/keepassx_tr.ts | 629 +++--- share/translations/keepassx_uk.ts | 431 ++-- share/translations/keepassx_zh_CN.ts | 668 +++--- share/translations/keepassx_zh_TW.ts | 81 +- 35 files changed, 5297 insertions(+), 4639 deletions(-) diff --git a/share/translations/keepassx_ar.ts b/share/translations/keepassx_ar.ts index d532bd15a..30cc76903 100644 --- a/share/translations/keepassx_ar.ts +++ b/share/translations/keepassx_ar.ts @@ -7,11 +7,11 @@ About - عن + حول Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> - للإبلاغ عن الأخطاء: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> + للإبلاغ عن العلل: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. @@ -19,7 +19,7 @@ Contributors - المساهمين + المساهمون <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> @@ -31,7 +31,7 @@ Include the following information whenever you report a bug: - قم بتضمين المعلومات التالية عند الإبلاغ عن خطأ: + قم بتضمين المعلومات التالية عند الإبلاغ عن علة: Copy to clipboard @@ -73,7 +73,7 @@ Kernel: %3 %4 Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. - + شكر خاص من فريق KeePassXC يذهب إلى debfx لإنشاء KeePassX الأصلي. Build Type: %1 @@ -98,7 +98,7 @@ Kernel: %3 %4 Deny - رفض + ارفض %1 has requested access to passwords for the following item(s). @@ -122,7 +122,7 @@ Please select whether you want to allow access. Auto-Type - KeePassXC - المكمل التلقائي - KeePassXC + الطباعة التلقائية - KeePassXC Auto-Type @@ -183,11 +183,11 @@ Please select whether you want to allow access. AutoTypeSelectDialog Auto-Type - KeePassXC - المكمل التلقائي - KeePassXC + الطباعة التلقائية - KeePassXC Select entry to Auto-Type: - حدد مدخل للمكمل التلقائي: + حدد مدخل للطباعة التلقائية: @@ -206,7 +206,7 @@ Please select whether you want to allow access. Deny - رفض + ارفض %1 has requested access to passwords for the following item(s). @@ -272,11 +272,11 @@ Please select whether you want to allow access. Only returns the best matches for a specific URL instead of all entries for the whole domain. - لا تعرض سوى أفضل التطابقات للرابط المحدد بدلًا من جميع الإدخالات للنطاق بأكمله. + لا تعرض سوى أفضل التطابقات للرابط المحدد بدلا من جميع الإدخالات للنطاق بأكمله. &Return only best-matching credentials - + &عرض أفضل مطابقة لبيانات الإعتماد فقط Sort &matching credentials by title @@ -317,7 +317,7 @@ Please select whether you want to allow access. Searc&h in all opened databases for matching credentials Credentials mean login data requested via browser extension - + البحث في جميع قواعد البيانات لمطابقة بيانات الإعتماد Automatically creating or updating string fields is not supported. @@ -329,11 +329,11 @@ Please select whether you want to allow access. Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup. - + تحديثات مسارات KeePassXC أو keepassxc-proxy تلقائيًا لبرامج المراسلات الأصلية عند بدء التشغيل. Update &native messaging manifest files at startup - + تحديث &أصل ملفات manifest للمراسلات الأصلية عند بدء التشغيل Support a proxy application between KeePassXC and browser extension. @@ -382,14 +382,17 @@ Please select whether you want to allow access. BrowserService KeePassXC: New key association request - + KeePassXC: طلب مصادقة مفتاح جديد You have received an association request for the above key. If you would like to allow it access to your KeePassXC database, give it a unique name to identify and accept it. - + لقد تلقيت طلب ارتباط للمفتاح أعلاه. + +إذا كنت ترغب في السماح له بالوصول إلى قاعدة بيانات KeePassXC ، +إعطه اسم فريد لمعرفته وقبوله. Save and allow access @@ -402,7 +405,8 @@ give it a unique name to identify and accept it. A shared encryption key with the name "%1" already exists. Do you want to overwrite it? - + مفتاح التشفير المشترك مع إسم "%1" موجود بالفعل. +هل تريد الكتابة عليه؟ KeePassXC: Update Entry @@ -444,7 +448,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption key(s) from KeePassXC settings. - + حٌذفت %n مفتاح تشفير من إعدادات KeePassXC.حٌذفت %n مفتاح تشفير من إعدادات KeePassXC.حٌذفت %n مفتاح تشفير من إعدادات KeePassXC.حُذفت %n مفاتيح تشفير من إعدادات KeePassXC.حُذفت %n مفتاح تشفير من إعدادات KeePassXC.حُذفت %n مفتاح تشفير من إعدادات KeePassXC. Removing stored permissions… @@ -460,11 +464,11 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entry(s). - + حُذفت الصلاحيات من %n مُدخل.حُذفت الصلاحيات من %n مُدخل.حُذفت الصلاحيات من %n مُدخل.حُذفت الصلاحيات من %n مُدخل.حُذفت الصلاحيات من %n مُدخل.حُذفت الصلاحيات من %n مُدخل. KeePassXC: No entry with permissions found! - + KeePassXC: لا يوجد مُدخل مع الصلاحيات الموجودة! The active database does not contain an entry with permissions. @@ -479,7 +483,7 @@ Please unlock the selected database or choose another one which is unlocked. Enter password: - أدخل كلمة المرور: + أدخل كلمة السر: Repeat password: @@ -499,7 +503,7 @@ Please unlock the selected database or choose another one which is unlocked. Cha&llenge Response - + استجابة التحدي Refresh @@ -531,7 +535,7 @@ Please unlock the selected database or choose another one which is unlocked. Do you really want to use an empty string as password? - هل تريد حقًا استخدام كلمات فارغة ككلمة مرور؟ + هل تريد حقًا استخدام مُدخل فارغ ككلمة مرور؟ Different passwords supplied. @@ -540,7 +544,8 @@ Please unlock the selected database or choose another one which is unlocked. Failed to set %1 as the Key file: %2 - + أخفق إعداد %1 كملف مفتاح: +%2 Legacy key file format @@ -551,11 +556,14 @@ Please unlock the selected database or choose another one which is unlocked. - + أنت تستخدم تنسيق ملف مفتاح قديم قد يصبح +غير مُعتمد في المستقبل. + +يرجى النظر في إنشاء ملف مفتاح جديد. Changing master key failed: no YubiKey inserted. - + إستبدال المفتاح الرئيسي أخفق: لم يُدرج YubiKey. @@ -566,11 +574,11 @@ Please consider generating a new key file. Append ' - Clone' to title - + أضف ' - Clone' إلى العنوان Replace username and password with references - + إستبدل اسم المستخدم وكلمة المرور بالتوصيات Copy history @@ -601,7 +609,7 @@ Please consider generating a new key file. Text is qualified by - + النص مؤهل من قبل Fields are separated by @@ -613,7 +621,7 @@ Please consider generating a new key file. First record has field names - + يحتوي السجل الأول على أسماء الحقول Number of headers line to discard @@ -621,7 +629,7 @@ Please consider generating a new key file. Consider '\' an escape character - + يعتبر '\' حرف هروب Preview @@ -637,7 +645,7 @@ Please consider generating a new key file. Empty fieldname - + إسم الحقل فارغ column @@ -685,15 +693,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - + %n بت،%n بت،%n بت،%n بت،%n بتات،%n بت، %n row(s), - + %n صف،%n صف،%n صف،%n صف،%n صف،%n صف، %n column(s) - + %n عمود%n عمود%n عمود%n عمود%n عمود%n عمود @@ -724,11 +732,11 @@ Please consider generating a new key file. Unable to open the database. - فتح قاعدة البيانات غير ممكن. + يتعذر فتح قاعدة البيانات. Can't open key file - لا يمكن فتح ملف المفتاح + يتعذر فتح ملف المفتاح Legacy key file format @@ -739,7 +747,10 @@ Please consider generating a new key file. unsupported in the future. Please consider generating a new key file. - + أنت تستخدم تنسيق ملف مفتاح قديم قد يصبح +غير مُعتمد في المستقبل. + +يرجى النظر في إنشاء ملف مفتاح جديد. Don't show this warning again @@ -770,7 +781,7 @@ Please consider generating a new key file. Can't open key file - لا يمكن فتح ملف المفتاح + يتعذر فتح ملف المفتاح Unable to open the database. @@ -803,7 +814,7 @@ You can now save it. Encryption - التشفير + التعمية Number of rounds too high @@ -814,15 +825,17 @@ You can now save it. You are using a very high number of key transform rounds with Argon2. If you keep this number, your database may take hours or days (or even longer) to open! - + أنت تستخدم عدد كبير جدًا من جولات تحول المفتاح مع Argon2. + +إذا احتفظت بهذا الرقم، فقد تستغرق قاعدة البيانات ساعات أو أيام (أو حتى أطول) لفتحها! Understood, keep number - + مفهوم، حافظ على العدد Cancel - إلغاء + ألغ Number of rounds too low @@ -833,32 +846,34 @@ If you keep this number, your database may take hours or days (or even longer) t You are using a very low number of key transform rounds with AES-KDF. If you keep this number, your database may be too easy to crack! - + أنت تستخدم عدد قليل جدًا من جولات تحول المفتاح مع AES-KDF. + +إذا احتفزت بهذا الرقم، قد تكون قاعدة البيانات الخاصة بك من السهل جدًا كسرها! KDF unchanged - + KDF دون تغيير Failed to transform key with new KDF parameters; KDF unchanged. - + أخفق تحويل المفتاح مع معطيات KDF الجديدة; KDF دون تغيير. MiB Abbreviation for Mebibytes (KDF settings) - + MiB MiB MiB MiB MiB MiB thread(s) Threads for parallel execution (KDF settings) - + موضوعموضوعموضوعمواضيعمواضيعمواضيع DatabaseSettingsWidgetEncryption Encryption Algorithm: - خورزامية التشفير: + خورزامية التعمية: AES: 256 Bit (default) @@ -870,15 +885,15 @@ If you keep this number, your database may be too easy to crack! Key Derivation Function: - + وظيفة مفتاح الإشتقاق: Transform rounds: - + جولات التحول: Benchmark 1-second delay - + مؤشر تأخير 1-ثانية Memory Usage: @@ -886,14 +901,14 @@ If you keep this number, your database may be too easy to crack! Parallelism: - + التماثل: DatabaseSettingsWidgetGeneral Database Meta Data - + البيانات الوصفية لقاعدة البيانات Database name: @@ -933,7 +948,7 @@ If you keep this number, your database may be too easy to crack! Enable &compression (recommended) - + تفعيل &الضغط (مستحسن) @@ -941,7 +956,7 @@ If you keep this number, your database may be too easy to crack! Root Root group - + الجذر KeePass 2 Database @@ -1072,11 +1087,11 @@ Disable safe saves and try again? DatabaseWidget Searching... - البحث... + يبْحَث... Change master key - تغيير كلمة المرور الرئيسية + تغيير كلمة السر الرئيسية Delete entry? @@ -1084,7 +1099,7 @@ Disable safe saves and try again? Do you really want to delete the entry "%1" for good? - + هل تريد حقًا حذف الإدخال "%1" بشكل دائم؟ Delete entries? @@ -1092,11 +1107,11 @@ Disable safe saves and try again? Do you really want to delete %1 entries for good? - + هل تريد حقًا حذف %1 إدخالات بشكل دائم؟ Move entry to recycle bin? - نقل المدخل إلى سلة المحذوفات؟ + نقل المُدخل إلى سلة المهملات؟ Do you really want to move entry "%1" to the recycle bin? @@ -1104,11 +1119,11 @@ Disable safe saves and try again? Move entries to recycle bin? - نقل المدخلات إلى سلة المحذوفات؟ + نقل المُدخلات إلى سلة المهملات؟ Do you really want to move %n entry(s) to the recycle bin? - + هل تريد حقًا نقل %n مُدخل إلى سلة المهملات؟هل تريد حقًا نقل %n مُدخل إلى سلة المهملات؟هل تريد حقًا نقل %n مُدخل إلى سلة المهملات؟هل تريد حقًا نقل %n مُدخل إلى سلة المهملات؟هل تريد حقًا نقل %n مُدخل إلى سلة المهملات؟هل تريد حقًا نقل %n مُدخل إلى سلة المهملات؟ Execute command? @@ -1116,11 +1131,11 @@ Disable safe saves and try again? Do you really want to execute the following command?<br><br>%1<br> - + هل تريد حقًا تنفيذ الأمر التالي؟<br><br>%1<br> Remember my choice - تذكر إختياري + تذكر ما اخترته Delete group? @@ -1128,7 +1143,7 @@ Disable safe saves and try again? Do you really want to delete the group "%1" for good? - + هل تريد حقًا حذف المجموعة "%1" بشكل دائم؟ Unable to calculate master key @@ -1136,7 +1151,7 @@ Disable safe saves and try again? No current database. - لا يوجد قاعدة بيانات حالية + لا يوجد قاعدة بيانات حالية. No source database, nothing to do. @@ -1165,11 +1180,12 @@ Disable safe saves and try again? The database file has changed and you have unsaved changes. Do you want to merge your changes? - + تم تغيير ملف قاعدة البيانات ولديك تغييرات لم يتم حفظها. +هل تريد دمج التغييرات؟ Could not open the new database file while attempting to autoreload this database. - + تعذر فتح ملف قاعدة البيانات الجديد أثناء محاولة إعادة تحميل قاعدة البيانات هذه تلقائيًا. Empty recycle bin? @@ -1184,7 +1200,7 @@ Do you want to merge your changes? DetailsWidget Generate TOTP Token - + إنشاء رمز TOTP Close @@ -1204,7 +1220,7 @@ Do you want to merge your changes? Expiration - + الإنتهاء Username @@ -1212,7 +1228,7 @@ Do you want to merge your changes? Autotype - + Auto-Type Searching @@ -1267,7 +1283,7 @@ Do you want to merge your changes? EditEntryWidget Entry - دخول + مُدخلة Advanced @@ -1275,11 +1291,11 @@ Do you want to merge your changes? Icon - رمز + أيقونة Auto-Type - المكمل التلقائي + الطباعة التلقائية Properties @@ -1299,7 +1315,7 @@ Do you want to merge your changes? (encrypted) - (مشفر) + (مُعمّى) Select private key @@ -1347,7 +1363,7 @@ Do you want to merge your changes? Press reveal to view or edit - + إضغط استكشاف للعرض او التحرير Tomorrow @@ -1355,27 +1371,27 @@ Do you want to merge your changes? %n week(s) - + %n أسبوعأسبوعأسبوعين%n أسابيع%n أسبوع%n أسابيع %n month(s) - + %n شهرشهرشهرين%n شهور%n شهور%n شهور 1 year - سنة 1 + سنة واحدة Apply generated password? - + تطبيق كلمة المرور المولدة؟ Do you want to apply the generated password to this entry? - + هل تريد تطبيق كلمة المرور المولدة لهذا المُدخل؟ Entry updated successfully. - + حُدث المُدخل بنجاح. @@ -1402,7 +1418,7 @@ Do you want to merge your changes? Reveal - + إستكشاف Attachments @@ -1410,11 +1426,11 @@ Do you want to merge your changes? Foreground Color: - + لون المقدمة: Background Color: - + لون الخلفية: @@ -1433,7 +1449,7 @@ Do you want to merge your changes? Window Associations - + مصادقات النافذة + @@ -1449,7 +1465,7 @@ Do you want to merge your changes? Use a specific sequence for this association: - + إستخدم تسلسل محدد لهذا الإرتباط: @@ -1495,15 +1511,15 @@ Do you want to merge your changes? Presets - + المسبقة Toggle the checkbox to reveal the notes section. - + إختر مربع الإختيار لإستكشاف قسم الملاحظات. Username: - اسم المستخدم + اسم المستخدم: Expires @@ -1526,7 +1542,7 @@ Do you want to merge your changes? Fingerprint - بصمة + البصمة Remove key from agent when database is closed/locked @@ -1546,7 +1562,7 @@ Do you want to merge your changes? Decrypt - فك تشفير + فك التعمية n/a @@ -1594,7 +1610,7 @@ Do you want to merge your changes? Icon - رمز + أيقونة Properties @@ -1614,11 +1630,11 @@ Do you want to merge your changes? Disable - قم ب تعطيل + تعطيل Inherit from parent group (%1) - + ورث من المجموعة الرئيسية (%1) @@ -1641,7 +1657,7 @@ Do you want to merge your changes? Auto-Type - المكمل التلقائي + الطباعة التلقائية &Use default Auto-Type sequence of parent group @@ -1660,15 +1676,15 @@ Do you want to merge your changes? Use custo&m icon - إستخدم الرمز المخصص + استخدم أيقونة مخصصة Add custom icon - إضافة رمز مخصص + أضف أيقونة مخصصة Delete custom icon - حذف الرمز المخصص + احذف أيقونة مخصصة Download favicon @@ -1680,7 +1696,7 @@ Do you want to merge your changes? Hint: You can enable Google as a fallback under Tools>Settings>Security - + تلميح: يمكنك تمكين قوقل كإجراء إضافي ضمن ألادوات>الإعدادات>الأمان Images @@ -1727,11 +1743,11 @@ Do you want to merge your changes? Uuid: - Uuid: + المعرف الفريد: Plugin Data - + بيانات الإضافة Remove @@ -1739,20 +1755,21 @@ Do you want to merge your changes? Delete plugin data? - + حذف بيانات الإضافة؟ Do you really want to delete the selected plugin data? This may cause the affected plugins to malfunction. - + هل تريد حقًا حذف بيانات الإضافة المحددة؟ +قد يؤدي إلى حدوث خلل في الإضافات المتأثرة. Key - + المفتاح Value - + القيمة @@ -1794,7 +1811,7 @@ This may cause the affected plugins to malfunction. Save - حفظ + احفظ Select files @@ -1802,7 +1819,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + هل أنت متأكد من حذف %n مرفق؟هل أنت متأكد من حذف %n مرفق؟هل أنت متأكد من حذف %n مرفق؟هل أنت متأكد من حذف %n مرفقات؟هل أنت متأكد من حذف %n مرفق؟هل أنت متأكد من حذف %n مرفقات؟ Confirm Remove @@ -1882,7 +1899,7 @@ This may cause the affected plugins to malfunction. Ref: Reference abbreviation - + المرجع: Group @@ -1922,11 +1939,11 @@ This may cause the affected plugins to malfunction. Modified - + التعديل Accessed - + الوصول Attachments @@ -1941,11 +1958,11 @@ This may cause the affected plugins to malfunction. Hide Usernames - إخفاء أسماء المستخدم + اخفي أسماء المستخدمين Hide Passwords - إخفاء كلمات المرور + اخفي كلمات السر Fit to window @@ -1979,7 +1996,7 @@ This may cause the affected plugins to malfunction. Cannot save the native messaging script file. - + لا يمكن حفظ ملف برنامج المراسلات الأصلية. @@ -2018,7 +2035,7 @@ This may cause the affected plugins to malfunction. Special Characters - رموز مخصصة + محارف خاصة /*_& ... @@ -2030,7 +2047,7 @@ This may cause the affected plugins to malfunction. Ensure that the password contains characters from every group - + تأكد من أن كلمة المرور تحتوي على أحرف من كل مجموعة Extended ASCII @@ -2056,7 +2073,7 @@ This may cause the affected plugins to malfunction. Unable to issue challenge-response. - + تعذر إصدار إستجابة التحدي. Wrong key or database file is corrupt. @@ -2067,7 +2084,7 @@ This may cause the affected plugins to malfunction. Kdbx3Writer Unable to issue challenge-response. - + تعذر إصدار إستجابة التحدي. Unable to calculate master key @@ -2086,110 +2103,110 @@ This may cause the affected plugins to malfunction. Invalid header checksum size - + حجم رأس تدقيق المجموع غير صحيح Header SHA256 mismatch - + رأس SHA256 غير متطابق Wrong key or database file is corrupt. (HMAC mismatch) - + المفتاح خاطئ أو ملف قاعدة البيانات تالف. (HMAC غير متطابق) Unknown cipher - + تشفير غير معروف Invalid header id size - + حجم معرف الرأس غير صحيح Invalid header field length - + رأس حقل الطول غير صحيح Invalid header data length - + طول بيانات الرأس غير صحيح Failed to open buffer for KDF parameters in header - + أخفق فتح مخزن بيانات مؤقت لمعطيات KDF في الرأس Unsupported key derivation function (KDF) or invalid parameters - + وظيفة إشتقاق المفاتيح غير مدعومة (KDF) أو المعطيات غير صحيحة Legacy header fields found in KDBX4 file. - + عُثر على حقول رأس قديمة في ملف KDBX4. Invalid inner header id size - + حجم معرف الرأس الداخلي غير صحيح Invalid inner header field length - + رأس حقل الطول الداخلي غير صحيح Invalid inner header binary size - + حجم ثنائي الرأس الداخلي غير صحيح Unsupported KeePass variant map version. Translation: variant map = data structure for storing meta data - + نسخة خريطة KeePass المتنوعة غير مدعومة. Invalid variant map entry name length Translation: variant map = data structure for storing meta data - + طول ادخال الاسم للخريطة المتنوعة غير صحيح Invalid variant map entry name data Translation: variant map = data structure for storing meta data - + بيانات ادخال الاسم الخريطة المتنوعة غير صحيحة Invalid variant map entry value length Translation: variant map = data structure for storing meta data - + طول قيمة إدخال الخريطة المتنوعة غير صحيح Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - + بيانات قيمة إدخال الخريطة المتنوعة غير صحيحة Invalid variant map Bool entry value length Translation: variant map = data structure for storing meta data - + طول قيمة إدخال Bool للخريطة المتنوعة غير صحيح Invalid variant map Int32 entry value length Translation: variant map = data structure for storing meta data - + طول قيمة إدخال Int32 للخريطة المتنوعة غير صحيح Invalid variant map UInt32 entry value length Translation: variant map = data structure for storing meta data - + طول قيمة إدخال UInt32 للخريطة المتنوعة غير صحيح Invalid variant map Int64 entry value length Translation: variant map = data structure for storing meta data - + طول قيمة إدخال Int64 للخريطة المتنوعة غير صحيح Invalid variant map UInt64 entry value length Translation: variant map = data structure for storing meta data - + طول قيمة إدخال UInt64 للخريطة المتنوعة غير صحيح Invalid variant map entry type Translation: variant map = data structure for storing meta data - نوع إدخال الخريطة المتنوعة غير صحيح + إدخال نوع الخريطة المتنوعة غير صحيح Invalid variant map field type size @@ -2215,7 +2232,7 @@ This may cause the affected plugins to malfunction. Failed to serialize KDF parameters variant map Translation comment: variant map = data structure for storing meta data - + أخفق تسلسل معطيات KDF للخريطة المتنوعة @@ -2226,7 +2243,7 @@ This may cause the affected plugins to malfunction. Unsupported cipher - + تشفير غير مدعوم Invalid compression flags length @@ -2269,7 +2286,10 @@ This may cause the affected plugins to malfunction. You can import it by clicking on Database > 'Import KeePass 1 database...'. This is a one-way migration. You won't be able to open the imported database with the old KeePassX 0.4 version. - + الملف المحدد هو قاعدة بيانات KeePass 1 القديمة (.kdb). + +يمكنك إستيراده عن طريق النقر على قاعدة البيانات > 'إستيراد قاعدة بيانات KeePass 1...'. +هذه الطريقة الوحيدة للترحيل. لن تتمكن من فتح قاعدة البيانات المستوردة من القديم في إصدار KeePassXC 0.4. Unsupported KeePass 2 database version. @@ -2284,7 +2304,7 @@ This is a one-way migration. You won't be able to open the imported databas No root group - + لا يوجد مجموعة رئيسية Missing icon uuid or data @@ -2296,7 +2316,7 @@ This is a one-way migration. You won't be able to open the imported databas Multiple group elements - + عناصر المجموعة المتعددة Null group uuid @@ -2308,7 +2328,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid EnableAutoType value - + قيمة EnableAutoType غير صحيحة Invalid EnableSearching value @@ -2316,7 +2336,7 @@ This is a one-way migration. You won't be able to open the imported databas No group uuid found - لم يُعثر على uuid المجموعة + لم يُعثر على مُعرف المجموعة الفريد Null DeleteObject uuid @@ -2340,11 +2360,11 @@ This is a one-way migration. You won't be able to open the imported databas No entry uuid found - لم يُعثر على uuid المُدخل + لم يُعثر على مُعرف المُدخل الفريد History element with different uuid - سجل العنصر مع uuid مختلف + أرِخ العنصر مسخدمًا معرف فريد مختلف Unable to decrypt entry string @@ -2392,7 +2412,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid uuid value - قيمة uuid غير صحيحة + قيمة المُعرف الفريد غير صحيحة Unable to decompress binary @@ -2460,7 +2480,7 @@ This is a one-way migration. You won't be able to open the imported databas Root - + الجذر Unable to calculate master key @@ -2472,7 +2492,7 @@ This is a one-way migration. You won't be able to open the imported databas Key transformation failed - فشل تحول المفتاح + تعذر تحويل المفتاح Invalid group field type number @@ -2484,7 +2504,7 @@ This is a one-way migration. You won't be able to open the imported databas Read group field data doesn't match size - + قراءة بيانات الحقل للمجموعة لا تتطابق مع الحجم Incorrect group id field size @@ -2563,33 +2583,6 @@ This is a one-way migration. You won't be able to open the imported databas نوع حقل الإدخال غير صحيح - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – مستحسن) - - Main @@ -2598,7 +2591,7 @@ This is a one-way migration. You won't be able to open the imported databas The lock file could not be created. Single-instance mode disabled. - تعذر إنشاء ملف القفل. تم تعطيل وضع المثيل الأحادي. + تعذر إنشاء ملف القفل. وضع المثيل الأحادي مُعطل. Another instance of KeePassXC is already running. @@ -2633,7 +2626,7 @@ This is a one-way migration. You won't be able to open the imported databas E&ntries - + المُدخلات Copy att&ribute to clipboard @@ -2753,7 +2746,7 @@ This is a one-way migration. You won't be able to open the imported databas &Perform Auto-Type - &تنفيذ الضغط التلقائي + &تنفيذ الطباعة التلقائية &Open URL @@ -2781,7 +2774,7 @@ This is a one-way migration. You won't be able to open the imported databas &Notes - &ملاحظات + &الملاحظات Copy notes to clipboard @@ -2805,15 +2798,15 @@ This is a one-way migration. You won't be able to open the imported databas Show TOTP - + إظهار TOTP Set up TOTP... - + إعداد TOTP... Copy &TOTP - + نسخ &TOTP E&mpty recycle bin @@ -2825,15 +2818,15 @@ This is a one-way migration. You won't be able to open the imported databas Access error for config file %1 - + خطأ في الوصول لملف التكوين %1 <p>It looks like you are using KeePassHTTP for browser integration. This feature has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a> (warning %1 of 3).</p> - + <p>يبدو أنك تستخدم KeePassHTTP كتكامل للمتصفح. تم إهمال هذه الميزة وستتم إزالتها في المستقبل.<br>يرجى التبديل إلى KeePassXC-Browser! للحصول على مساعدة بالإستبدال، إنتقل إلى دليل الإستبدال<a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration"></a> (التحذير %1 من 3).</p> read-only - + للقراءة فقط Settings @@ -2841,7 +2834,7 @@ This is a one-way migration. You won't be able to open the imported databas Toggle window - + تبديل النافذة Quit KeePassXC @@ -2869,20 +2862,22 @@ This is a one-way migration. You won't be able to open the imported databas Please touch the button on your YubiKey! - + يرجى لمس الزر المتواجد على YubiKey! WARNING: You are using an unstable build of KeePassXC! There is a high risk of corruption, maintain a backup of your databases. This version is not meant for production use. - + تحذير: أنت تستخدم بناء غير مستقر من KeePassXC! +هناك خطر كبير للعطب، حافظ على أخذ نسخة إحتياطية لقاعدة البيانات. +ليس المقصود من هذا الإصدار للإستخدام الأساسي. OpenSSHKey Invalid key file, expecting an OpenSSH key - + ملف المفتاح غير صحيح، متوقع أنه مفتاح OpenSSH PEM boundary mismatch @@ -2890,7 +2885,7 @@ This version is not meant for production use. Base64 decoding failed - + تعذر فك تشفير Base64 Key file way too small. @@ -2970,7 +2965,7 @@ This version is not meant for production use. Unknown KDF: %1 - + KDF غير معروف: %1 Unknown key type: %1 @@ -3022,11 +3017,11 @@ This version is not meant for production use. Sort matching entries by &username - + فرز الإدخالات المطابقة حسب اسم المستخدم Sort &matching entries by title - + فرز الإدخالات المطابقة حسب العنوان R&emove all shared encryption keys from active database @@ -3058,7 +3053,7 @@ This version is not meant for production use. Searc&h in all opened databases for matching entries - + بحث في جميع قواعد البيانات المفتوحة لمطابقة الإدخالات Automatically creating or updating string fields is not supported. @@ -3086,7 +3081,7 @@ This version is not meant for production use. <p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p> - + <p>يبدو أنك تستخدم KeePassHTTP كتكامل للمتصفح. تم إهمال هذه الميزة وستتم إزالتها في المستقبل.<br>يرجى التبديل إلى KeePassXC-Browser! للحصول على مساعدة بالإستبدال، <a href="https://keepassxc.org/docs/keepassxc-browser-migration">إنتقل إلى دليل الإستبدال</a>.</p> Cannot bind to privileged ports @@ -3116,7 +3111,7 @@ Using default port 19455. entropy - غير قادر + entropy Password @@ -3132,7 +3127,7 @@ Using default port 19455. Lower Case Letters - الحروف صغيرة + أحرف صغيرة Numbers @@ -3299,7 +3294,7 @@ Using default port 19455. Key file of the database. - ملف المفتاح لقاعدة البيانات + ملف المفتاح لقاعدة البيانات. path @@ -3315,7 +3310,7 @@ Using default port 19455. URL for the entry. - الرابط للمُدخل + الرابط للمُدخل. URL @@ -3392,7 +3387,7 @@ Using default port 19455. Insert password to unlock %1: - ادخل كلمة المرور لإلغاء القفل %1: + ادخل كلمة المرور لإلغاء قفل %1: Failed to load key file %1 : %2 @@ -3436,7 +3431,7 @@ Available commands: Search term. - + مصطلح البحث. Merge two databases. @@ -3456,7 +3451,7 @@ Available commands: Key file of the database to merge from. - ملف قاعدة البيانات للدمج منه. + ملف المفتاح لقاعدة البيانات للدمج منه. Show an entry's information. @@ -3464,7 +3459,7 @@ Available commands: Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given. - + أسماء الخصائص المراد عرضها. يمكن تحديد هذا الخيار أكثر من مرة، مع عرض كل خاصية لكل سطر بالترتيب المحدد. إذا لم يتم تحديد خاصية، يتم إعطاء ملخص للخصائص الإفتراضية. attribute @@ -3476,11 +3471,11 @@ Available commands: NULL device - + جهاز غير معروف error reading from device - + خطأ القراءة من الجهاز file empty ! @@ -3490,11 +3485,35 @@ Available commands: malformed string - + سلسلة غير صحيحة missing closing quote - + إغلاق الإقتباس مفقود + + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – مستحسن) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) Group @@ -3534,11 +3553,11 @@ Available commands: YubiKey[%1] Challenge Response - Slot %2 - %3 - + YubiKey[%1] إستجابة التحدي - فتحة %2 - %3 Press - + اضغط Passive @@ -3550,11 +3569,11 @@ Available commands: Generate a new random diceware passphrase. - + إنشاء عبارة مرور diceware عشوائية جديدة. Word count for the diceware passphrase. - + عدد الكلمات لعبارة مرور diceware. count @@ -3563,7 +3582,8 @@ Available commands: Wordlist for the diceware generator. [Default: EFF English] - + قائمة الكلمات لمولد diceware. +[الإفتراضية: EFF English] Generate a new random password. @@ -3571,7 +3591,7 @@ Available commands: Length of the generated password. - طول كلمة المرور المُنشئة. + طول كلمة المرور المولدة. Use lowercase characters in the generated password. @@ -3602,7 +3622,7 @@ Available commands: Error writing to underlying device: - + حدث خطأ أثناء الكتابة إلى الجهاز الأساسي: Error opening underlying device: @@ -3614,18 +3634,18 @@ Available commands: Internal zlib error when decompressing: - + خطأ zlib داخلي عند فك الضغط: QtIOCompressor::open The gzip format not supported in this version of zlib. - تنسيق gzip غير مدعوم في ه + تنسيق gzip غير مدعوم في هذا الإصدار من zlib. Internal zlib error: - + خطأ zlib داخلي: @@ -3648,20 +3668,22 @@ Available commands: Limit search to selected group - + حدد البحث في المجموعة المحددة Service KeePassXC: New key association request - + KeePassXC: طلب مصادقة مفتاح جديد You have received an association request for the above key. If you would like to allow it access to your KeePassXC database give it a unique name to identify and accept it. - + لقد تلقيت طلب ارتباط للمفتاح أعلاه. +إذا كنت ترغب في السماح له بالوصول إلى قاعدة بيانات KeePassXC +إعطه اسم فريد لمعرفته وقبوله. KeePassXC: Overwrite existing key? @@ -3697,11 +3719,11 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - + حذف %n مفتاح تشفير من إعدادات KeePassX/Http.حذف %n مفتاح تشفير من إعدادات KeePassX/Http.حُذفت %n مفتاح تشفير من إعدادات KeePassX/Http.حُذفت %n مفتاح تشفير من إعدادات KeePassX/Http.حُذفت %n مفتاح تشفير من إعدادات KeePassX/Http.حُذفت %n مفتاح تشفير من إعدادات KeePassX/Http. KeePassXC: No keys found - KeePassXC: لم يتم العثور على أية مفاتيح + KeePassXC: لم يُعثر على أية مفاتيح No shared encryption-keys found in KeePassHttp Settings. @@ -3729,11 +3751,11 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entries. - + حُذفت الصلاحيات من %n مدخل.حُذفت الصلاحيات من %n مدخل.حُذفت الصلاحيات من %n مدخل.حُذفت الصلاحيات من %n مدخل.حُذفت الصلاحيات من %n مدخل.حُذفت الصلاحيات من %n مدخلات. KeePassXC: No entry with permissions found! - + KeePassXC: لا يوجد مُدخل مع الصلاحيات الموجودة! The active database does not contain an entry with permissions. @@ -3752,11 +3774,11 @@ Please unlock the selected database or choose another one which is unlocked. Security - الأمن + الأمان Access error for config file %1 - + خطأ في الوصول لملف التكوين %1 @@ -3787,7 +3809,7 @@ Please unlock the selected database or choose another one which is unlocked. Automatically save after every change - + الحفظ تلقائيًا بعد كل تعديل Automatically reload the database when modified externally @@ -3811,11 +3833,11 @@ Please unlock the selected database or choose another one which is unlocked. Hide the Details view - + إخفاء عرض التفاصيل Show a system tray icon - إظهار علامة تبويب + اظهر أيقونة البرنامج في صينية النظام Hide window to system tray when minimized @@ -3827,7 +3849,7 @@ Please unlock the selected database or choose another one which is unlocked. Dark system tray icon - + رمز شريط المهام المظلم Language @@ -3835,7 +3857,7 @@ Please unlock the selected database or choose another one which is unlocked. Auto-Type - المكمل التلقائي + الطباعة التلقائية Use entry title to match windows for global Auto-Type @@ -3860,7 +3882,7 @@ Please unlock the selected database or choose another one which is unlocked. ms Milliseconds - م.ثانية + مل.ثانية Startup @@ -3880,7 +3902,7 @@ Please unlock the selected database or choose another one which is unlocked. Entry Management - + إدارة الإدخالات General @@ -3904,11 +3926,11 @@ Please unlock the selected database or choose another one which is unlocked. Lock databases after inactivity of - أغلق قواعد البيانات بعد حالات عدم النشاط ل + أغلق قواعد البيانات بعد حالة عدم النشاط ل Convenience - + السهولة Lock databases when session is locked or lid is closed @@ -3920,7 +3942,7 @@ Please unlock the selected database or choose another one which is unlocked. Don't require password repeat when it is visible - + لا تطلب تكرار كلمة المرور عندما تكون مرئية Show passwords in cleartext by default @@ -3940,7 +3962,7 @@ Please unlock the selected database or choose another one which is unlocked. Use Google as fallback for downloading website icons - + إستخدم قوقل كمرجع لتنزيل رموز مواقع الويب Re-lock previously locked database after performing Auto-Type @@ -3951,7 +3973,7 @@ Please unlock the selected database or choose another one which is unlocked.SetupTotpDialog Setup TOTP - + إعداد TOTP Key: @@ -4007,11 +4029,11 @@ Please unlock the selected database or choose another one which is unlocked. Copy - نسخة + نسخ Expires in - وتنتهي في + تنتهي في seconds @@ -4033,7 +4055,7 @@ Please unlock the selected database or choose another one which is unlocked. Create new database - إنشاء قاعدة بيانات جديدة + أنشىء قاعدة بيانات جديدة Open existing database @@ -4049,7 +4071,7 @@ Please unlock the selected database or choose another one which is unlocked. Recent databases - قواعد البيانات الحديثة + المفتوحة مؤخرًا Welcome to KeePassXC %1 @@ -4068,7 +4090,7 @@ Please unlock the selected database or choose another one which is unlocked. Path of the entry to remove. - مسار المُدخل للحذف. + مسار المُدخل التي ستحذف. KeePassXC - cross-platform password manager @@ -4080,15 +4102,15 @@ Please unlock the selected database or choose another one which is unlocked. path to a custom config file - مسار ملف التكوين المخصص + مسار ملف الاعدادات المخصص key file of the database - ملف المفتاح لقاعدة البيانات + ملف مفتاح قاعدة البيانات read password of the database from stdin - قراءة كلمة مرور قاعدة البيانات من stdin + قراءة كلمة سر قاعدة البيانات من الدخل القياسي "stdin" Parent window handle diff --git a/share/translations/keepassx_bn.ts b/share/translations/keepassx_bn.ts index 1996cb58d..f6894492d 100644 --- a/share/translations/keepassx_bn.ts +++ b/share/translations/keepassx_bn.ts @@ -15,11 +15,11 @@ KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. - GNU General Public License (GPL) সংস্করণ ২ বা (আপনার ইচ্ছানুসারে) সংস্করণ ৩ এর অধীনে KeePassXC বিনামূল্যে বিতরন করা হয়। + GNU General Public License (GPL) সংস্করণ 2 বা (আপনার ইচ্ছানুসারে) সংস্করণ 3 এর অধীনে KeePassXC বিনামূল্যে বিতরন করা হয়। Contributors - অবদানকারী + অবদানকারীগণ <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> @@ -27,11 +27,11 @@ Debug Info - ডিবাগ তথ্য + ডিবাগের তথ্য Include the following information whenever you report a bug: - যখন আপনি একটি বাগ রিপোর্ট করুন নিচের তথ্যগুলো অন্তর্ভুক্ত করুন: + যখন আপনি একটি বাগ রিপোর্ট করবেন, নিচের তথ্যগুলো অন্তর্ভুক্ত করুন: Copy to clipboard @@ -256,7 +256,7 @@ Please select whether you want to allow access. Show a &notification when credentials are requested Credentials mean login data requested via browser extension - প্রমাণপ্রত্রাদি অনুরোধ করা হলে একটি &বিজ্ঞপ্তি দেখান + প্রমাণপ্রত্রাদি অনুরোধ করা হলে একটি বিজ্ঞপ্তি দেখান Re&quest to unlock the database if it is locked @@ -303,7 +303,7 @@ Please select whether you want to allow access. Never &ask before accessing credentials Credentials mean login data requested via browser extension - তথে প্রবেশ করার পূর্বে কখনোই জিজ্ঞাসা করবেন না + তথ্যে প্রবেশ করার পূর্বে কখনোই জিজ্ঞাসা করবেন না Never ask before &updating credentials @@ -367,7 +367,7 @@ Please select whether you want to allow access. Executable Files (*) - + এক্সিকিউটেবল ফাইল (*) Select custom proxy location @@ -382,7 +382,7 @@ Please select whether you want to allow access. BrowserService KeePassXC: New key association request - KeePassXC: নতুন কী (key) যুক্ত করার আবেদন + KeePassXC: নতুন কী যুক্ত করার আবেদন You have received an association request for the above key. @@ -431,7 +431,7 @@ Please unlock the selected database or choose another one which is unlocked. The active database does not contain a settings entry. - + সক্রিয় ডাটাবেস সেটিংসের এন্ট্রি ধারণ করে না। KeePassXC: No keys found @@ -447,7 +447,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption key(s) from KeePassXC settings. - + KeePassXC সেটিংস থেকে সফলভাবে %n এনক্রিপশন key(s) অপসারণ করা হয়েছে।KeePassXC সেটিংস থেকে সফলভাবে %n এনক্রিপশন key(s) অপসারণ করা হয়েছে। Removing stored permissions… @@ -463,7 +463,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entry(s). - + %N entry(s) থেকে সফলভাবে অনুমতিসমূহ অপসারণ করা হয়েছে।%N entry(s) থেকে সফলভাবে অনুমতিসমূহ অপসারণ করা হয়েছে। KeePassXC: No entry with permissions found! @@ -557,7 +557,7 @@ unsupported in the future. Please consider generating a new key file. আপনি পূর্ববর্তী কী ফাইল ফরম্যাট ব্যবহার করেছেন যা ভবিষ্যতে অসমর্থিত হতে পারে । -নতুন কী (key) ফাইল তৈরি করুন। +নতুন কী ফাইল তৈরি করুন। Changing master key failed: no YubiKey inserted. @@ -615,7 +615,7 @@ Please consider generating a new key file. Comments start with - মন্তব্য দিয়ে শুরু + মন্তব্য শুরু হয়েছে First record has field names @@ -647,7 +647,7 @@ Please consider generating a new key file. column - + কলাম Imported from CSV file @@ -691,15 +691,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - + %n byte(s), %n byte(s), %n row(s), - + %n সারি (সমূহ) %n সারি (সমূহ) %n column(s) - + %n column(s)%n কলাম(সমূহ) @@ -858,12 +858,12 @@ If you keep this number, your database may be too easy to crack! MiB Abbreviation for Mebibytes (KDF settings) - + এম-বিMiB thread(s) Threads for parallel execution (KDF settings) - + thread(s) thread(s) @@ -969,7 +969,7 @@ If you keep this number, your database may be too easy to crack! File not found! - + ফাইল খুঁজে পাওয়া যায়নি। Unable to open the database. @@ -989,7 +989,7 @@ If you keep this number, your database may be too easy to crack! All files (*) - + সব ফাইল (*) Merge database @@ -1049,16 +1049,17 @@ Save changes? locked - আটকানো + তালাবদ্ধ Lock database - তালাবদ্ধ ডাকাবেস + তালাবদ্ধ ডাটাবেস Can't lock the database as you are currently editing it. Please press cancel to finish your changes or discard them. - + আপনি বর্তমানে এটি সম্পাদনা করছেন তাই ডাটাবেস লক করতে পারছি না। +অনুগ্রহ করে আপনার পরিবর্তনগুলো সমাপ্ত করুন বা বাতিল চাপুন। This database has been modified. @@ -1119,7 +1120,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - + আপনি কি সত্যিই %n entry(s) রিসাইকেল বিনে সরাতে চান?আপনি কি সত্যিই %n এন্ট্রি(সমূহ) রিসাইকেল বিনে সরাতে চান? Execute command? @@ -1327,7 +1328,7 @@ Do you want to merge your changes? Entry history - + এন্ট্রি ইতিহাস Add entry @@ -1367,15 +1368,15 @@ Do you want to merge your changes? %n week(s) - + দিনগুলো সপ্তাহগুলো মাসগুলো %n%n সপ্তাহ(s) %n month(s) - + %n month(s)%n মাস(s) 1 year - ১ বছর + 1 বছর Apply generated password? @@ -1595,7 +1596,7 @@ Do you want to merge your changes? Require user confirmation when this key is used - + এ কী যখন ব্যবহার করা হয় ব্যবহারকারী অনুমোদন প্রয়োজন @@ -1614,7 +1615,7 @@ Do you want to merge your changes? Add group - দলযোগ করুন + দল যোগ করুন Edit group @@ -1684,7 +1685,7 @@ Do you want to merge your changes? Download favicon - + Favicon ডাউনলোড করুন Unable to fetch favicon. @@ -1773,7 +1774,7 @@ This may cause the affected plugins to malfunction. - Clone Suffix added to cloned entries - -ক্লোন + -নকল @@ -1815,7 +1816,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + আপনি কি নিশ্চিত যে আপনি %n সংযোজন (গুলো) অপসারণ করতে চান?আপনি কি নিশ্চিত যে আপনি %n সংযুক্তি (গুলো) অপসারণ করতে চান? Confirm Remove @@ -1931,7 +1932,7 @@ This may cause the affected plugins to malfunction. Created - সৃষ্টি করেছেন + সৃষ্টি করা হয়েছে Modified @@ -1939,7 +1940,7 @@ This may cause the affected plugins to malfunction. Accessed - + প্রবেশ করা হয়েছে Attachments @@ -1966,7 +1967,7 @@ This may cause the affected plugins to malfunction. Fit to contents - + বিষয়বস্তুর সাথে মিল করুন Reset to defaults @@ -1988,11 +1989,11 @@ This may cause the affected plugins to malfunction. HostInstaller KeePassXC: Cannot save file! - + KeePassXC: ফাইল সংরক্ষণ হয়নি! Cannot save the native messaging script file. - + স্থানীয় মেসেজিং স্ক্রিপ্ট ফাইল সংরক্ষণ হয়নি। @@ -2011,7 +2012,7 @@ This may cause the affected plugins to malfunction. A-Z - + A-Z Lower Case Letters @@ -2019,7 +2020,7 @@ This may cause the affected plugins to malfunction. a-z - + a-z Numbers @@ -2039,7 +2040,7 @@ This may cause the affected plugins to malfunction. Exclude look-alike characters - + একই ধরনের অক্ষরসমূহ বাদ দিন Ensure that the password contains characters from every group @@ -2091,7 +2092,7 @@ This may cause the affected plugins to malfunction. Kdbx4Reader missing database headers - ডাটাবেসের শিরোনাম নেই + ডাটাবেসের হেডারসমূহ নেই Unable to calculate master key @@ -2099,7 +2100,7 @@ This may cause the affected plugins to malfunction. Invalid header checksum size - শিরোনামের চেকসাম আকার সঠিক নয় + হেডারের চেকসাম আকার সঠিক নয় Header SHA256 mismatch @@ -2119,7 +2120,7 @@ This may cause the affected plugins to malfunction. Invalid header field length - + হেডার ক্ষেত্রের দৈর্ঘ্য সঠিক নয় Invalid header data length @@ -2576,33 +2577,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - - - - Twofish: 256-bit - - - - ChaCha20: 256-bit - - - - AES-KDF (KDBX 4) - - - - AES-KDF (KDBX 3.1) - - - - Argon2 (KDBX 4 – recommended) - - - Main @@ -3503,6 +3477,30 @@ Available commands: missing closing quote + + AES: 256-bit + + + + Twofish: 256-bit + + + + ChaCha20: 256-bit + + + + Argon2 (KDBX 4 – recommended) + + + + AES-KDF (KDBX 4) + + + + AES-KDF (KDBX 3.1) + + Group দল @@ -3934,7 +3932,7 @@ Please unlock the selected database or choose another one which is unlocked. Hide passwords in the preview panel - + প্রদর্শন প্যানেলে পাসওয়ার্ড লুকান Hide entry notes by default @@ -3942,7 +3940,7 @@ Please unlock the selected database or choose another one which is unlocked. Privacy - + গোপণীয়তা Use Google as fallback for downloading website icons @@ -3973,7 +3971,7 @@ Please unlock the selected database or choose another one which is unlocked. Use custom settings - + সচারচর সেটিংসমূহ ব্যবহার করুন Note: Change these settings only if you know what you are doing. @@ -3985,7 +3983,7 @@ Please unlock the selected database or choose another one which is unlocked. 8 digits - 8 (আট) ডিজিট + ৮ ডিজিট 6 digits @@ -4009,7 +4007,7 @@ Please unlock the selected database or choose another one which is unlocked. 000000 - ০০০০০০ + 000000 Copy @@ -4035,7 +4033,7 @@ Please unlock the selected database or choose another one which is unlocked.WelcomeWidget Start storing your passwords securely in a KeePassXC database - + আপনার পাসওয়ার্ডসমূহ নিরাপদে KeePassXC ডাটাবেসে সংরক্ষণ করুন Create new database @@ -4094,7 +4092,7 @@ Please unlock the selected database or choose another one which is unlocked. read password of the database from stdin - + stdin থেকে ডাটাবেসের পাসওয়ার্ড পড় Parent window handle diff --git a/share/translations/keepassx_ca.ts b/share/translations/keepassx_ca.ts index 20316f161..b5b1a45fe 100644 --- a/share/translations/keepassx_ca.ts +++ b/share/translations/keepassx_ca.ts @@ -130,7 +130,7 @@ Seleccioneu si voleu permetre l'accés. The Syntax of your Auto-Type statement is incorrect! - La sintaxi de l'Auto-Type no és correcte! + This Auto-Type command contains a very long delay. Do you really want to proceed? @@ -260,11 +260,11 @@ Seleccioneu si voleu permetre l'accés. Re&quest to unlock the database if it is locked - Sol·licita el desbloqueig de la base de dades si està blocada + Sol·licitar el desbloqueig de la base de dades si està blocada Only entries with the same scheme (http://, https://, ...) are returned. - Només es retornen les entrades amb el mateix patró (http://, https://, ...) + Només es retornen les entrades amb el mateix esquema (http://, https://, ...) &Match URL scheme (e.g., https://...) @@ -286,7 +286,7 @@ Seleccioneu si voleu permetre l'accés. Sort matching credentials by &username Credentials mean login data requested via browser extension - Ordena les entrades coincidents per nom d'&usuari + &Disconnect all browsers @@ -393,7 +393,7 @@ give it a unique name to identify and accept it. Save and allow access - Desa i autoritza l'accés + Desa i autoritza l'accès KeePassXC: Overwrite existing key? @@ -402,7 +402,7 @@ give it a unique name to identify and accept it. A shared encryption key with the name "%1" already exists. Do you want to overwrite it? - Ja existeix una clau de xifratge compartida amb el nom "%1". + Ja existeix una clau de xifrat compartida amb el nom "%1". Voleu sobreescriure-la? @@ -437,7 +437,7 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra. No shared encryption keys found in KeePassXC Settings. - No s'han trobat claus de xifratge compartides en la configuració de KeePassHttp. + KeePassXC: Removed keys from database @@ -445,7 +445,7 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra. Successfully removed %n encryption key(s) from KeePassXC settings. - Reeixidament eliminat %n encriptació clau (s) de configuració KeePassXC.Eliminada(es) correctament %n clau(s) de xifratge de la configuració de KeePassXC. + Removing stored permissions… @@ -492,7 +492,7 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra. Browse - Navegar + Navega Create @@ -504,7 +504,7 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra. Refresh - L'actualitza + Actualitza Key files @@ -575,7 +575,7 @@ Please consider generating a new key file. Copy history - Copia el historial + Còpia el historial @@ -752,7 +752,7 @@ Please consider generating a new key file. Key files - Arxius de clau + Fitxers de clau Select key file @@ -804,7 +804,7 @@ You can now save it. Encryption - Xifratge + Encriptatge Number of rounds too high @@ -859,7 +859,7 @@ If you keep this number, your database may be too easy to crack! DatabaseSettingsWidgetEncryption Encryption Algorithm: - Algorisme de xifratge: + Algorisme de d’encriptatge: AES: 256 Bit (default) @@ -1108,7 +1108,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - Realment voleu moure %n entry(s) a la Paperera de reciclatge?Realment voleu moure %n entrada(es) a la paperera? + Execute command? @@ -1299,7 +1299,7 @@ Do you want to merge your changes? (encrypted) - (xifrat) + (encriptat) Select private key @@ -1355,11 +1355,11 @@ Do you want to merge your changes? %n week(s) - %n setmanes%n setmana(es) + %n month(s) - %n mes (OS)%n mes(os) + 1 year @@ -2421,7 +2421,7 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa Unsupported encryption algorithm. - Algoritme de xifratge no admès. + Algoritme d'encriptació no admès. Unsupported KeePass database version. @@ -2561,33 +2561,6 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa - - KeePass2 - - AES: 256-bit - - - - Twofish: 256-bit - - - - ChaCha20: 256-bit - - - - AES-KDF (KDBX 4) - - - - AES-KDF (KDBX 3.1) - - - - Argon2 (KDBX 4 – recommended) - - - Main @@ -2667,7 +2640,7 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa &Close database - Tanca la base de dades + Tanca base de dades &New database @@ -2727,11 +2700,11 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa Copy &username - Copia el nom d'&usuari + Còpia el nom d'&usuari Copy username to clipboard - Copia el nom d'usuari al porta-retalls + Còpia el nom d'usuari al porta-retalls Cop&y password @@ -2739,11 +2712,11 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa Copy password to clipboard - Copia la contrasenya al porta-retalls + Còpia la contrasenya al porta-retalls &Settings - &Configuració + &Conficuració Password Generator @@ -2751,7 +2724,7 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa &Perform Auto-Type - Realitza compleció automàtica + Realitza complació automètica &Open URL @@ -2759,7 +2732,7 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa &Lock databases - &Bloqueja la bases de dades + &bloqueja la bases de dades &Title @@ -3028,7 +3001,7 @@ This version is not meant for production use. R&emove all shared encryption keys from active database - Suprimeix totes les claus de xifratge compartides de la base de dades activa + Suprimeix totes les claus d'encriptació compartides de la base de dades activa Re&move all stored permissions from entries in active database @@ -3488,6 +3461,30 @@ Available commands: missing closing quote falta la cometa de tancament + + AES: 256-bit + + + + Twofish: 256-bit + + + + ChaCha20: 256-bit + + + + Argon2 (KDBX 4 – recommended) + + + + AES-KDF (KDBX 4) + + + + AES-KDF (KDBX 3.1) + + Group Grup @@ -3699,7 +3696,7 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra. No shared encryption-keys found in KeePassHttp Settings. - No s'han trobat claus de xifratge compartides en la configuració de KeePassHttp. + No s'han trobat claus d'encriptació compartides en la configuració de KeePassHttp. KeePassXC: Settings not available! diff --git a/share/translations/keepassx_cs.ts b/share/translations/keepassx_cs.ts index 5520774ab..1bdd41bec 100644 --- a/share/translations/keepassx_cs.ts +++ b/share/translations/keepassx_cs.ts @@ -86,7 +86,7 @@ Jádro systému: %3 %4 AccessControlDialog KeePassXC HTTP Confirm Access - Potvrzení přístupu KeePassXC HTTP + Potvrzení přístupu pro KeePassXC HTTP Remember this decision @@ -153,11 +153,11 @@ Umožnit přístup? Sequence - Pořadí + Posloupnost Default sequence - Výchozí pořadí + Výchozí posloupnost @@ -325,7 +325,7 @@ Umožnit přístup? &Return advanced string fields which start with "KPH: " - Odpovědět také kolonkami pok&ročilých textových řetězců které začínají na „KPH: “ + Odpovědět kolonkami pok&ročilých textových řetězců které začínají na „KPH: “ Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup. @@ -448,7 +448,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Successfully removed %n encryption key(s) from KeePassXC settings. - %n šifrovací klíč úspěšně odebrán z nastavení KeePassXC.%n šifrovací klíče úspěšně odebrány z nastavení KeePassXC.%n šifrovacích klíčů úspěšně odebráno z nastavení KeePassXC.%n šifrovacích klíčů úspěšně odebráno z nastavení KeePassXC. + Úspěšně odebrán %n šifrovací klíč z nastavení KeePassXC.Úspěšně odebrány %n šifrovací klíče z nastavení KeePassXC.Úspěšně odebráno %n šifrovacích klíčů z nastavení KeePassXC.Úspěšně odebráno %n šifrovacích klíčů z nastavení KeePassXC. Removing stored permissions… @@ -464,7 +464,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Successfully removed permissions from %n entry(s). - Z %n položky úspěšně odebrána oprávnění.Ze %n položek úspěšně odebrána oprávnění.Z %n položek úspěšně odebrána oprávnění.Z %n položek úspěšně odebrána oprávnění. + Úspěšně odebrána oprávnění z %n položky.Úspěšně odebrána oprávnění ze %n položek.Úspěšně odebrána oprávnění z %n položek.Úspěšně odebrána oprávnění z %n položek. KeePassXC: No entry with permissions found! @@ -503,7 +503,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Cha&llenge Response - Výzva–odpověď + &Výzva–odpověď Refresh @@ -549,14 +549,14 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Legacy key file format - Starý formát souboru s klíčem + Starší formát souboru s klíčem You are using a legacy key file format which may become unsupported in the future. Please consider generating a new key file. - Používáte starý formát souboru s klíčem, který v budoucnu nemusí být podporován. + Používáte starší formát souboru s klíčem, který v budoucnu nemusí být podporován. Zvažte vytvoření nového souboru s klíčem. @@ -608,7 +608,7 @@ Zvažte vytvoření nového souboru s klíčem. Text is qualified by - Text je zařazován pomocí + Text je určen pomocí Fields are separated by @@ -624,11 +624,11 @@ Zvažte vytvoření nového souboru s klíčem. Number of headers line to discard - Počet řádek s hlavičkou, kterou zahodit + Počet řádků hlavičky, které zahodit Consider '\' an escape character - Považovat „\“ za únikový znak + Považovat „\“ za význam zbavující (escape) znak Preview @@ -644,7 +644,7 @@ Zvažte vytvoření nového souboru s klíčem. Empty fieldname - Prázný název kolonky + Prázdný název kolonky column @@ -685,18 +685,18 @@ Zvažte vytvoření nového souboru s klíčem. Unable to calculate master key - Nedaří se spočítat hlavní klíč + Nedaří se vypočítat hlavní klíč CsvParserModel %n byte(s), - %n bajt%n bajty%n bajtů%n bajtů + %n bajt,%n bajty,%n bajtů,%n bajtů, %n row(s), - %n řádek%n řádky%n řádků%n řádků + %n řádek,%n řádky,%n řádků,%n řádků, %n column(s) @@ -735,7 +735,7 @@ Zvažte vytvoření nového souboru s klíčem. Can't open key file - Soubor s klíčem se nedaří otevřít + Nedaří se otevřít soubor s klíčem Legacy key file format @@ -783,7 +783,7 @@ Zvažte vytvoření nového souboru s klíčem. Unable to open the database. - Nedaří se otevřít databázi. + Databázi se nedaří otevřít. Database opened fine. Nothing to do. @@ -958,7 +958,7 @@ Pokud tento počet ponecháte, může být velmi snadné prolomit šifrování v KeePass 2 Database - Databáze ve formátu KeePass verze 2 + Databáze ve formátu KeePass 2 All files @@ -970,7 +970,7 @@ Pokud tento počet ponecháte, může být velmi snadné prolomit šifrování v File not found! - Soubor nebyl nalezen! + Soubor nenalezen! Unable to open the database. @@ -1113,7 +1113,7 @@ Vypnout bezpečné ukládání a zkusit to znovu? Do you really want to move entry "%1" to the recycle bin? - Opravdu přesunout záznam "%1" do Koše? + Opravdu chcete přesunout záznam „%1“ do Koše? Move entries to recycle bin? @@ -1121,7 +1121,7 @@ Vypnout bezpečné ukládání a zkusit to znovu? Do you really want to move %n entry(s) to the recycle bin? - Opravdu přesunout %n záznam do Koše? ()Opravdu přesunout %n záznamy do Koše? ()Opravdu přesunout %n záznamů do Koše?Opravdu přesunout %n záznamů do Koše? + Opravdu chcete přesunout %n položku do Koše?Opravdu chcete přesunout %n položky do Koše?Opravdu chcete přesunout %n položek do Koše?Opravdu chcete přesunout %n položek do Koše? Execute command? @@ -1129,7 +1129,7 @@ Vypnout bezpečné ukládání a zkusit to znovu? Do you really want to execute the following command?<br><br>%1<br> - Opravdu spustit následující příkaz?<br><br>%1<br> + Opravdu chcete spustit následující příkaz?<br><br>%1<br> Remember my choice @@ -1145,7 +1145,7 @@ Vypnout bezpečné ukládání a zkusit to znovu? Unable to calculate master key - Nedaří se spočítat hlavní klíč + Nedaří se vypočítat hlavní klíč No current database. @@ -1183,7 +1183,7 @@ Přejete si je sloučit? Could not open the new database file while attempting to autoreload this database. - Nepodařilo se otevřít nový soubor, obsahující aktuální verzi této databáze. + Nepodařilo se otevřít nový databázový soubor při pokusu o automatické znovunačtení této databáze. Empty recycle bin? @@ -1191,7 +1191,7 @@ Přejete si je sloučit? Are you sure you want to permanently delete everything from your recycle bin? - Opravdu chcete natrvalo smazat všechno z Koše? + Opravdu natrvalo smazat všechno z Koše? @@ -1416,7 +1416,7 @@ Přejete si je sloučit? Reveal - Odhalit + Odkrýt Attachments @@ -1439,11 +1439,11 @@ Přejete si je sloučit? Inherit default Auto-Type sequence from the &group - Převzít výchozí pořadí automatického vyplňování od skupiny + &Převzít výchozí posloupnost automatického vyplňování od skupiny &Use custom Auto-Type sequence: - Po&užít vlastní pořadí automatického vyplňování: + Po&užít uživatelem určenou posloupnost automatického vyplňování: Window Associations @@ -1632,7 +1632,7 @@ Přejete si je sloučit? Inherit from parent group (%1) - Převzít od nadřazené skupiny (%1) + Převzít z nadřazené skupiny (%1) @@ -1659,11 +1659,11 @@ Přejete si je sloučit? &Use default Auto-Type sequence of parent group - Převzít výchozí pořadí a&utomatického vyplňování nadřazené skupiny + Převzít výchozí posloupnost a&utomatického vyplňování z nadřazené skupiny Set default Auto-Type se&quence - Nastavit výchozí pořadí automatického vyplňování + &Nastavit výchozí posloupnost automatického vyplňování @@ -1674,15 +1674,15 @@ Přejete si je sloučit? Use custo&m icon - Použít svou vlastní ikonu + Použít uživatele&m určenou ikonu Add custom icon - Přidat svou vlastní ikonu + Přidat uživatelem určenou ikonu Delete custom icon - Smazat svou vlastní ikonu + Smazat uživatelem určenou ikonu Download favicon @@ -1714,7 +1714,7 @@ Přejete si je sloučit? Custom icon already exists - Tato vlastní ikona už existuje + Tato uživatelem určená ikona už existuje Confirm Delete @@ -1741,7 +1741,7 @@ Přejete si je sloučit? Uuid: - Univerzálně jedinečný identifikátor: + Nikde se neopakující se identifikátor: Plugin Data @@ -2060,7 +2060,7 @@ Dotčený zásuvný modul to může rozbít. Close message - Uzavřít správu + Zavřít zprávu @@ -2437,7 +2437,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Not a KeePass database. - Nejedná se o databázi Keepass. + Nejedná se o databázi KeePass. Unsupported encryption algorithm. @@ -2445,7 +2445,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Unsupported KeePass database version. - Nepodporovaná verze databáze KeePass. + Nepodporovaná verze KeePass databáze. Unable to read encryption IV @@ -2482,11 +2482,11 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Unable to calculate master key - Nedaří se spočítat hlavní klíč + Nedaří se vypočítat hlavní klíč Wrong key or database file is corrupt. - Byl zadán chybný klíč, nebo je poškozen databázový soubor. + Byl zadán nesprávný klíč, nebo je soubor s databází poškozený. Key transformation failed @@ -2581,33 +2581,6 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Neplatný typ kolonky položky - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – doporučeno) - - Main @@ -2620,7 +2593,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Another instance of KeePassXC is already running. - Již je spuštěná jiná instance KeePassXC. + Už je spuštěná jiná instance KeePassXC. Fatal error while testing the cryptographic functions. @@ -2647,7 +2620,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Help - Nápověda + &Nápověda E&ntries @@ -2663,7 +2636,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Groups - Skupiny + S&kupiny &Tools @@ -2671,7 +2644,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Quit - Ukončit + &Ukončit &About @@ -2683,11 +2656,11 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Save database - Uložit databázi + &Uložit databázi &Close database - Zavřít databázi + &Zavřít databázi &New database @@ -2707,7 +2680,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Delete entry - Smazat záznam + &Smazat záznam &Add new group @@ -2715,11 +2688,11 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Edit group - Upravit skupinu + &Upravit skupinu &Delete group - Smazat skupinu + &Smazat skupinu Sa&ve database as... @@ -2739,11 +2712,11 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Clone entry - Klonovat záznam + &Klonovat záznam &Find - Najít + &Najít Copy &username @@ -2755,7 +2728,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Cop&y password - Zkopírovat heslo + &Zkopírovat heslo Copy password to clipboard @@ -2767,7 +2740,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Password Generator - Generátor hesel + Vytváření hesel &Perform Auto-Type @@ -2779,7 +2752,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev &Lock databases - Uzamknout databáze + &Uzamknout databáze &Title @@ -2823,15 +2796,15 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev Show TOTP - Zobrazit TOTP + Zobrazit na času založené jednorázové heslo (TOTP) Set up TOTP... - Nastavit TOTP… + Nastavit na času založené jednorázové heslo (TOTP)… Copy &TOTP - Zkopírovat &TOTP + Zkopírovat na času založené jednorázové heslo (&TOTP) E&mpty recycle bin @@ -3022,19 +2995,19 @@ Tato verze není určena pro produkční použití. Only returns the best matches for a specific URL instead of all entries for the whole domain. - Vrátí pouze nejlepší shody pro konkrétní URL adresu namísto všech položek pro celou doménu. + Odpoví pouze nejlepšími shodami pro konkrétní URL adresu namísto všech položek pro celou doménu. &Return only best matching entries - V&racet pouze nejlépe odpovídající položky + Odpovídat pouze nejlépe se shodujícími položkami Re&quest to unlock the database if it is locked - Vyžádat odemknutí zamčené databáze + &Vyžádat odemknutí zamčené databáze Only entries with the same scheme (http://, https://, ftp://, ...) are returned. - Jsou vráceny pouze položky se stejným schématem (http://, https://, ftp://, …). + Je odpovězeno pouze položkami se stejným schématem (http://, https://, ftp://, …). &Match URL schemes @@ -3058,7 +3031,7 @@ Tato verze není určena pro produkční použití. Password Generator - Generátor hesel + Vytváření hesel Advanced @@ -3074,7 +3047,7 @@ Tato verze není určena pro produkční použití. Only the selected database has to be connected with a client. - Pouze označené databáze budou spojeny s klientem. + Pouze označené databáze je třeba spojit s klientem. Searc&h in all opened databases for matching entries @@ -3082,11 +3055,11 @@ Tato verze není určena pro produkční použití. Automatically creating or updating string fields is not supported. - Automatická vytváření nebo aktualizace nejsou u textových kolonek podporované! + Automatické vytváření nebo aktualizace není u textových kolonek podporované! &Return advanced string fields which start with "KPH: " - Odpovědět také kolonkami pok&ročilých textových řetězců které začínají na „KPH:“ + Odpovědět také kolonkami pok&ročilých textových řetězců které začínají na „KPH: “ HTTP Port: @@ -3116,7 +3089,7 @@ Tato verze není určena pro produkční použití. Cannot bind to privileged ports below 1024! Using default port 19455. Není možné navázat na porty s číslem nižším, než 1024! -Náhradně bude použit port 19455. +Náhradně bude použit výchozí port 19455. @@ -3220,7 +3193,7 @@ Náhradně bude použit port 19455. Password Quality: %1 - Kvalita hesla: %1 + Odolnost hesla: %1 Poor @@ -3495,7 +3468,7 @@ Příkazy k dispozici: NULL device - NULL zařízení + Prázdné zařízení error reading from device @@ -3509,12 +3482,36 @@ Příkazy k dispozici: malformed string - špatně formovaný řetězec + chybně formovaný řetězec missing closing quote chybějící uzavírací uvozovka + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – doporučeno) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Skupina @@ -3549,7 +3546,7 @@ Příkazy k dispozici: Browser Integration - Napojení na webový prohlížeč + Napojení webového prohlížeče YubiKey[%1] Challenge Response - Slot %2 - %3 @@ -3641,7 +3638,7 @@ Příkazy k dispozici: QtIOCompressor::open The gzip format not supported in this version of zlib. - Použitý formát gzip komprese není podporován verzí knihovny zlib, která je právě používána na tomto systému. + Formát komprese gzip není podporován verzí knihovny zlib, která je právě používána na tomto systému. Internal zlib error: @@ -3692,8 +3689,8 @@ neopakující se název pro identifikaci a potvrďte ho. A shared encryption-key with the name "%1" already exists. Do you want to overwrite it? - Již existuje sdílený šifrovací klíč s názvem „%1“. -Chcete ho přepsat? + Už existuje sdílený šifrovací klíč s názvem „%1“. +Chcete ho chcete přepsat? KeePassXC: Update Entry @@ -3723,7 +3720,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. KeePassXC: No keys found - KeePassXC: Klíče nebyly nalezeny + KeePassXC: Nebyly nalezeny žádné klíče No shared encryption-keys found in KeePassHttp Settings. @@ -3735,7 +3732,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. The active database does not contain an entry of KeePassHttp Settings. - Právě otevřená databáze neobsahuje žádný záznam nastavení KeePassHttp. + Právě otevřená databáze neobsahuje žádný záznam nastavení pro KeePassHttp. Removing stored permissions... @@ -3751,11 +3748,11 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Successfully removed permissions from %n entries. - Úspěšně odebrána oprávnění z %n položky.Úspěšně odebrána oprávnění ze %n položek.Úspěšně odebrána oprávnění ze %n položek.Úspěšně odebrána oprávnění ze %n položek. + Úspěšně odebrána oprávnění z %n položky.Úspěšně odebrána oprávnění ze %n položek.Úspěšně odebrána oprávnění z %n položek.Úspěšně odebrána oprávnění z %n položek. KeePassXC: No entry with permissions found! - KeePassXC: Nebyl nalezen záznam s oprávněními! + KeePassXC: Nebyl nalezen žádný záznam s oprávněními! The active database does not contain an entry with permissions. @@ -3778,7 +3775,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Access error for config file %1 - Chyba přístupu pro soubor s nastaveními %1 + Chyba přístupu k souboru s nastaveními %1 @@ -3813,7 +3810,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Automatically reload the database when modified externally - V případě úpravy zvenčí, automaticky opětovně načíst databázi + V případě úpravy zvenčí, automaticky znovu načíst databázi Minimize when copying to clipboard @@ -3825,11 +3822,11 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Use group icon on entry creation - Pro vytvářený záznam použít ikonu skupiny, do které spadá + Pro vytvářený záznam použít ikonu skupiny, pod kterou je vytvářen Don't mark database as modified for non-data changes (e.g., expanding groups) - Neoznačovat databázi jako upravenou při změnách, nepostihujících údaje (např. rozšíření skupin) + Neoznačovat databázi jako upravenou při změnách, nepostihujících údaje (např. rozkliknutí skupin) Hide the Details view @@ -3934,7 +3931,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. Lock databases when session is locked or lid is closed - Zamknout databáze když je zamčena relace nebo je zavřeno víko notebooku + Zamknout databáze když je zamčeno sezení uživatele v operačním systému nebo je zavřeno víko notebooku Lock databases after minimizing the window @@ -3973,7 +3970,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. SetupTotpDialog Setup TOTP - Nastavit TOTP + Nastavit na času založené jednorázové heslo (TOTP) Key: @@ -4102,7 +4099,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou. path to a custom config file - umístění vlastního souboru s nastaveními + umístění uživatelem určeného souboru s nastaveními key file of the database diff --git a/share/translations/keepassx_da.ts b/share/translations/keepassx_da.ts index 88ea408dd..61e0cac87 100644 --- a/share/translations/keepassx_da.ts +++ b/share/translations/keepassx_da.ts @@ -268,7 +268,7 @@ Vælg venligst hvorvidt du vil tillade denne adgang. &Match URL scheme (e.g., https://...) - &Match URL skemaer + &Match URL skemaer (f.eks., https://...) Only returns the best matches for a specific URL instead of all entries for the whole domain. @@ -448,7 +448,7 @@ Lås den valgte database op eller vælg en anden som er åbnet. Successfully removed %n encryption key(s) from KeePassXC settings. - + Succesfuldt fjernet %n kryptering nøgle(r) fra KeePassXC indstillinger.Fjernede succesfuldt %n krypteringsnøgle(r) fra KeePassXC indstillinger. Removing stored permissions… @@ -692,15 +692,15 @@ Overvej at generere en ny nøglefil. CsvParserModel %n byte(s), - %n byte(s),%n byte(s), + %n row(s), - %n række(r),%n række(r), + %n column(s) - %n kolonne(r)%n kolonne(r) + @@ -899,7 +899,7 @@ Hvis du beholder dette antal, så kan din database være nem af knække! Parallelism: - Parallelitet + Parallelitet: @@ -1026,7 +1026,7 @@ Gem disse ændringer? Writing the database failed. - Kan ikke skrive til databasen. + Skrivning til database fejler. Passwords @@ -1121,7 +1121,7 @@ Så sikre gem fra og prøv igen? Do you really want to move %n entry(s) to the recycle bin? - Ønsker du virkelig at flytte %n post over i papirkurven?Ønsker du virkelig at flytte %n poster over i papirkurven? + Execute command? @@ -1133,7 +1133,7 @@ Så sikre gem fra og prøv igen? Remember my choice - + Husk mit valg Delete group? @@ -1145,7 +1145,7 @@ Så sikre gem fra og prøv igen? Unable to calculate master key - Kan ikke beregne hovednøgle + Kan ikke beregne hovednøgle No current database. @@ -1182,7 +1182,7 @@ Do you want to merge your changes? Could not open the new database file while attempting to autoreload this database. - + Kunne ikke åbne den nye databasefil i forsøget på at auto-indlæse denne database. Empty recycle bin? @@ -1320,7 +1320,7 @@ Do you want to merge your changes? File too large to be a private key - + Filen er for stor til at være en privat nøgle Failed to open private key @@ -1368,11 +1368,11 @@ Do you want to merge your changes? %n week(s) - %n uge%n uger + %n month(s) - %n måned%n måneder + 1 year @@ -1442,7 +1442,7 @@ Do you want to merge your changes? &Use custom Auto-Type sequence: - + Brug brugerdefineret Auto-Indsæt sekvens: Window Associations @@ -1752,7 +1752,7 @@ Do you want to merge your changes? Delete plugin data? - Slet plugin data + Slet plugin data? Do you really want to delete the selected plugin data? @@ -1993,7 +1993,7 @@ Dette kan få det påvirkede plugin til at svigte. Cannot save the native messaging script file. - + Kan ikke gemme besked-script filen! @@ -2112,7 +2112,7 @@ Dette kan få det påvirkede plugin til at svigte. Unknown cipher - + Ukendt ciffer Invalid header id size @@ -2272,7 +2272,7 @@ Dette kan få det påvirkede plugin til at svigte. Invalid inner random stream cipher - + Invalid ciffer for indre tilfældig strøm Not a KeePass database. @@ -2436,7 +2436,7 @@ Dette er en envejs konvertering. Du vil ikke være i stand til at åbne den impo Not a KeePass database. - Ikke en KeePass database. + Dette er ikke en KeePass database. Unsupported encryption algorithm. @@ -2444,7 +2444,7 @@ Dette er en envejs konvertering. Du vil ikke være i stand til at åbne den impo Unsupported KeePass database version. - Ikke understøttet KeePass database version + KeePass database version ikke understøttet. Unable to read encryption IV @@ -2580,33 +2580,6 @@ Dette er en envejs konvertering. Du vil ikke være i stand til at åbne den impo Invalid post-felt-type - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – anbefalet) - - Main @@ -2666,7 +2639,7 @@ Dette er en envejs konvertering. Du vil ikke være i stand til at åbne den impo &Tools - + &Værktøj &Quit @@ -2774,7 +2747,7 @@ Dette er en envejs konvertering. Du vil ikke være i stand til at åbne den impo &Open URL - &Åbn URL + &Åben URL &Lock databases @@ -3045,7 +3018,7 @@ Denne version er ikke beregnet til at blive brugt i produktion. Sort &matching entries by title - Sorter &passende poster efter navn + Sorter matchende poster efter &navn R&emove all shared encryption keys from active database @@ -3191,7 +3164,7 @@ Bruger standard port 19455. Word Separator: - Ord separator + Ord separator: Generate @@ -3211,7 +3184,7 @@ Bruger standard port 19455. Apply - Entropy: %1 bit + Gennemfør Entropy: %1 bit @@ -3310,7 +3283,7 @@ Bruger standard port 19455. Add a new entry to a database. - Tilføj en ny post til en database + Tilføj en ny post til en database. Path of the database. @@ -3403,7 +3376,7 @@ Bruger standard port 19455. Extract and print the content of a database. - Udtræk og print indeholdet af en database. + Dekomprimer og print indeholdet af en database. Path of the database to extract. @@ -3498,21 +3471,46 @@ Tilgængelige kommandoer: error reading from device - + fejl ved læsning fra enhed file empty ! - + fil tom ! + malformed string - + Misdannet streng missing closing quote Mangler afsluttende kvoteringstegn + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – anbefalet) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Gruppe @@ -3827,7 +3825,7 @@ Lås den valgte database op eller vælg en anden som er åbnet. Don't mark database as modified for non-data changes (e.g., expanding groups) - + Markér ikke databasen som ændret når ikke-data ændres (f.eks. udvidelse af grupper) Hide the Details view @@ -3928,11 +3926,11 @@ Lås den valgte database op eller vælg en anden som er åbnet. Convenience - + Bekvemmelighed Lock databases when session is locked or lid is closed - + Lås databaser når sessionen låses eller låget er lukket Lock databases after minimizing the window @@ -3948,7 +3946,7 @@ Lås den valgte database op eller vælg en anden som er åbnet. Hide passwords in the preview panel - + Skjul kodeord i forhåndsvisningspanelet Hide entry notes by default @@ -4019,7 +4017,7 @@ Lås den valgte database op eller vælg en anden som er åbnet. TotpDialog Timed Password - + Tidsbaseret kodeord 000000 @@ -4088,7 +4086,7 @@ Lås den valgte database op eller vælg en anden som er åbnet. Path of the entry to remove. - + Sti til posten, som skal fjernes. KeePassXC - cross-platform password manager diff --git a/share/translations/keepassx_de.ts b/share/translations/keepassx_de.ts index 927f2878e..e6bb1ba63 100644 --- a/share/translations/keepassx_de.ts +++ b/share/translations/keepassx_de.ts @@ -35,7 +35,7 @@ Copy to clipboard - In Zwischenablage kopieren + In die Zwischenablage kopieren Version %1 @@ -272,7 +272,7 @@ Bitte wählen Sie, ob Sie den Zugriff erlauben möchten. Only returns the best matches for a specific URL instead of all entries for the whole domain. - Zeige nur die am besten passenden Einträge für eine URL anstatt aller Einträge der ganzen Domäne. + Nur die am besten passenden Einträge für eine URL anstatt aller Einträge der ganzen Domäne anzeigen. &Return only best-matching credentials @@ -341,7 +341,7 @@ Bitte wählen Sie, ob Sie den Zugriff erlauben möchten. Use a &proxy application between KeePassXC and browser extension - Verwende eine &Proxy-Anwendungen zwischen KeePassXC und Browser-Erweiterung + &Proxy-Anwendung zwischen KeePassXC und Browser-Erweiterung verwenden Use a custom proxy location if you installed a proxy manually. @@ -448,7 +448,7 @@ Bitte entsperren Sie die ausgewählte Datenbank oder wählen Sie eine andere, di Successfully removed %n encryption key(s) from KeePassXC settings. - Es wurden erfolgreich %n Schlüssel aus den KeePassXC-Einstellungen entfernt.Es wurden erfolgreich %n Schlüssel aus den KeePassXC-Einstellungen entfernt. + Es wurde erfolgreich %n Schlüssel aus den KeePassXC-Einstellungen entfernt.Es wurden erfolgreich %n Schlüssel aus den KeePassXC-Einstellungen entfernt. Removing stored permissions… @@ -472,7 +472,7 @@ Bitte entsperren Sie die ausgewählte Datenbank oder wählen Sie eine andere, di The active database does not contain an entry with permissions. - The aktive Datenbank enthält keinen Eintrag mit Zugangsdaten. + Diese Datenbank enthält keinen Eintrag mit Zugangsdaten. @@ -561,7 +561,7 @@ Bitte denken Sie darüber nach, eine neue Schlüsseldatei zu generieren. Changing master key failed: no YubiKey inserted. - Ändern des Hauptschlüssels fehlgeschlagen: kein YubiKey eingesteckt. + Ändern des Master-Passworts fehlgeschlagen: kein YubiKey eingesteckt. @@ -576,7 +576,7 @@ Bitte denken Sie darüber nach, eine neue Schlüsseldatei zu generieren. Replace username and password with references - Benutzernamen und Passwort mit Referencen ersetzen + Benutzernamen und Passwort mit Referenzen ersetzen Copy history @@ -683,14 +683,14 @@ Bitte denken Sie darüber nach, eine neue Schlüsseldatei zu generieren. Unable to calculate master key - Berechnung des Hauptschlüssels gescheitert + Berechnung des Master-Passworts gescheitert CsvParserModel %n byte(s), - %n Byte,%n Byte, + %n Byte%n Bytes %n row(s), @@ -705,7 +705,7 @@ Bitte denken Sie darüber nach, eine neue Schlüsseldatei zu generieren.DatabaseOpenWidget Enter master key - Hauptschlüssel eingeben + Master-Passwort eingeben Key File: @@ -729,7 +729,7 @@ Bitte denken Sie darüber nach, eine neue Schlüsseldatei zu generieren. Unable to open the database. - Öffnen der Datenbank nicht möglich. + Öffnen der Datenbank ist nicht möglich. Can't open key file @@ -781,7 +781,7 @@ Bitte denken Sie darüber nach, eine neue Schlüsseldatei zu generieren. Unable to open the database. - Öffnen der Datenbank nicht möglich. + Öffnen der Datenbank ist nicht möglich. Database opened fine. Nothing to do. @@ -862,7 +862,7 @@ Wenn Sie diese Anzahl beibehalten, könnte Ihre Datenbank zu einfach zu knacken thread(s) Threads for parallel execution (KDF settings) - ThreadThreads + Thread Threads @@ -996,11 +996,11 @@ Wenn Sie diese Anzahl beibehalten, könnte Ihre Datenbank zu einfach zu knacken Open KeePass 1 database - KeePass 1 Datenbank öffnen + KeePass 1-Datenbank öffnen KeePass 1 database - KeePass 1 Datenbank + KeePass 1-Datenbank Close? @@ -1032,7 +1032,7 @@ Save changes? Save database as - Datenbank speichern unter + Datenbank speichern als Export database to CSV file @@ -1057,16 +1057,16 @@ Save changes? Can't lock the database as you are currently editing it. Please press cancel to finish your changes or discard them. - Datenbank kann nicht gesperrt werden, da sie gerade bearbeitet wird. + Die Datenbank kann nicht gesperrt werden, da sie gerade bearbeitet wird. Wählen sie „Abbrechen“, um die Änderungen zu speichern oder sie zurückzunehmen. This database has been modified. Do you want to save the database before locking it? Otherwise your changes are lost. - Dieses Datenbank wurde geändert. + Diese Datenbank wurde geändert. Soll sie gespeichert werden bevor sie gesperrt wird? -Anderenfalls gehen Ihre Änderungen verloren. +Anderenfalls gehen Ihre Änderungen seit dem letzten Speichern verloren. Disable safe saves? @@ -1087,7 +1087,7 @@ Sicheres Speichern deaktivieren und erneut versuchen? Change master key - Hauptschlüssel ändern + Master-Passwort ändern Delete entry? @@ -1119,7 +1119,7 @@ Sicheres Speichern deaktivieren und erneut versuchen? Do you really want to move %n entry(s) to the recycle bin? - Wollen Sie wirklich %n Eintrag in den Papierkorb verschieben?Wollen Sie wirklich %n Einträge in den Papierkorb verschieben? + Möchten Sie wirklich %n Eintrag aus dem Papierkorb löschen?Möchten Sie wirklich %n Einträge aus dem Papierkorb löschen? Execute command? @@ -1143,7 +1143,7 @@ Sicheres Speichern deaktivieren und erneut versuchen? Unable to calculate master key - Berechnung des Hauptschlüssels gescheitert + Berechnung des Master-Passworts gescheitert No current database. @@ -1339,7 +1339,7 @@ Möchten Sie Ihre Änderungen zusammenführen? Different passwords supplied. - Unterschiedliche Passwörter eingegeben. + Passwörter sind unterschiedlich New attribute @@ -1367,11 +1367,11 @@ Möchten Sie Ihre Änderungen zusammenführen? %n week(s) - %n Woche%n Woche(n) + %n Woche%n Wochen %n month(s) - %n Monat%n Monat(en) + %n Monat%n Monate 1 year @@ -1566,7 +1566,7 @@ Möchten Sie Ihre Änderungen zusammenführen? Copy to clipboard - In die Zwischenablage kopieren + In Zwischenablage kopieren Private key @@ -1645,7 +1645,7 @@ Möchten Sie Ihre Änderungen zusammenführen? Expires - Erlischt + Verfällt Search @@ -1708,7 +1708,7 @@ Möchten Sie Ihre Änderungen zusammenführen? Can't read icon - Icon kann nicht gelesen werden + Symbol kann nicht gelesen werden Custom icon already exists @@ -1720,7 +1720,7 @@ Möchten Sie Ihre Änderungen zusammenführen? This icon is used by %1 entries, and will be replaced by the default icon. Are you sure you want to delete it? - Dieses Icon wird noch von %1 Einträgen verwendet und würde mit dem Standard-Icon ersetzt. Sind Sie sicher, dass die fortfahren wollen? + Dieses Symbol wird noch von %1 Einträgen verwendet und würde mit dem Standard-Symbol ersetzt. Sind Sie sicher, dass die fortfahren wollen? @@ -1803,7 +1803,7 @@ Dies kann dazu führen, dass die jeweiligen Plugins nicht mehr richtig funktioni Open - Öffnen + Offen Save @@ -1815,7 +1815,7 @@ Dies kann dazu führen, dass die jeweiligen Plugins nicht mehr richtig funktioni Are you sure you want to remove %n attachment(s)? - Sind Sie sicher, dass Sie %n Anhang löschen wollen?Sind Sie sicher, dass Sie %n Anhänge löschen möchten? + Sind Sie sicher, dass sie einen Anhang löschen möchten?Sind Sie sicher, dass sie %n Anhänge löschen möchten? Confirm Remove @@ -1973,7 +1973,7 @@ Dies kann dazu führen, dass die jeweiligen Plugins nicht mehr richtig funktioni Attachments (icon) - Anhänge (Icon) + Anhänge (Symbol) @@ -2303,7 +2303,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Missing icon uuid or data - Fehlende Icon-UUID oder -Daten + Fehlende Symbol-UUID oder -Daten Missing custom data key or value @@ -2319,7 +2319,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Invalid group icon number - Ungültige Gruppen-Icon-Anzahl + Ungültige Gruppen-Symbol-Anzahl Invalid EnableAutoType value @@ -2347,7 +2347,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Invalid entry icon number - Ungültige Eintrags-Icon-Anzahl + Ungültige Eintrags-Symbol-Anzahl History element in history entry @@ -2423,7 +2423,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Unable to open the database. - Öffnen der Datenbank nicht möglich. + Öffnen der Datenbank ist nicht möglich. @@ -2479,7 +2479,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Unable to calculate master key - Berechnung des Hauptschlüssels gescheitert + Berechnung des Master-Passworts gescheitert Wrong key or database file is corrupt. @@ -2523,7 +2523,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Incorrect group icon field size - Falsche Feldgröße für Gruppen-Icon + Falsche Feldgröße für Gruppen-Symbol Incorrect group level field size @@ -2559,7 +2559,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Invalid entry icon field size - Falsche Feldgröße für Eintrags-Icon + Falsche Feldgröße für Eintrags-Symbol Invalid entry creation time field size @@ -2578,33 +2578,6 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Ungültiger Eintrags-Feldtyp - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – empfohlen) - - Main @@ -2692,7 +2665,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Merge from KeePassX database - Aus KeePassXC-Datenbank zusammenführen + Mit KeePassXC-Datenbank zusammenführen &Add new entry @@ -2724,7 +2697,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Change &master key... - Ha&uptschlüssel ändern... + &Master-Passwort ändern... &Database settings @@ -2748,7 +2721,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann Copy username to clipboard - Benutzername in die Zwischenablage kopieren + Benutzernamen in die Zwischenablage kopieren Cop&y password @@ -2776,7 +2749,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann &Lock databases - Datenbanken &sperren + Datenbank &sperren &Title @@ -3019,7 +2992,7 @@ Diese Version ist nicht für den Produktiveinsatz gedacht. Only returns the best matches for a specific URL instead of all entries for the whole domain. - Zeige nur die am besten passenden Einträge für eine URL anstatt aller Einträge der ganzen Domäne. + Nur die am besten passenden Einträge für eine URL anstatt aller Einträge der ganzen Domäne anzeigen. &Return only best matching entries @@ -3039,11 +3012,11 @@ Diese Version ist nicht für den Produktiveinsatz gedacht. Sort matching entries by &username - Sortiere gefundene Einträge nach &Benutzername + Passende Einträge nach &Benutzernamen sortieren Sort &matching entries by title - Sortiere gefundene Einträge nach Titel + &Passende Einträge nach Titel sortieren R&emove all shared encryption keys from active database @@ -3324,11 +3297,11 @@ Es wird der Standard-Port 19455 verwendet. Username for the entry. - Nutzername für den Eintrag + Benutzername für den Eintrag username - Nutzername + Benutzername URL for the entry. @@ -3511,6 +3484,30 @@ Verfügbare Kommandos: missing closing quote Schließendes Anführungszeichen fehlt + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – empfohlen) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Gruppe @@ -3959,7 +3956,7 @@ Bitte entsperren Sie die ausgewählte Datenbank oder wählen Sie eine andere, di Use Google as fallback for downloading website icons - Verwende Google als Fallback fürs Herunterladen von Website-Icons + Google als Reservesystem fürs Herunterladen von Website-Symbolen verwenden Re-lock previously locked database after performing Auto-Type diff --git a/share/translations/keepassx_el.ts b/share/translations/keepassx_el.ts index a27baa6a8..e8ca2dc83 100644 --- a/share/translations/keepassx_el.ts +++ b/share/translations/keepassx_el.ts @@ -238,19 +238,19 @@ Please select whether you want to allow access. &Google Chrome - + &Google Chrome &Firefox - + &Firefox &Chromium - + &Chromium &Vivaldi - + &Vivaldi Show a &notification when credentials are requested @@ -533,7 +533,7 @@ Please unlock the selected database or choose another one which is unlocked. Different passwords supplied. - Έχετε εισάγει διαφορετικούς κωδικούς. + Παρέχονται διαφορετικοί κωδικοί. Failed to set %1 as the Key file: @@ -627,7 +627,7 @@ Please consider generating a new key file. Column layout - + Διάταξη στηλών Not present in CSV file @@ -721,11 +721,11 @@ Please consider generating a new key file. Unable to open the database. - Αδύνατο να ανοιχτεί η βάση δεδομένων. + Δεν είναι δυνατό να ανοίξει τη βάση δεδομένων. Can't open key file - Αποτυχία ανοίγματος αρχείο κλειδιού + Δεν είναι δυνατό να ανοίξει αρχείο κλειδιού Legacy key file format @@ -740,11 +740,11 @@ Please consider generating a new key file. Don't show this warning again - + Να μην εμφανιστεί ξανά αυτή η προειδοποίηση All files - Όλα τα αρχεία + Όλα τα αρχεία Key files @@ -799,12 +799,12 @@ You can now save it. Encryption - + Κρυπτογράφηση Number of rounds too high Key transformation rounds - + Αριθμός γύρων πάρα πολύ υψηλός You are using a very high number of key transform rounds with Argon2. @@ -818,12 +818,12 @@ If you keep this number, your database may take hours or days (or even longer) t Cancel - + Άκυρο Number of rounds too low Key transformation rounds - + Αριθμός γύρων πάρα πολύ χαμηλός You are using a very low number of key transform rounds with AES-KDF. @@ -854,7 +854,7 @@ If you keep this number, your database may be too easy to crack! DatabaseSettingsWidgetEncryption Encryption Algorithm: - + Αλγόριθμος κρυπτογράφησης: AES: 256 Bit (default) @@ -878,18 +878,18 @@ If you keep this number, your database may be too easy to crack! Memory Usage: - + Χρήση μνήμης: Parallelism: - + Παραλληλισμός: DatabaseSettingsWidgetGeneral Database Meta Data - + Μετα-δεδομένα βάσης Database name: @@ -905,7 +905,7 @@ If you keep this number, your database may be too easy to crack! History Settings - + Ρυθμίσεις ιστορικού Max. history items: @@ -925,7 +925,7 @@ If you keep this number, your database may be too easy to crack! Additional Database Settings - + Πρόσθετες ρυθμίσεις βάσης δεδομένων Enable &compression (recommended) @@ -949,7 +949,7 @@ If you keep this number, your database may be too easy to crack! Open database - Άνοιγμα βάσης δεδομένων + Άνοιγμα Βάσης Δεδομένων File not found! @@ -1017,7 +1017,7 @@ Save changes? Save database as - Αποθήκευση βάσης δεδομένων σαν + Αποθήκευση βάσης δεδομένων ως Export database to CSV file @@ -1029,7 +1029,7 @@ Save changes? New database - Νέα βάση δεδομένων + Νέα Βάση Δεδομένων locked @@ -1601,7 +1601,7 @@ Do you want to merge your changes? Edit group - Επεξεργασία ομάδας + Επεξεργασία Ομάδας Enable @@ -2398,7 +2398,7 @@ This is a one-way migration. You won't be able to open the imported databas Unable to open the database. - Αποτυχία ανοίγματος βάσης δεδομένων. + Δεν είναι δυνατό να ανοίξει τη βάση δεδομένων. @@ -2409,7 +2409,7 @@ This is a one-way migration. You won't be able to open the imported databas Not a KeePass database. - Δεν ειναι βάση δεδομένων KeePass. + Δεν είναι βάση δεδομένων KeePass. Unsupported encryption algorithm. @@ -2553,33 +2553,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - - - - Twofish: 256-bit - - - - ChaCha20: 256-bit - - - - AES-KDF (KDBX 4) - - - - AES-KDF (KDBX 3.1) - - - - Argon2 (KDBX 4 – recommended) - - - Main @@ -3479,6 +3452,30 @@ Available commands: missing closing quote + + AES: 256-bit + + + + Twofish: 256-bit + + + + ChaCha20: 256-bit + + + + Argon2 (KDBX 4 – recommended) + + + + AES-KDF (KDBX 4) + + + + AES-KDF (KDBX 3.1) + + Group Όμαδα diff --git a/share/translations/keepassx_en.ts b/share/translations/keepassx_en.ts index a0e1b99b2..ec50f7b37 100644 --- a/share/translations/keepassx_en.ts +++ b/share/translations/keepassx_en.ts @@ -39,11 +39,6 @@ Copy to clipboard - - Version %1 - - - Revision: %1 @@ -75,32 +70,35 @@ Kernel: %3 %4 - Build Type: %1 - - - - - - AccessControlDialog - - KeePassXC HTTP Confirm Access + Version %1 - Remember this decision + Build Type: %1 - Allow + Auto-Type - Deny + Browser Integration - %1 has requested access to passwords for the following item(s). -Please select whether you want to allow access. + SSH Agent + + + + YubiKey + + + + TouchID + + + + None @@ -111,6 +109,237 @@ Please select whether you want to allow access. + + ApplicationSettingsWidget + + Application Settings + + + + General + + + + Security + + + + Access error for config file %1 + + + + + ApplicationSettingsWidgetGeneral + + Basic Settings + + + + Startup + + + + Start only a single instance of KeePassXC + + + + Remember last databases + + + + Remember last key files and security dongles + + + + Load previous databases on startup + + + + Minimize window at application startup + + + + File Management + + + + Safely save database files (may be incompatible with Dropbox, etc) + + + + Backup database file before saving + + + + Automatically save after every change + + + + Automatically save on exit + + + + Don't mark database as modified for non-data changes (e.g., expanding groups) + + + + Automatically reload the database when modified externally + + + + Entry Management + + + + Use group icon on entry creation + + + + Minimize when copying to clipboard + + + + Hide the entry preview panel + + + + General + + + + Hide toolbar (icons) + + + + Minimize instead of app exit + + + + Show a system tray icon + + + + Dark system tray icon + + + + Hide window to system tray when minimized + + + + Language + + + + Auto-Type + + + + Use entry title to match windows for global Auto-Type + + + + Use entry URL to match windows for global Auto-Type + + + + Always ask before performing Auto-Type + + + + Global Auto-Type shortcut + + + + Auto-Type typing delay + + + + ms + Milliseconds + + + + Auto-Type start delay + + + + + ApplicationSettingsWidgetSecurity + + Timeouts + + + + Clear clipboard after + + + + sec + Seconds + + + + Lock databases after inactivity of + + + + min + + + + Forget TouchID after inactivity of + + + + Convenience + + + + Lock databases when session is locked or lid is closed + + + + Forget TouchID when session is locked or lid is closed + + + + Lock databases after minimizing the window + + + + Re-lock previously locked database after performing Auto-Type + + + + Don't require password repeat when it is visible + + + + Don't hide passwords when editing them + + + + Don't use placeholder for empty password fields + + + + Hide passwords in the entry preview panel + + + + Hide entry notes by default + + + + Privacy + + + + Use DuckDuckGo as fallback for downloading website icons + + + AutoType @@ -211,6 +440,26 @@ Please select whether you want to allow access. + + BrowserEntrySaveDialog + + KeePassXC-Browser Save Entry + + + + Ok + + + + Cancel + + + + You have multiple databases open. +Please select the correct database for saving credentials. + + + BrowserOptionDialog @@ -284,14 +533,6 @@ Please select whether you want to allow access. Credentials mean login data requested via browser extension - - &Disconnect all browsers - - - - Forget all remembered &permissions - - Advanced @@ -357,14 +598,6 @@ Please select whether you want to allow access. <b>Warning:</b> The following options can be dangerous! - - Executable Files (*.exe);;All Files (*.*) - - - - Executable Files (*) - - Select custom proxy location @@ -377,6 +610,22 @@ Please select whether you want to allow access. KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. + + &Tor Browser + + + + <b>Warning</b>, the keepassxc-proxy application was not found!<br />Please check the KeePassXC installation directory or confirm the custom path in advanced options.<br />Browser integration WILL NOT WORK without the proxy application.<br />Expected Path: + + + + Executable Files + + + + All Files + + BrowserService @@ -412,154 +661,46 @@ Do you want to overwrite it? Do you want to update the information in %1 - %2? - - KeePassXC: Database locked! - - - - The active database is locked! -Please unlock the selected database or choose another one which is unlocked. - - - - KeePassXC: Settings not available! - - - - The active database does not contain a settings entry. - - - - KeePassXC: No keys found - - - - No shared encryption keys found in KeePassXC Settings. - - - - KeePassXC: Removed keys from database - - - - Successfully removed %n encryption key(s) from KeePassXC settings. - - - - - - - Removing stored permissions… - - Abort - KeePassXC: Removed permissions + Converting attributes to custom data… + + + + KeePassXC: Converted KeePassHTTP attributes + + + + Successfully converted attributes from %1 entry(s). +Moved %2 keys to custom data. - Successfully removed permissions from %n entry(s). + Successfully moved %n keys to custom data. - KeePassXC: No entry with permissions found! + KeePassXC: No entry with KeePassHTTP attributes found! - The active database does not contain an entry with permissions. - - - - - ChangeMasterKeyWidget - - Password + The active database does not contain an entry with KeePassHTTP attributes. - Enter password: + KeePassXC: Legacy browser integration settings detected - Repeat password: - - - - &Key file - - - - Browse - - - - Create - - - - Cha&llenge Response - - - - Refresh - - - - Key files - - - - All files - - - - Create Key File... - - - - Unable to create Key File : - - - - Select a key file - - - - Empty password - - - - Do you really want to use an empty string as password? - - - - Different passwords supplied. - - - - Failed to set %1 as the Key file: -%2 - - - - Legacy key file format - - - - You are using a legacy key file format which may become -unsupported in the future. - -Please consider generating a new key file. - - - - Changing master key failed: no YubiKey inserted. + Legacy browser integration settings have been detected. +Do you want to upgrade the settings to the latest standard? +This is necessary to maintain compatibility with the browser plugin. @@ -640,14 +781,6 @@ Please consider generating a new key file. Not present in CSV file - - Empty fieldname - - - - column - - Imported from CSV file @@ -656,51 +789,37 @@ Please consider generating a new key file. Original data: - - Error(s) detected in CSV file ! - - - - more messages skipped] - - Error + + Empty fieldname %1 + + + + column %1 + + + + Error(s) detected in CSV file! + + + + [%n more message(s) skipped] + + + + + CSV import: writer has errors: - - - - - - CsvImportWizard - - Error - - - - Unable to calculate master key +%1 CsvParserModel - - %n byte(s), - - - - - - - %n row(s), - - - - - %n column(s) @@ -708,6 +827,60 @@ Please consider generating a new key file. + + %1, %2, %3 + file info: bytes, rows, columns + + + + %n byte(s) + + + + + + + %n row(s) + + + + + + + + Database + + Root + Root group name + + + + File %1 does not exist. + + + + Unable to open file %1. + + + + Error while reading the database: %1 + + + + Could not save, database has no file name. + + + + File cannot be written as it is opened in read-only mode. + + + + + DatabaseOpenDialog + + Unlock Database - KeePassXC + + DatabaseOpenWidget @@ -735,14 +908,6 @@ Please consider generating a new key file. Challenge Response: - - Unable to open the database. - - - - Can't open key file - - Legacy key file format @@ -770,51 +935,253 @@ Please consider generating a new key file. Select key file - - - DatabaseRepairWidget - Repair database + TouchID for quick unlock - Error + Unable to open the database: +%1 - Can't open key file - - - - Unable to open the database. - - - - Database opened fine. Nothing to do. - - - - Success - - - - The database has been successfully repaired -You can now save it. - - - - Unable to repair the database. + Can't open key file: +%1 - DatabaseSettingsWidget + DatabaseSettingWidgetMetaData + + Passwords + + + + + DatabaseSettingsDialog + + Advanced Settings + + General - Encryption + Security + + + + Master Key + + + + Encryption Settings + + + + Browser Integration + + + + + DatabaseSettingsWidgetBrowser + + KeePassXC-Browser settings + + + + &Disconnect all browsers + + + + Forg&et all site-specific settings on entries + + + + Move KeePassHTTP attributes to KeePassXC-Browser &custom data + + + + Stored keys + + + + Remove + + + + Delete the selected key? + + + + Do you really want to delete the selected key? +This may prevent connection to the browser plugin. + + + + Key + + + + Value + + + + Enable Browser Integration to access these settings. + + + + Disconnect all browsers + + + + Do you really want to disconnect all browsers? +This may prevent connection to the browser plugin. + + + + KeePassXC: No keys found + + + + No shared encryption keys found in KeePassXC settings. + + + + KeePassXC: Removed keys from database + + + + Successfully removed %n encryption key(s) from KeePassXC settings. + + + + + + + Forget all site-specific settings on entries + + + + Do you really want forget all site-specific settings on every entry? +Permissions to access entries will be revoked. + + + + Removing stored permissions… + + + + Abort + + + + KeePassXC: Removed permissions + + + + Successfully removed permissions from %n entry(s). + + + + + + + KeePassXC: No entry with permissions found! + + + + The active database does not contain an entry with permissions. + + + + Move KeePassHTTP attributes to custom data + + + + Do you really want to move all legacy browser integration data to the latest standard? +This is necessary to maintain compatibility with the browser plugin. + + + + + DatabaseSettingsWidgetEncryption + + Encryption Algorithm: + + + + AES: 256 Bit (default) + + + + Twofish: 256 Bit + + + + Key Derivation Function: + + + + Transform rounds: + + + + Benchmark 1-second delay + + + + Memory Usage: + + + + Parallelism: + + + + Decryption Time: + + + + ?? s + + + + Change + + + + 100 ms + + + + 5 s + + + + Higher values offer more protection, but opening the database will take longer. + + + + Database format: + + + + This is only important if you need to use your database with other programs. + + + + KDBX 4.0 (recommended) + + + + KDBX 3.1 + + + + unchanged + Database decryption time is unchanged @@ -871,40 +1238,21 @@ If you keep this number, your database may be too easy to crack! - - - DatabaseSettingsWidgetEncryption - - Encryption Algorithm: - + + %1 ms + milliseconds + + + + - - AES: 256 Bit (default) - - - - Twofish: 256 Bit - - - - Key Derivation Function: - - - - Transform rounds: - - - - Benchmark 1-second delay - - - - Memory Usage: - - - - Parallelism: - + + %1 s + seconds + + + + @@ -955,12 +1303,51 @@ If you keep this number, your database may be too easy to crack! - DatabaseTabWidget + DatabaseSettingsWidgetMasterKey - Root - Root group + Add additional protection... + + No encryption key added + + + + You must add at least one encryption key to secure your database! + + + + No password set + + + + WARNING! You have not set a password. Using a database without a password is strongly discouraged! + +Are you sure you want to continue without a password? + + + + Unknown error + + + + Failed to change master key + + + + + DatabaseSettingsWidgetMetaDataSimple + + Database Name: + + + + Description: + + + + + DatabaseTabWidget KeePass 2 Database @@ -973,30 +1360,10 @@ If you keep this number, your database may be too easy to crack! Open database - - File not found! - - - - Unable to open the database. - - - - File opened in read only mode. - - - - Open CSV file - - CSV file - - All files (*) - - Merge database @@ -1009,36 +1376,6 @@ If you keep this number, your database may be too easy to crack! KeePass 1 database - - Close? - - - - "%1" is in edit mode. -Discard changes and close anyway? - - - - Save changes? - - - - "%1" was modified. -Save changes? - - - - Writing the database failed. - - - - Passwords - - - - Save database as - - Export database to CSV file @@ -1048,35 +1385,39 @@ Save changes? - New database + Database creation error - locked + The created database has no key or KDF, refusing to save it. +This is definitely a bug, please report it to the developers. - Lock database + The database file does not exist or is not accessible. - Can't lock the database as you are currently editing it. -Please press cancel to finish your changes or discard them. + Select CSV file - This database has been modified. -Do you want to save the database before locking it? -Otherwise your changes are lost. + New Database - Disable safe saves? + %1 [New Database] + Database tab name modifier - KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. -Disable safe saves and try again? + %1 [Locked] + Database tab name modifier + + + + %1 [Read-only] + Database tab name modifier @@ -1086,38 +1427,14 @@ Disable safe saves and try again? Searching... - - Change master key - - - - Delete entry? - - Do you really want to delete the entry "%1" for good? - - Delete entries? - - - - Do you really want to delete %1 entries for good? - - - - Move entry to recycle bin? - - Do you really want to move entry "%1" to the recycle bin? - - Move entries to recycle bin? - - Do you really want to move %n entry(s) to the recycle bin? @@ -1145,10 +1462,6 @@ Disable safe saves and try again? Do you really want to delete the group "%1" for good? - - Unable to calculate master key - - No current database. @@ -1182,10 +1495,6 @@ Disable safe saves and try again? Do you want to merge your changes? - - Could not open the new database file while attempting to autoreload this database. - - Empty recycle bin? @@ -1194,87 +1503,82 @@ Do you want to merge your changes? Are you sure you want to permanently delete everything from your recycle bin? - - - DetailsWidget + + Do you really want to delete %n entry(s) for good? + + + + + + + Delete entry(s)? + + + + + + + Move entry(s) to recycle bin? + + + + + - Generate TOTP Token + File opened in read only mode. - Close + Lock Database? - General + You are editing an entry. Discard changes and lock anyway? - Password + "%1" was modified. +Save changes? - URL + Database was modified. +Save changes? - Expiration + Save changes? - Username + Could not open the new database file while attempting to autoreload. +Error: %1 - Autotype + Disable safe saves? - Searching + KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. +Disable safe saves and try again? - Attributes + Writing the database failed. +%1 - Attachments + Passwords - Notes + Save database as - Window - - - - Sequence - - - - Search - - - - Clear - - - - Never - - - - [PROTECTED] - - - - Disabled - - - - Enabled + KeePass 2 Database @@ -1356,14 +1660,6 @@ Do you want to merge your changes? Are you sure you want to remove this attribute? - - [PROTECTED] - - - - Press reveal to view or edit - - Tomorrow @@ -1382,10 +1678,6 @@ Do you want to merge your changes? - - 1 year - - Apply generated password? @@ -1398,6 +1690,25 @@ Do you want to merge your changes? Entry updated successfully. + + Entry has unsaved changes + + + + New attribute %1 + + + + [PROTECTED] Press reveal to view or edit + + + + %n year(s) + + + + + EditEntryWidgetAdvanced @@ -1699,10 +2010,6 @@ Do you want to merge your changes? Unable to fetch favicon. - - Hint: You can enable Google as a fallback under Tools>Settings>Security - - Images @@ -1711,14 +2018,6 @@ Do you want to merge your changes? All files - - Select Image - - - - Can't read icon - - Custom icon already exists @@ -1728,9 +2027,49 @@ Do you want to merge your changes? - This icon is used by %1 entries, and will be replaced by the default icon. Are you sure you want to delete it? + Custom icon successfully downloaded + + Hint: You can enable DuckDuckGo as a fallback under Tools>Settings>Security + + + + Select Image(s) + + + + Successfully loaded %1 of %n icon(s) + + + + + + + No icons were loaded + + + + %n icon(s) already exist in the database + + + + + + + The following icon(s) failed: + + + + + + + This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it? + + + + + EditWidgetProperties @@ -1779,8 +2118,7 @@ This may cause the affected plugins to malfunction. Entry - - Clone - Suffix added to cloned entries + %1 - Clone @@ -1828,10 +2166,6 @@ This may cause the affected plugins to malfunction. - - Confirm Remove - - Save attachments @@ -1869,6 +2203,10 @@ This may cause the affected plugins to malfunction. %1 + + Confirm remove + + EntryAttributesModel @@ -1951,6 +2289,102 @@ This may cause the affected plugins to malfunction. Attachments + + Yes + + + + TOTP + + + + + EntryPreviewWidget + + Generate TOTP Token + + + + Close + + + + General + + + + Username + + + + Password + + + + Expiration + + + + URL + + + + Attributes + + + + Attachments + + + + Notes + + + + Autotype + + + + Window + + + + Sequence + + + + Searching + + + + Search + + + + Clear + + + + Never + + + + [PROTECTED] + + + + <b>%1</b>: %2 + attributes line + + + + Enabled + + + + Disabled + + EntryView @@ -1989,6 +2423,11 @@ This may cause the affected plugins to malfunction. Recycle Bin + + [empty] + group has no children + + HostInstaller @@ -2001,61 +2440,6 @@ This may cause the affected plugins to malfunction. - - HttpPasswordGeneratorWidget - - Length: - - - - Character Types - - - - Upper Case Letters - - - - A-Z - - - - Lower Case Letters - - - - a-z - - - - Numbers - - - - 0-9 - - - - Special Characters - - - - /*_& ... - - - - Exclude look-alike characters - - - - Ensure that the password contains characters from every group - - - - Extended ASCII - - - KMessageWidget @@ -2081,6 +2465,26 @@ This may cause the affected plugins to malfunction. Wrong key or database file is corrupt. + + missing database headers + + + + Header doesn't match hash + + + + Invalid header id size + + + + Invalid header field length + + + + Invalid header data length + + Kdbx3Writer @@ -2239,10 +2643,6 @@ This may cause the affected plugins to malfunction. KdbxReader - - Invalid cipher uuid length - - Unsupported cipher @@ -2294,6 +2694,14 @@ This is a one-way migration. You won't be able to open the imported databas Unsupported KeePass 2 database version. + + Invalid cipher uuid length: %1 (length=%2) + + + + Unable to parse UUID: %1 + + KdbxXmlReader @@ -2414,6 +2822,12 @@ This is a one-way migration. You won't be able to open the imported databas Translator meant is a binary data inside an entry + + XML error: +%1 +Line %2, column %3 + + KeePass1OpenWidget @@ -2577,31 +2991,106 @@ This is a one-way migration. You won't be able to open the imported databas Invalid entry field type + + unable to seek to content position + + - KeePass2 + KeyComponentWidget - AES: 256-bit + Key Component - Twofish: 256-bit + Key Component Description - ChaCha20: 256-bit + Cancel - AES-KDF (KDBX 4) + Key Component set, click to change or remove - AES-KDF (KDBX 3.1) + Add %1 + Add a key component - Argon2 (KDBX 4 – recommended) + Change %1 + Change a key component + + + + Remove %1 + Remove a key component + + + + %1 set, click to change or remove + Change or remove a key component + + + + + KeyFileEditWidget + + Browse + + + + Generate + + + + Key File + + + + <p>You can add a key file containing random bytes for additional security.</p><p>You must keep it secret and never lose it or you will be locked out!</p> + + + + Legacy key file format + + + + You are using a legacy key file format which may become +unsupported in the future. + +Please go to the master key settings and generate a new key file. + + + + Error loading the key file '%1' +Message: %2 + + + + Key files + + + + All files + + + + Create Key File... + + + + Error creating key file + + + + Unable to create key file: %1 + + + + Select a key file @@ -2642,10 +3131,6 @@ This is a one-way migration. You won't be able to open the imported databas &Recent databases - - Import - - &Help @@ -2654,14 +3139,6 @@ This is a one-way migration. You won't be able to open the imported databas E&ntries - - Copy att&ribute to clipboard - - - - Time-based one-time password - - &Groups @@ -2690,30 +3167,10 @@ This is a one-way migration. You won't be able to open the imported databas &Close database - - &New database - - - - Merge from KeePassX database - - - - &Add new entry - - - - &View/Edit entry - - &Delete entry - - &Add new group - - &Edit group @@ -2726,14 +3183,6 @@ This is a one-way migration. You won't be able to open the imported databas Sa&ve database as... - - Change &master key... - - - - &Database settings - - Database settings @@ -2750,10 +3199,6 @@ This is a one-way migration. You won't be able to open the imported databas Copy username to clipboard - - Cop&y password - - Copy password to clipboard @@ -2766,14 +3211,6 @@ This is a one-way migration. You won't be able to open the imported databas Password Generator - - &Perform Auto-Type - - - - &Open URL - - &Lock databases @@ -2806,22 +3243,6 @@ This is a one-way migration. You won't be able to open the imported databas &Export to CSV file... - - Import KeePass 1 database... - - - - Import CSV file... - - - - Re&pair database... - - - - Show TOTP - - Set up TOTP... @@ -2842,14 +3263,6 @@ This is a one-way migration. You won't be able to open the imported databas Access error for config file %1 - - <p>It looks like you are using KeePassHTTP for browser integration. This feature has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a> (warning %1 of 3).</p> - - - - read-only - - Settings @@ -2862,26 +3275,6 @@ This is a one-way migration. You won't be able to open the imported databas Quit KeePassXC - - KeePass 2 Database - - - - All files - - - - Open database - - - - Save repaired database - - - - Writing the database failed. - - Please touch the button on your YubiKey! @@ -2905,6 +3298,229 @@ This version is not meant for production use. We recommend you use the AppImage available on our downloads page. + + &Import + + + + Copy att&ribute... + + + + TOTP... + + + + &New database... + + + + Create a new database + + + + &Merge from database... + + + + Merge from another KDBX database + + + + &New entry + + + + Add a new entry + + + + &Edit entry + + + + View or edit entry + + + + &New group + + + + Add a new group + + + + Change master &key... + + + + &Database settings... + + + + Copy &password + + + + Perform &Auto-Type + + + + Open &URL + + + + KeePass 1 database... + + + + Import a KeePass 1 database + + + + CSV file... + + + + Import a CSV file + + + + Show TOTP... + + + + Show TOTP QR Code... + + + + + Merger + + Creating missing %1 [%2] + + + + Relocating %1 [%2] + + + + Overwriting %1 [%2] + + + + older entry merged from database "%1" + + + + Adding backup for older target %1 [%2] + + + + Adding backup for older source %1 [%2] + + + + Reapplying older target entry on top of newer source %1 [%2] + + + + Reapplying older source entry on top of newer target %1 [%2] + + + + Synchronizing from newer source %1 [%2] + + + + Synchronizing from older source %1 [%2] + + + + Deleting child %1 [%2] + + + + Deleting orphan %1 [%2] + + + + Changed deleted objects + + + + Adding missing icon %1 + + + + + NewDatabaseWizard + + Create a new KeePassXC database... + + + + Root + Root group + + + + + NewDatabaseWizardPage + + WizardPage + + + + En&cryption Settings + + + + Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. + + + + Advanced Settings + + + + Simple Settings + + + + + NewDatabaseWizardPageEncryption + + Encryption Settings + + + + Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings. + + + + + NewDatabaseWizardPageMasterKey + + Database Master Key + + + + A master key known only to you protects your database. + + + + + NewDatabaseWizardPageMetaData + + General Database Information + + + + Please fill in the display name and an optional description for your new database: + + OpenSSHKey @@ -3006,123 +3622,33 @@ We recommend you use the AppImage available on our downloads page. - OptionDialog + PasswordEditWidget - Dialog + Enter password: - This is required for accessing your databases from ChromeIPass or PassIFox + Confirm password: - Enable KeePassHTTP server + Password - General + <p>A password is the primary method for securing your database.</p><p>Good passwords are long and unique. KeePassXC can generate one for you.</p> - Sh&ow a notification when credentials are requested - Credentials mean login data requested via browser extension + Password cannot be empty. - Only returns the best matches for a specific URL instead of all entries for the whole domain. + Passwords do not match. - &Return only best matching entries - - - - Re&quest to unlock the database if it is locked - - - - Only entries with the same scheme (http://, https://, ftp://, ...) are returned. - - - - &Match URL schemes - - - - Sort matching entries by &username - - - - Sort &matching entries by title - - - - R&emove all shared encryption keys from active database - - - - Re&move all stored permissions from entries in active database - - - - Password Generator - - - - Advanced - - - - Always allow &access to entries - - - - Always allow &updating entries - - - - Only the selected database has to be connected with a client. - - - - Searc&h in all opened databases for matching entries - - - - Automatically creating or updating string fields is not supported. - - - - &Return advanced string fields which start with "KPH: " - - - - HTTP Port: - - - - Default port: 19455 - - - - KeePassXC will listen to this port on 127.0.0.1 - - - - <b>Warning:</b> The following options can be dangerous! - - - - <p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p> - - - - Cannot bind to privileged ports - - - - Cannot bind to privileged ports below 1024! -Using default port 19455. + Generate master password @@ -3193,18 +3719,10 @@ Using default port 19455. Wordlist: - - Word Count: - - Word Separator: - - Generate - - Copy @@ -3217,10 +3735,6 @@ Using default port 19455. Close - - Apply - - Entropy: %1 bit @@ -3249,6 +3763,140 @@ Using default port 19455. Password quality + + ExtendedASCII + + + + Switch to advanced mode + + + + Advanced + + + + Upper Case Letters A to F + + + + A-Z + + + + Lower Case Letters A to F + + + + a-z + + + + 0-9 + + + + Braces + + + + {[( + + + + Punctuation + + + + .,:; + + + + Quotes + + + + " ' + + + + Math + + + + <*+!?= + + + + Dashes + + + + \_|-/ + + + + Logograms + + + + #$%&&@^`~ + + + + Switch to simple mode + + + + Simple + + + + Character set to exclude from generated password + + + + Do not include: + + + + Add non-hex letters to "do not include" list + + + + Hex + + + + Excluded characters: "0", "1", "l", "I", "O", "|", "﹒" + + + + Word Co&unt: + + + + Regenerate + + + + + QCoreApplication + + main + Remove an entry from the database. + + + + main + Path of the database. + + + + main + Path of the entry to remove. + + QObject @@ -3421,10 +4069,6 @@ Using default port 19455. Insert password to unlock %1: - - Failed to load key file %1 : %2 - - WARNING: You are using a legacy key file format which may become unsupported in the future. @@ -3503,11 +4147,6 @@ Available commands: error reading from device - - file empty ! - - - malformed string @@ -3544,10 +4183,6 @@ Available commands: Created - - Legacy Browser Integration - - Browser Integration @@ -3576,10 +4211,6 @@ Available commands: Word count for the diceware passphrase. - - count - - Wordlist for the diceware generator. [Default: EFF English] @@ -3590,27 +4221,340 @@ Available commands: - Length of the generated password. + Invalid value for password length %1. - Use lowercase characters in the generated password. + Could not create entry with path %1. - Use uppercase characters in the generated password. + Enter password for new entry: - Use numbers in the generated password. + Writing the database failed %1. - Use special characters in the generated password. + Successfully added entry %1. - Use extended ASCII in the generated password. + Copy the current TOTP to the clipboard. + + + + Invalid timeout value %1. + + + + Entry %1 not found. + + + + Entry with path %1 has no TOTP set up. + + + + Entry's current TOTP copied to the clipboard! + + + + Entry's password copied to the clipboard! + + + + Clearing the clipboard in %1 second(s)... + + + + + + + Clipboard cleared! + + + + Silence password prompt and other secondary outputs. + + + + count + CLI parameter + + + + Invalid value for password length: %1 + + + + Could not find entry with path %1. + + + + Not changing any field for entry %1. + + + + Enter new password for entry: + + + + Writing the database failed: %1 + + + + Successfully edited entry %1. + + + + Length %1 + + + + Entropy %1 + + + + Log10 %1 + + + + Multi-word extra bits %1 + + + + Type: Bruteforce + + + + Type: Dictionary + + + + Type: Dict+Leet + + + + Type: User Words + + + + Type: User+Leet + + + + Type: Repeated + + + + Type: Sequence + + + + Type: Spatial + + + + Type: Date + + + + Type: Bruteforce(Rep) + + + + Type: Dictionary(Rep) + + + + Type: Dict+Leet(Rep) + + + + Type: User Words(Rep) + + + + Type: User+Leet(Rep) + + + + Type: Repeated(Rep) + + + + Type: Sequence(Rep) + + + + Type: Spatial(Rep) + + + + Type: Date(Rep) + + + + Type: Unknown%1 + + + + Entropy %1 (%2) + + + + *** Password length (%1) != sum of length of parts (%2) *** + + + + Failed to load key file %1: %2 + + + + File %1 does not exist. + + + + Unable to open file %1. + + + + Error while reading the database: +%1 + + + + Error while parsing the database: +%1 + + + + Length of the generated password + + + + Use lowercase characters + + + + Use uppercase characters + + + + Use numbers. + + + + Use special characters + + + + Use extended ASCII + + + + Exclude character set + + + + chars + + + + Exclude similar looking characters + + + + Include characters from every selected group + + + + Recursively list the elements of the group. + + + + Cannot find group %1. + + + + Error reading merge file: +%1 + + + + Unable to save database to file : %1 + + + + Unable to save database to file: %1 + + + + Successfully recycled entry %1. + + + + Successfully deleted entry %1. + + + + Show the entry's current TOTP. + + + + ERROR: unknown attribute %1. + + + + No program defined for clipboard manipulation + + + + Unable to start program %1 + + + + file empty + + + + %1: (row, col) %2,%3 + + + + AES: 256-bit + + + + Twofish: 256-bit + + + + ChaCha20: 256-bit + + + + Argon2 (KDBX 4 – recommended) + + + + AES-KDF (KDBX 4) + + + + AES-KDF (KDBX 3.1) + + + + Invalid Settings + TOTP + + + + Invalid Key + TOTP @@ -3666,10 +4610,6 @@ Available commands: No agent running, cannot remove identity. - - Agent does not have this identity. - - Agent refused this identity. Possible reasons include: @@ -3688,11 +4628,62 @@ Available commands: - SearchWidget + SearchHelpWidget - Search... + Search Help + + Search terms are as follows: [modifiers][field:]["]term["] + + + + Every search term must match (ie, logical AND) + + + + Modifiers + + + + exclude term from results + + + + match term exactly + + + + use regex in term + + + + Fields + + + + Term Wildcards + + + + match anything + + + + match one + + + + logical OR + + + + Examples + + + + + SearchWidget Search @@ -3701,321 +4692,68 @@ Available commands: Clear - - Case Sensitive - - Limit search to selected group + + Search Help + + + + Search (%1)... + Search placeholder text, %1 is the keyboard shortcut + + + + Case sensitive + + - Service + TotpDialog - KeePassXC: New key association request + Timed Password - You have received an association request for the above key. -If you would like to allow it access to your KeePassXC database -give it a unique name to identify and accept it. + 000000 - KeePassXC: Overwrite existing key? - - - - A shared encryption-key with the name "%1" already exists. -Do you want to overwrite it? - - - - KeePassXC: Update Entry - - - - Do you want to update the information in %1 - %2? - - - - KeePassXC: Database locked! - - - - The active database is locked! -Please unlock the selected database or choose another one which is unlocked. - - - - KeePassXC: Removed keys from database + Copy - Successfully removed %n encryption-key(s) from KeePassX/Http Settings. + Expires in <b>%n</b> second(s) + + + TotpExportSettingsDialog - KeePassXC: No keys found + Copy - No shared encryption-keys found in KeePassHttp Settings. + NOTE: These TOTP settings are custom and may not work with other authenticators. + TOTP QR code dialog warning - KeePassXC: Settings not available! + There was an error creating the QR code. - The active database does not contain an entry of KeePassHttp Settings. - - - - Removing stored permissions... - - - - Abort - - - - KeePassXC: Removed permissions - - - - Successfully removed permissions from %n entries. - - - - - - - KeePassXC: No entry with permissions found! - - - - The active database does not contain an entry with permissions. + Closing in %1 seconds. - SettingsWidget - - Application Settings - - - - General - - - - Security - - - - Access error for config file %1 - - - - - SettingsWidgetGeneral - - Basic Settings - - - - Start only a single instance of KeePassXC - - - - Remember last databases - - - - Remember last key files and security dongles - - - - Load previous databases on startup - - - - Automatically save on exit - - - - Automatically save after every change - - - - Automatically reload the database when modified externally - - - - Minimize when copying to clipboard - - - - Minimize window at application startup - - - - Use group icon on entry creation - - - - Don't mark database as modified for non-data changes (e.g., expanding groups) - - - - Hide the Details view - - - - Show a system tray icon - - - - Hide window to system tray when minimized - - - - Hide window to system tray instead of app exit - - - - Dark system tray icon - - - - Language - - - - Auto-Type - - - - Use entry title to match windows for global Auto-Type - - - - Use entry URL to match windows for global Auto-Type - - - - Always ask before performing Auto-Type - - - - Global Auto-Type shortcut - - - - ms - Milliseconds - - - - Startup - - - - File Management - - - - Safely save database files (may be incompatible with Dropbox, etc) - - - - Backup database file before saving - - - - Entry Management - - - - General - - - - Auto-Type typing delay - - - - Auto-Type start delay - - - - - SettingsWidgetSecurity - - Timeouts - - - - Clear clipboard after - - - - sec - Seconds - - - - Lock databases after inactivity of - - - - Convenience - - - - Lock databases when session is locked or lid is closed - - - - Lock databases after minimizing the window - - - - Don't require password repeat when it is visible - - - - Show passwords in cleartext by default - - - - Hide passwords in the preview panel - - - - Hide entry notes by default - - - - Privacy - - - - Use Google as fallback for downloading website icons - - - - Re-lock previously locked database after performing Auto-Type - - - - - SetupTotpDialog + TotpSetupDialog Setup TOTP @@ -4037,7 +4775,7 @@ Please unlock the selected database or choose another one which is unlocked. - Note: Change these settings only if you know what you are doing. + Custom Settings @@ -4045,11 +4783,8 @@ Please unlock the selected database or choose another one which is unlocked. - 8 digits - - - - 6 digits + sec + Seconds @@ -4057,49 +4792,15 @@ Please unlock the selected database or choose another one which is unlocked. - sec - Seconds - - - - - TotpDialog - - Timed Password + 6 digits - 000000 + 7 digits - Copy - - - - Expires in - - - - seconds - - - - - UnlockDatabaseWidget - - Unlock database - - - - - UrlFetchProgressDialog - - Download Progress - - - - Downloading %1. + 8 digits @@ -4134,20 +4835,31 @@ Please unlock the selected database or choose another one which is unlocked. + + YubiKeyEditWidget + + Refresh + + + + YubiKey Challenge-Response + + + + <p>If you own a <a href="https://www.yubico.com/">YubiKey</a>, you can use it for additional security.</p><p>The YubiKey requires one of its slots to be programmed as <a href="https://www.yubico.com/products/services-software/personalization-tools/challenge-response/">HMAC-SHA1 Challenge-Response</a>.</p> + + + + No YubiKey detected, please ensure it's plugged in. + + + + No YubiKey inserted. + + + main - - Remove an entry from the database. - - - - Path of the database. - - - - Path of the entry to remove. - - KeePassXC - cross-platform password manager diff --git a/share/translations/keepassx_en_GB.ts b/share/translations/keepassx_en_GB.ts index 735921bbf..80db1ef30 100644 --- a/share/translations/keepassx_en_GB.ts +++ b/share/translations/keepassx_en_GB.ts @@ -3,8 +3,7 @@ AboutDialog About KeePassXC - About KeePassXC - + About KeePassXC About @@ -334,7 +333,7 @@ Please select whether you want to allow access. Update &native messaging manifest files at startup - + Update &native messaging manifest files at startup Support a proxy application between KeePassXC and browser extension. @@ -449,7 +448,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption key(s) from KeePassXC settings. - + Successfully removed %n encryption key from KeePassXC settings.Successfully removed %n encryption keys from KeePassXC settings. Removing stored permissions… @@ -465,7 +464,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entry(s). - + Successfully removed permissions from %n entry.Successfully removed permissions from %n entries. KeePassXC: No entry with permissions found! @@ -496,7 +495,7 @@ Please unlock the selected database or choose another one which is unlocked. Browse - Browser + Browse Create @@ -586,7 +585,7 @@ Please consider generating a new key file. CsvImportWidget Import CSV fields - + Import CSV fields filename @@ -716,7 +715,7 @@ Please consider generating a new key file. Browse - Browser + Browse Refresh @@ -1109,7 +1108,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - + Do you really want to move %n entry to the recycle bin?Do you really want to move %n entries to the recycle bin? Execute command? @@ -1357,11 +1356,11 @@ Do you want to merge your changes? %n week(s) - + %n week%n weeks %n month(s) - + %n month%n months 1 year @@ -1805,7 +1804,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + Are you sure you want to remove %n attachment?Are you sure you want to remove %n attachments? Confirm Remove @@ -1865,7 +1864,7 @@ This may cause the affected plugins to malfunction. EntryHistoryModel Last modified - + Last modified Title @@ -1956,7 +1955,7 @@ This may cause the affected plugins to malfunction. Fit to contents - + Fit to contents Reset to defaults @@ -2121,7 +2120,7 @@ This may cause the affected plugins to malfunction. Unsupported key derivation function (KDF) or invalid parameters - + Unsupported key derivation function (KDF) or invalid parameters Legacy header fields found in KDBX4 file. @@ -2569,33 +2568,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - - - - Twofish: 256-bit - - - - ChaCha20: 256-bit - - - - AES-KDF (KDBX 4) - - - - AES-KDF (KDBX 3.1) - - - - Argon2 (KDBX 4 – recommended) - - - Main @@ -2707,7 +2679,7 @@ This is a one-way migration. You won't be able to open the imported databas &Delete group - + &Delete group Sa&ve database as... @@ -2799,7 +2771,7 @@ This is a one-way migration. You won't be able to open the imported databas Import KeePass 1 database... - + Import KeePass 1 database... Import CSV file... @@ -3157,7 +3129,7 @@ Using default port 19455. Pick characters from every group - + Pick characters from every group &Length: @@ -3336,7 +3308,7 @@ Using default port 19455. Length for the generated password. - + Length for the generated password. length @@ -3494,6 +3466,30 @@ Available commands: missing closing quote + + AES: 256-bit + + + + Twofish: 256-bit + + + + ChaCha20: 256-bit + + + + Argon2 (KDBX 4 – recommended) + + + + AES-KDF (KDBX 4) + + + + AES-KDF (KDBX 3.1) + + Group Group @@ -3577,7 +3573,7 @@ Available commands: Use uppercase characters in the generated password. - + Use uppercase characters in the generated password. Use numbers in the generated password. @@ -3727,7 +3723,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entries. - + Successfully removed permissions from %n entry.Successfully removed permissions from %n entries. KeePassXC: No entry with permissions found! @@ -3813,7 +3809,7 @@ Please unlock the selected database or choose another one which is unlocked. Show a system tray icon - + Show a system tray icon Hide window to system tray when minimized @@ -4047,7 +4043,7 @@ Please unlock the selected database or choose another one which is unlocked. Recent databases - + Recent databases Welcome to KeePassXC %1 diff --git a/share/translations/keepassx_en_US.ts b/share/translations/keepassx_en_US.ts index a7a9627e3..f964e8a99 100644 --- a/share/translations/keepassx_en_US.ts +++ b/share/translations/keepassx_en_US.ts @@ -768,14 +768,6 @@ Please consider generating a new key file. Select key file Select key file - - authenticate to access the database - authenticate to access the database - - - authenticate a privileged operation - authenticate a privileged operation - DatabaseRepairWidget @@ -1131,7 +1123,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - Do you really want to move %n entry(s) to the recycle bin?Do you really want to move %n entry(s) to the recycle bin? + Do you really want to move %n entry to the recycle bin?Do you really want to move %n entries to the recycle bin? Execute command? @@ -1379,11 +1371,11 @@ Do you want to merge your changes? %n week(s) - %n week(s)%n week(s) + %n week%n weeks %n month(s) - %n month(s)%n month(s) + %n month%n months 1 year @@ -1751,7 +1743,7 @@ Do you want to merge your changes? Uuid: - Uuid: + UUID: Plugin Data @@ -1827,7 +1819,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - Are you sure you want to remove %n attachments?Are you sure you want to remove %n attachments? + Are you sure you want to remove %n attachment?Are you sure you want to remove %n attachments? Confirm Remove @@ -2591,33 +2583,6 @@ This is a one-way migration. You won't be able to open the imported databas Invalid entry field type - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – recommended) - - Main @@ -3526,6 +3491,30 @@ Available commands: missing closing quote missing closing quote + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – recommended) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Group @@ -3730,7 +3719,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Successfully removed %n encryption key from KeePassHTTP Settings.Successfully removed %n encryption keys from KeePassHTTP Settings. + Successfully removed %n encryption key from KeePassXC/HTTP Settings.Successfully removed %n encryption keys from KeePassXC/HTTP Settings. KeePassXC: No keys found diff --git a/share/translations/keepassx_es.ts b/share/translations/keepassx_es.ts index 092eada9f..6515762a5 100644 --- a/share/translations/keepassx_es.ts +++ b/share/translations/keepassx_es.ts @@ -27,7 +27,7 @@ Debug Info - Información de Depuración + Información de depuración Include the following information whenever you report a bug: @@ -73,12 +73,12 @@ Núcleo: %3 %4 Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. - Un agradecimiento especial del equipo de KeePassXC a debfx por crear el KeePassX original. + El equipo de KeePassXC quiere agradecer de manera especial el trabajo de debfx por la creación de KeePassX. Build Type: %1 - Tipo de compilación: %1 + Tipo de Build: %1 @@ -391,9 +391,9 @@ If you would like to allow it access to your KeePassXC database, give it a unique name to identify and accept it. ¿Quiere asociar la base de datos al navegador? -Si quiere autirizar el acceso a la base de datos de KeePassXC -de un nombre único para identificar la autorización -(se guarda como una entrada más) +Si quiere autorizar el acceso a la base de datos de KeePassXC, +proporcione un nombre único para identificar la autorización +y acepte. Save and allow access @@ -449,7 +449,7 @@ Por favor desbloquee la base de datos seleccionada o elija otra que esté desblo Successfully removed %n encryption key(s) from KeePassXC settings. - Quitado con éxito %n llaves de cifrado de configuración KeePassXC.Quitado con éxito %n llaves de cifrado de configuración KeePassXC. + Quitado con éxito %n llave(s) de cifrado de la configuración de KeePassXC.Quitado con éxito %n llave(s) de cifrado de la configuración de KeePassXC. Removing stored permissions… @@ -465,7 +465,7 @@ Por favor desbloquee la base de datos seleccionada o elija otra que esté desblo Successfully removed permissions from %n entry(s). - Con éxito quitar permisos de %n ello.Removidos con éxito permisos de %n entrada(s). + Los permisos fueron eliminados exitosamente de %n entrada(s).Los permisos fueron eliminados exitosamente de %n entrada(s). KeePassXC: No entry with permissions found! @@ -496,7 +496,7 @@ Por favor desbloquee la base de datos seleccionada o elija otra que esté desblo Browse - Abrir archivo + Navegar Create @@ -626,7 +626,7 @@ Considere generar un nuevo archivo llave. Number of headers line to discard - Cantidad de líneas a descartar del encabezado + Cantidad de líneas a descartar de la cabecera Consider '\' an escape character @@ -698,11 +698,11 @@ Considere generar un nuevo archivo llave. %n row(s), - filas de %n, %n fila(s), + %n fila(s), %n fila(s), %n column(s) - %n columnas%n columna(s) + %n columna(s)%n columna(s) @@ -733,7 +733,7 @@ Considere generar un nuevo archivo llave. Unable to open the database. - Incapaz de abrir la base de datos. + No se pudo abrir la base de datos. Can't open key file @@ -774,7 +774,7 @@ Considere generar un nuevo archivo llave. DatabaseRepairWidget Repair database - Reparar base de datos + Reparar la base de datos Error @@ -867,7 +867,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!< thread(s) Threads for parallel execution (KDF settings) - o de los hiloshilo(s) + hilo(s)hilo(s) @@ -961,7 +961,7 @@ Si conserva este número, ¡su base de datos puede ser muy fácil de descifrar!< KeePass 2 Database - Base de datos KeePass 2 + Base de datos de KeePass 2 All files @@ -1029,7 +1029,7 @@ Save changes? Writing the database failed. - La escritura de la base de datos falló. + Fallo al escribir la base de datos. Passwords @@ -1075,13 +1075,13 @@ De lo contrario se perderán los cambios. Disable safe saves? - ¿Inhabilitar guardado seguro? + ¿Deshabilitar guardados seguros? KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. Disable safe saves and try again? - KeePassXC no ha podido guardar varias veces la base de datos. Es probable que esto se deba a que los servicios de sincronización de archivos mantienen un bloqueo en el archivo guardado. -¿Deshabilite las copias seguras y vuelva a intentarlo? + KeePassXC no ha podido guardar la base de datos varias veces. Esto es probablemente causado por los servicios de sincronización de archivos manteniendo un bloqueo en el archivo. +¿Desactivar la guarda segura y volver a intentarlo? @@ -1124,7 +1124,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - ¿Realmente quiere mover la entrada "%1" a la papelera de reciclaje?¿Realmente quiere mover las entradas "%1" a la papelera de reciclaje? + ¿Estás seguro de mover %n entrada(s) a la papelera de reciclaje?¿Estás seguro de mover %n entrada(s) a la papelera de reciclaje? Execute command? @@ -1148,7 +1148,7 @@ Disable safe saves and try again? Unable to calculate master key - No se puede calcular la llave maestra + No se puede calcular la clave maestra No current database. @@ -1371,11 +1371,11 @@ Do you want to merge your changes? %n week(s) - %n semana%n semana(s) + %n semana(s)%n semana(s) %n month(s) - %n mes%n mes(es) + %n mes(es)%n mes(es) 1 year @@ -1383,15 +1383,15 @@ Do you want to merge your changes? Apply generated password? - ¿Aplicar la contraseña generada? + ¿Aplicar contraseña generada? Do you want to apply the generated password to this entry? - ¿Desea aplicar la contraseña generada a esta entrada? + ¿Desea aplicar la contraseña generada en esta entrada? Entry updated successfully. - Entrada actualizada con éxito. + Entrada actualizada. @@ -1426,11 +1426,11 @@ Do you want to merge your changes? Foreground Color: - Color de Primer Plano: + Color de primer plano: Background Color: - Color de Segundo Plano: + Color de fondo: @@ -1465,7 +1465,7 @@ Do you want to merge your changes? Use a specific sequence for this association: - Usa una secuencia específica para esta asociación: + Utilizar una secuencia específica para esta asociación: @@ -1534,7 +1534,7 @@ Do you want to merge your changes? Remove key from agent after - Eliminar después la clave del agente + Retirar la llave del agente después seconds @@ -1755,7 +1755,7 @@ Do you want to merge your changes? Delete plugin data? - Eliminar los datos del complemento? + Eliminar los datos del plugin? Do you really want to delete the selected plugin data? @@ -1819,7 +1819,7 @@ Esto puede causar un mal funcionamiento de los complementos afectados. Are you sure you want to remove %n attachment(s)? - ¿Está seguro que desea eliminar %n adjunto(s)?¿Está seguro que desea eliminar %n adjunto(s)? + ¿Confirma que desea quitar %n dato(s) adjunto(s)?¿Confirma que desea quitar %n dato(s) adjunto(s)? Confirm Remove @@ -1858,7 +1858,8 @@ Esto puede causar un mal funcionamiento de los complementos afectados. Unable to open attachments: %1 - No se pueden abrir los datos adjuntos:%1 + No se pueden abrir los datos adjuntos: +%1 Unable to open files: @@ -1886,7 +1887,7 @@ Esto puede causar un mal funcionamiento de los complementos afectados. Username - Nombre de usuario + Nombre de usuario: URL @@ -2185,32 +2186,32 @@ Esto puede causar un mal funcionamiento de los complementos afectados. Invalid variant map Int32 entry value length Translation: variant map = data structure for storing meta data - Largo inválido en valor de entrada Int32 de mapeo de variante + Largo inválido en valor de entrada Int32 de mapa variante Invalid variant map UInt32 entry value length Translation: variant map = data structure for storing meta data - Largo inválido en valor de entrada UInt32 de mapeo de variante + Largo inválido en valor de entrada UInt32 de mapa variante Invalid variant map Int64 entry value length Translation: variant map = data structure for storing meta data - Largo inválido en valor de entrada Int64 de mapeo de variante + Largo inválido en valor de entrada Int64 de mapa variante Invalid variant map UInt64 entry value length Translation: variant map = data structure for storing meta data - Largo inválido en valor de entrada UInt64 de mapeo de variante + Largo inválido en valor de entrada UInt64 de mapa variante Invalid variant map entry type Translation: variant map = data structure for storing meta data - Tipo de entrada inválida de mapeo devariante + Entrada inválida de mapa variante Invalid variant map field type size Translation: variant map = data structure for storing meta data - Mapei de variante inválido en campo de tipo tamaño + Tamaño inválido de mapa variante @@ -2246,7 +2247,7 @@ Esto puede causar un mal funcionamiento de los complementos afectados. Invalid compression flags length - Largo de banderas de compresión inválido + Tamaño de flags de compresión inválido Unsupported compression algorithm @@ -2307,7 +2308,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada Missing icon uuid or data - Datos o uuid del ícono faltantes + Falta icono uuid o datos Missing custom data key or value @@ -2351,11 +2352,11 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada Invalid entry icon number - Número de ícono de entrada inválido + Número de icono de entrada no válida History element in history entry - Elemento de la historia en la entrada de la historia + Elemento del historial en la entrada del historial No entry uuid found @@ -2375,7 +2376,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada Entry string key or value missing - Falta clave de entrada o valor + Falta clave de entrada de texto o valor Duplicate attachment found @@ -2427,7 +2428,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada Unable to open the database. - Incapaz de abrir la base de datos. + No se pudo abrir la base de datos. @@ -2579,34 +2580,7 @@ Esta migración es en único sentido. No podrá abrir la base de datos importada Invalid entry field type - Tipo del campo de entrada inválido - - - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – recomendado) + Tipo de la entrada para el campo inválido @@ -2903,7 +2877,7 @@ Esta versión no es para uso de producción. OpenSSHKey Invalid key file, expecting an OpenSSH key - Archivo de clave no válida, esperando una clave de OpenSSH + Archivo llave no válido, esperando una llave de OpenSSH PEM boundary mismatch @@ -2947,11 +2921,11 @@ Esta versión no es para uso de producción. Key derivation failed, key file corrupted? - Derivación de la llave falló, ¿archivo llave dañado? + La derivación de la clave falló, ¿archivo de claves dañado? Decryption failed, wrong passphrase? - ¿Error de descifrado, contraseña incorrecta? + ¿Error de descifrado, frase de contraseña incorrecta? Unexpected EOF while reading public key @@ -3161,7 +3135,7 @@ Usando el puerto por defecto 19455 Special Characters - Caracteres especiales: + Caracteres especiales Extended ASCII @@ -3304,7 +3278,7 @@ Usando el puerto por defecto 19455 No logins found - No se encuentraron logins + No se encontraron logins Unknown error @@ -3401,7 +3375,7 @@ Usando el puerto por defecto 19455 Perform advanced analysis on the password. - Realizar análisis avanzado en la contraseña. + Realizar análisis avanzado sobre la contraseña. Extract and print the content of a database. @@ -3424,9 +3398,10 @@ Usando el puerto por defecto 19455 unsupported in the future. Please consider generating a new key file. - ADVERTENCIA: Usted está utilizando un formato de archivo llave heredado que puede ser no compatible en el futuro. + ADVERTENCIA: Está usando un fichero de claves con un formato antiguo que puede ser +incompatible en el futuro. -Por favor considere generar un nuevo archivo de llave. +Por favor, considere generar un nuevo fichero. @@ -3516,6 +3491,30 @@ Comandos disponibles: missing closing quote comilla de cierre faltante + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – recomendado) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Grupo @@ -3578,7 +3577,7 @@ Comandos disponibles: count - Número + número Wordlist for the diceware generator. @@ -3596,23 +3595,23 @@ Comandos disponibles: Use lowercase characters in the generated password. - Utilizar caracteres en minúsculas en la contraseña generada. + Usar caracteres en minúsculas en la contraseña generada. Use uppercase characters in the generated password. - Utilizar caracteres en mayúsculas en la contraseña generada. + Usar caracteres en mayúsculas en la contraseña generada. Use numbers in the generated password. - Utilizar números en la contraseña generada. + Usar números en la contraseña generada. Use special characters in the generated password. - Utilizar caracteres especiales en la contraseña generada. + Usar caracteres especiales en la contraseña generada. Use extended ASCII in the generated password. - Utilizar ASCII extendido en la contraseña generada. + Usar ASCII extendido en la contraseña generada. @@ -3720,7 +3719,7 @@ Por favor desbloquee la base de datos seleccionada o elija otra que esté desblo Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Quitado con éxito cifrado %n-llaves de KeePassX y Http.Quitadas con éxito %n- llaves de encriptación de las opciones de KeePassX/Http. + Quitado con éxito %n llave(s) de cifrado de la configuración de KeePassXC/Http.Quitado con éxito %n llave(s) de cifrado de la configuración de KeePassXC/Http. KeePassXC: No keys found @@ -3752,7 +3751,7 @@ Por favor desbloquee la base de datos seleccionada o elija otra que esté desblo Successfully removed permissions from %n entries. - Con éxito quitar permisos de entradas %n.Removidos permisos de %n entradas exitosamente. + Los permisos fueron eliminados exitosamente de %n entradas.Los permisos fueron eliminados exitosamente de %n entradas. KeePassXC: No entry with permissions found! @@ -3862,7 +3861,7 @@ Por favor desbloquee la base de datos seleccionada o elija otra que esté desblo Use entry title to match windows for global Auto-Type - Use título de entrada para acertar ventanas en Auto-Tipeado global. + Use título de entrada para acertar ventanas en Auto-Tipeado Use entry URL to match windows for global Auto-Type @@ -3967,7 +3966,7 @@ Por favor desbloquee la base de datos seleccionada o elija otra que esté desblo Re-lock previously locked database after performing Auto-Type - Volver a bloquear la base de datos previamente bloqueada después de hacer Auto-Escritura + Volver a bloquear la base de datos tras realizar una Auto-Escritura diff --git a/share/translations/keepassx_eu.ts b/share/translations/keepassx_eu.ts index 2ac5f25b2..922b6efd8 100644 --- a/share/translations/keepassx_eu.ts +++ b/share/translations/keepassx_eu.ts @@ -45,7 +45,7 @@ Revision: %1 - Berrikuspena: + Berrikuspena: %1 Distribution: %1 @@ -680,15 +680,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - + byte %n%n byte %n row(s), - + lerro %n%n lerro %n column(s) - + zutabe %n%n zutabe @@ -1097,7 +1097,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - + Ziur zaude sarrera %n zakarrontzira mugitu nahi duzula?Ziur zaude %n sarrera zakarrontzira mugitu nahi dituzula? Execute command? @@ -1344,11 +1344,11 @@ Do you want to merge your changes? %n week(s) - + aste %n%n aste %n month(s) - + hilabete %n%n hilabete 1 year @@ -1434,7 +1434,7 @@ Do you want to merge your changes? Window title: - Leihoaren titulua: + Leihoaren izenburua: Use a specific sequence for this association: @@ -1791,7 +1791,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + Ziur zaude eranskin %n kendu nahi duzula?Ziur zaude %n eranskin kendu nahi dituzula? Confirm Remove @@ -2547,33 +2547,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – gomendatua) - - Main @@ -3472,6 +3445,30 @@ Available commands: missing closing quote + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – gomendatua) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Taldea diff --git a/share/translations/keepassx_fi.ts b/share/translations/keepassx_fi.ts index 113927dcd..e61a383a8 100644 --- a/share/translations/keepassx_fi.ts +++ b/share/translations/keepassx_fi.ts @@ -61,7 +61,7 @@ CPU architecture: %2 Kernel: %3 %4 Käyttöjärjestelmä: %1 Suoritinarkkitehtuuri: %2 -Kernel: %3 %4 +Ydin: %3 %4 Enabled extensions: @@ -102,8 +102,8 @@ Kernel: %3 %4 %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 pyytää pääsyä seuraavien tietueiden salasanoihin. -Ole hyvä ja valitse sallitaanko pääsy. + %1 pyytää pääsyä seuraavien kohteiden salasanoihin. +Valitse sallitaanko pääsy. @@ -117,7 +117,7 @@ Ole hyvä ja valitse sallitaanko pääsy. AutoType Couldn't find an entry that matches the window title: - Ikkunan nimeä vastaavaa merkintää ei löytynyt: + Ikkunan nimeä vastaavaa tietuetta ei löytynyt: Auto-Type - KeePassXC @@ -182,11 +182,11 @@ Ole hyvä ja valitse sallitaanko pääsy. AutoTypeSelectDialog Auto-Type - KeePassXC - Automaattitäydennys - KeePassXC + Automaattisyöttö - KeePassXC Select entry to Auto-Type: - Valitse merkintä automaattitäydennystä varten: + Valitse tietue automaattisyöttöä varten: @@ -210,7 +210,7 @@ Ole hyvä ja valitse sallitaanko pääsy. %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 pyytää pääsyä seuraavien tietueiden salasanoihin. + %1 pyytää pääsyä seuraavien kohteiden salasanoihin. Ole hyvä ja valitse sallitaanko pääsy. @@ -374,7 +374,7 @@ Ole hyvä ja valitse sallitaanko pääsy. We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment. - Valitettavasti KeePassXC-Browser ei ole vielä tuettu Snap-julkaisuissa. + Valitettavasti KeePassXC-Browser ei tällä hetkellä tue Snap-julkaisuja. @@ -489,7 +489,7 @@ Avaa valittu tietokanta tai valitse toinen avattu tietokanta. &Key file - Avaintiedosto + &Avaintiedosto Browse @@ -521,7 +521,7 @@ Avaa valittu tietokanta tai valitse toinen avattu tietokanta. Unable to create Key File : - Avaintiedoston luonti ei onnistunut: + Avaintiedoston luonti ei onnistunut: Select a key file @@ -568,7 +568,7 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. CloneDialog Clone Options - Kopiointiasetukset + Kloonausasetukset Append ' - Clone' to title @@ -643,11 +643,11 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. Empty fieldname - Tyhjä kenttänimi + Tyhjä kenttänimi column - sarake + sarake Imported from CSV file @@ -655,7 +655,7 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. Original data: - Alkuperäiset tiedot: + Alkuperäiset tiedot: Error(s) detected in CSV file ! @@ -663,7 +663,7 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. more messages skipped] - viestiä lisää ohitettu] + viestiä lisää ohitettu] Error @@ -691,15 +691,15 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. CsvParserModel %n byte(s), - %n tavua,%n tavua, + %n tavu.%n tavua, %n row(s), - %n riviä,%n riviä, + %n rivi.%n riviä, %n column(s) - %n saraketta%n saraketta + %n sarake.%n saraketta @@ -714,7 +714,7 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. Password: - Salasana + Salasana: Browse @@ -730,11 +730,11 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. Unable to open the database. - Tietokannan avaaminen ei onnistunut. + Tietokannan avaaminen epäonnistui. Can't open key file - Avaintiedostoa ei voitu avata + Avaintiedoston avaaminen epäonnistui Legacy key file format @@ -791,7 +791,7 @@ Ole hyvä ja harkitse uuden avaintiedoston luomista. Success - Onnistui! + Onnistui The database has been successfully repaired @@ -859,12 +859,12 @@ Jos pidät tämän arvon, tietokanta voi olla liian helppo murtaa! MiB Abbreviation for Mebibytes (KDF settings) - Mt Mt + MiB Mt thread(s) Threads for parallel execution (KDF settings) - säie(ttä) säie(ttä) + säie säie(ttä) @@ -926,7 +926,7 @@ Jos pidät tämän arvon, tietokanta voi olla liian helppo murtaa! Max. history items: - Maks. historiamerkintöjen lukumäärä: + Maks. historia-kohteiden lukumäärä: Max. history size: @@ -974,7 +974,7 @@ Jos pidät tämän arvon, tietokanta voi olla liian helppo murtaa! Unable to open the database. - Tietokannan avaaminen ei onnistunut. + Tietokannan avaaminen epäonnistui. File opened in read only mode. @@ -1021,7 +1021,7 @@ Hylkää muutokset ja sulje? "%1" was modified. Save changes? - Kohdetta "%1" muokattiin. + Tietuetta "%1" muokattiin. Tallennetaanko muutokset? @@ -1050,7 +1050,7 @@ Tallennetaanko muutokset? locked - Lukittu + lukittu Lock database @@ -1121,7 +1121,7 @@ Ota turvallinen tallennus pois käytöstä ja yritä uudelleen? Do you really want to move %n entry(s) to the recycle bin? - Haluatko varmasti siirtää %n kappaletta alkioita roskakoriin?Haluatko varmasti siirtää %n tietuetta roskakoriin? + Haluatko varmasti siirtää %n tietueen roskakoriin?Haluatko varmasti siirtää %n tietuetta roskakoriin? Execute command? @@ -1198,7 +1198,7 @@ Haluatko yhdistää muutoksesi? DetailsWidget Generate TOTP Token - Luo aikapohjaisen salasanan (TOTP) tunniste + Luo ajastetun kertakäyttöisen salasanan (TOTP) tunniste Close @@ -1226,11 +1226,11 @@ Haluatko yhdistää muutoksesi? Autotype - Automaattitäydennys + Automaattisyöttö Searching - Etsitään + Hakeminen Attributes @@ -1293,7 +1293,7 @@ Haluatko yhdistää muutoksesi? Auto-Type - Automaattitäydennys + Automaattisyöttö Properties @@ -1369,11 +1369,11 @@ Haluatko yhdistää muutoksesi? %n week(s) - %n viikkoa%n viikkoa + %n viikko%n viikkoa %n month(s) - %n kuukautta%n kuukautta + %n kuukausi%n kuukautta 1 year @@ -1435,19 +1435,19 @@ Haluatko yhdistää muutoksesi? EditEntryWidgetAutoType Enable Auto-Type for this entry - Salli automaattitäydennys tälle merkinnälle + Salli automaattisyöttö tälle tietueelle Inherit default Auto-Type sequence from the &group - Peri automaattitäydennyksen oletussekvenssi &ryhmältä + Peri automaattisyötön oletussekvenssi &ryhmältä &Use custom Auto-Type sequence: - &Käytä mukautettua automaattitäydennyksen sekvenssiä: + &Käytä mukautettua automaattisyötön sekvenssiä: Window Associations - Ikkunoiden yhteysasetukset + Ikkunoiden liitokset + @@ -1509,11 +1509,11 @@ Haluatko yhdistää muutoksesi? Presets - Esiasetus + Esiasetukset Toggle the checkbox to reveal the notes section. - Ruksi valintaruutu näyttääksesi huomautusosio. + Ruksi valintaruutu näyttääksesi muistiinpano-osio. Username: @@ -1532,11 +1532,11 @@ Haluatko yhdistää muutoksesi? Remove key from agent after - Poista avain agentilta viiveellä + Poista avain agentista kun on kulunut seconds - s + sekuntia Fingerprint @@ -1544,7 +1544,7 @@ Haluatko yhdistää muutoksesi? Remove key from agent when database is closed/locked - Poista avain agentista kun tietokanta on suljettu/lukittu + Poista avain agentista kun tietokanta suljetaan/lukitaan Public key @@ -1552,7 +1552,7 @@ Haluatko yhdistää muutoksesi? Add key to agent when database is opened/unlocked - Lisää avain agenttiin kun tietokanta on avattu/lukitus avattu + Lisää avain agenttiin kun tietokanta avataan/lukitaan Comment @@ -1655,15 +1655,15 @@ Haluatko yhdistää muutoksesi? Auto-Type - Automaattitäydennys + Automaattisyöttö &Use default Auto-Type sequence of parent group - &Peri auromaattitäydennyksen sekvenssi isäntäryhmältä + &Peri automaattisyötön sekvenssi emoryhmältä Set default Auto-Type se&quence - Aseta automaattitäydennyksen &oletussekvenssi + Aseta automaattisyötön &oletussekvenssi @@ -1817,7 +1817,7 @@ Tämä voi vikaannuttaa tietoa käyttävän liitännäisen. Are you sure you want to remove %n attachment(s)? - Haluatko varmasti poistaa %n liitettä?Haluatko varmasti poistaa %n liitettä? + Haluatko varmasti poistaa &n liitettä?Haluatko varmasti poistaa %n liitettä? Confirm Remove @@ -1897,7 +1897,7 @@ Tämä voi vikaannuttaa tietoa käyttävän liitännäisen. Ref: Reference abbreviation - Viittaus: + Viittaus: Group @@ -2426,7 +2426,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant Unable to open the database. - Tietokannan avaaminen ei onnistunut. + Tietokannan avaaminen epäonnistui. @@ -2581,33 +2581,6 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant Virheellinen tietueen kentän tyyppi - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – suositeltu) - - Main @@ -2639,7 +2612,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant &Recent databases - &Viimeisimmät tietokannat + Viimeisimmät tietokannat Import @@ -2663,7 +2636,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant &Groups - &Ryhmät + Ryhmät &Tools @@ -2671,7 +2644,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant &Quit - L&opeta + &Lopeta &About @@ -2695,11 +2668,11 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant Merge from KeePassX database - Yhdistä KeePassX-tietokannasta + Yhdistä KeePassXC-tietokannasta &Add new entry - &Lisää tietue + Lisää &tietue &View/Edit entry @@ -2731,7 +2704,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant &Database settings - Tietok&annan asetukset + Tietokannan &asetukset Database settings @@ -2747,7 +2720,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant Copy &username - Kopioi &käyttäjätunnus + Kopioi käyttäjä&tunnus Copy username to clipboard @@ -2771,11 +2744,11 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant &Perform Auto-Type - S&uorita automaattitäydennys + Suorita automaattisyöttö &Open URL - &Avaa URL + Avaa &URL &Lock databases @@ -2803,7 +2776,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant Copy notes to clipboard - Kopioi huomautukset leikepöydälle + Kopioi muistiinpanot leikepöydälle &Export to CSV file... @@ -2867,7 +2840,7 @@ Tämä muunnos toimii yhdensuuntaisesti. Et välttämättä saa enää tietokant KeePass 2 Database - Keepass 2 -tietokanta + KeePass 2 -tietokanta All files @@ -2922,7 +2895,7 @@ Tätä versiota ei ole tarkoitettu päivittäiseen käyttöön. Found zero keys - Yhtään avainta ei löytynyt + Löytyi nolla avainta Failed to read public key. @@ -2934,23 +2907,23 @@ Tätä versiota ei ole tarkoitettu päivittäiseen käyttöön. No private key payload to decrypt - Salauksen purku epäonnistui: salainen avain ei sisällä dataa + Salauksen purku epäonnistui: yksityisen avaimen sisältö on tyhjä Trying to run KDF without cipher - Yritetään tehdä avainderivaatiofunktiota ilman salausta + Yritetään tehdä avainmuunnosfunktiota ilman salausta Passphrase is required to decrypt this key - Avaimen purkuun vaaditaan tunnuslause + Avaimen purkuun vaaditaan salalause Key derivation failed, key file corrupted? - Avaimen derivointi epäonnistui. Onko avaintiedosto korruptoitunut? + Avaimen muuntaminen epäonnistui. Onko avaintiedosto korruptoitunut? Decryption failed, wrong passphrase? - Salauksen purku epäonnistui, väärä tunnuslause? + Salauksen purku epäonnistui, väärä salalause? Unexpected EOF while reading public key @@ -3038,15 +3011,15 @@ Tätä versiota ei ole tarkoitettu päivittäiseen käyttöön. &Match URL schemes - Sovita verkko-osoitteen kaavaan + &Sovita verkko-osoitteen kaavaan Sort matching entries by &username - Järjestä &vastaavat tietueet käyttäjätunnuksen mukaan + Järjestä sopivat tietueet &käyttäjätunnuksen mukaan Sort &matching entries by title - Järjestä &vastaavat merkinnät otsikon mukaan + Järjestä sopivat tietueet &nimen mukaan R&emove all shared encryption keys from active database @@ -3062,7 +3035,7 @@ Tätä versiota ei ole tarkoitettu päivittäiseen käyttöön. Advanced - Lisää.. + Lisäasetukset Always allow &access to entries @@ -3180,7 +3153,7 @@ Käytetään oletusporttia 19455. Passphrase - Tunnuslause + Salalause Wordlist: @@ -3196,7 +3169,7 @@ Käytetään oletusporttia 19455. Generate - Generoi + Luo Copy @@ -3311,7 +3284,7 @@ Käytetään oletusporttia 19455. Add a new entry to a database. - Lisää uusi tietue tietokantaan + Lisää uusi tietue tietokantaan. Path of the database. @@ -3327,7 +3300,7 @@ Käytetään oletusporttia 19455. Username for the entry. - Tietueen käyttäjänimi + Tietueen käyttäjänimi. username @@ -3380,11 +3353,11 @@ Käytetään oletusporttia 19455. Title for the entry. - Tietueen otsikko. + Tietueen nimi title - otsikko + nimi Path of the entry to edit. @@ -3505,7 +3478,7 @@ Käytettävissä olevat komennot: file empty ! - tiedosto tyhjä ! + tiedosto on tyhjä ! @@ -3516,6 +3489,30 @@ Käytettävissä olevat komennot: missing closing quote lainausmerkki puuttuu lopusta + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – suositeltu) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Ryhmä @@ -3619,7 +3616,7 @@ Käytettävissä olevat komennot: QtIOCompressor Internal zlib error when compressing: - Sisäinen zlib-virhe pakatessa: + Sisäinen zlib virhe pakatessa: Error writing to underlying device: @@ -3646,7 +3643,7 @@ Käytettävissä olevat komennot: Internal zlib error: - Sisäinen zlib-virhe: + Sisäinen zlib-virhe: @@ -3720,7 +3717,7 @@ Avaa valittu tietokanta tai valitse toinen avattu tietokanta. Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Poistettiin %n salausavain KeePassX/Http-asetuksista.Poistettiin %n salausavainta KeePassX/Http-asetuksista. + Poistettiin %n salausavain(ta) KeePassX/Http-asetuksista.Poistettiin %n salausavain(ta) KeePassX/Http-asetuksista. KeePassXC: No keys found @@ -3752,11 +3749,11 @@ Avaa valittu tietokanta tai valitse toinen avattu tietokanta. Successfully removed permissions from %n entries. - Poistettiin käyttöoikeus %n tietueelta.Poistettiin käyttöoikeus %n tietueelta. + Poistettiin käyttöoikeudet %1:n tietueen tiedoista.Poistettiin käyttöoikeudet %1:n tietueen tiedoista. KeePassXC: No entry with permissions found! - KeePassXC: Merkintää käyttöoikeuksilla ei löytynyt! + KeePassXC: Tietuetta käyttöoikeuksilla ei löytynyt! The active database does not contain an entry with permissions. @@ -3858,27 +3855,27 @@ Avaa valittu tietokanta tai valitse toinen avattu tietokanta. Auto-Type - Automaattitäydennys + Automaattisyöttö Use entry title to match windows for global Auto-Type - Tietue on sopiva, jos sen nimi sisältyy kohdeikkunan otsikkoon yleisessä automaattitäydennyksessä + Tietue on sopiva, jos sen nimi sisältyy kohdeikkunan otsikkoon yleisessä automaattisyötössä Use entry URL to match windows for global Auto-Type - Tietue on sopiva, jos sen osoite sisältyy kohdeikkunan otsikkoon yleisessä automaattitäydennyksessä + Tietue on sopiva, jos sen osoite sisältyy kohdeikkunan otsikkoon yleisessä automaattisyötössä Always ask before performing Auto-Type - Kysy aina ennen automaattitäydennyksen käyttämistä + Kysy aina ennen automaattisyötön käyttämistä Global Auto-Type shortcut - Globaalin automaattitäydennyksen pikanäppäin + Yleisen automaattisyötön pikanäppäin Auto-Type delay - Automaattitäydennyksen viive + Automaattisyötön viive ms @@ -3955,7 +3952,7 @@ Avaa valittu tietokanta tai valitse toinen avattu tietokanta. Hide entry notes by default - Piilota tietueiden huomautukset + Piilota tietueiden muistiinpanot Privacy diff --git a/share/translations/keepassx_fr.ts b/share/translations/keepassx_fr.ts index 202d6c7b8..8017f9855 100644 --- a/share/translations/keepassx_fr.ts +++ b/share/translations/keepassx_fr.ts @@ -11,7 +11,7 @@ Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> - Signalez les bogues sur <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> + Signaler les bogues sur : <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. @@ -23,7 +23,7 @@ <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> - <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Voir Contributions sur GitHub</a> + <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Voir les contributions sur GitHub</a> Debug Info @@ -45,11 +45,11 @@ Revision: %1 - Révision : %1 + Révision : %1 Distribution: %1 - Distribution : %1 + Distribution : %1 Libraries: @@ -59,17 +59,17 @@ Operating system: %1 CPU architecture: %2 Kernel: %3 %4 - Système d’exploitation : %1 -Architecture processeur : %2 -Noyau : %3 %4 + Système d’exploitation : %1 +Architecture d’UCT : %2 +Noyau : %3 %4 Enabled extensions: - Extensions activées : + Extensions activées : Project Maintainers: - Mainteneurs du projet : + Mainteneurs du projet : Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. @@ -78,7 +78,7 @@ Noyau : %3 %4 Build Type: %1 - Genre de la version : %1 + Type version : %1 @@ -103,7 +103,7 @@ Noyau : %3 %4 %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 a demandé l’accès aux mots de passe pour l’élément suivant (ou les éléments suivants). + %1 a demandé l’accès aux mots de passe pour les éléments suivants. Veuillez indiquer si vous souhaitez autoriser l’accès. @@ -118,7 +118,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. AutoType Couldn't find an entry that matches the window title: - Impossible de trouver une entrée qui corresponde au titre de la fenêtre : + Impossible de trouver une entrée qui corresponde au titre de la fenêtre : Auto-Type - KeePassXC @@ -187,7 +187,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. Select entry to Auto-Type: - Choisissez une entrée pour la saisie automatique : + Sélectionner une entrée à saisir automatiquement : @@ -223,7 +223,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. This is required for accessing your databases with KeePassXC-Browser - Ceci est obligatoire pour accéder à vos bases de données à partir de KeePassXC-Browser + Ceci est requis pour accéder à vos bases de données à partir de KeePassXC-Browser Enable KeepassXC browser integration @@ -256,7 +256,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. Show a &notification when credentials are requested Credentials mean login data requested via browser extension - Afficher une &notification lorsque les identifiants sont demandés + Afficher une &notification quand les identifiants sont demandés Re&quest to unlock the database if it is locked @@ -264,11 +264,11 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. Only entries with the same scheme (http://, https://, ...) are returned. - Seules les entrées avec le même schéma (http://, https://, …) sont retournées. + Seules les entrées répondant au même format (http://, https://, …) sont retournées. &Match URL scheme (e.g., https://...) - &Correspondance du format de l’URL (exemple https://…) + &Correspondre au format d’URL (p. ex. https://…) Only returns the best matches for a specific URL instead of all entries for the whole domain. @@ -276,17 +276,17 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. &Return only best-matching credentials - Retourner uniquement l’identifiant qui correspond le mieux + Ne &retourner que les meilleures correspondances d’identifiants Sort &matching credentials by title Credentials mean login data requested via browser extension - Trier les identifiants trouvés par titre + Trier par &titre les identifiants correspondants Sort matching credentials by &username Credentials mean login data requested via browser extension - Trier les identifiants trouvés par &nom d’utilisateur + Trier par nom d’&utilisateur les identifiants correspondants &Disconnect all browsers @@ -294,7 +294,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. Forget all remembered &permissions - Oublier toutes les &autorisations accordées + Oublier toutes les autorisations accordées Advanced @@ -308,7 +308,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. Never ask before &updating credentials Credentials mean login data requested via browser extension - Ne &jamais demander avant de mettre à jour les identifiants + Ne jamais demander avant de &mettre à jour les identifiants Only the selected database has to be connected with a client. @@ -317,15 +317,15 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. Searc&h in all opened databases for matching credentials Credentials mean login data requested via browser extension - Cherc&her des identifiants qui correspondent dans toutes les bases de données ouvertes + &Chercher les identifiants correspondants dans toutes les bases de données ouvertes Automatically creating or updating string fields is not supported. - La création ou la mise à jour automatiques ne sont pas prises en charge pour les champs textuels ! + La création ou la mise a jour automatique ne sont pas pris en charge pour les champs de chaînes de caractères ! &Return advanced string fields which start with "KPH: " - &Retourne les champs textuels avancés qui commencent par « KPH:  » + &Retourner les champs avancés de chaîne qui commencent par « KPH: » Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup. @@ -355,7 +355,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. Browse... Button for opening file dialog - Parcourir… + Parcourir... <b>Warning:</b> The following options can be dangerous! @@ -375,7 +375,7 @@ Veuillez indiquer si vous souhaitez autoriser l’accès. We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment. - Nous sommes désolés, mais KeePassXC-Browser n’est pas encore pris en charge pour les versions Snap. + Nous sommes désolés, mais KeePassXC-Browser n’est pas disponible via Snap pour le moment. @@ -432,7 +432,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut The active database does not contain a settings entry. - La base de données active ne contient pas d’entrée pour les paramètres. + La base de données active ne contient pas d’entrée paramètre. KeePassXC: No keys found @@ -440,7 +440,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut No shared encryption keys found in KeePassXC Settings. - Aucune clé de chiffrement n’a été trouvée dans les paramètres de KeePassXC. + Aucune clé de chiffrement trouvée dans les paramètres de KeePassXC. KeePassXC: Removed keys from database @@ -448,7 +448,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Successfully removed %n encryption key(s) from KeePassXC settings. - %n clé de chiffrement a été retirée avec succès des paramètres de KeePassXC.%n clés de chiffrement ont été retirées avec succès des paramètres de KeePassXC. + Cette clé de chiffrement a été retirée avec succès des paramètres de KeepassXC.%n clés de chiffrement ont été retirées avec succès des paramètres de KeePassXC. Removing stored permissions… @@ -491,11 +491,11 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut &Key file - &Fichier-clé + &fichier-clé Browse - Naviguer + Parcourir Create @@ -523,7 +523,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Unable to create Key File : - Impossible de créer un fichier-clé : + Impossible de créer le fichier-clé : Select a key file @@ -539,7 +539,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Different passwords supplied. - Les mots de passe que vous avez saisis sont différents. + Les mots de passe insérés sont différents. Failed to set %1 as the Key file: @@ -573,7 +573,7 @@ Veuillez envisager de générer un nouveau fichier-clé. Append ' - Clone' to title - Ajouter « - Clone » au titre + Ajouter ' - Clone' au titre Replace username and password with references @@ -588,7 +588,7 @@ Veuillez envisager de générer un nouveau fichier-clé. CsvImportWidget Import CSV fields - Importer les champs CSV + Importer les champs du CSV filename @@ -624,11 +624,11 @@ Veuillez envisager de générer un nouveau fichier-clé. Number of headers line to discard - Nombre de lignes d’en-tête à ignorer + Nombre de lignes d’en-têtes à ignorer Consider '\' an escape character - Utiliser « \ » comme caractère d’échappement + Considère '\' comme un échappement Preview @@ -652,11 +652,11 @@ Veuillez envisager de générer un nouveau fichier-clé. Imported from CSV file - Importé du fichier CSV + Importé depuis un fichier CSV Original data: - Données originales : + Données originales : Error(s) detected in CSV file ! @@ -664,7 +664,7 @@ Veuillez envisager de générer un nouveau fichier-clé. more messages skipped] - d’autres messages ont été cachés] + plus de messages ignorés] Error @@ -673,7 +673,7 @@ Veuillez envisager de générer un nouveau fichier-clé. CSV import: writer has errors: - Import CSV : erreurs d’écriture : + Importation du CSV : le scripteur comporte des erreurs : @@ -719,7 +719,7 @@ Veuillez envisager de générer un nouveau fichier-clé. Browse - Naviguer + Parcourir Refresh @@ -752,7 +752,7 @@ Veuillez envisager de générer un nouveau fichier-clé. Don't show this warning again - Ne plus afficher cet avertissement + Ne plus afficher cette avertissement All files @@ -787,7 +787,7 @@ Veuillez envisager de générer un nouveau fichier-clé. Database opened fine. Nothing to do. - La base de données s’est bien ouverte. Aucune action n’est nécéssaire. + La base de données s’est bien ouverte. Aucune action nécéssaire. Success @@ -833,7 +833,7 @@ Si vous conservez ce nombre, votre base de données pourrait prendre des heures Cancel - Annuler + Annulé Number of rounds too low @@ -854,12 +854,12 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro Failed to transform key with new KDF parameters; KDF unchanged. - Échec de la transformation de la clé avec les nouveaux paramètres KDF ; KDF inchangé. + Échec de la transformation de la clé avec les nouveaux paramètres KDF; KDF inchangé. MiB Abbreviation for Mebibytes (KDF settings) - MioMio + MiB MiB thread(s) @@ -871,7 +871,7 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro DatabaseSettingsWidgetEncryption Encryption Algorithm: - Algorithme de chiffrement : + Algorithme de chiffrement : AES: 256 Bit (default) @@ -883,7 +883,7 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro Key Derivation Function: - Fonction de dérivation de clé (KDF) : + Fonction de dérivation de clé (KDF) : Transform rounds: @@ -891,7 +891,7 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro Benchmark 1-second delay - Mesurer pour un délai d’une seconde + Benchmark avec 1 seconde de délai Memory Usage: @@ -910,15 +910,15 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro Database name: - Nom de la base de données : + Nom de la base de données : Database description: - Description de la base de données : + Description de la base de données : Default username: - Nom d’utilisateur par défaut : + Nom d’utilisateur par défaut : History Settings @@ -934,7 +934,7 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro MiB - Mio + MiB Use recycle bin @@ -958,7 +958,7 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro KeePass 2 Database - Base de données Keepass 2 + Base de données KeePass 2 All files @@ -1002,7 +1002,7 @@ Si vous conservez ce nombre, votre base de données pourrait être craquées tro KeePass 1 database - Base de données Keepass 1 + Base de données KeePass 1 Close? @@ -1085,7 +1085,7 @@ Désactiver les enregistrements sécurisés et ressayer ? DatabaseWidget Searching... - Recherche… + Recherche... Change master key @@ -1230,7 +1230,7 @@ Voulez-vous fusionner vos changements ? Searching - Recherche… + Recherche... Attributes @@ -1266,7 +1266,7 @@ Voulez-vous fusionner vos changements ? [PROTECTED] - [PROTÉGÉ] + [PROTÉGER] Disabled @@ -1341,7 +1341,7 @@ Voulez-vous fusionner vos changements ? Different passwords supplied. - Les mots de passe ne sont pas identiques. + Les mots de passe insérés sont différents. New attribute @@ -1349,7 +1349,7 @@ Voulez-vous fusionner vos changements ? Confirm Remove - Confirmez la suppression + Confirmer la suppression Are you sure you want to remove this attribute? @@ -1439,11 +1439,11 @@ Voulez-vous fusionner vos changements ? Inherit default Auto-Type sequence from the &group - Utiliser la séquence de saisie automatique par défaut du groupe + Utiliser la séquence par défaut de saisie automatique du &groupe &Use custom Auto-Type sequence: - Utiliser une séquence de saisie automatique personnalisée : + &Utiliser une séquence personnalisée de saisie automatique : Window Associations @@ -1459,7 +1459,7 @@ Voulez-vous fusionner vos changements ? Window title: - Titre de la fenêtre : + Titre de la fenêtre : Use a specific sequence for this association: @@ -1489,7 +1489,7 @@ Voulez-vous fusionner vos changements ? EditEntryWidgetMain URL: - URL : + URL : Password: @@ -1501,7 +1501,7 @@ Voulez-vous fusionner vos changements ? Title: - Titre : + Titre : Notes @@ -1517,7 +1517,7 @@ Voulez-vous fusionner vos changements ? Username: - Nom d’utilisateur : + Nom d’utilisateur : Expires @@ -1536,7 +1536,7 @@ Voulez-vous fusionner vos changements ? seconds - secondes + secondes Fingerprint @@ -1581,7 +1581,7 @@ Voulez-vous fusionner vos changements ? Browse... Button for opening file dialog - Parcourir… + Parcourir... Attachment @@ -1597,7 +1597,7 @@ Voulez-vous fusionner vos changements ? Require user confirmation when this key is used - Demander une confirmation de l’utilisateur lorsque cette clé est utilisée + Requiert une confirmation de l’utilisateur quand cette clé est utilisée @@ -1651,7 +1651,7 @@ Voulez-vous fusionner vos changements ? Search - Chercher + Recherche Auto-Type @@ -1659,7 +1659,7 @@ Voulez-vous fusionner vos changements ? &Use default Auto-Type sequence of parent group - &Utiliser la séquence de saisie automatique du groupe parent + &Utiliser la séquence par défaut de saisie automatique du groupe parent Set default Auto-Type se&quence @@ -1694,7 +1694,7 @@ Voulez-vous fusionner vos changements ? Hint: You can enable Google as a fallback under Tools>Settings>Security - Astuce : Vous pouvez activer Google comme second recours sous Outils > Paramètres > Sécurité + Astuce : Vous pouvez activer Google en second recours sous Outils>Paramètres>Sécurité Images @@ -1702,7 +1702,7 @@ Voulez-vous fusionner vos changements ? All files - Tous les dossiers + Tous les fichiers Select Image @@ -1729,19 +1729,19 @@ Voulez-vous fusionner vos changements ? EditWidgetProperties Created: - Créé le : + Créé : Modified: - Modifié le : + Modifié : Accessed: - Accédé le : + Consulté : Uuid: - UUID : + Uuid : Plugin Data @@ -1829,7 +1829,7 @@ This may cause the affected plugins to malfunction. Unable to create directory: %1 - Impossible de créer le répertoire : + Impossible de créer le répertoire : %1 @@ -1861,7 +1861,7 @@ This may cause the affected plugins to malfunction. Unable to open files: %1 - Impossible d’ouvrir le fichier : + Impossible d’ouvrir le fichier : %1 @@ -1896,7 +1896,7 @@ This may cause the affected plugins to malfunction. Ref: Reference abbreviation - Réf : + Réf : Group @@ -1951,27 +1951,27 @@ This may cause the affected plugins to malfunction. EntryView Customize View - Personnaliser la vue + Personnaliser l’affichage Hide Usernames - Cacher les noms d’utilisateurs + Masquer les noms d’utilisateur Hide Passwords - Cacher les mots de passe + Masquer les mots de passe Fit to window - Adapter à la fenêtre + Ajuster à la fenêtre Fit to contents - Adapter au contenu + Ajuster au contenu Reset to defaults - Remettre les paramètres par défaut + Réinitialiser aux valeurs par défaut Attachments (icon) @@ -2000,11 +2000,11 @@ This may cause the affected plugins to malfunction. HttpPasswordGeneratorWidget Length: - Longueur : + Longueur : Character Types - Types de caractères : + Types de caractères: Upper Case Letters @@ -2036,7 +2036,7 @@ This may cause the affected plugins to malfunction. /*_& ... - /*_& … + /*_& ... Exclude look-alike characters @@ -2100,7 +2100,7 @@ This may cause the affected plugins to malfunction. Invalid header checksum size - Taille de la somme de contrôle de l’en-tête invalide + Taille de la somme de contrôle de l’en-tête non valide Header SHA256 mismatch @@ -2116,15 +2116,15 @@ This may cause the affected plugins to malfunction. Invalid header id size - Taille de l’id de l’en-tête invalide + Taille de l’id de l’en-tête non valide Invalid header field length - Longueur du champ de l’en-tête invalide + Longueur du champ de l’en-tête non valide Invalid header data length - Longueur des données de l’en-tête invalide + Longueur des données de l’en-tête non valide Failed to open buffer for KDF parameters in header @@ -2132,7 +2132,7 @@ This may cause the affected plugins to malfunction. Unsupported key derivation function (KDF) or invalid parameters - Fonction de dérivation de clé (KDF) non supporté ou paramètres invalide + Fonction de dérivation de clé (KDF) non supporté ou paramètres non valides Legacy header fields found in KDBX4 file. @@ -2140,15 +2140,15 @@ This may cause the affected plugins to malfunction. Invalid inner header id size - Taille de l’id de l’en-tête interne invalide + Taille de l’id de l’en-tête interne non valide Invalid inner header field length - Longueur du champ de l’en-tête interne invalide + Longueur du champ de l’en-tête interne non valide Invalid inner header binary size - Taille binaire de l’en-tête interne invalide + Taille binaire de l’en-tête interne non valide Unsupported KeePass variant map version. @@ -2158,69 +2158,69 @@ This may cause the affected plugins to malfunction. Invalid variant map entry name length Translation: variant map = data structure for storing meta data - Longueur du nom de la table des variantes invalide. + Longueur du nom de la table des variantes non valide. Invalid variant map entry name data Translation: variant map = data structure for storing meta data - Contenu du nom de la table des variantes invalide. + Contenu du nom de la table des variantes non valide. Invalid variant map entry value length Translation: variant map = data structure for storing meta data - Longueur de l’entrée dans la table des variantes invalide. + Longueur de l’entrée dans la table des variantes non valide. Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - Contenu de l’entrée dans la table des variantes invalide. + Contenu de l’entrée dans la table des variantes non valide. Invalid variant map Bool entry value length Translation: variant map = data structure for storing meta data - Longueur de l’entrée de type Booléen dans la table des variantes invalide. + Longueur de l’entrée de type Booléen dans la table des variantes non valide. Invalid variant map Int32 entry value length Translation: variant map = data structure for storing meta data - Longueur de l’entrée de type Int32 dans la table des variantes invalide. + Longueur de l’entrée de type Int32 dans la table des variantes non valide. Invalid variant map UInt32 entry value length Translation: variant map = data structure for storing meta data - Longueur de l’entrée de type UInt32 dans la table des variantes invalide. + Longueur de l’entrée de type UInt32 dans la table des variantes non valide. Invalid variant map Int64 entry value length Translation: variant map = data structure for storing meta data - Longueur de l’entrée de type Int64 dans la table des variantes invalide. + Longueur de l’entrée de type Int64 dans la table des variantes non valide. Invalid variant map UInt64 entry value length Translation: variant map = data structure for storing meta data - Longueur de l’entrée de type UInt64 dans la table des variantes invalide. + Longueur de l’entrée de type UInt64 dans la table des variantes non valide. Invalid variant map entry type Translation: variant map = data structure for storing meta data - Longueur de l’entrée dans la table des variantes invalide. + Longueur de l’entrée dans la table des variantes non valide. Invalid variant map field type size Translation: variant map = data structure for storing meta data - Longueur du type de champ dans la table des variantes invalide. + Longueur du type de champ dans la table des variantes non valide. Kdbx4Writer Invalid symmetric cipher algorithm. - Algorithme de chiffrement symétrique invalide. + Algorithme de chiffrement symétrique non valide. Invalid symmetric cipher IV size. IV = Initialization Vector for symmetric cipher - Taille du vecteur d’initialisation du chiffrement symétrique invalide. + Taille du vecteur d’initialisation du chiffrement symétrique non valide. Unable to calculate master key @@ -2236,7 +2236,7 @@ This may cause the affected plugins to malfunction. KdbxReader Invalid cipher uuid length - Longueur de l’UUID du chiffrement invalide + Longueur de l’uuid du chiffrement non valide Unsupported cipher @@ -2244,7 +2244,7 @@ This may cause the affected plugins to malfunction. Invalid compression flags length - Longueur des paramètres de compression invalide. + Longueur des paramètres de compression non valides. Unsupported compression algorithm @@ -2252,11 +2252,11 @@ This may cause the affected plugins to malfunction. Invalid master seed size - Taille de la semence primaire invalide. + Taille de semence primaire non valide. Invalid transform seed size - Taille de la semence germée invalide. + Taille de la semence germée non valide. Invalid transform rounds size @@ -2264,15 +2264,15 @@ This may cause the affected plugins to malfunction. Invalid start bytes size - Taille des octets de début invalide + Taille des octets de début non valide Invalid random stream id size - Taille de l’identifiant du flux aléatoire invalide. + Taille de l’identifiant du flux aléatoire non valide. Invalid inner random stream cipher - Taille du chiffrement du flux intérieur aléatoire invalide. + Taille du chiffrement du flux intérieur aléatoire non valide. Not a KeePass database. @@ -2285,7 +2285,7 @@ You can import it by clicking on Database > 'Import KeePass 1 database...'. This is a one-way migration. You won't be able to open the imported database with the old KeePassX 0.4 version. Le fichier sélectionné est une ancienne base de données KeePass 1 (.kdb). -Vous pouvez l’importer en cliquant sur Base de données>« Importer une base de données KeePass 1… » +Vous pouvez l’importer en cliquant sur Base de données>'Importer une base de données KeePass 1...' Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base de données importée avec l’ancienne version de KeePassX 0.4. @@ -2297,7 +2297,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base KdbxXmlReader XML parsing failure: %1 - Erreur d’analyse XML : %1 + Erreur d’analyse XML : %1 No root group @@ -2305,11 +2305,11 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Missing icon uuid or data - Données ou UUID de l’icône manquant + Données ou uuid de l’icône manquant Missing custom data key or value - Valeur ou clé de données personnalisée manquante + Valeur ou clé de donnée personnalisée manquante Multiple group elements @@ -2317,39 +2317,39 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Null group uuid - UUID du groupe sans valeur + Uuid du groupe sans valeur Invalid group icon number - Numéro de l’icône du groupe invalide + Numéro de l’icône du groupe non valide Invalid EnableAutoType value - Valeur EnableAutoType invalide + Valeur EnableAutoType non valide Invalid EnableSearching value - Valeur de EnableSearching invalide + Valeur de EnableSearching non valide No group uuid found - Aucun UUID de groupe trouvé + Aucun uuid de groupe trouvé Null DeleteObject uuid - UUID de DeleteObject sans valeur + Uuid de DeleteObject sans valeur Missing DeletedObject uuid or time - Temps ou UUID de DeletedObject manquant + Temps ou uuid de DeletedObject manquant Null entry uuid - UUID de l’entrée sans valeur + Uuid de l’entrée sans valeur Invalid entry icon number - Numéro de l’icône de l’entrée invalide + Numéro de l’icône de l’entrée non valide History element in history entry @@ -2357,11 +2357,11 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base No entry uuid found - Aucun UUID d’entrée trouvé + Aucun uuid d’entrée trouvé History element with different uuid - Élément de l’historique avec un UUID différent + Élément de l’historique avec un uuid différent Unable to decrypt entry string @@ -2389,27 +2389,27 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Invalid bool value - Valeur booléenne invalide + Valeur bool non valide Invalid date time value - Valeur d’horodatage invalide + Valeur date time non valide Invalid color value - Valeur de couleur invalide + Valeur de couleur non valide Invalid color rgb part - Partie de couleur RVB invalide + Partie de couleur RVB non valide Invalid number value - Valeur de nombre invalide + Valeur de nombre non valide Invalid uuid value - Valeur UUID invalide + Valeur uuid non valide Unable to decompress binary @@ -2421,7 +2421,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base KeePass1OpenWidget Import KeePass1 database - Importer une base de données au format KeePass1 + Importer une base de données au format KeePass 1 Unable to open the database. @@ -2453,11 +2453,11 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Invalid number of groups - Nombre de groupes invalide + Nombre de groupes non valide Invalid number of entries - Nombre d’entrées invalide + Nombre d’entrées non valide Invalid content hash size @@ -2465,7 +2465,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Invalid transform seed size - Taille de la semence germée invalide. + Taille de la semence germée non valide. Invalid number of transform rounds @@ -2493,11 +2493,11 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Invalid group field type number - Numéro du type de champ groupe invalide. + Numéro du type de champ groupe non valide. Invalid group field size - Taille du champ groupe invalide + Taille du champ groupe non valide Read group field data doesn't match size @@ -2505,11 +2505,11 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Incorrect group id field size - Taille du champ « identifiant du groupe » incorrect + Taille du champ "identifiant du groupe" incorrect Incorrect group creation time field size - Taille du champ « date du la création du groupe » incorrect. + Taille du champ "date du la création du groupe" incorrect. Incorrect group modification time field size @@ -2517,15 +2517,15 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Incorrect group access time field size - Taille du champ « date d’accès au groupe » incorrect. + Taille du champ "date d’accès au groupe" incorrect. Incorrect group expiry time field size - Taille du champ « date d’expiration du groupe » incorrect. + Taille du champ "date d’expiration du groupe" incorrect. Incorrect group icon field size - Taille du champ « icône du groupe » incorrect. + Taille du champ "icône du groupe" incorrect. Incorrect group level field size @@ -2545,7 +2545,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Invalid entry field size - Taille du champ de l’entrée invalide + Taille du champ de l’entrée non valide Read entry field data doesn't match size @@ -2553,23 +2553,23 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Invalid entry uuid field size - Taille du champ UUID de l’entrée invalide + Taille du champ uuid de l’entrée non valide Invalid entry group id field size - Taille du champ id du groupe de l’entrée invalide + Taille du champ id du groupe de l’entrée non valide Invalid entry icon field size - Taille du champ icône de l’entrée invalide + Taille du champ icône de l’entrée non valide Invalid entry creation time field size - Taille du champ date de création de l’entrée invalide + Taille du champ date de création de l’entrée non valide Invalid entry modification time field size - Taille du champ date de modification de l’entrée invalide + Taille du champ date de modification de l’entrée non valide Invalid entry expiry time field size @@ -2580,42 +2580,15 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Champ d’entrée type est invalide - - KeePass2 - - AES: 256-bit - AES : 256 bits - - - Twofish: 256-bit - Twofish : 256 bits - - - ChaCha20: 256-bit - ChaCha20 : 256 bits - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – recommandé) - - Main Existing single-instance lock file is invalid. Launching new instance. - Le fichier de verrouillage de l’instance unique existant est invalide. Lancement d’une nouvelle instance. + Le fichier de verrouillage de l’instance unique existant n’est pas valide. Lancement d’une nouvelle instance. The lock file could not be created. Single-instance mode disabled. - Le fichier verrou ne peut pas être créé. Le mode instance-unique est désactivé. + Le fichier de verrouillage ne peut pas être créé. Le mode d’instance unique est désactivé. Another instance of KeePassXC is already running. @@ -2638,7 +2611,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Recent databases - Bases de données récentes + &Bases de données récentes Import @@ -2646,11 +2619,11 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Help - Aide + &Aide E&ntries - Entrées + E&ntrées Copy att&ribute to clipboard @@ -2662,7 +2635,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Groups - Groupes + &Groupes &Tools @@ -2682,11 +2655,11 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Save database - Enregistrer la base de données + &Enregistrer la base de données &Close database - Fermer la base de données + &Fermer la base de données &New database @@ -2694,19 +2667,19 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Merge from KeePassX database - Fusionner depuis la base de données KeePassX + Fusionner avec la base de données de KeePassX... &Add new entry - Ajouter une nouvelle entrée + &Ajouter une nouvelle entrée &View/Edit entry - Voir/Editer l’entrée + &Voir/Éditer l’entrée &Delete entry - Supprimer l’entrée + &Supprimer l’entrée &Add new group @@ -2722,7 +2695,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Sa&ve database as... - Sau&ver la base de données sous… + En&registrer la base de données sous... Change &master key... @@ -2730,7 +2703,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Database settings - Paramètres de la base de &données + &Paramètres de la base de données Database settings @@ -2738,7 +2711,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Clone entry - Cloner l’entrée + &Cloner l’entrée &Find @@ -2754,7 +2727,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Cop&y password - Copier le mot de passe + Cop&ier le mot de passe Copy password to clipboard @@ -2762,15 +2735,15 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Settings - Paramètres + &Paramètres Password Generator - Générateur de mot de passe + Générateur de mots de passe &Perform Auto-Type - Effectuer la saisie automatique + &Exécuter la saisie automatique &Open URL @@ -2778,7 +2751,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Lock databases - Verrouiller les bases de données + &Verrouiller les bases de données &Title @@ -2806,19 +2779,19 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base &Export to CSV file... - &Exporter dans un fichier CSV… + &Exporter dans un fichier CSV... Import KeePass 1 database... - Importer une base de données KeePass 1… + Importer une base de données KeePass 1... Import CSV file... - Importer un fichier CSV… + Importer un fichier CSV... Re&pair database... - Ré&parer la base de données… + Ré&parer la base de données... Show TOTP @@ -2826,7 +2799,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Set up TOTP... - Configurer TOTP… + Configurer TOTP... Copy &TOTP @@ -2850,7 +2823,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base read-only - Lecture seulement + Lecture seule Settings @@ -2862,7 +2835,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Quit KeePassXC - Quitter KeePass XC + Quitter KeePassXC KeePass 2 Database @@ -2874,7 +2847,7 @@ Il s’agit d’une migration à sens unique. Vous ne pourrez pas ouvrir la base Open database - Ouvrir une base de données + Ouvrir la base de données Save repaired database @@ -2921,7 +2894,7 @@ Cette version n’est pas destinée à la production. Found zero keys - Acune clé n’a été trouvée + Zéro clés trouvées Failed to read public key. @@ -2929,7 +2902,7 @@ Cette version n’est pas destinée à la production. Corrupted key file, reading private key failed - Le fichier-clé est corrompu. Échec de lecture de la clé privée. + Le fichier-clé est corrompu, échec de lecture de la clé privée No private key payload to decrypt @@ -2953,11 +2926,11 @@ Cette version n’est pas destinée à la production. Unexpected EOF while reading public key - Fin de fichier inattendue lors de la lecture de la clé publique + End-of-file inattendu lors de la lecture de la clé publique Unexpected EOF while reading private key - Fin de fichier inattendue lors de la lecture de la clé privée + End-of-file inattendu lors de la lecture de la clé privée Can't write public key as it is empty @@ -2973,27 +2946,27 @@ Cette version n’est pas destinée à la production. Unexpected EOF when writing private key - Fin de fichier inattendue lors de l’écriture de la clé privée + End-of-file inattendu lors de l’écriture de la clé privée Unsupported key type: %1 - Type de clé non supporté : %1 + Type de clé non géré: %1 Unknown cipher: %1 - Chiffrement inconnu : %1 + Chiffrement inconnu : %1 Cipher IV is too short for MD5 kdf - Le vecteur d’initialisation du chiffrement est trop court pour la KDF MD5 + Le vecteur d’initialisation du chiffrage est trop court pour la KDF MD5 Unknown KDF: %1 - KDF inconnu : %1 + KDF inconnu : %1 Unknown key type: %1 - Type de clé inconnu : %1 + Type de clé inconnu : %1 @@ -3017,7 +2990,7 @@ Cette version n’est pas destinée à la production. Sh&ow a notification when credentials are requested Credentials mean login data requested via browser extension - Montrer une notification quand les références sont demandées + &Afficher une notification quand les identifiants sont demandés Only returns the best matches for a specific URL instead of all entries for the whole domain. @@ -3025,7 +2998,7 @@ Cette version n’est pas destinée à la production. &Return only best matching entries - &Retourner seulement les meilleures entrées + &Retourner seulement les meilleurs entrées Re&quest to unlock the database if it is locked @@ -3033,7 +3006,7 @@ Cette version n’est pas destinée à la production. Only entries with the same scheme (http://, https://, ftp://, ...) are returned. - Seules les entrées avec le même schéma (http://, https://, ftp://, …) sont retournées. + Seules les entrées avec le même schéma (http://, https://, ftp://, ...) sont retournées. &Match URL schemes @@ -3057,7 +3030,7 @@ Cette version n’est pas destinée à la production. Password Generator - Générateur de mot de passe + Générateur de mots de passe Advanced @@ -3081,23 +3054,23 @@ Cette version n’est pas destinée à la production. Automatically creating or updating string fields is not supported. - La création ou la mise a jour automatiques ne sont pas prises en charge pour les champs textuels ! + La création ou la mise a jour automatique ne sont pas pris en charge pour les champs de chaines de caractères ! &Return advanced string fields which start with "KPH: " - &Retourne les champs textuels avancés qui commencent par « KPH : » + & Retourne les champs avancés de chaîne de caractères qui commencent par "KPH :" HTTP Port: - Port HTTP : + Port HTTP : Default port: 19455 - Port par défaut : 19455 + Port par défaut : 19455 KeePassXC will listen to this port on 127.0.0.1 - KeepassXC va écouter ce port sur 127.0.0.1 + KeePassXC va écouter ce port sur 127.0.0.1 <b>Warning:</b> The following options can be dangerous! @@ -3143,7 +3116,7 @@ Le port 19455 par défaut sera utilisé. Character Types - Types de caractères + Types de caractères: Upper Case Letters @@ -3155,7 +3128,7 @@ Le port 19455 par défaut sera utilisé. Numbers - Nombres + Chiffres Special Characters @@ -3167,7 +3140,7 @@ Le port 19455 par défaut sera utilisé. Exclude look-alike characters - Exclure les caractères se ressemblant + Exclure les caractères qui se ressemblent Pick characters from every group @@ -3175,7 +3148,7 @@ Le port 19455 par défaut sera utilisé. &Length: - &Longueur : + &Longueur : Passphrase @@ -3183,15 +3156,15 @@ Le port 19455 par défaut sera utilisé. Wordlist: - Liste de mots : + Liste de mots : Word Count: - Nombre de mots : + Nombre de mots : Word Separator: - Séparateur de mots : + Séparateur de mot : Generate @@ -3219,12 +3192,12 @@ Le port 19455 par défaut sera utilisé. Password Quality: %1 - Qualité du mot de passe : %1 + Qualité du mot de passe : %1 Poor Password quality - Mauvais + Pauvre Weak @@ -3302,7 +3275,7 @@ Le port 19455 par défaut sera utilisé. No logins found - Aucun identifiant trouvé + Aucuns identifiants trouvés Unknown error @@ -3367,7 +3340,7 @@ Le port 19455 par défaut sera utilisé. Path of the entry to clip. clip = copy to clipboard - Chemin de l’entrée à copier. + Chemin de l’entrée à épingler. Timeout in seconds before clearing the clipboard. @@ -3411,7 +3384,7 @@ Le port 19455 par défaut sera utilisé. Insert password to unlock %1: - Insérer le mot de passe pour déverrouiller %1 : + Insérer le mot de passe pour déverrouiller %1 : Failed to load key file %1 : %2 @@ -3433,7 +3406,7 @@ Available commands: -Commandes disponibles : +Commandes disponibles : @@ -3446,7 +3419,7 @@ Commandes disponibles : Path of the group to list. Default is / - Chemin du groupe à lister. Par défaut : / + Chemin du groupe à lister. Par défaut : / Find entries quickly. @@ -3470,7 +3443,7 @@ Commandes disponibles : Use the same credentials for both database files. - Utiliser les mêmes identifiants pour les deux bases de données. + Utiliser les mêmes identifiants pour les deux fichiers de base de données. Key file of the database to merge from. @@ -3508,11 +3481,35 @@ Commandes disponibles : malformed string - chaîne mal formée + chaîne de caractères malformée missing closing quote - Guillemet fermant manquant + fermeture de citation manquante + + + AES: 256-bit + AES : 256 bits + + + Twofish: 256-bit + Twofish : 256 bits + + + ChaCha20: 256-bit + ChaCha20 : 256 bits + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – recommandé) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) Group @@ -3568,11 +3565,11 @@ Commandes disponibles : Generate a new random diceware passphrase. - Créer une nouvelle phrase de passe générée au hasard par des dés. + Créer une nouvelle phrase de passe générée avec la méthode du lancer de dés. Word count for the diceware passphrase. - Nombre de mots de la phrase de passe générée par des dés. + Nombre de mots de la phrase de passe générée avec la méthode du lancer de dés. count @@ -3581,8 +3578,8 @@ Commandes disponibles : Wordlist for the diceware generator. [Default: EFF English] - Liste de mots pour le générateur par Diceware. -[par défaut : anglais EFF] + Liste de mots pour le générateur par dés. +[Par défaut : FFÉ anglais] Generate a new random password. @@ -3617,23 +3614,23 @@ Commandes disponibles : QtIOCompressor Internal zlib error when compressing: - Erreur interne zlib lors de la compression : + Erreur interne zlib lors de la compression : Error writing to underlying device: - Erreur d’écriture sur le périphérique concerné : + Erreur d’écriture sur le périphérique concerné : Error opening underlying device: - Erreur d’ouverture du périphérique concerné : + Erreur d’ouverture du périphérique concerné : Error reading data from underlying device: - Erreur de lecture sur le périphérique concerné : + Erreur de lecture sur le périphérique concerné : Internal zlib error when decompressing: - Erreur interne zlib lors de la décompression : + Erreur interne zlib lors de la décompression : @@ -3644,18 +3641,18 @@ Commandes disponibles : Internal zlib error: - Erreur interne zlib : + Erreur interne zlib : SearchWidget Search... - Recherche… + Recherche... Search - Chercher + Recherche Clear @@ -3682,7 +3679,7 @@ If you would like to allow it access to your KeePassXC database give it a unique name to identify and accept it. Vous avez reçu une demande d’association pour la clé ci-dessus. Si vous voulez autoriser cette clé à accéder à votre base de données KeePassXC, -attribuez lui un nom unique pour l’identifier et acceptez-la. +attribuez lui un nom unique pour l’identifier et acceptez la. KeePassXC: Overwrite existing key? @@ -3700,7 +3697,7 @@ Voulez-vous la remplacer ? Do you want to update the information in %1 - %2? - Voulez-vous mettre à jour l’information dans %1 - %2 ? + Voulez-vous mettre à jour les informations dans %1 - %2 ? KeePassXC: Database locked! @@ -3718,7 +3715,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - %n clé de chiffrement a été retirée avec succès des paramètres de KeePassXC/HTTP.%n clés chiffrement ont été retirées avec succès des paramètres KeePassXC/HTTP. + %n clé de chiffrement a été retirée avec succès des paramètres de KeePassXC/HTTP.%n clés de chiffrement ont été retirées avec succès des paramètres de KeePassXC/HTTP. KeePassXC: No keys found @@ -3750,7 +3747,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Successfully removed permissions from %n entries. - Les autorisations de %n entrées ont été correctement supprimées.Les autorisations de %n entrées ont été correctement supprimées. + Les autorisations d’%n entrée ont été retirées avec succès.Les autorisations de %n entrées ont été retirées avec succès. KeePassXC: No entry with permissions found! @@ -3788,7 +3785,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Start only a single instance of KeePassXC - Démarrer une seule instance de KeePassXC + Démarrer uniquement une seule instance de KeePassXC Remember last databases @@ -3804,7 +3801,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Automatically save on exit - Enregistrer automatiquement à la sortie + Enregistrer automatiquement en quittant Automatically save after every change @@ -3828,7 +3825,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Don't mark database as modified for non-data changes (e.g., expanding groups) - Ne pas indiquer la base de données comme modifiée pour les changements hors-données (par exemple : groupes développés) + Ne pas indiquer la base de données comme modifiée pour les changements hors-données (par exemple : groupes développés) Hide the Details view @@ -3876,7 +3873,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Auto-Type delay - Délai de remplissage de la saisie automatique + Délais de remplissage de la saisie automatique ms @@ -3889,7 +3886,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut File Management - Gestion de fichiers + Gestion des fichiers Safely save database files (may be incompatible with Dropbox, etc) @@ -3912,7 +3909,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut SettingsWidgetSecurity Timeouts - Timeouts + Expirations Clear clipboard after @@ -3929,15 +3926,15 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Convenience - Commodités + Convenance Lock databases when session is locked or lid is closed - Verrouiller les bases de données lorsque la session est verrouillée ou le capot fermé + Verrouiller les bases de données quand la session est verrouillée ou l’écran rabattu Lock databases after minimizing the window - Verrouiller la base de données lorsque la fenêtre est minimisée + Verrouiller les bases de données lorsque la fenêtre est minimisée Don't require password repeat when it is visible @@ -3965,7 +3962,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Re-lock previously locked database after performing Auto-Type - Verrouiller à nouveau la base de données précédemment verrouillée après avoir effectué la saisie automatique + Verrouiller à nouveau la base de données précédemment verrouillée après avoir réalisé la saisie automatique @@ -3976,7 +3973,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Key: - Clé : + Clé : Default RFC 6238 token settings @@ -3992,11 +3989,11 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Note: Change these settings only if you know what you are doing. - Attention : modifiez ces paramètres seulement si vous savez ce que vous faites. + Attention : modifiez ces paramètres seulement si vous savez ce que vous faites. Time step: - Période de temps : + Période de temps : 8 digits @@ -4008,7 +4005,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Code size: - Taille du code : + Taille du code : sec @@ -4066,7 +4063,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Import from CSV - Import depuis un fichier CSV + Importer depuis un CSV Recent databases @@ -4074,7 +4071,7 @@ Veuillez déverrouiller la base de données sélectionnée ou en choisir une aut Welcome to KeePassXC %1 - Bienvenue dans KeePassXC %1 + Bienvenue sur KeePassXC %1 diff --git a/share/translations/keepassx_hu.ts b/share/translations/keepassx_hu.ts index eaf3a4722..8ad2b2747 100644 --- a/share/translations/keepassx_hu.ts +++ b/share/translations/keepassx_hu.ts @@ -15,7 +15,7 @@ KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. - A KeePassXC a GNU General Public License (GPL) 2. vagy (válaszhatóan ) 3. verziója szerint kerül terjesztésre. + A KeePassXC a GNU General Public License (GPL) 2-es vagy (válaszhatóan) 3-as verziója szerint kerül terjesztésre. Contributors @@ -777,7 +777,7 @@ Megfontolandó egy új kulcsfájl készítése. Can't open key file - Nem lehet megnyitni a kulcsfájl + Nem lehet megnyitni a kulcsfájlt Unable to open the database. @@ -992,7 +992,7 @@ Ezt a számot megtartva az adatbázis nagyon könnyen törhető lesz. Merge database - Adatbázis összeolvasztása + Adatbázis egyesítése Open KeePass 1 database @@ -1171,7 +1171,7 @@ Letiltható a biztonságos mentés és úgy megkísérelhető a mentés? Merge Request - Összeolvasztási kérelem + Egyesítési kérelem The database file has changed and you have unsaved changes. @@ -1422,7 +1422,7 @@ Egyesíti a módosításokat? Foreground Color: - Előtérszín: + Előtérszín Background Color: @@ -1739,11 +1739,11 @@ Egyesíti a módosításokat? Uuid: - Uuid: + UUID: Plugin Data - Bővítmény adati + Beépülő adati Remove @@ -2035,7 +2035,7 @@ Ez a kijelölt bővítmény hibás működését eredményezheti. /*_& ... - /*_& ... + /*_& … Exclude look-alike characters @@ -2579,33 +2579,6 @@ Ez egyirányú migráció. Nem lehet majd megnyitni az importált adatbázist a Érvénytelen bejegyzésmező-típus - - KeePass2 - - AES: 256-bit - AES: 256 bites - - - Twofish: 256-bit - Twofish: 256 bites - - - ChaCha20: 256-bit - ChaCha20: 256 bites - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – ajánlott) - - Main @@ -2693,7 +2666,7 @@ Ez egyirányú migráció. Nem lehet majd megnyitni az importált adatbázist a Merge from KeePassX database - Összeolvasztás KeePassX adatbázisból + Egyesítés KeePassX adatbázisból &Add new entry @@ -3512,6 +3485,30 @@ Elérhető parancsok: missing closing quote hiányzó lezáró idézőjel + + AES: 256-bit + AES: 256 bites + + + Twofish: 256-bit + Twofish: 256 bites + + + ChaCha20: 256-bit + ChaCha20: 256 bites + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – ajánlott) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Csoport diff --git a/share/translations/keepassx_id.ts b/share/translations/keepassx_id.ts index a5135b2c4..74dd6523c 100644 --- a/share/translations/keepassx_id.ts +++ b/share/translations/keepassx_id.ts @@ -23,7 +23,7 @@ <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> - <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Lihat Semua Kontribusi pada GitHub</a> + <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Lihat Semua Kontribusi di GitHub</a> Debug Info @@ -514,7 +514,7 @@ Silakan buka kunci atau pilih yang lainnya yang tidak terkunci. All files - Semua berkas + Semua Berkas Create Key File... @@ -592,11 +592,11 @@ Harap pertimbangkan membuat berkas kunci baru. filename - filename + nama berkas size, rows, columns - size, rows, columns + ukuran, baris, kolom Encoding @@ -757,7 +757,7 @@ Harap pertimbangkan membuat berkas kunci baru. All files - Semua berkas + Semua Berkas Key files @@ -955,11 +955,11 @@ If you keep this number, your database may be too easy to crack! KeePass 2 Database - Basis data KeePass 2 + Basis Data KeePass 2 All files - Semua berkas + Semua Berkas Open database @@ -1023,7 +1023,7 @@ Simpan perubahan? Writing the database failed. - Gagal membuat basis data. + Gagal menyimpan basis data. Passwords @@ -1699,7 +1699,7 @@ Apakah Anda ingin menggabungkan ubahan Anda? All files - Semua berkas + Semua Berkas Select Image @@ -2578,33 +2578,6 @@ Ini adalah migrasi satu arah. Anda tidak akan bisa membuka basis data yang diimp Tipe ruas entri tidak valid - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – direkomendasikan) - - Main @@ -3513,6 +3486,30 @@ Perintah yang tersedia: missing closing quote kehilangan tanda kutip tutup + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – direkomendasikan) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Grup @@ -3717,7 +3714,7 @@ Silakan buka kunci atau pilih yang lainnya yang tidak terkunci. Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Berhasil membuang %n kunci terenkripsi dari Pengaturan KeePassXC/Http. + Berhasil membuang %n kunci enkripsi dari Pengaturan KeePassX/Http KeePassXC: No keys found @@ -3749,7 +3746,7 @@ Silakan buka kunci atau pilih yang lainnya yang tidak terkunci. Successfully removed permissions from %n entries. - Berhasil membuang izin dari %n entri. + Berhasil membuang perizinan dari %n entri. KeePassXC: No entry with permissions found! diff --git a/share/translations/keepassx_it.ts b/share/translations/keepassx_it.ts index d4efd9632..fa24d81bf 100644 --- a/share/translations/keepassx_it.ts +++ b/share/translations/keepassx_it.ts @@ -3,7 +3,7 @@ AboutDialog About KeePassXC - Info su KeePassXC + Informazioni su KeePassXC About @@ -27,7 +27,7 @@ Debug Info - Informazioni debug + Informazioni di debug Include the following information whenever you report a bug: @@ -86,11 +86,11 @@ Kernel: %3 %4 AccessControlDialog KeePassXC HTTP Confirm Access - KeePassXC HTTP conferma accesso + Conferma l'accesso per KeePassXC HTTP Remember this decision - Ricorda questa decisione + Ricorda questa scelta Allow @@ -122,7 +122,7 @@ Seleziona se vuoi consentire l'accesso. Auto-Type - KeePassXC - Auto-completamento - KeePassXC + KeePassXC - Completamento automatico Auto-Type @@ -187,7 +187,7 @@ Seleziona se vuoi consentire l'accesso. Select entry to Auto-Type: - Seleziona una voce per l'auto-completamento: + Seleziona una voce per il completamento automatico: @@ -198,7 +198,7 @@ Seleziona se vuoi consentire l'accesso. Remember this decision - Ricorda questa scelta + Ricorda questa decisione Allow @@ -447,7 +447,7 @@ Sblocca il database selezionato o scegline un altro che sia sbloccato. Successfully removed %n encryption key(s) from KeePassXC settings. - Rimosso con successo %n chiavi di crittografia da KeePassXC impostazioni.Rimossa(e) con successo %n chiave(i) di crittografia dalle impostazioni di KeePassXC. + Rimossa con successo %n chiave di cifratura dalle impostazioni di KeePassXC.Rimosse con successo %n chiavi di cifratura dalle impostazioni di KeePassXC. Removing stored permissions… @@ -463,7 +463,7 @@ Sblocca il database selezionato o scegline un altro che sia sbloccato. Successfully removed permissions from %n entry(s). - Rimosso con successo le autorizzazioni da %n ha.Rimossa(e) con successo le autorizzazioni da %n voce(i). + Permessi rimossi con successo da %n voce.Permessi rimossi con successo da %n voci. KeePassXC: No entry with permissions found! @@ -865,7 +865,7 @@ Se continui con questo numero, il tuo database potrebbe essere decifrato molto f thread(s) Threads for parallel execution (KDF settings) - iscritto (i) thread(s) + threadthread @@ -935,7 +935,7 @@ Se continui con questo numero, il tuo database potrebbe essere decifrato molto f MiB - MiB + MB Use recycle bin @@ -1027,7 +1027,7 @@ Vuoi salvare le modifiche? Writing the database failed. - Scrittura del database non riuscita. + Scrittura database non riuscita. Passwords @@ -1043,7 +1043,7 @@ Vuoi salvare le modifiche? Writing the CSV file failed. - Scrittura file CSV fallita. + Scrittura file CSV non riuscita. New database @@ -1122,7 +1122,7 @@ Disabilitare i salvataggi sicuri e riprovare? Do you really want to move %n entry(s) to the recycle bin? - Vuoi veramente spostare %n elemento nel Cestino?Vuoi veramente cestinare %n voci? + Vuoi veramente cestinare %n voce?Vuoi veramente cestinare %n voci? Execute command? @@ -1444,7 +1444,7 @@ Vuoi fondere i cambiamenti? &Use custom Auto-Type sequence: - &Usa sequenza di compeltamento automatico personalizzata: + &Usa sequenza di completamento automatico personalizzata: Window Associations @@ -1818,7 +1818,7 @@ Ciò potrebbe causare malfunzionamenti ai plugin interessati. Are you sure you want to remove %n attachment(s)? - Sei sicuro di che voler rimuovere %n allegati?Sei sicuro di voler rimuovere %n allegato(i)? + Sei sicuro di voler rimuovere %n allegato?Sei sicuro di voler rimuovere %n allegati? Confirm Remove @@ -2006,7 +2006,7 @@ Ciò potrebbe causare malfunzionamenti ai plugin interessati. Character Types - Tipi carattere + Tipi di carattere Upper Case Letters @@ -2582,33 +2582,6 @@ Si tratta di una migrazione unidirezionale. Non sarà possibile aprire il databa Tipo di dato non valido - - KeePass2 - - AES: 256-bit - AES: 256 bit - - - Twofish: 256-bit - Twofish: 256 bit - - - ChaCha20: 256-bit - ChaCha20: 256 bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – raccomandato) - - Main @@ -2939,7 +2912,7 @@ Questa versione non è pensata per essere utilizzata in ambito di produzione. Trying to run KDF without cipher - Tentativo di eseguire KDF senza crittografia + Sto cercando di eseguire KDF senza cifratura Passphrase is required to decrypt this key @@ -3019,7 +2992,7 @@ Questa versione non è pensata per essere utilizzata in ambito di produzione. Sh&ow a notification when credentials are requested Credentials mean login data requested via browser extension - Visualizza una n&otifica quando sono richeste le credenziali + Visualizza una n&otifica quando sono richieste le credenziali Only returns the best matches for a specific URL instead of all entries for the whole domain. @@ -3517,6 +3490,30 @@ Comandi disponibili: missing closing quote virgoletta di chiusura mancante + + AES: 256-bit + AES: 256 bit + + + Twofish: 256-bit + Twofish: 256 bit + + + ChaCha20: 256-bit + ChaCha20: 256 bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – raccomandato) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Gruppo @@ -3721,7 +3718,7 @@ Sblocca il database selezionato o scegline un altro che sia sbloccato. Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Rimossa con successo %n chiave di cifratura dalle impostazioni di KeePassX/Http.Rimossa(e) con successo %n chiave(i) di cifratura dalle impostazioni di KeePassX/Http. + Rimossa con successo %n chiave di cifratura dalle impostazioni di KeePassX/Http.Rimosse con successo %n chiavi di cifratura dalle impostazioni di KeePassX/Http. KeePassXC: No keys found @@ -3839,15 +3836,15 @@ Sblocca il database selezionato o scegline un altro che sia sbloccato. Show a system tray icon - Visualizza un'icona nell'area di notifica del sistema + Visualizza un'icona nell'area di notifica di sistema Hide window to system tray when minimized - Nascondi la finestra nell'area di notifica del sistema quando viene minimizzata + Nascondi la finestra nell'area di notifica di sistema quando viene minimizzata Hide window to system tray instead of app exit - Nascondi la finestra nella barra di sistema invece di chiudere l'applicazione + Nascondi la finestra nell'area di notifica di sistema invece di chiudere l'applicazione Dark system tray icon @@ -3987,7 +3984,7 @@ Sblocca il database selezionato o scegline un altro che sia sbloccato. Steam token settings - Impostazioni del token di Steam + Impostazioni del token di vapore Use custom settings diff --git a/share/translations/keepassx_ja.ts b/share/translations/keepassx_ja.ts index 10a27b2b8..a54aaa06b 100644 --- a/share/translations/keepassx_ja.ts +++ b/share/translations/keepassx_ja.ts @@ -657,7 +657,7 @@ Please consider generating a new key file. Original data: - 元データ: + 元データ: Error(s) detected in CSV file ! @@ -861,7 +861,7 @@ If you keep this number, your database may be too easy to crack! MiB Abbreviation for Mebibytes (KDF settings) - MiB + MiB thread(s) @@ -1023,7 +1023,7 @@ Discard changes and close anyway? "%1" was modified. Save changes? - "%1" は編集されています。 + "%1" は更新されています。 変更を保存しますか? @@ -1036,7 +1036,7 @@ Save changes? Save database as - データベースを別名で保存 + ファイル名をつけてデータベースを保存 Export database to CSV file @@ -1319,15 +1319,15 @@ Do you want to merge your changes? Select private key - 私有鍵を選択 + 秘密鍵を選択 File too large to be a private key - ファイルが大きすぎるため私有鍵にできません + ファイルが大きすぎるため秘密鍵にできません Failed to open private key - 私有鍵を開くのに失敗しました + 秘密鍵を開くのに失敗しました Entry history @@ -1371,11 +1371,11 @@ Do you want to merge your changes? %n week(s) - %n週間 + %n 週間 %n month(s) - %nヶ月 + %n ヶ月 1 year @@ -1574,7 +1574,7 @@ Do you want to merge your changes? Private key - 私有鍵 + 秘密鍵 External file @@ -1641,7 +1641,7 @@ Do you want to merge your changes? EditGroupWidgetMain Name - グループ名 + 名前 Notes @@ -1899,7 +1899,7 @@ This may cause the affected plugins to malfunction. Ref: Reference abbreviation - 参照: + 参照: Group @@ -2583,33 +2583,6 @@ This is a one-way migration. You won't be able to open the imported databas エントリーのフィールドタイプが不正です - - KeePass2 - - AES: 256-bit - AES: 256 ビット - - - Twofish: 256-bit - Twofish: 256 ビット - - - ChaCha20: 256-bit - ChaCha20: 256 ビット - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – 推奨) - - Main @@ -2932,11 +2905,11 @@ This version is not meant for production use. Corrupted key file, reading private key failed - キーファイルが破損しているため私有鍵の読み取りに失敗しました + キーファイルが破損しているため秘密鍵の読み取りに失敗しました No private key payload to decrypt - 復号する私有鍵のペイロードがありません + 復号する秘密鍵のデータがありません Trying to run KDF without cipher @@ -2960,7 +2933,7 @@ This version is not meant for production use. Unexpected EOF while reading private key - 私有鍵の読み取り中に予期しない EOF がありました + 秘密鍵の読み取り中に予期しない EOF がありました Can't write public key as it is empty @@ -2972,11 +2945,11 @@ This version is not meant for production use. Can't write private key as it is empty - 私有鍵が空のため書き込めません + 秘密鍵が空欄のままでは保存できません Unexpected EOF when writing private key - 私有鍵の書き込み中に予期しない EOF がありました + 秘密鍵の書き込み中に予期しない EOF がありました Unsupported key type: %1 @@ -3518,6 +3491,30 @@ Available commands: missing closing quote 閉じ引用符がありません + + AES: 256-bit + AES: 256 ビット + + + Twofish: 256-bit + Twofish: 256 ビット + + + ChaCha20: 256-bit + ChaCha20: 256 ビット + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – 推奨) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group グループ @@ -3625,15 +3622,15 @@ Available commands: Error writing to underlying device: - 基本デバイスへの書き込み時にエラーが発生しました: + 基本デバイスへの書き込み時にエラーが発生しました: Error opening underlying device: - 基本デバイスを開く時にエラーが発生しました: + 基本デバイスを開く際にエラーが発生しました: Error reading data from underlying device: - 基本デバイスから読み込み時にエラーが発生しました: + 基本デバイスから読み込み時にエラーが発生しました: Internal zlib error when decompressing: diff --git a/share/translations/keepassx_ko.ts b/share/translations/keepassx_ko.ts index e7522e572..6b7bfc849 100644 --- a/share/translations/keepassx_ko.ts +++ b/share/translations/keepassx_ko.ts @@ -11,7 +11,7 @@ Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> - <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> 사이트에 버그를 보고해 주십시오 + <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> 사이트에 버그를 보고해 주십시오 KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. @@ -447,7 +447,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption key(s) from KeePassXC settings. - + KeePassXC 설정에서 암호화 키 %n개를 삭제했습니다. Removing stored permissions… @@ -463,7 +463,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entry(s). - + 항목 %n개에 저장된 권한을 삭제했습니다. KeePassXC: No entry with permissions found! @@ -655,7 +655,7 @@ Please consider generating a new key file. Original data: - 원본 데이터: + 원본 데이터: Error(s) detected in CSV file ! @@ -1144,7 +1144,7 @@ Disable safe saves and try again? Unable to calculate master key - 마스터 키를 계산할 수 없음 + 마스터 키를 계산할 수 없습니다 No current database. @@ -1815,7 +1815,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + 첨부 항목 %n개를 삭제하시겠습니까? Confirm Remove @@ -2480,7 +2480,7 @@ This is a one-way migration. You won't be able to open the imported databas Unable to calculate master key - 마스터 키를 계산할 수 없음 + 마스터 키를 계산할 수 없습니다 Wrong key or database file is corrupt. @@ -2579,33 +2579,6 @@ This is a one-way migration. You won't be able to open the imported databas 잘못된 항목 필드 크기 - - KeePass2 - - AES: 256-bit - AES: 256비트 - - - Twofish: 256-bit - Twofish: 256비트 - - - ChaCha20: 256-bit - ChaCha20: 256비트 - - - AES-KDF (KDBX 4) - AES-KDF(KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF(KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2(KDBX 4 – 추천) - - Main @@ -3514,6 +3487,30 @@ Available commands: missing closing quote 닫는 따옴표 없음 + + AES: 256-bit + AES: 256비트 + + + Twofish: 256-bit + Twofish: 256비트 + + + ChaCha20: 256-bit + ChaCha20: 256비트 + + + Argon2 (KDBX 4 – recommended) + Argon2(KDBX 4 – 추천) + + + AES-KDF (KDBX 4) + AES-KDF(KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF(KDBX 3.1) + Group 그룹 @@ -3616,23 +3613,23 @@ Available commands: QtIOCompressor Internal zlib error when compressing: - 압축 중 내부 zlib 오류 발생: + 압축 중 내부 zlib 오류 발생: Error writing to underlying device: - 장치에 기록하는 중 오류 발생: + 장치에 기록하는 중 오류 발생: Error opening underlying device: - 장치를 여는 중 오류 발생: + 장치를 여는 중 오류 발생: Error reading data from underlying device: - 장치에서 읽는 중 오류 발생: + 장치에서 읽는 중 오류 발생: Internal zlib error when decompressing: - 압축 푸는 중 내부 zlib 오류 발생: + 압축 푸는 중 내부 zlib 오류 발생: @@ -4031,7 +4028,7 @@ Please unlock the selected database or choose another one which is unlocked. Expires in - 만료 시간: + 만료 시간 seconds diff --git a/share/translations/keepassx_lt.ts b/share/translations/keepassx_lt.ts index 03be1edb5..cc8545195 100644 --- a/share/translations/keepassx_lt.ts +++ b/share/translations/keepassx_lt.ts @@ -45,7 +45,7 @@ Revision: %1 - Revizija: %1 + Poversijis: %1 Distribution: %1 @@ -445,7 +445,7 @@ Prašome atrakinti pasirinktą duomenų bazę arba pasirinkti kitą, kuri būtų Successfully removed %n encryption key(s) from KeePassXC settings. - + %n šifravimo raktas sėkmingai pašalintas iš KeePassXC nustatymų.%n šifravimo raktai sėkmingai pašalinti iš KeePassXC nustatymų.%n šifravimo raktų sėkmingai pašalinta iš KeePassXC nustatymų.%n šifravimo raktų sėkmingai pašalinta iš KeePassXC nustatymų. Removing stored permissions… @@ -568,7 +568,7 @@ Please consider generating a new key file. Append ' - Clone' to title - Pridėti prie antraštės " - Dublikatas" + Pridėti prie pavadinimo " - Dublikatas" Replace username and password with references @@ -848,12 +848,12 @@ If you keep this number, your database may be too easy to crack! MiB Abbreviation for Mebibytes (KDF settings) - + MiB MiB MiB MiB thread(s) Threads for parallel execution (KDF settings) - + gija gijos gijų gijų @@ -987,7 +987,7 @@ If you keep this number, your database may be too easy to crack! Open KeePass 1 database - Atverti KeePass 1 duomenų bazę + Atverkite KeePass 1 duomenų bazę KeePass 1 database @@ -995,12 +995,12 @@ If you keep this number, your database may be too easy to crack! Close? - Uždaryti? + Užverti? "%1" is in edit mode. Discard changes and close anyway? - "%1" yra taisymo veiksenoje. + "%1" yra redagavimo veiksenoje. Vis tiek atmesti pakeitimus ir užverti? @@ -1048,7 +1048,7 @@ Save changes? Can't lock the database as you are currently editing it. Please press cancel to finish your changes or discard them. - Nepavyksta užrakinti duomenų bazės, kadangi šiuo metu ją taisote. + Nepavyksta užrakinti duomenų bazės, kadangi šiuo metu ją redaguojate. Spauskite atšaukti, kad užbaigtumėte savo pakeitimus arba juos atmestumėte. @@ -1109,7 +1109,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - Ar tikrai norite perkelti %n įrašą į šiukšlinę?Ar tikrai norite perkelti %n įrašus į šiukšlinę?Ar tikrai norite perkelti %n įrašų į šiukšlinę?Ar tikrai norite perkelti %n įrašų į šiukšlinę? + Execute command? @@ -1325,7 +1325,7 @@ Ar norite sulieti savo pakeitimus? Edit entry - Taisyti įrašą + Keisti įrašą Different passwords supplied. @@ -1608,7 +1608,7 @@ Ar norite sulieti savo pakeitimus? Edit group - Taisyti grupę + Keisti grupę Enable @@ -1804,7 +1804,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + Ar tikrai norite pašalinti %n priedą?Ar tikrai norite pašalinti %n priedus?Ar tikrai norite pašalinti %n priedų?Ar tikrai norite pašalinti %n priedų? Confirm Remove @@ -2568,33 +2568,6 @@ Tai yra vienakryptis perkėlimas. Jūs negalėsite atverti importuotos duomenų Neteisingas įrašo lauko tipas - - KeePass2 - - AES: 256-bit - AES: 256 bitų - - - Twofish: 256-bit - Twofish: 256 bitų - - - ChaCha20: 256-bit - ChaCha20: 256 bitų - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – rekomenduojama) - - Main @@ -2774,7 +2747,7 @@ Tai yra vienakryptis perkėlimas. Jūs negalėsite atverti importuotos duomenų Copy title to clipboard - Kopijuoti antraštę į iškarpinę + Kopijuoti pavadinimą į iškarpinę &URL @@ -3357,7 +3330,7 @@ Naudojamas numatytasis prievadas 19455. Timeout in seconds before clearing the clipboard. - Laiko limitas, sekundėmis, prieš išvalant iškarpinę. + Skirtas laikas, sekundėmis, prieš išvalant iškarpinę. Edit an entry. @@ -3365,7 +3338,7 @@ Naudojamas numatytasis prievadas 19455. Title for the entry. - Įrašo antraštė. + Įrašo pavadinimas. title @@ -3466,7 +3439,7 @@ Prieinamos komandos: Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given. - Požymių, kuriuos rodyti, pavadinimai. Ši parinktis gali būti nurodyta daugiau nei vieną kartą, kiekvienoje eilutėje nurodyta tvarka rodant po atskirą požymį. Jei nėra nurodyti jokie požymiai, bus nurodyta numatytųjų požymių santrauka. + attribute @@ -3498,6 +3471,30 @@ Prieinamos komandos: missing closing quote trūksta užveriamosios kabutės + + AES: 256-bit + AES: 256 bitų + + + Twofish: 256-bit + Twofish: 256 bitų + + + ChaCha20: 256-bit + ChaCha20: 256 bitų + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – rekomenduojama) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Grupė @@ -3702,7 +3699,7 @@ Prašome atrakinti pasirinktą duomenų bazę arba pasirinkti kitą, kuri būtų Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - %n šifravimo raktas sėkmingai pašalintas iš KeePassX/Http nustatymų.%n šifravimo raktai sėkmingai pašalinti iš KeePassX/Http nustatymų.%n šifravimo raktų sėkmingai pašalinta iš KeePassX/Http nustatymų.%n šifravimo raktų sėkmingai pašalinta iš KeePassX/Http nustatymų. + KeePassXC: No keys found @@ -3734,7 +3731,7 @@ Prašome atrakinti pasirinktą duomenų bazę arba pasirinkti kitą, kuri būtų Successfully removed permissions from %n entries. - Leidimai sėkmingai pašalinti iš %n įrašo.Leidimai sėkmingai pašalinti iš %n įrašų.Leidimai sėkmingai pašalinti iš %n įrašų.Leidimai sėkmingai pašalinti iš %n įrašų. + KeePassXC: No entry with permissions found! diff --git a/share/translations/keepassx_nb.ts b/share/translations/keepassx_nb.ts index 854ad820b..b83e5b2ee 100644 --- a/share/translations/keepassx_nb.ts +++ b/share/translations/keepassx_nb.ts @@ -23,11 +23,11 @@ <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> - <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Se bidrag på GitHub</a> + <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Se Bidrag på GitHub</a> Debug Info - Feilsøkingsinformasjon + Debuggingsinfo Include the following information whenever you report a bug: @@ -35,7 +35,7 @@ Copy to clipboard - Kopier til utklippstavla + Kopier til utklippstavle Version %1 @@ -60,7 +60,7 @@ CPU architecture: %2 Kernel: %3 %4 Operativsystem: %1 -CPU-arkitektur: %2 +CPU arkitektur: %2 Kjerne: %3 %4 @@ -69,7 +69,7 @@ Kjerne: %3 %4 Project Maintainers: - Prosjektets vedlikeholdere: + Prosjektets Vedlikeholdere: Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. @@ -86,7 +86,7 @@ Kjerne: %3 %4 AccessControlDialog KeePassXC HTTP Confirm Access - KeePassXC HTTP: Bekreft tilgang + KeePassXC HTTP Bekreft Tilgang Remember this decision @@ -118,7 +118,7 @@ Velg om du vil gi tilgang eller ikke. AutoType Couldn't find an entry that matches the window title: - Finner ingen oppføring som samsvarer med vindustittelen: + Kunne ikke finne en oppføring som samsvarer med vindutittelen: Auto-Type - KeePassXC @@ -153,7 +153,7 @@ Velg om du vil gi tilgang eller ikke. Sequence - Rekkefølge + Sekvens Default sequence @@ -227,15 +227,15 @@ Velg om du vil gi tilgang eller ikke. Enable KeepassXC browser integration - Slå på integrasjon for nettlesere + Slå på nettlesertillegget General - Generell + Generelt Enable integration for these browsers: - Slå på integrasjon for disse nettleserne: + Bruk tillegget i disse nettleserne: &Google Chrome @@ -329,7 +329,7 @@ Velg om du vil gi tilgang eller ikke. Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup. - Oppdaterer KeePassXC eller keepassxc-proxy binær-sti automatisk til innebygd meldings-skript ved oppstart. + Oppdaterer automatisk KeePassXC eller sti til binær keepassxc-proxy til lokale meldings-skript ved oppstart. Update &native messaging manifest files at startup @@ -367,7 +367,7 @@ Velg om du vil gi tilgang eller ikke. Executable Files (*) - Kørbare filer (*) + Programfiler (*) Select custom proxy location @@ -375,7 +375,7 @@ Velg om du vil gi tilgang eller ikke. We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment. - KeePassXC sin nettleserintegrasjon er ikke tilgjengelig for «Snap»-utgaver enda. + Nettlesertillegget er foreløpig ikke tilgjengelig for «Snap»-utgaver. @@ -490,7 +490,7 @@ Lås opp valgt database eller velg en annen som er åpen. &Key file - &Nøkkelfil + Nø&kkelfil Browse @@ -561,7 +561,7 @@ Vurder å opprette en ny nøkkelfil. Changing master key failed: no YubiKey inserted. - Klonevalg + Endring av hovednøkkel feilet: YubiKey ikke satt inn @@ -572,7 +572,7 @@ Vurder å opprette en ny nøkkelfil. Append ' - Clone' to title - Tilføy ' - Clone' til tittel + Tilføy ' - Klone' til tittel Replace username and password with references @@ -643,7 +643,7 @@ Vurder å opprette en ny nøkkelfil. Empty fieldname - Tomt filnavn + Tomt feltnavn column @@ -717,7 +717,7 @@ Vurder å opprette en ny nøkkelfil. Browse - Bla gjennom + Bla Refresh @@ -729,11 +729,11 @@ Vurder å opprette en ny nøkkelfil. Unable to open the database. - Klarte ikke åpne databasen. + Kunne ikke åpne databasen. Can't open key file - Klarte ikke åpne nøkkelfilen. + Kan ikke åpne nøkkelfil Legacy key file format @@ -777,11 +777,11 @@ Vurder å opprette en ny nøkkelfil. Can't open key file - Klarte ikke åpne nøkkelfilen. + Kan ikke åpne nøkkelfil Unable to open the database. - Klarte ikke åpne databasen. + Kunne ikke åpne databasen. Database opened fine. Nothing to do. @@ -799,14 +799,14 @@ Du kan nå lagre den. Unable to repair the database. - Klarte ikke reparere databasen. + Kan ikke fikse databasen. DatabaseSettingsWidget General - Generelt + Generell Encryption @@ -823,7 +823,7 @@ Du kan nå lagre den. If you keep this number, your database may take hours or days (or even longer) to open! Du bruker et svært høyt antall nøkkeltransformasjons-runder med Argon2. -Dersom du beholder dette tallet så vil det ta timer eller dager (og kanskje lengre) å åpne databasen! +Dersom du beholder dette antallet så vil det ta timer eller dager (og kanskje lengre) å åpne databasen! Understood, keep number @@ -869,7 +869,7 @@ Dersom du beholder dette antallet så kan databasen være for lett å knekke!DatabaseSettingsWidgetEncryption Encryption Algorithm: - Krypteringsalgoritme + Krypteringsalgoritme: AES: 256 Bit (default) @@ -920,15 +920,15 @@ Dersom du beholder dette antallet så kan databasen være for lett å knekke! History Settings - Historie-innstillinger + Historikk-innstillinger Max. history items: - Maks. historisk antall: + Maks. historikk-antall: Max. history size: - Maks. historisk størrelse: + Maks. historikk-størrelse: MiB @@ -1057,8 +1057,8 @@ Lagre endringer? Can't lock the database as you are currently editing it. Please press cancel to finish your changes or discard them. - Kan ikke låse database fordi du redigerer den. -Klikk avbryt for å fullføre endringene eller forkaste dem. + Kan ikke låse databasen fordi du redigerer den. +Klikk Avbryt for å fullføre eller forkaste endringene. This database has been modified. @@ -1103,7 +1103,7 @@ Deaktivere sikker lagring og prøve igjen? Do you really want to delete %1 entries for good? - Vil du virkelig slette %1 oppføringer for godt? + Ønsker du virkelig å slette %1 oppføringer for godt? Move entry to recycle bin? @@ -1135,7 +1135,7 @@ Deaktivere sikker lagring og prøve igjen? Delete group? - Slett gruppe + Slett gruppe? Do you really want to delete the group "%1" for good? @@ -1212,7 +1212,7 @@ Vil du slå sammen fila med endringene dine? URL - Adresse + URL Expiration @@ -1248,7 +1248,7 @@ Vil du slå sammen fila med endringene dine? Sequence - Rekkefølge + Sekvens Search @@ -1299,7 +1299,7 @@ Vil du slå sammen fila med endringene dine? History - Historikk + Historie SSH Agent @@ -1379,11 +1379,11 @@ Vil du slå sammen fila med endringene dine? Apply generated password? - Bruk generert passord? + Vil du bruke det lagde passordet? Do you want to apply the generated password to this entry? - Vil du bruke det genererte passordet for denne oppføringen? + Vil du bruke det lagde passordet for denne oppføringen? Entry updated successfully. @@ -1433,7 +1433,7 @@ Vil du slå sammen fila med endringene dine? EditEntryWidgetAutoType Enable Auto-Type for this entry - Slå på Autoskriv for denne oppføringa + Aktiver Autoskriv for denne oppføringa Inherit default Auto-Type sequence from the &group @@ -1487,7 +1487,7 @@ Vil du slå sammen fila med endringene dine? EditEntryWidgetMain URL: - Adresse: + URL: Password: @@ -1566,7 +1566,7 @@ Vil du slå sammen fila med endringene dine? Copy to clipboard - Kopier til utklippstavla + Kopier til utklippstavle Private key @@ -1657,18 +1657,18 @@ Vil du slå sammen fila med endringene dine? &Use default Auto-Type sequence of parent group - Br&uk standard Autoskriv-sekvens fra foreldre-gruppa + &Bruk standard Autoskriv-sekvens fra foreldre-gruppa Set default Auto-Type se&quence - Angi standard Autoskriv-&sekvens + &Angi standard Autoskriv-sekvens EditWidgetIcons &Use default icon - &Bruk et standardikon + &Bruk standard ikon Use custo&m icon @@ -1680,7 +1680,7 @@ Vil du slå sammen fila med endringene dine? Delete custom icon - Fjern selvvalgt ikon + Slett selvvalgt ikon Download favicon @@ -1887,7 +1887,7 @@ Dette kan føre til feil for de berørte programtilleggene. URL - Adresse + URL @@ -1911,7 +1911,7 @@ Dette kan føre til feil for de berørte programtilleggene. URL - Adresse + URL Never @@ -1954,11 +1954,11 @@ Dette kan føre til feil for de berørte programtilleggene. Hide Usernames - Skjul brukernavn + Masker brukernavn Hide Passwords - Skjul passord + Masker passord Fit to window @@ -1970,7 +1970,7 @@ Dette kan føre til feil for de berørte programtilleggene. Reset to defaults - Tilbakestill til standardinnstillinger + Resette til standard Attachments (icon) @@ -2091,7 +2091,7 @@ Dette kan føre til feil for de berørte programtilleggene. Kdbx4Reader missing database headers - Manglende database-headere. + manglende database-headere Unable to calculate master key @@ -2115,15 +2115,15 @@ Dette kan føre til feil for de berørte programtilleggene. Invalid header id size - Ugyldig størrelse: header-id + Ugyldig: Header id size Invalid header field length - Ugyldig lengde: header-felt + Ugyldig: Header field length Invalid header data length - Ugyldig lengde: header-data + Ugyldig: Header data length Failed to open buffer for KDF parameters in header @@ -2139,15 +2139,15 @@ Dette kan føre til feil for de berørte programtilleggene. Invalid inner header id size - Ugyldig størrelse: indre header-id + Ugyldig: Inner header id size Invalid inner header field length - Ugyldig lengde: indre header-felt + Ugyldig: Inner header field length Invalid inner header binary size - Ugyldig størrelse: binary inner header + Ugyldig: Inner header binary size Unsupported KeePass variant map version. @@ -2376,7 +2376,7 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Duplicate attachment found - Duplikat: Attachment found + Duplikat: Attachment Entry binary key or value missing @@ -2424,7 +2424,7 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Unable to open the database. - Kunne ikke åpne databasen. + Kan ikke åpne databasen. @@ -2452,11 +2452,11 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Invalid number of groups - Ugyldig: Number of groups + Ugyldig antall grupper Invalid number of entries - Ugyldig: Number of entries + Ugyldig antall oppføringer Invalid content hash size @@ -2504,11 +2504,11 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Incorrect group id field size - Stemmer ikke: Group id field size + Feil: Group id field size Incorrect group creation time field size - Stemmer ikke: Group creation time field size + Feil: Group creation time field size Incorrect group modification time field size @@ -2532,19 +2532,19 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Invalid group field type - Ugyldig felttype: gruppe + Ugyldig: Group field type Missing group id or level - Manglende gruppe-id eller nivå + Mangler: group id or level Missing entry field type number - Manglende felt-type i oppføring: nummer + Mangler: Entry field type number Invalid entry field size - Ugyldig felt-størrelse i oppføring + Ugyldig: Entry field size Read entry field data doesn't match size @@ -2552,58 +2552,31 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Invalid entry uuid field size - Ugyldig felt-størrelse i oppføring: uuid + Ugyldig: Entry uuid field size Invalid entry group id field size - Ugyldig felt-størrelse i oppføring: gruppe-id + Ugyldig: Entry group id field size Invalid entry icon field size - Ugyldig felt-størrelse i oppføring: ikon + Ugyldig: Entry icon field size Invalid entry creation time field size - Ugyldig felt-størrelse i oppføring: opprettelsestidspunkt + Ugyldig: Entry creation time field size Invalid entry modification time field size - Ugyldig felt-størrelse i oppføring: endringstidspunkt + Ugyldig: Entry modification time field size Invalid entry expiry time field size - Ugyldig felt-størrelse i oppføring: utløpstidspunkt + Ugyldig: Entry expiry time field size Invalid entry field type - Ugyldig felt-type i oppføring - - - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – anbefalt) + Ugyldig: Entry field type @@ -2721,11 +2694,11 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Sa&ve database as... - La&gre database som... + Lag&re database som... Change &master key... - Endre &hovednøkkel + Endre &hovednøkkel... &Database settings @@ -2773,7 +2746,7 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de &Open URL - Åpne &nettadresse + Åpne &URL &Lock databases @@ -2817,7 +2790,7 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Re&pair database... - &Reparer database + Re&parer database... Show TOTP @@ -2841,7 +2814,7 @@ Dette er en en-veis-migrasjon. Du kan ikke åpne den importerte databasen med de Access error for config file %1 - Feil ved tilgang for filen %1 + Feil ved tilgang for konfigurasjonsfilen %1 <p>It looks like you are using KeePassHTTP for browser integration. This feature has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a> (warning %1 of 3).</p> @@ -2984,7 +2957,7 @@ Denne versjonen er ikke ment for produksjonsmiljø. Cipher IV is too short for MD5 kdf - Cipher IV er for kort for MD5 kdf + Krypterings-IV er for kort for MD5 kdf Unknown KDF: %1 @@ -3068,7 +3041,7 @@ Denne versjonen er ikke ment for produksjonsmiljø. Always allow &updating entries - Alltid tillat å oppdatere oppføringer + Tillat alltid å &oppdatere oppføringer Only the selected database has to be connected with a client. @@ -3104,7 +3077,7 @@ Denne versjonen er ikke ment for produksjonsmiljø. <p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p> - <p>KeePassHTTP er blitt gammeldags og vil bli fjerna i framtida.<br>Vær vennlig å bytt til KeePassXC-nettleseren isteden! For hjelp med overgangen, besøk vår <a href="https://keepassxc.org/docs/keepassxc-browser-migration">overgangs-håndbok</a>.</p> + <p>KeePassHTTP er blitt gammeldags og vil bli fjerna i framtida.<br>Vennligst bytt til KeePassXC-nettleseren isteden! For hjelp med overgangen, besøk vår <a href="https://keepassxc.org/docs/keepassxc-browser-migration">overgangs-håndbok</a>.</p> Cannot bind to privileged ports @@ -3170,7 +3143,7 @@ Bruker standard port 19455. Pick characters from every group - Bruk tegn fra hver gruppe + Velg tegn fra hver gruppe &Length: @@ -3178,7 +3151,7 @@ Bruker standard port 19455. Passphrase - Adgangsfrase + Passordfrase Wordlist: @@ -3190,11 +3163,11 @@ Bruker standard port 19455. Word Separator: - Skilleord: + Ord-skilletegn: Generate - Generer + Lag passord Copy @@ -3233,7 +3206,7 @@ Bruker standard port 19455. Good Password quality - God + Bra Excellent @@ -3329,7 +3302,7 @@ Bruker standard port 19455. username - brukernamn + brukernavn URL for the entry. @@ -3345,11 +3318,11 @@ Bruker standard port 19455. Generate a password for the entry. - Generer et passord til oppføringen. + Generer et passord til oppføringa. Length for the generated password. - Lengde for det genererte passordet. + Lengde på det genererte passordet. length @@ -3390,7 +3363,7 @@ Bruker standard port 19455. Estimate the entropy of a password. - Beregn entropien til pasordet. + Beregn entropien til et passord. Password for which to estimate the entropy. @@ -3481,7 +3454,7 @@ Tilgjengelige kommandoer: Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given. - Navn på attributtene som skal vises. Dette alternativet kan spesifiseres mer enn en gang, med hvert attributt vist ett per linje i den oppgitte rekkefølgen. Hvis ingen attributter er oppgitt, er det gitt en oppsummering av standardattributtene. + Navn på attributtene som skal vises. Dette alternativet kan spesifiseres mer enn en gang, med hvert attributt vist ett per linje i den oppgitte rekkefølgen. Hvis ingen attributter er oppgitt, blir det gitt en oppsummering av standardattributtene. attribute @@ -3513,6 +3486,30 @@ Tilgjengelige kommandoer: missing closing quote Manglende avsluttende anførselstegn + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – anbefalt) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Gruppe @@ -3535,7 +3532,7 @@ Tilgjengelige kommandoer: Last Modified - Sist endret + Sist endra Created @@ -3585,7 +3582,7 @@ Tilgjengelige kommandoer: Generate a new random password. - Lag et nytt tilfeldig passord. + Generer et nytt tilfeldig passord. Length of the generated password. @@ -3609,7 +3606,7 @@ Tilgjengelige kommandoer: Use extended ASCII in the generated password. - Bruk utvida ASCII i det genererte passordet. + Bruk utvida ASCII i det lagde passordet. @@ -3724,7 +3721,7 @@ Lås opp valgt database eller velg en annen som er åpen. No shared encryption-keys found in KeePassHttp Settings. - Ingen delte krypteringsnøkler funnet i KeePassHttp-innstillinger + Ingen delte krypteringsnøkler funnet i KeePassHttp-oppføringa. KeePassXC: Settings not available! @@ -3732,7 +3729,7 @@ Lås opp valgt database eller velg en annen som er åpen. The active database does not contain an entry of KeePassHttp Settings. - Den aktive databasen mangler oppføring for KeePassHttp-innstillinger. + Den aktive databasen mangler KeePassHttp-oppføring. Removing stored permissions... @@ -3748,7 +3745,7 @@ Lås opp valgt database eller velg en annen som er åpen. Successfully removed permissions from %n entries. - Fjernet tillatelser fra %n oppføringer.Fjernet tillatelser fra %n oppføringer. + Fjernet tillatelser fra %n oppføring.Fjernet tillatelser fra %n oppføringer. KeePassXC: No entry with permissions found! @@ -3767,7 +3764,7 @@ Lås opp valgt database eller velg en annen som er åpen. General - Generelt + Generell Security @@ -3786,7 +3783,7 @@ Lås opp valgt database eller velg en annen som er åpen. Start only a single instance of KeePassXC - Kjør kun én instans av KeePassXC om gangen + Tillat kun én instans av KeePassXC om gangen Remember last databases @@ -3806,7 +3803,7 @@ Lås opp valgt database eller velg en annen som er åpen. Automatically save after every change - Lagre automatisk etter enhver endring + Lagre automatisk etter hver endring Automatically reload the database when modified externally @@ -3814,7 +3811,7 @@ Lås opp valgt database eller velg en annen som er åpen. Minimize when copying to clipboard - Minimer ved kopiering til utklippstavla + Minimer ved kopi til utklippstavla Minimize window at application startup @@ -3838,15 +3835,15 @@ Lås opp valgt database eller velg en annen som er åpen. Hide window to system tray when minimized - Skjul vindu til systemstatusfelt når minimert + Skjul vindu til systemkurven når minimert Hide window to system tray instead of app exit - Skjul vindu til systemstatusfelt i stedet for app-avslutning + Skjul vindu til systemkurven i stedet for app-avslutning Dark system tray icon - Mørkt ikon i systemkurven + Mørkt systemkurvikon Language @@ -3899,7 +3896,7 @@ Lås opp valgt database eller velg en annen som er åpen. Entry Management - Oppførings-administrasjon + Oppføringshåndtering General @@ -3978,7 +3975,7 @@ Lås opp valgt database eller velg en annen som er åpen. Default RFC 6238 token settings - Standard RFC 6238 token innstillinger + Standard RFC 6238 token-innstillinger Steam token settings @@ -4060,11 +4057,11 @@ Lås opp valgt database eller velg en annen som er åpen. Import from KeePass 1 - Importer fra KeePass 1 + Importer KeePass 1-database Import from CSV - Importer fra CSV + Importer fra CSV-fil Recent databases @@ -4107,11 +4104,11 @@ Lås opp valgt database eller velg en annen som er åpen. read password of the database from stdin - Les database-passord fra stdin + Les database-passord fra standard input Parent window handle - Adresse til foreldre-vindu + Foreldre-vindu handle \ No newline at end of file diff --git a/share/translations/keepassx_nl_NL.ts b/share/translations/keepassx_nl_NL.ts index 15bd21554..cac860f97 100644 --- a/share/translations/keepassx_nl_NL.ts +++ b/share/translations/keepassx_nl_NL.ts @@ -503,7 +503,7 @@ Ontgrendel de geselecteerde database of kies een ontgrendelde database. Cha&llenge Response - Test - Antwoord + Cha&llenge/response Refresh @@ -727,7 +727,7 @@ Overweeg a.u.b. een nieuw sleutelbestand te genereren. Challenge Response: - Test - Resultaat: + Challenge/response: Unable to open the database. @@ -1097,7 +1097,7 @@ Veilig opslaan afschakelen en opnieuw proberen? Do you really want to delete the entry "%1" for good? - Weet je zeker dat je het item "%1" wil verwijderen? + Weet je zeker dat je het item "%1" definitief wil verwijderen? Delete entries? @@ -1361,7 +1361,7 @@ Wil je de wijzigingen samenvoegen? Press reveal to view or edit - Druk Weergeven om te bekijken of bewerken + Druk Tonen om te bekijken of bewerken Tomorrow @@ -1416,7 +1416,7 @@ Wil je de wijzigingen samenvoegen? Reveal - Weergeven + Tonen Attachments @@ -1470,7 +1470,7 @@ Wil je de wijzigingen samenvoegen? EditEntryWidgetHistory Show - Weergeven + Tonen Restore @@ -1513,7 +1513,7 @@ Wil je de wijzigingen samenvoegen? Toggle the checkbox to reveal the notes section. - Selecteer om notities weer te geven. + Schakelen aan om notities te tonen. Username: @@ -2071,7 +2071,7 @@ Hierdoor werken de plugins mogelijk niet meer goed. Unable to issue challenge-response. - Kan Test-Resultaat niet uitgeven. + Kan challenge/response niet uitvoeren. Wrong key or database file is corrupt. @@ -2082,7 +2082,7 @@ Hierdoor werken de plugins mogelijk niet meer goed. Kdbx3Writer Unable to issue challenge-response. - Kan Test-Resultaat niet uitgeven. + Kan challenge/response niet uitvoeren. Unable to calculate master key @@ -2581,33 +2581,6 @@ Deze actie is niet omkeerbaar. Je kunt de geïmporteerde database niet meer open Ongeldig item veldtype - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 - aanbevolen) - - Main @@ -3483,7 +3456,7 @@ Beschikbare opdrachten: Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given. - Namen van de weer te geven kenmerken. Deze optie kan meer dan eens worden opgegeven, waarbij elk kenmerk op een regel wordt getoond in de opgegeven volgorde. Als er geen kenmerken worden opgegeven, wordt een samenvatting van de standaardkenmerken gegeven. + Namen van de te tonen kenmerken. Deze optie kan meer dan eens worden opgegeven, waarbij elk kenmerk op een regel wordt getoond in de opgegeven volgorde. Als er geen kenmerken worden opgegeven, wordt een samenvatting van de standaardkenmerken gegeven. attribute @@ -3515,6 +3488,30 @@ Beschikbare opdrachten: missing closing quote afsluitend aanhalingsteken ontbreekt + + AES: 256-bit + AES: 256-bits + + + Twofish: 256-bit + Twofish: 256-bits + + + ChaCha20: 256-bit + ChaCha20: 256-bits + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – aanbevolen) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Groep @@ -3553,7 +3550,7 @@ Beschikbare opdrachten: YubiKey[%1] Challenge Response - Slot %2 - %3 - YubiKey[%1] Test - Resultaat - Slot %2 - %3 + YubiKey[%1] challenge/response - slot %2 - %3 Press @@ -3837,7 +3834,7 @@ Ontgrendel de geselecteerde database of kies een ontgrendelde database. Show a system tray icon - Icoon in het systeemvak weergeven + Icoon in het systeemvak tonen Hide window to system tray when minimized diff --git a/share/translations/keepassx_pl.ts b/share/translations/keepassx_pl.ts index 49a1e633b..d50418675 100644 --- a/share/translations/keepassx_pl.ts +++ b/share/translations/keepassx_pl.ts @@ -31,7 +31,7 @@ Include the following information whenever you report a bug: - Uwzględnij następujące informacje, gdy zgłaszasz błąd: + Przy zgłaszaniu błędów uwzględnij następujące informacje: Copy to clipboard @@ -60,7 +60,7 @@ CPU architecture: %2 Kernel: %3 %4 System operacyjny: %1 -Architektura CPU: %2 +Architektura procesora: %2 Jądro: %3 %4 @@ -118,31 +118,31 @@ Wybierz, czy chcesz zezwolić na dostęp. AutoType Couldn't find an entry that matches the window title: - Nie mogę znaleźć wpisu, który by pasował do tytułu okna: + Nie znaleziono wpisu pasującego do nazwy okna: Auto-Type - KeePassXC - Auto-uzupełnianie - KeePassXC + Autouzupełnianie - KeePassXC Auto-Type - Auto-uzupełnianie + Autouzupełnianie The Syntax of your Auto-Type statement is incorrect! - Składnia instrukcji auto-uzupełniania jest niepoprawna! + Składnia instrukcji autouzupełniania jest niepoprawna! This Auto-Type command contains a very long delay. Do you really want to proceed? - Polecenie auto-uzupełniania zawiera bardzo długie opóźnienie. Czy chcesz kontynuować? + Polecenie autouzupełniania zawiera bardzo długie opóźnienie. Czy chcesz kontynuować? This Auto-Type command contains very slow key presses. Do you really want to proceed? - Polecenie auto-uzupełniania zawiera bardzo wolne wciśnięcia klawiszy. Czy chcesz kontynuować? + Polecenie autouzupełniania zawiera bardzo wolne wciśnięcia klawiszy. Czy chcesz kontynuować? This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed? - Polecenie auto-uzupełniania zawiera argumenty, które powtarzają się bardzo często. Czy chcesz kontynuować? + Polecenie autouzupełniania zawiera argumenty, które powtarzają się bardzo często. Czy chcesz kontynuować? @@ -183,11 +183,11 @@ Wybierz, czy chcesz zezwolić na dostęp. AutoTypeSelectDialog Auto-Type - KeePassXC - Auto-uzupełnianie - KeePassXC + Autouzupełnianie - KeePassXC Select entry to Auto-Type: - Wybierz wpis do auto-uzupełniania: + Wybierz wpis do autouzupełnienia: @@ -223,7 +223,7 @@ Wybierz, czy chcesz zezwolić na dostęp. This is required for accessing your databases with KeePassXC-Browser - Wymagane jest to w celu uzyskania dostępu do baz danych za pomocą KeePassXC-Browser + Wymagane jest to aby uzyskać dostęp do baz danych za pomocą KeePassXC-Browser Enable KeepassXC browser integration @@ -272,7 +272,7 @@ Wybierz, czy chcesz zezwolić na dostęp. Only returns the best matches for a specific URL instead of all entries for the whole domain. - Zwracaj tylko najlepsze dopasowania wpisów dla URL zamiast wszystkich wpisów całej domeny. + Zwracaj tylko najlepsze dopasowania wpisów dla URL zamiast wszystkich wpisów domeny. &Return only best-matching credentials @@ -341,11 +341,11 @@ Wybierz, czy chcesz zezwolić na dostęp. Use a &proxy application between KeePassXC and browser extension - Używaj aplikacj &proxy pomiędzy KeePassXC a rozszerzeniem przeglądarki + Używaj aplikację &proxy pomiędzy KeePassXC a rozszerzeniem przeglądarki Use a custom proxy location if you installed a proxy manually. - Używaj niestandardowej lokalizacji proxy, jeżeli zainstalowałeś proxy ręcznie. + Używaj niestandardowej lokalizacji proxy, jeżeli została zainstalowana ręcznie. Use a &custom proxy location @@ -464,7 +464,7 @@ Proszę odblokować wybraną bazę albo wybrać inną, która jest odblokowana.< Successfully removed permissions from %n entry(s). - Pomyślnie usunięto uprawnienia z %n wpisu.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisów. + Pomyślnie usunięto uprawnienia z %n wpisu.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisu(ów). KeePassXC: No entry with permissions found! @@ -523,11 +523,11 @@ Proszę odblokować wybraną bazę albo wybrać inną, która jest odblokowana.< Unable to create Key File : - Nie można utworzyć pliku klucza : + Nie można utworzyć pliku klucza : Select a key file - Wybierz plik z kluczem + Wybierz plik klucza Empty password @@ -556,8 +556,8 @@ Proszę odblokować wybraną bazę albo wybrać inną, która jest odblokowana.< unsupported in the future. Please consider generating a new key file. - Używasz przestarzałego formatu pliku klucza, który może być -nieobsługiwany w przyszłości. + Używasz przestarzałego formatu pliku klucza, który może nie być +obsługiwany w przyszłości. Proszę rozważyć wygenerowanie nowego pliku klucza. @@ -645,11 +645,11 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. Empty fieldname - Puste pole tytuł + Pusty tytuł pola column - kolumna + kolumna Imported from CSV file @@ -657,11 +657,11 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. Original data: - Oryginalne dane: + Oryginalne dane: Error(s) detected in CSV file ! - Wykryto błąd lub błędy w pliku CSV ! + Wykryto błąd(błędy) w pliku CSV ! more messages skipped] @@ -693,15 +693,15 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. CsvParserModel %n byte(s), - %n bajt, %n bajty, %n bajtów, %n bajt, + %n bajt, %n bajty, %n bajtów, %n bajtów, %n row(s), - %n rząd, %n rzędy, %n rzędów, %n rząd, + %n rząd, %n rzędy, %n rzędów, %n rzędów, %n column(s) - %n kolumna%n kolumny%n kolumn%n kolumna + %n kolumna%n kolumny%n kolumn%n kolumn @@ -736,7 +736,7 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. Can't open key file - Nie mogę otworzyć pliku z kluczem + Nie można otworzyć pliku klucza Legacy key file format @@ -747,8 +747,8 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. unsupported in the future. Please consider generating a new key file. - Używasz przestarzałego formatu pliku klucza, który może być -nieobsługiwany w przyszłości. + Używasz przestarzałego formatu pliku klucza, który może nie być +obsługiwany w przyszłości. Proszę rozważyć wygenerowanie nowego pliku klucza. @@ -766,7 +766,7 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. Select key file - Wybierz plik z kluczem + Wybierz plik klucza @@ -781,7 +781,7 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. Can't open key file - Nie mogę otworzyć pliku z kluczem + Nie mogę otworzyć pliku klucza Unable to open the database. @@ -799,11 +799,11 @@ Proszę rozważyć wygenerowanie nowego pliku klucza. The database has been successfully repaired You can now save it. Baza danych została naprawiona -Możesz teraz ją już zapisać. +Możesz ją teraz zapisać. Unable to repair the database. - Nie mogę naprawić bazy danych. + Nie udało się naprawić bazy danych. @@ -819,7 +819,7 @@ Możesz teraz ją już zapisać. Number of rounds too high Key transformation rounds - Liczba rund zbyt duża + Zbyt duża liczba rund You are using a very high number of key transform rounds with Argon2. @@ -840,7 +840,7 @@ Jeśli zachowasz ten numer, otworzenie twojej bazy danych może zająć kilka go Number of rounds too low Key transformation rounds - Liczba rund zbyt mała + Za mała liczba rund You are using a very low number of key transform rounds with AES-KDF. @@ -848,7 +848,7 @@ Jeśli zachowasz ten numer, otworzenie twojej bazy danych może zająć kilka go If you keep this number, your database may be too easy to crack! Używasz bardzo małej liczby rund transformacji klucza z AES-KDF. -Jeśli zachowasz ten numer, twoja baza danych może być zbyt łatwa do złamania! +Jeśli zachowasz tę liczbę, twoja baza danych może być zbyt łatwa do złamania! KDF unchanged @@ -968,7 +968,7 @@ Jeśli zachowasz ten numer, twoja baza danych może być zbyt łatwa do złamani Open database - Otwieranie bazy danych + Otwórz bazę danych File not found! @@ -984,7 +984,7 @@ Jeśli zachowasz ten numer, twoja baza danych może być zbyt łatwa do złamani Open CSV file - Otwieranie pliku CSV + Otwórz plik CSV CSV file @@ -996,11 +996,11 @@ Jeśli zachowasz ten numer, twoja baza danych może być zbyt łatwa do złamani Merge database - Połączenie baz danych + Połącz bazy danych Open KeePass 1 database - Otwieranie bazy danych KeePass 1 + Otwórz bazę danych KeePass 1 KeePass 1 database @@ -1028,7 +1028,7 @@ Zapisać zmiany? Writing the database failed. - Błąd w zapisywaniu bazy danych. + Błąd przy zapisie bazy danych. Passwords @@ -1040,7 +1040,7 @@ Zapisać zmiany? Export database to CSV file - Eksportowanie bazy danych do pliku CSV + Eksportuj bazę danych do pliku CSV Writing the CSV file failed. @@ -1069,7 +1069,7 @@ Naciśnij anuluj, aby zakończyć zmiany albo porzucić je. Do you want to save the database before locking it? Otherwise your changes are lost. Baza danych została zmodyfikowana. -Czy chcesz zapisać przed zablokowaniem jej? +Czy chcesz zapisać zmiany przed zablokowaniem? W przeciwnym wypadku zmiany zostaną porzucone. @@ -1123,7 +1123,7 @@ Wyłączyć bezpieczne zapisywanie i spróbować ponownie? Do you really want to move %n entry(s) to the recycle bin? - Czy na pewno chcesz przenieść %n wpis do kosza?Czy na pewno chcesz przenieść %n wpisów do kosza?Czy na pewno chcesz przenieść %n wpisów do kosza?Czy na pewno chcesz przenieść %n wpisów do kosza? + Czy na pewno chcesz przenieść %n wpis do kosza?Czy na pewno chcesz przenieść %n wpisy do kosza?Czy na pewno chcesz przenieść %n wpisów do kosza?Czy na pewno chcesz przenieść %n wpisów do kosza? Execute command? @@ -1371,11 +1371,11 @@ Czy chcesz połączyć twoje zmiany? %n week(s) - %n tydzień%n tygodni(e)%n tygodni(e)%n tygodni(e) + %n tydzień%n tygodnie%n tygodni%n tygodni %n month(s) - %n miesiąc%n miesiąc(e)%n miesiąc(e)%n miesiąc(e) + %n miesiąc%n miesiące%n miesięcy%n miesięcy 1 year @@ -1899,7 +1899,7 @@ Może to spowodować nieprawidłowe działanie wtyczek. Ref: Reference abbreviation - Odniesienie: + Odniesienie: Group @@ -2435,7 +2435,7 @@ Jest to migracja w jedną stronę. Nie będzie można otworzyć importowanej baz KeePass1Reader Unable to read keyfile. - Nie można otworzyć pliku z kluczem. + Nie można otworzyć pliku klucza. Not a KeePass database. @@ -2583,33 +2583,6 @@ Jest to migracja w jedną stronę. Nie będzie można otworzyć importowanej baz Nieprawidłowy typ pola wpisu - - KeePass2 - - AES: 256-bit - AES: 256-bitowy - - - Twofish: 256-bit - Twofish: 256-bitowy - - - ChaCha20: 256-bit - ChaCha20: 256-bitowy - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – zalecany) - - Main @@ -3150,7 +3123,7 @@ Używam domyślnego portu 19455. Upper Case Letters - Duże litery + Wielkie litery Lower Case Letters @@ -3353,7 +3326,7 @@ Używam domyślnego portu 19455. Length for the generated password. - Długość wygenerowanego hasła + Długość wygenerowanego hasła. length @@ -3418,7 +3391,7 @@ Używam domyślnego portu 19455. Failed to load key file %1 : %2 - Nieudane ładowanie pliku z kluczem %1 : %2 + Nieudane ładowanie pliku klucza %1 : %2 WARNING: You are using a legacy key file format which may become @@ -3454,7 +3427,7 @@ Dostępne polecenia: Find entries quickly. - Szybkie wyszukiwanie wpisów. + Szybkie wyszukiwanie wpisów. Search term. @@ -3518,6 +3491,30 @@ Dostępne polecenia: missing closing quote brak cytatu zamknięcia + + AES: 256-bit + AES: 256-bitowy + + + Twofish: 256-bit + Twofish: 256-bitowy + + + ChaCha20: 256-bit + ChaCha20: 256-bitowy + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – zalecany) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Grupa @@ -3621,34 +3618,34 @@ Dostępne polecenia: QtIOCompressor Internal zlib error when compressing: - Błąd wewnętrzny zlib podczas kompresowania: + Błąd wewnętrzny zlib podczas kompresowania: Error writing to underlying device: - Błąd w zapisie na urządzenie: + Błąd w zapisie na urządzenie: Error opening underlying device: - Błąd w otwieraniu z urządzenia: + Błąd w otwieraniu z urządzenia: Error reading data from underlying device: - Błąd w odczycie danych z urządzenia: + Błąd w odczycie danych z urządzenia: Internal zlib error when decompressing: - Błąd wewnętrzny zlib podczas dekompresowania: + Błąd wewnętrzny zlib podczas dekompresowania: QtIOCompressor::open The gzip format not supported in this version of zlib. - Format gzip nie wspierany przez tą wersję zlib. + Format gzip nie wspierany przez tę wersję zlib. Internal zlib error: - Błąd wewnętrzny zlib: + Błąd wewnętrzny zlib: @@ -3722,7 +3719,7 @@ Proszę odblokować wybraną bazę albo wybrać inną, która jest odblokowana.< Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Pomyślnie usunięto %n klucz szyfrowania z ustawień KeePassX/Http.Pomyślnie usunięto %n klucze szyfrowania z ustawień KeePassX/Http.Pomyślnie usunięto %n kluczy szyfrowania z ustawień KeePassX/Http.Pomyślnie usunięto %n klucz szyfrowania z ustawień KeePassX/Http. + Pomyślnie usunięto %n klucz szyfrowania z ustawień KeePassX/Http.Pomyślnie usunięto %n klucze szyfrowania z ustawień KeePassX/Http.Pomyślnie usunięto %n kluczy szyfrowania z ustawień KeePassX/Http.Pomyślnie usunięto %n kluczy szyfrowania z ustawień KeePassX/Http. KeePassXC: No keys found @@ -3754,7 +3751,7 @@ Proszę odblokować wybraną bazę albo wybrać inną, która jest odblokowana.< Successfully removed permissions from %n entries. - Pomyślnie usunięto uprawnienia z %n wpisu.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisu. + Pomyślnie usunięto uprawnienia z %n wpisu.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisów.Pomyślnie usunięto uprawnienia z %n wpisów. KeePassXC: No entry with permissions found! @@ -3773,7 +3770,7 @@ Proszę odblokować wybraną bazę albo wybrać inną, która jest odblokowana.< General - Główne + Ogólne Security diff --git a/share/translations/keepassx_pt_BR.ts b/share/translations/keepassx_pt_BR.ts index 1295e117d..16550f825 100644 --- a/share/translations/keepassx_pt_BR.ts +++ b/share/translations/keepassx_pt_BR.ts @@ -23,11 +23,11 @@ <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> - <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Ver colaborações no GitHub</a> + <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Ver Colaborações no GitHub</a> Debug Info - Informações de depuração + Informações de Depuração Include the following information whenever you report a bug: @@ -69,11 +69,11 @@ Kernel: %3 %4 Project Maintainers: - Mantedores do projeto: + Mantedores do Projeto: Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. - A equipe do KeePassXC agradece especialmente a debfx pela criação do KeePassX original. + A equipe KeePassXC agradece especialmente a debfx pela criação do KeePassX original. Build Type: %1 @@ -111,7 +111,7 @@ Selecione se deseja permitir o acesso. AgentSettingsWidget Enable SSH Agent (requires restart) - Habilitar agente SSH (requer reinicialização) + Habilitar Agente SSH (requer reinicialização) @@ -122,27 +122,27 @@ Selecione se deseja permitir o acesso. Auto-Type - KeePassXC - Autodigitação - KeePassXC + Auto-Digitação - KeePassXC Auto-Type - Autodigitação + Auto-Digitação The Syntax of your Auto-Type statement is incorrect! - A sintaxe da sua sequência de autodigitação está incorreta! + A sintaxe da sua sequência de Auto-Digitação está incorreta! This Auto-Type command contains a very long delay. Do you really want to proceed? - Este comando de autodigitação contém um tempo de espera muito longo. Você tem certeza de que deseja continuar? + Este comando de Auto-Digitação contém um tempo de espera muito longo. Você tem certeza que deseja continuar? This Auto-Type command contains very slow key presses. Do you really want to proceed? - Este comando de autodigitação contém pressionamentos de teclas muito lentos. Você tem certeza de que deseja continuar? + Este comando Autotipo contém pressionamentos de teclas muito lentos. Você realmente deseja prosseguir? This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed? - Este comando de autodigitação contém parâmetros que são repetidos muitas vezes. Você tem certeza de que deseja continuar? + Este comando Auto-Type contém os argumentos que são repetidos muitas vezes. Você realmente deseja prosseguir? @@ -183,11 +183,11 @@ Selecione se deseja permitir o acesso. AutoTypeSelectDialog Auto-Type - KeePassXC - Autodigitação - KeePassXC + Auto-Digitação - KeePassXC Select entry to Auto-Type: - Escolha uma entrada para digitar automaticamente: + Escolha uma entrada para Auto-Digitar: @@ -294,7 +294,7 @@ Selecione se deseja permitir o acesso. Forget all remembered &permissions - Descartar todas as &permissões salvas + Descartar todas as permissões salvas Advanced @@ -303,7 +303,7 @@ Selecione se deseja permitir o acesso. Never &ask before accessing credentials Credentials mean login data requested via browser extension - Nunca peça confirmação antes de &acessar as credenciais + Nunca peça confirmação antes de acessar as credenciais Never ask before &updating credentials @@ -317,7 +317,7 @@ Selecione se deseja permitir o acesso. Searc&h in all opened databases for matching credentials Credentials mean login data requested via browser extension - &Buscar em todos os bancos de dados abertos por credenciais correspondêntes + &Buscar em todos os bancos de dados abertos por credenciais correspondentes Automatically creating or updating string fields is not supported. @@ -333,7 +333,7 @@ Selecione se deseja permitir o acesso. Update &native messaging manifest files at startup - Atualizar arquivos de manifesto de mensagens &nativas na inicialização + Atualizar arquivos de manifesto de mensagens nativos na inicialização Support a proxy application between KeePassXC and browser extension. @@ -404,8 +404,8 @@ Se você gostaria de permitir acesso ao seu banco de dados KeePassXC, atribua um A shared encryption key with the name "%1" already exists. Do you want to overwrite it? - Uma chave de criptografia compartilhada com o nome "%1" já existe. -Você deseja sobrescrever-la? + Uma chave de criptografia compartilhada com o nome "% 1" já existe. +Você deseja sobrescreve-la? KeePassXC: Update Entry @@ -439,7 +439,7 @@ Desbloqueie base de dados selecionada ou escolha outra que esteja desbloqueada.< No shared encryption keys found in KeePassXC Settings. - Nenhuma chave criptográfica compartilhada encontrada nas Configurações do KeePassXC. + Nenhuma chave criptográfica compartilhada encontrada nas Configurações do KeePassXC KeePassXC: Removed keys from database @@ -447,7 +447,7 @@ Desbloqueie base de dados selecionada ou escolha outra que esteja desbloqueada.< Successfully removed %n encryption key(s) from KeePassXC settings. - Removido com sucesso %n chave(s) criptográficas das configurações do KeePassXC.Removido com sucesso %n chave(s) criptográficas das configurações do KeePassXC. + Removido com êxito a chave de criptografia de %n (s) de KeePassXC configurações.Removido com sucesso% n chave (s) criptográficas das configurações do KeePassXC. Removing stored permissions… @@ -463,7 +463,7 @@ Desbloqueie base de dados selecionada ou escolha outra que esteja desbloqueada.< Successfully removed permissions from %n entry(s). - Removido com êxito as permissões de %n entrada(s).Removido com êxito as permissões de %n entrada(s). + Removido com êxito as permissões de %n entradas.Removido com êxito as permissões de %n entradas. KeePassXC: No entry with permissions found! @@ -510,11 +510,11 @@ Desbloqueie base de dados selecionada ou escolha outra que esteja desbloqueada.< Key files - Arquivos-Chave + Arquivos-chave All files - Todos os Arquivos + Todos arquivos Create Key File... @@ -691,15 +691,15 @@ Por favor, considere gerar um novo arquivo de chave. CsvParserModel %n byte(s), - %n bytes(s),%n bytes(s), + %n byte(s), %n byte(s), %n row(s), - %n linha(s), %n row(s), + %n linha(s), %n linha(s), %n column(s) - coluna (s) %n%n coluna(s) + %n coluna(s)%n coluna(s) @@ -734,7 +734,7 @@ Por favor, considere gerar um novo arquivo de chave. Can't open key file - Não foi possível abrir o arquivo-chave + Não foi possível abrir arquivo-chave Legacy key file format @@ -747,7 +747,7 @@ unsupported in the future. Please consider generating a new key file. Você está usando um formato de arquivo de chave legado que pode tornar-se sem suporte no futuro. -Por favor, considere gerar um novo arquivo de chave. +Por favor, considere-se gerar um novo arquivo de chave. Don't show this warning again @@ -755,7 +755,7 @@ Por favor, considere gerar um novo arquivo de chave. All files - Todos os arquivos + Todos arquivos Key files @@ -863,7 +863,7 @@ Se você manter este número, seu banco de dados pode ser facilmente crackeado!< thread(s) Threads for parallel execution (KDF settings) - thread(s) thread(s) + thread (s) thread(s) @@ -933,7 +933,7 @@ Se você manter este número, seu banco de dados pode ser facilmente crackeado!< MiB - MiB + MB Use recycle bin @@ -945,7 +945,7 @@ Se você manter este número, seu banco de dados pode ser facilmente crackeado!< Enable &compression (recommended) - Ativar &compressão (recomendado) + Ativar &compressão @@ -957,11 +957,11 @@ Se você manter este número, seu banco de dados pode ser facilmente crackeado!< KeePass 2 Database - Banco de Dados KeePass 2 + Banco de dados Keepass 2 All files - Todos os arquivos + Todos arquivos Open database @@ -1025,7 +1025,7 @@ Salvar alterações? Writing the database failed. - Escrever no banco de dados falhou. + Escrita do banco de dados falhou. Passwords @@ -1077,7 +1077,7 @@ Do contrário, suas alterações serão perdidas. KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. Disable safe saves and try again? KeePassXC não pôde salvar o banco de dados após várias tentativas. Isto é causado provavelmente pelo serviço de sincronização de arquivo que mantém um bloqueio ao salvar o arquivo. -Deseja desabilitar salvamento seguro e tentar de novo? +Deseja desabilitar salvamento seguro e tentar novamente? @@ -1088,7 +1088,7 @@ Deseja desabilitar salvamento seguro e tentar de novo? Change master key - Alterar chave mestra + Alterar chave-mestra Delete entry? @@ -1120,7 +1120,7 @@ Deseja desabilitar salvamento seguro e tentar de novo? Do you really want to move %n entry(s) to the recycle bin? - Você realmente deseja mover %n entrada para a lixeira?Você realmente deseja mover %n entradas para a lixeira? + Você quer realmente mudar %n entradas para a lixeira?Você deseja realmente mover %n entrada(s) para a lixeira? Execute command? @@ -1144,7 +1144,7 @@ Deseja desabilitar salvamento seguro e tentar de novo? Unable to calculate master key - Não foi possível calcular chave mestra + Não foi possível calcular a chave mestre No current database. @@ -1372,7 +1372,7 @@ Você deseja combinar suas alterações? %n month(s) - %n mês%n mese(s) + %n mese(s)%n mese(s) 1 year @@ -1638,7 +1638,7 @@ Você deseja combinar suas alterações? EditGroupWidgetMain Name - Nom + Nome Notes @@ -1650,7 +1650,7 @@ Você deseja combinar suas alterações? Search - Buscar + Pesquisar Auto-Type @@ -1662,7 +1662,7 @@ Você deseja combinar suas alterações? Set default Auto-Type se&quence - Definir se&quência padrão de Auto-Digitar + Definir sequência padrão de Auto-Digitar @@ -1693,7 +1693,8 @@ Você deseja combinar suas alterações? Hint: You can enable Google as a fallback under Tools>Settings>Security - Dica: Você pode ativar o Google como um recurso em Ferramentas>Configurações>Segurança + Dica: Você pode ativar o Google como um recurso em +Ferramentas>Configurações>Segurança Images @@ -1701,7 +1702,7 @@ Você deseja combinar suas alterações? All files - Todos os arquivos + Todos arquivos Select Image @@ -1849,7 +1850,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Unable to open attachment: %1 - Não foi possível abrir anexos: + Não foi possível abrir anexo: %1 @@ -1959,7 +1960,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Hide Passwords - Ocultar Senhas + Ocultar senhas Fit to window @@ -2108,7 +2109,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Wrong key or database file is corrupt. (HMAC mismatch) - Arquivo errado chave ou banco de dados está corrompido. (Incompatibilidade de HMAC) + Chave inválida ou arquivo banco de dados está corrompido. (Incompatibilidade de HMAC) Unknown cipher @@ -2116,7 +2117,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Invalid header id size - Tamanho de header id inválido + Tamanho do id do cabeçalho inválido Invalid header field length @@ -2140,7 +2141,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Invalid inner header id size - Cabeçalho interno inválido tamanho do id + Tamanho do id do cabeçalho interno inválido Invalid inner header field length @@ -2158,57 +2159,57 @@ Isto pode causar mal funcionamento dos plugins afetados. Invalid variant map entry name length Translation: variant map = data structure for storing meta data - Mapa variante inválido item comprimento do nome + Comprimento inválido do nome da entrada da variante do mapa Invalid variant map entry name data Translation: variant map = data structure for storing meta data - Mapa de variante inválido dados de nome da entrada + Dados inválidos do nome da entrada da variante do mapa Invalid variant map entry value length Translation: variant map = data structure for storing meta data - Comprimento de valor de entrada inválida de mapa variante + Comprimento inválido do valor de entrada do mapa Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - Mapa de variante inválido dados de valor da entrada + Dados inválidos do valor da entrada da variante do mapa Invalid variant map Bool entry value length Translation: variant map = data structure for storing meta data - Mapa de variante inválida valor do comprimento do item Bool + Comprimento inválido do valor booleano da entrada da variante do mapa Invalid variant map Int32 entry value length Translation: variant map = data structure for storing meta data - Mapa de variante inválida valor do comprimento do item Int32 + Comprimento inválido do valor da entrada Int32 da variante do mapa Invalid variant map UInt32 entry value length Translation: variant map = data structure for storing meta data - Mapa de variante inválida valor do comprimento do item UInt32 + Comprimento inválido do valor da entrada UInt32 da variante do mapa Invalid variant map Int64 entry value length Translation: variant map = data structure for storing meta data - Mapa de variante inválido Int64 comprimento de valor de entrada + Comprimento inválido do valor da entrada Int64 da variante do mapa Invalid variant map UInt64 entry value length Translation: variant map = data structure for storing meta data - Mapa de variante inválida valor do comprimento do item UInt64 + Comprimento inválido do valor da entrada UInt64 da variante do mapa Invalid variant map entry type Translation: variant map = data structure for storing meta data - Tipo de entrada mapa variante inválido + Tipo inválido da entrada da variante do mapa Invalid variant map field type size Translation: variant map = data structure for storing meta data - Tamanho do campo de tipo de mapa de variante inválido + Tamanho inválido do tipo de campo da variante do mapa @@ -2229,7 +2230,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Failed to serialize KDF parameters variant map Translation comment: variant map = data structure for storing meta data - Falha ao serializar mapa variante do KDF parâmetros + Falha ao serializar mapa variante do parâmetros KDF @@ -2268,7 +2269,7 @@ Isto pode causar mal funcionamento dos plugins afetados. Invalid random stream id size - Tamanho de id de stream aleatório inválido + Tamanho de ID de fluxo aleatório inválido Invalid inner random stream cipher @@ -2313,7 +2314,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Multiple group elements - Elementos de grupo múltiplo + Vários elementos do grupo Null group uuid @@ -2333,7 +2334,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da No group uuid found - Nenhum uuid de grupo encontrado + Nenhum grupo uuid encontrado Null DeleteObject uuid @@ -2349,7 +2350,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Invalid entry icon number - Entrada de número de ícone inválida + Item inválido número de ícone History element in history entry @@ -2444,7 +2445,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Unsupported KeePass database version. - Versão do banco de dados KeePass não suportada. + Versão não suportada do banco de dados KeePass. Unable to read encryption IV @@ -2453,7 +2454,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Invalid number of groups - Número de grupos inválido + Número inválido de grupos Invalid number of entries @@ -2465,7 +2466,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Invalid transform seed size - Tamanho de semente de transformação inválido + Tamanho de sementes de transformação inválido Invalid number of transform rounds @@ -2481,7 +2482,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Unable to calculate master key - Não foi possível calcular a chave mestra + Não foi possível calcular a chave mestre Wrong key or database file is corrupt. @@ -2509,11 +2510,11 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Incorrect group creation time field size - Group incorreto tamanho do campo de hora de criação + Grupo incorreto tamanho do campo de hora de criação Incorrect group modification time field size - Grupo incorreto tamanho do campo de hora de criação + Grupo incorreto tamanho do campo de hora de modificação Incorrect group access time field size @@ -2537,7 +2538,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Missing group id or level - Id do grupo ou nível ausente + Grupo ausente id ou nível Missing entry field type number @@ -2553,11 +2554,11 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Invalid entry uuid field size - Entrada inválida tamanho do campo uuid + Item inválido tamanho do campo uuid Invalid entry group id field size - Item inválido tamanho do campo de id de groupo + Item inválido tamanho do campo de id de grupo Invalid entry icon field size @@ -2580,33 +2581,6 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Tipo de campo de entrada inválido - - KeePass2 - - AES: 256-bit - AES: 256 bits - - - Twofish: 256-bit - Twofish: 256 bits - - - ChaCha20: 256-bit - ChaCha20: 256 bits - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – recomendado) - - Main @@ -2678,7 +2652,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da &Open database... - &Abrir base de dados... + &Abrir banco de dados... &Save database @@ -2722,7 +2696,7 @@ Isto é uma migração de caminho único. Você não poderá abrir o banco de da Sa&ve database as... - Sal&var base de dados como... + Sal&var banco de dados como... Change &master key... @@ -2925,7 +2899,7 @@ Esta versão não se destina ao uso em produção. Failed to read public key. - Falha ao ler chave pública. + Falha ao ler chave pública Corrupted key file, reading private key failed @@ -2953,27 +2927,27 @@ Esta versão não se destina ao uso em produção. Unexpected EOF while reading public key - EOF inesperado enquanto lendo a chave pública + EOF inesperado enquanto lendo a chave pública. Unexpected EOF while reading private key - EOF inesperado enquanto lendo a chave privada + EOF inesperado enquanto lendo a chave privada. Can't write public key as it is empty - Não é possível escrever a chave pública enquanto estiver vazio + Não é possível escrever a chave pública enquanto estiver vazio. Unexpected EOF when writing public key - EOF inesperado enquanto escrevendo a chave pública + EOF inesperado enquanto escrevendo a chave pública. Can't write private key as it is empty - Não é possível escrever a chave privada enquanto estiver vazio + EOF inesperado enquanto escrevendo a chave privada. Unexpected EOF when writing private key - EOF inesperado enquanto escrevendp a chave privada + EOF inesperado enquanto escrevendp a chave privada. Unsupported key type: %1 @@ -3143,7 +3117,7 @@ Usando porta padrão 19455. Character Types - Tipos de Caracteres + Tipo de Caracteres Upper Case Letters @@ -3167,7 +3141,7 @@ Usando porta padrão 19455. Exclude look-alike characters - Excluir caracteres similares + Excluir caracteres semelhantes Pick characters from every group @@ -3314,7 +3288,7 @@ Usando porta padrão 19455. Path of the database. - Caminho do banco de dados. + Caminho do banco de dados Key file of the database. @@ -3411,7 +3385,7 @@ Usando porta padrão 19455. Insert password to unlock %1: - Inserir a senha para desbloquear %1: + Inserir a senha para desbloquear 1%: Failed to load key file %1 : %2 @@ -3494,7 +3468,7 @@ Comandos disponíveis: NULL device - Dispositivo nulo + Dispositivo NULL error reading from device @@ -3512,7 +3486,31 @@ Comandos disponíveis: missing closing quote - cotação de fechamento ausente + apóstrofo de fechamento ausente + + + AES: 256-bit + AES: 256 bits + + + Twofish: 256-bit + Twofish: 256 bits + + + ChaCha20: 256-bit + ChaCha20: 256 bits + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – recomendado) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) Group @@ -3582,7 +3580,7 @@ Comandos disponíveis: Wordlist for the diceware generator. [Default: EFF English] Lista de palavras para o gerador diceware. -[Padrão: EFF inglês] +[Padrão: EFF Inglês] Generate a new random password. @@ -3598,7 +3596,7 @@ Comandos disponíveis: Use uppercase characters in the generated password. - Use letras maiúsculas para a senha gerada. + Use letras maiúsculas para a senha gerada. Use numbers in the generated password. @@ -3718,7 +3716,7 @@ Desbloqueie base de dados selecionada ou escolha outra que esteja desbloqueada.< Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Removido com êxito %n chave(s) de criptografia das configurações do KeePassX/Http.Removido com êxito %n chave(s) de criptografia das configurações do KeePassX/Http. + Removido com êxito a criptografia de %n-chave (s) de configurações KeePassX/Http.Removido com êxito %n a(s) chave(s) criptografia(s) das configurações KeePassX/Http. KeePassXC: No keys found @@ -3750,7 +3748,7 @@ Desbloqueie base de dados selecionada ou escolha outra que esteja desbloqueada.< Successfully removed permissions from %n entries. - Removido com êxito as permissões de %n entradas.Removido com êxito as permissões de %n entradas. + Removido com êxito as permissões de %n entradas.Removido com êxito as permissões das %n entradas. KeePassXC: No entry with permissions found! @@ -4081,15 +4079,15 @@ Desbloqueie base de dados selecionada ou escolha outra que esteja desbloqueada.< main Remove an entry from the database. - Remover entrada do banco de dados. + Remover item da base de dados Path of the database. - Caminho do banco de dados. + Caminho do banco de dados Path of the entry to remove. - Caminho do item à ser removido. + Caminho para remover entrada. KeePassXC - cross-platform password manager diff --git a/share/translations/keepassx_pt_PT.ts b/share/translations/keepassx_pt_PT.ts index 5ef30eecc..9382ad941 100644 --- a/share/translations/keepassx_pt_PT.ts +++ b/share/translations/keepassx_pt_PT.ts @@ -3,11 +3,11 @@ AboutDialog About KeePassXC - Acerca do KeePassXC + Sobre o KeePassXC About - Acerca + Sobre Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> @@ -359,7 +359,7 @@ Selecione se deseja permitir o acesso. <b>Warning:</b> The following options can be dangerous! - <b>AVISO</b>: as opções seguintes podem ser perigosas! + <b>Aviso</b>: as opções seguintes podem ser perigosas! Executable Files (*.exe);;All Files (*.*) @@ -375,7 +375,7 @@ Selecione se deseja permitir o acesso. We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment. - Lamentamos mas, no momento, o KeePassXC-Browser não tem suporte a versões Snap. + Lamentamos, mas, no momento, o KeePassXC-Browser não tem suporte a versões Snap. @@ -464,7 +464,7 @@ Desbloqueie a base de dados selecionada ou escolha outra que esteja desbloqueada Successfully removed permissions from %n entry(s). - Removidas com sucesso as permissões de %n entrada.Removidas com sucesso as permissões de %n entradas. + Removidas com sucesso as permissões de %n entrada.Removidas com sucesso as permissões de %n entrada(s). KeePassXC: No entry with permissions found! @@ -483,7 +483,7 @@ Desbloqueie a base de dados selecionada ou escolha outra que esteja desbloqueada Enter password: - Digite a palavra-passe: + Introduza a palavra-passe: Repeat password: @@ -523,7 +523,7 @@ Desbloqueie a base de dados selecionada ou escolha outra que esteja desbloqueada Unable to create Key File : - Incapaz de criar o ficheiro-chave: + Não foi possível criar o ficheiro-chave: Select a key file @@ -637,7 +637,7 @@ Deve considerar a geração de uma novo ficheiro-chave. Column layout - Disposição das colunas + Disposição de colunas Not present in CSV file @@ -686,29 +686,29 @@ Deve considerar a geração de uma novo ficheiro-chave. Unable to calculate master key - Impossível de calcular a chave-mestre + Não foi possível calcular a chave-mestre CsvParserModel %n byte(s), - %n bytes, %n bytes, + %n byte,%n byte(s), %n row(s), - %n linha,%n linhas, + %n linha,%n linha(s), %n column(s) - %n coluna,%n colunas, + %n coluna,%n coluna(s), DatabaseOpenWidget Enter master key - Digite a chave-mestre + Introduza a chave-mestre Key File: @@ -732,7 +732,7 @@ Deve considerar a geração de uma novo ficheiro-chave. Unable to open the database. - Incapaz de abrir a base de dados. + Não foi possível abrir a base de dados. Can't open key file @@ -785,7 +785,7 @@ Deve considerar a geração de uma novo ficheiro-chave. Unable to open the database. - Incapaz de abrir a base de dados. + Não foi possível abrir a base de dados. Database opened fine. Nothing to do. @@ -803,7 +803,7 @@ Agora já a pode guardar. Unable to repair the database. - Incapaz de reparar a base de dados. + Não foi possível reparar a base de dados. @@ -893,7 +893,7 @@ Se mantiver este número, a sua base de dados pode ser desbloqueada muito facilm Benchmark 1-second delay - + Testar atraso de 1 segundo Memory Usage: @@ -976,7 +976,7 @@ Se mantiver este número, a sua base de dados pode ser desbloqueada muito facilm Unable to open the database. - Incapaz de abrir a base de dados. + Não foi possível abrir a base de dados. File opened in read only mode. @@ -1087,7 +1087,7 @@ Desativar salvaguardas e tentar novamente? DatabaseWidget Searching... - Pesquisar... + Pesquisar.. Change master key @@ -1123,7 +1123,7 @@ Desativar salvaguardas e tentar novamente? Do you really want to move %n entry(s) to the recycle bin? - Pretende realmente mover a entrada(s) %n para a reciclagem ?Deseja mesmo mover %n entrada(s) para a reciclagem? + Deseja mesmo mover %n entrada para a reciclagem?Deseja mesmo mover %n entradas para a reciclagem? Execute command? @@ -1147,7 +1147,7 @@ Desativar salvaguardas e tentar novamente? Unable to calculate master key - Impossível de calcular a chave-mestre + Não foi possível calcular a chave-mestre No current database. @@ -1371,11 +1371,11 @@ Deseja juntar as suas alterações? %n week(s) - %n semana(s)%n semana(s) + %n semana%n semanas %n month(s) - %n mês%n mês(es) + %n mês%n meses 1 year @@ -1418,7 +1418,7 @@ Deseja juntar as suas alterações? Reveal - Revelar + Mostrar Attachments @@ -1692,7 +1692,7 @@ Deseja juntar as suas alterações? Unable to fetch favicon. - Incapaz de obter o 'favicon'. + Não foi possível obter o 'favicon'. Hint: You can enable Google as a fallback under Tools>Settings>Security @@ -1819,7 +1819,7 @@ Esta ação pode implicar um funcionamento errático. Are you sure you want to remove %n attachment(s)? - Tem a certeza de que deseja remover %n anexo?Tem a certeza de que deseja remover %n anexos? + Tem a certeza de que deseja remover %n anexo?Tem a certeza de que deseja remover %n anexo(s)? Confirm Remove @@ -2119,15 +2119,15 @@ Esta ação pode implicar um funcionamento errático. Invalid header id size - + Tamanho do id do cabeçalho inválido Invalid header field length - + Comprimento do campo de cabeçalho inválido Invalid header data length - + Comprimento dos dados de cabeçalho inválido Failed to open buffer for KDF parameters in header @@ -2143,75 +2143,75 @@ Esta ação pode implicar um funcionamento errático. Invalid inner header id size - + Tamanho do id do cabeçalho interno inválido Invalid inner header field length - + Comprimento do campo de cabeçalho interno inválido Invalid inner header binary size - + Tamanho binário do cabeçalho interno inválido Unsupported KeePass variant map version. Translation: variant map = data structure for storing meta data - + Versão não suportada do mapa variante KeePass. Invalid variant map entry name length Translation: variant map = data structure for storing meta data - + Comprimento inválido no nome da entrada da variante do mapa Invalid variant map entry name data Translation: variant map = data structure for storing meta data - + Dados inválidos no nome da entrada da variante do mapa Invalid variant map entry value length Translation: variant map = data structure for storing meta data - + Comprimento inválido no valor de entrada do mapa Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - + Dados inválidos no valor da entrada da variante do mapa Invalid variant map Bool entry value length Translation: variant map = data structure for storing meta data - + Comprimento inválido do valor booleano da entrada da variante do mapa Invalid variant map Int32 entry value length Translation: variant map = data structure for storing meta data - + Comprimento inválido do valor da entrada Int32 da variante do mapa Invalid variant map UInt32 entry value length Translation: variant map = data structure for storing meta data - + Comprimento inválido do valor da entrada UInt32 da variante do mapa Invalid variant map Int64 entry value length Translation: variant map = data structure for storing meta data - + Comprimento inválido do valor da entrada Int64 da variante do mapa Invalid variant map UInt64 entry value length Translation: variant map = data structure for storing meta data - + Comprimento inválido do valor da entrada UInt64 da variante do mapa Invalid variant map entry type Translation: variant map = data structure for storing meta data - + Tipo inválido da entrada da variante do mapa Invalid variant map field type size Translation: variant map = data structure for storing meta data - + Tamanho inválido do tipo de campo da variante do mapa @@ -2223,7 +2223,7 @@ Esta ação pode implicar um funcionamento errático. Invalid symmetric cipher IV size. IV = Initialization Vector for symmetric cipher - Algoritmo inválido de cifra simétrica IV. + Tamanho inválido da cifra simétrica IV. Unable to calculate master key @@ -2232,14 +2232,14 @@ Esta ação pode implicar um funcionamento errático. Failed to serialize KDF parameters variant map Translation comment: variant map = data structure for storing meta data - + Falhou a serialização dos parâmetros da KDF (função de derivação de chave) da variante do mapa KdbxReader Invalid cipher uuid length - + Comprimento inválido da cifra UUID (identificador único universal) Unsupported cipher @@ -2247,7 +2247,7 @@ Esta ação pode implicar um funcionamento errático. Invalid compression flags length - + Comprimento inválido da compressão de flags Unsupported compression algorithm @@ -2255,11 +2255,11 @@ Esta ação pode implicar um funcionamento errático. Invalid master seed size - + Tamanho inválido da semente mestre Invalid transform seed size - + Tamanho inválido da semente de transformação Invalid transform rounds size @@ -2267,15 +2267,15 @@ Esta ação pode implicar um funcionamento errático. Invalid start bytes size - + Tamanho inválido dos bytes iniciais Invalid random stream id size - + Tamanho inválido do ID do fluxo aleatório Invalid inner random stream cipher - + Cifra inválida de fluxo aleatório interno Not a KeePass database. @@ -2320,11 +2320,11 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Null group uuid - + UUID (identificador único universal) de grupo nulo Invalid group icon number - + Número inválido de ícone de grupo Invalid EnableAutoType value @@ -2340,19 +2340,19 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Null DeleteObject uuid - + UUID (identificador único universal) de DeleteObject nulo Missing DeletedObject uuid or time - + Tempo ou UUID (identificador único universal) de DeletedObject em falta Null entry uuid - + Entrada de UUID (identificador único universal) nula Invalid entry icon number - + Número inválido da entrada de ícone History element in history entry @@ -2360,7 +2360,7 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados No entry uuid found - + Não foi encontrada nenhuma entrada UUID (identificador único universal) History element with different uuid @@ -2428,14 +2428,14 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Unable to open the database. - Incapaz de abrir a base de dados. + Não foi possível abrir a base de dados. KeePass1Reader Unable to read keyfile. - Incapaz de ler o ficheiro-chave. + Não foi possível ler o ficheiro-chave. Not a KeePass database. @@ -2468,7 +2468,7 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Invalid transform seed size - + Tamanho inválido da semente de transformação Invalid number of transform rounds @@ -2484,7 +2484,7 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Unable to calculate master key - Incapaz de calcular a chave-mestre + Não foi possível calcular a chave-mestre Wrong key or database file is corrupt. @@ -2496,43 +2496,43 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Invalid group field type number - + Número inválido do tipo de grupo de campo Invalid group field size - + Tamanho inválido do grupo de campo Read group field data doesn't match size - + Leitura de grupo de dados do campo não coincidem no tamanho Incorrect group id field size - + Tamanho incorreto de campo de ID de grupo Incorrect group creation time field size - + Tamanho incorreto do campo do grupo de tempo de criação Incorrect group modification time field size - + Tamanho de campo de hora de alteração de grupo incorreto Incorrect group access time field size - + Tamanho de campo de tempo de acesso de grupo incorreto Incorrect group expiry time field size - + Tamanho de campo de tempo de expiração de grupo incorreto Incorrect group icon field size - + Tamanho do campo do ícone de grupo incorreto Incorrect group level field size - + Tamanho de campo do nível de grupo incorreto Invalid group field type @@ -2544,7 +2544,7 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Missing entry field type number - + Falta a entrada de tipo número no campo Invalid entry field size @@ -2552,64 +2552,37 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Read entry field data doesn't match size - + Dados de campo de entrada não coincidem no tamanho Invalid entry uuid field size - + Tamanho da entrada para o campo UUID (identificador único universal) inválido Invalid entry group id field size - + Tamanho da entrada para o campo identificador de grupo inválido Invalid entry icon field size - + Tamanho da entrada para o campo ícone inválido Invalid entry creation time field size - + Tamanho da entrada para o campo tempo de criação inválido Invalid entry modification time field size - + Tamanho da entrada para o campo tempo de alteração inválido Invalid entry expiry time field size - + Tamanho da entrada para o campo tempo de expiração inválido Invalid entry field type Tipo inválido para o campo da entrada - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – recomendado) - - Main @@ -2769,7 +2742,7 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados Password Generator - Gerador de palavras-passe + Gerador de palavra-passe &Perform Auto-Type @@ -2916,7 +2889,7 @@ Esta versão não deve ser utilizada para uma utilização regular. Key file way too small. - Ficheiro-chave muito pequeno + Ficheiro-chave muito pequeno. Key file magic header id invalid @@ -3052,7 +3025,7 @@ Esta versão não deve ser utilizada para uma utilização regular. R&emove all shared encryption keys from active database - R&emover todas as chaves cifradas partilhadas da base de dados ativa + R&emover todas as chaves de cifra partilhadas da base de dados ativa Re&move all stored permissions from entries in active database @@ -3104,7 +3077,7 @@ Esta versão não deve ser utilizada para uma utilização regular. <b>Warning:</b> The following options can be dangerous! - <b>AVISO</b>: as opções seguintes podem ser perigosas! + <b>Atenção</b>: as opções seguintes podem ser perigosas! <p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p> @@ -3162,7 +3135,7 @@ Será utilizada a porta 19455 (padrão). Special Characters - Caracteres especiais + Caracteres especiais Extended ASCII @@ -3414,7 +3387,7 @@ Será utilizada a porta 19455 (padrão). Insert password to unlock %1: - Digite a palavra-passe para desbloquear %1. + Introduza a palavra-passe para desbloquear %1. Failed to load key file %1 : %2 @@ -3518,6 +3491,30 @@ Comandos disponíveis: missing closing quote carácter de fecho em falta + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – recomendado) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Grupo @@ -3696,7 +3693,7 @@ KeePassXC, atribua um nome único para a identificar e aceitar. A shared encryption-key with the name "%1" already exists. Do you want to overwrite it? Já existe uma chave de cifra partilhada com o nome "%1". -Deseja substituir a chave de cifra? +Deseja substituir a chave atual? KeePassXC: Update Entry @@ -3718,11 +3715,11 @@ Desbloqueie a base de dados selecionada ou escolha outra que esteja desbloqueada KeePassXC: Removed keys from database - KeePassXC: Remover chaves da base de dados + KeePassXC: Chaves removidas da base de dados Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Removida com sucesso %n chave de cifra das definições KeePassX/Http.Removidas com sucesso % chaves de cifra das definições KeePassX/Http. + Removidas com sucesso % chave de cifra das definições KeePassX/Http.Removidas com sucesso % chaves de cifra das definições KeePassX/Http. KeePassXC: No keys found @@ -3730,7 +3727,7 @@ Desbloqueie a base de dados selecionada ou escolha outra que esteja desbloqueada No shared encryption-keys found in KeePassHttp Settings. - Nenhuma chaves de cifra partilhadas encontrada nas definições do KeePassHttp. + Não existem chaves de cifra partilhadas nas definições do KeePassHttp. KeePassXC: Settings not available! diff --git a/share/translations/keepassx_ro.ts b/share/translations/keepassx_ro.ts index 5a6c5ae5c..ded0193bd 100644 --- a/share/translations/keepassx_ro.ts +++ b/share/translations/keepassx_ro.ts @@ -23,7 +23,7 @@ <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> - <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Vezi contribuțiile pe GitHub</a> + <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Vezi contibuțiile pe GitHub</a> Debug Info @@ -40,7 +40,7 @@ Version %1 - Versiune %1 + Versiunea %1 @@ -116,7 +116,7 @@ Please select whether you want to allow access. AutoType Couldn't find an entry that matches the window title: - Nu a putut fi gasită nicio intrare care să coincidă cu titlul ferestrei: + Nu a putut fi gasită o intrare care să coincidă cu titlul ferestrei: Auto-Type - KeePassXC @@ -471,7 +471,7 @@ Please unlock the selected database or choose another one which is unlocked.ChangeMasterKeyWidget Password - Parola + Parolă Enter password: @@ -684,11 +684,11 @@ Please consider generating a new key file. %n row(s), - %n rând, %n rânduri, %n rânduri, + %n column(s) - %n coloană%n coloane%n coloane + @@ -769,7 +769,7 @@ Please consider generating a new key file. Unable to open the database. - Baza de date nu poate fi deschisă. + Nu pot deschide baza de date. Database opened fine. Nothing to do. @@ -955,7 +955,7 @@ If you keep this number, your database may be too easy to crack! Unable to open the database. - Baza de date nu poate fi deschisă. + Nu pot deschide baza de date. File opened in read only mode. @@ -975,7 +975,7 @@ If you keep this number, your database may be too easy to crack! Merge database - Îmbina baza de date + Îmbină baza de date Open KeePass 1 database @@ -1125,7 +1125,7 @@ Disable safe saves and try again? No current database. - Nu există o bază de date curentă. + Nu există o baza de date curentă. No source database, nothing to do. @@ -1173,7 +1173,7 @@ Do you want to merge your changes? DetailsWidget Generate TOTP Token - Generează token TOTP + Genereaza token TOTP Close @@ -1185,7 +1185,7 @@ Do you want to merge your changes? Password - Parola + Parolă URL @@ -1328,7 +1328,7 @@ Do you want to merge your changes? Are you sure you want to remove this attribute? - Sigur doriți să eliminați acest atribut? + Sunteți sigur că doriți să eliminați acest atribut? [PROTECTED] @@ -1344,11 +1344,11 @@ Do you want to merge your changes? %n week(s) - %n săptămână%n săptămâni%n săptămâni + %n month(s) - %n lună%n luni%n luni + 1 year @@ -1767,7 +1767,7 @@ This may cause the affected plugins to malfunction. EntryAttachmentsWidget Form - Formular + De la Add @@ -1890,7 +1890,7 @@ This may cause the affected plugins to malfunction. Password - Parola + Parolă Notes @@ -2547,33 +2547,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - - - - Twofish: 256-bit - - - - ChaCha20: 256-bit - - - - AES-KDF (KDBX 4) - - - - AES-KDF (KDBX 3.1) - - - - Argon2 (KDBX 4 – recommended) - - - Main @@ -3278,7 +3251,7 @@ Using default port 19455. Path of the database. - Calea către baza de date. + Calea către baza de date Key file of the database. @@ -3476,6 +3449,30 @@ Comenzi disponibile: missing closing quote + + AES: 256-bit + + + + Twofish: 256-bit + + + + ChaCha20: 256-bit + + + + Argon2 (KDBX 4 – recommended) + + + + AES-KDF (KDBX 4) + + + + AES-KDF (KDBX 3.1) + + Group Grup diff --git a/share/translations/keepassx_ru.ts b/share/translations/keepassx_ru.ts index 14f0590d4..45810522b 100644 --- a/share/translations/keepassx_ru.ts +++ b/share/translations/keepassx_ru.ts @@ -15,7 +15,7 @@ KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. - KeePassXC распространяется на условиях Стандартной общественной лицензии GNU (GPL) версии 2 или (на ваше усмотрение) версии 3. + KeePassXC распространяется на условиях универсальной общественной лицензии GNU (GPL) версии 2 или 3 (на ваше усмотрение). Contributors @@ -31,7 +31,7 @@ Include the following information whenever you report a bug: - Включите следующую информацию, когда сообщаете об ошибке: + Добавьте в сообщение об ошибке следующую информацию: Copy to clipboard @@ -60,12 +60,12 @@ CPU architecture: %2 Kernel: %3 %4 Операционная система: %1 -Архитектура CPU: %2 +Архитектура ЦП: %2 Ядро: %3 %4 Enabled extensions: - Включенные расширения: + Включённые расширения: Project Maintainers: @@ -103,7 +103,7 @@ Kernel: %3 %4 %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 запросил доступ к паролям для следующего элемента(ов). + %1 запросил доступ к паролям для следующих элементов. Разрешить доступ? @@ -111,7 +111,7 @@ Please select whether you want to allow access. AgentSettingsWidget Enable SSH Agent (requires restart) - Включить SSH агент (необходим перезапуск) + Включить SSH-агент (необходим перезапуск) @@ -134,15 +134,15 @@ Please select whether you want to allow access. This Auto-Type command contains a very long delay. Do you really want to proceed? - Слишком большая задержка в инструкции автоввода. Действительно продолжить? + Слишком большая задержка в команде автоввода. Действительно продолжить? This Auto-Type command contains very slow key presses. Do you really want to proceed? - Инструкция автоввода содержит очень медленные нажатия клавиш. Вы действительно хотите продолжить? + Команда автоввода содержит очень медленные нажатия клавиш. Действительно продолжить? This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed? - Эта команда автоввода содержит часто повторяющиеся аргументы. Вы действительно хотите продолжить? + Команда автоввода содержит часто повторяющиеся аргументы. Действительно продолжить? @@ -211,7 +211,7 @@ Please select whether you want to allow access. %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 запросил доступ к паролям для следующего элемента(-ов). + %1 запросил доступ к паролям для следующих элементов. Разрешить доступ? @@ -281,7 +281,7 @@ Please select whether you want to allow access. Sort &matching credentials by title Credentials mean login data requested via browser extension - Сортировать &подходящие учетные данные по названию + Сортировать &подходящие учётные данные по названию Sort matching credentials by &username @@ -308,7 +308,7 @@ Please select whether you want to allow access. Never ask before &updating credentials Credentials mean login data requested via browser extension - Никогда не спрашивать перед &обновлением учетных данных + Не спрашивать перед &обновлением учётных данных Only the selected database has to be connected with a client. @@ -317,7 +317,7 @@ Please select whether you want to allow access. Searc&h in all opened databases for matching credentials Credentials mean login data requested via browser extension - &Поиск во всех открытых базах данных для сопоставления учетных данных + &Поиск во всех открытых базах для сопоставления учётных данных Automatically creating or updating string fields is not supported. @@ -337,15 +337,15 @@ Please select whether you want to allow access. Support a proxy application between KeePassXC and browser extension. - Поддержка прокси приложения между KeePassXC и расширением браузера. + Поддержка прокси-приложения между KeePassXC и расширением браузера. Use a &proxy application between KeePassXC and browser extension - Использование &прокси приложения между KeePassXC и расширением браузера + Использование &прокси-приложения между KeePassXC и расширением браузера Use a custom proxy location if you installed a proxy manually. - Использовать пользовательский путь к прокси, если вы установили прокси вручную. + Использовать пользовательский путь к прокси при установке прокси вручную. Use a &custom proxy location @@ -359,7 +359,7 @@ Please select whether you want to allow access. <b>Warning:</b> The following options can be dangerous! - <b>Предупреждение:</b> Следующие параметры могут быть опасны! + <b>ВНИМАНИЕ:</b> Следующие параметры могут быть опасны! Executable Files (*.exe);;All Files (*.*) @@ -375,7 +375,7 @@ Please select whether you want to allow access. We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment. - Извините, но KeePassXC-Browser не поддерживается для Snap выпусков на данный момент. + KeePassXC-Browser сейчас не поддерживается для выпусков Snap. @@ -405,8 +405,8 @@ give it a unique name to identify and accept it. A shared encryption key with the name "%1" already exists. Do you want to overwrite it? - Разделяемый секретный ключ с именем "%1" уже существует. -Вы действительно хотите перезаписать его? + Общий секретный ключ с именем "%1" уже существует. +Вы действительно хотите его перезаписать? KeePassXC: Update Entry @@ -440,7 +440,7 @@ Please unlock the selected database or choose another one which is unlocked. No shared encryption keys found in KeePassXC Settings. - Разделяемые секретные ключи не найдены в настройках KeePassXC. + Общие секретные ключи не найдены в настройках KeePassXC. KeePassXC: Removed keys from database @@ -448,11 +448,11 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption key(s) from KeePassXC settings. - Успешно удален %n ключ шифрования из параметров KeePassXC.Успешно удалено %n ключей шифрования из параметров KeePassXC.Успешно удалено %n ключей шифрования из параметров KeePassXC.Успешно удалено %n ключей шифрования из параметров KeePassXC. + Успешно удалена %n ключ(и) шифрования от KeePassXC параметров.Успешно удалена %n ключ(и) шифрования от KeePassXC параметров.Успешно удалена %n ключ(и) шифрования от KeePassXC параметров.Успешно удалено ключей шифрования из параметров KeePassXC: %n. Removing stored permissions… - Удаление сохраненных разрешений... + Удаление сохранённых разрешений... Abort @@ -460,19 +460,19 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Removed permissions - KeePassXC: Права доступа удалены + KeePassXC: Разрешения удалены Successfully removed permissions from %n entry(s). - Успешно удалены доступы для %n записи.Успешно удалены доступы для %n записей.Успешно удалены доступы для %n записей.Успешно удалены доступы для %n записей. + Успешно удалены разрешения от %n entry(s).Успешно удалены разрешения от %n entry(s).Успешно удалены разрешения от %n entry(s).Успешно удалены разрешения из %n записей. KeePassXC: No entry with permissions found! - KeePassXC: Не найдена запись с правами доступа! + KeePassXC: Не найдена запись с разрешениями! The active database does not contain an entry with permissions. - Активная база данных не содержит записей с назначенными правами доступа. + Активная база данных не содержит записей с назначенными разрешениями. @@ -491,7 +491,7 @@ Please unlock the selected database or choose another one which is unlocked. &Key file - &Ключ-файл + &Ключевой файл Browse @@ -503,7 +503,7 @@ Please unlock the selected database or choose another one which is unlocked. Cha&llenge Response - Запрос ответа + Ответ на &вызов Refresh @@ -511,7 +511,7 @@ Please unlock the selected database or choose another one which is unlocked. Key files - Файлы-ключи + Ключевые файлы All files @@ -519,15 +519,15 @@ Please unlock the selected database or choose another one which is unlocked. Create Key File... - Создать ключ-файл... + Создать ключевой файл... Unable to create Key File : - Невозможно создать ключ-файл: + Невозможно создать ключевой файл: Select a key file - Выбрать ключ-файл + Выберите ключевой файл Empty password @@ -544,7 +544,7 @@ Please unlock the selected database or choose another one which is unlocked. Failed to set %1 as the Key file: %2 - Не удалось установить %1 как файл-ключ: + Не удалось установить %1 ключевым файлом: %2 @@ -556,13 +556,13 @@ Please unlock the selected database or choose another one which is unlocked. - Вы используете устаревший формат ключевого файла, поддержка которого может быть прекращена в будущем. + Вы используете ключевой файл устаревшего формата, поддержка которого может быть прекращена в будущем. -Рассмотрите возможность создания нового ключевого файла. +По возможности создайте новый ключевой файл. Changing master key failed: no YubiKey inserted. - Не удалось сменить мастер-ключ: ни один YubiKey не вставлен. + Не удалось сменить мастер-ключ: YubiKey не подключен. @@ -592,11 +592,11 @@ Please consider generating a new key file. filename - Имя файла + имя файла size, rows, columns - размер, строк, столбцов + размер, строк, столбцов Encoding @@ -604,7 +604,7 @@ Please consider generating a new key file. Codec - Кодек + Кодировка Text is qualified by @@ -612,35 +612,35 @@ Please consider generating a new key file. Fields are separated by - Параметры разделены + Разделитель полей Comments start with - Комментарии начинаются с + Символ начала комментария First record has field names - Первая запись полей + Первая запись содержит имена полей Number of headers line to discard - Количество строк заголовков для удаления + Пропустить строк в начале Consider '\' an escape character - Рассматривать маскирующим символом «\» + Символ «\» является экранирующим Preview - Предпросмотр + Предварительный просмотр Column layout - Расположение столбцов + Назначение столбцов Not present in CSV file - Отсутствует в CSV файле + Отсутствует в CSV-файле Empty fieldname @@ -648,11 +648,11 @@ Please consider generating a new key file. column - Столбец + столбец Imported from CSV file - Импортировано из CSV файла + Импортировано из CSV-файла Original data: @@ -660,7 +660,7 @@ Please consider generating a new key file. Error(s) detected in CSV file ! - Обнаружена ошибка в файле CSV! + Ошибки в файле CSV! more messages skipped] @@ -692,15 +692,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - %n байт, %n байт(а), %n байт(а), %n байт(а), + %n байт(а), %n байт(а), %n байт(а), %n байт, %n row(s), - %n строка, %n строк, %n строк, %n строк, + %n строк, %n строк, %n строк, %n строк, %n column(s) - %n столбец%n столбцов%n столбцов%n столбцов + %n столбцов%n столбцов%n столбцов%n столбцов @@ -711,7 +711,7 @@ Please consider generating a new key file. Key File: - Ключ-файл: + Ключевой файл: Password: @@ -727,7 +727,7 @@ Please consider generating a new key file. Challenge Response: - Запрос ответа: + Ответ на вызов: Unable to open the database. @@ -735,7 +735,7 @@ Please consider generating a new key file. Can't open key file - Не удается открыть ключ-файл + Невозможно открыть ключевой файл Legacy key file format @@ -746,9 +746,9 @@ Please consider generating a new key file. unsupported in the future. Please consider generating a new key file. - Вы используете устаревший формат ключевого файла, поддержка которого может быть прекращена в будущем. + Вы используете ключевой файл устаревшего формата, поддержка которого может быть прекращена в будущем. -Рассмотрите возможность создания нового ключевого файла. +По возможности создайте новый ключевой файл. Don't show this warning again @@ -760,11 +760,11 @@ Please consider generating a new key file. Key files - Ключ-файлы + Ключевые файлы Select key file - Выберите файл-ключ + Выберите ключевой файл @@ -779,7 +779,7 @@ Please consider generating a new key file. Can't open key file - Не могу открыть файл-ключ + Невозможно открыть ключевой файл Unable to open the database. @@ -787,7 +787,7 @@ Please consider generating a new key file. Database opened fine. Nothing to do. - База данных открылось прекрасно. Больше нечего делать. + База данных успешна открыта. Больше нечего делать. Success @@ -796,8 +796,8 @@ Please consider generating a new key file. The database has been successfully repaired You can now save it. - База данных была восстановлена. -Теперь можете сохранить её. + База данных восстановлена. +Теперь можете её сохранить. Unable to repair the database. @@ -823,9 +823,9 @@ You can now save it. You are using a very high number of key transform rounds with Argon2. If you keep this number, your database may take hours or days (or even longer) to open! - Используется слишком большое количество раундов преобразования ключа Argon2. + Слишком большое число раундов преобразования ключа Argon2. -При сохранении этого значение открытие базы данных может занять часы или дни (или даже больше)! +Если оставить это значение, открытие базы данных может занять часы, дни или даже больше! Understood, keep number @@ -844,9 +844,9 @@ If you keep this number, your database may take hours or days (or even longer) t You are using a very low number of key transform rounds with AES-KDF. If you keep this number, your database may be too easy to crack! - Вы используете слишком мало раундов преобразования ключа AES-KDF. + Слишком мало раундов преобразования ключа AES-KDF. -При сохранении этого значения, база данных может быть взломана слишком легко! +Если оставить это значение, базу данных можно будет слишком легко взломать! KDF unchanged @@ -864,7 +864,7 @@ If you keep this number, your database may be too easy to crack! thread(s) Threads for parallel execution (KDF settings) - потокпотоковпотоковпоток(ов) + потокпотокапотоковпотоков @@ -887,7 +887,7 @@ If you keep this number, your database may be too easy to crack! Transform rounds: - Раундов преобразований: + Раундов преобразования: Benchmark 1-second delay @@ -926,7 +926,7 @@ If you keep this number, your database may be too easy to crack! Max. history items: - Макс. количество записей истории: + Максимум записей в истории: Max. history size: @@ -934,7 +934,7 @@ If you keep this number, your database may be too easy to crack! MiB - МиБ + МиБ Use recycle bin @@ -974,7 +974,7 @@ If you keep this number, your database may be too easy to crack! Unable to open the database. - Не удаётся открыть базу данных. + Невозможно открыть базу данных. File opened in read only mode. @@ -1042,7 +1042,7 @@ Save changes? Writing the CSV file failed. - Не удалось записать CSV файл. + Не удалось записать CSV-файл. New database @@ -1059,8 +1059,8 @@ Save changes? Can't lock the database as you are currently editing it. Please press cancel to finish your changes or discard them. - Невозможно заблокировать базу данных, так как вы в настоящее время редактируете её. -Нажмите Отмена, чтобы завершить изменения или отклонить их. + Невозможно заблокировать базу данных, так как вы её сейчас редактируете. +Нажмите 'Отмена', чтобы завершить изменения или отменить их. This database has been modified. @@ -1077,7 +1077,7 @@ Otherwise your changes are lost. KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. Disable safe saves and try again? - KeePassXC не удалось сохранить базу данных несколько раз. Это могло быть вызвано службами синхронизации файлов, выполняющими блокировку сохранения файла. + KeePassXC несколько раз не удалось сохранить базу данных. Это могло быть вызвано службами синхронизации файлов, блокирующими файл при сохранении. Отключить безопасное сохранение и повторить попытку? @@ -1089,7 +1089,7 @@ Disable safe saves and try again? Change master key - Изменить мастер-пароль + Сменить мастер-пароль Delete entry? @@ -1105,7 +1105,7 @@ Disable safe saves and try again? Do you really want to delete %1 entries for good? - Удалить навсегда %1 записей? + Удалить навсегда записи (%1)? Move entry to recycle bin? @@ -1121,7 +1121,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - Вы действительно хотите поместить %n запись в корзину?Вы действительно хотите поместить %n записи в корзину?Вы действительно хотите поместить %n записей в корзину?Вы действительно хотите поместить %n записей в корзину? + Вы действительно хотите переместить %n entry(s) в корзину?Вы действительно хотите переместить %n entry(s) в корзину?Вы действительно хотите переместить %n entry(s) в корзину?Вы действительно хотите переместить записи (%n) в корзину? Execute command? @@ -1165,11 +1165,11 @@ Disable safe saves and try again? File has changed - Файл был изменен + Файл изменён The database file has changed. Do you want to load the changes? - Файл базу данных изменился. Загрузить изменения? + Файл базы данных был изменён. Загрузить изменения? Merge Request @@ -1178,8 +1178,8 @@ Disable safe saves and try again? The database file has changed and you have unsaved changes. Do you want to merge your changes? - База данных была изменена, а также присутствуют несохраненные изменения. -Вы хотите объединить изменения? + База данных была изменена, есть несохранённые изменения. +Объединить изменения? Could not open the new database file while attempting to autoreload this database. @@ -1191,14 +1191,14 @@ Do you want to merge your changes? Are you sure you want to permanently delete everything from your recycle bin? - Удалить все из корзины? + Удалить всё из корзины? DetailsWidget Generate TOTP Token - Генерировать TOTP токен + Генерировать токен TOTP Close @@ -1285,7 +1285,7 @@ Do you want to merge your changes? Advanced - Расширенные + Дополнительные Icon @@ -1305,7 +1305,7 @@ Do you want to merge your changes? SSH Agent - SSH Агент + SSH-агент n/a @@ -1317,15 +1317,15 @@ Do you want to merge your changes? Select private key - Выберите закрытый ключ + Выберите частный ключ File too large to be a private key - Слишком большой файл закрытого ключа + Слишком большой файл для частного ключа Failed to open private key - Не удалось открыть закрытый ключ + Не удалось открыть частный ключ Entry history @@ -1337,7 +1337,7 @@ Do you want to merge your changes? Edit entry - Править запись + Редактировать запись Different passwords supplied. @@ -1357,11 +1357,11 @@ Do you want to merge your changes? [PROTECTED] - [ЗАЩИЩЕННЫЙ] + [ЗАЩИЩЁННЫЙ] Press reveal to view or edit - Нажмите Показать для просмотра или редактирования + Нажмите 'Показать' для просмотра или редактирования Tomorrow @@ -1369,11 +1369,11 @@ Do you want to merge your changes? %n week(s) - %n неделя%n недели%n недель%n недель + %n нед%n нед%n нед%n нед. %n month(s) - %n месяц%n месяца%n месяцев%n месяцев + %n месяц(-а)(-ев)%n месяц(-а)(-ев)%n месяц(-а)(-ев)%n мес. 1 year @@ -1424,7 +1424,7 @@ Do you want to merge your changes? Foreground Color: - Цвет переднего плана: + Основной цвет: Background Color: @@ -1443,7 +1443,7 @@ Do you want to merge your changes? &Use custom Auto-Type sequence: - Использовать сво&ю последовательность автоввода: + &Использовать свою последовательность автоввода: Window Associations @@ -1489,7 +1489,7 @@ Do you want to merge your changes? EditEntryWidgetMain URL: - Адрес: + URL-адрес: Password: @@ -1513,7 +1513,7 @@ Do you want to merge your changes? Toggle the checkbox to reveal the notes section. - Установите для отображения раздела примечаний. + Включите для отображения раздела примечаний. Username: @@ -1536,7 +1536,7 @@ Do you want to merge your changes? seconds - секунд + сек Fingerprint @@ -1544,15 +1544,15 @@ Do you want to merge your changes? Remove key from agent when database is closed/locked - Убрать ключ из агента при закрытии/блокировании базы данных + Убрать ключ из агента при закрытии/блокировке базы данных Public key - Открытый ключ + Публичный ключ Add key to agent when database is opened/unlocked - Добавить ключ в агент, после открытия/разблокировки базы данных + Добавить ключ в агент при открытии/разблокировке базы данных Comment @@ -1572,7 +1572,7 @@ Do you want to merge your changes? Private key - Закрытый ключ + Частный ключ External file @@ -1581,7 +1581,7 @@ Do you want to merge your changes? Browse... Button for opening file dialog - Обзор... + Просмотр... Attachment @@ -1597,7 +1597,7 @@ Do you want to merge your changes? Require user confirmation when this key is used - Требовать подтверждения пользователя, когда этот ключ используется + Требовать подтверждения при использовании этого ключа @@ -1632,7 +1632,7 @@ Do you want to merge your changes? Inherit from parent group (%1) - Наследовать у родительской группы (%1) + Наследовать от родительской группы (%1) @@ -1663,7 +1663,7 @@ Do you want to merge your changes? Set default Auto-Type se&quence - Установить по умолчанию последовательность автоввода + Установить последовательность автоввода по умолчанию @@ -1694,7 +1694,7 @@ Do you want to merge your changes? Hint: You can enable Google as a fallback under Tools>Settings>Security - Подсказка: в качестве резервного варианта для получения значков сайтов возможно использовать Google. Включите этот параметр в меню «Инструменты» -> «Настройки» -> «Безопасность» + Подсказка: в качестве резервного варианта для получения значков сайтов можно использовать Google. Включите этот параметр в меню «Инструменты -> Параметры -> Безопасность». Images @@ -1710,7 +1710,7 @@ Do you want to merge your changes? Can't read icon - Не могу прочитать значок + Невозможно прочитать значок Custom icon already exists @@ -1722,7 +1722,7 @@ Do you want to merge your changes? This icon is used by %1 entries, and will be replaced by the default icon. Are you sure you want to delete it? - Этот значок использует %1 записей и будет заменен значком по умолчанию. Хотите удалить его? + Этот значок используется в %1 записях, он будет заменен на стандартный. Хотите удалить его? @@ -1745,7 +1745,7 @@ Do you want to merge your changes? Plugin Data - Данные плагина + Данные плагинов Remove @@ -1753,7 +1753,7 @@ Do you want to merge your changes? Delete plugin data? - Удалить данные плагина? + Удалить данные плагинов? Do you really want to delete the selected plugin data? @@ -1775,7 +1775,7 @@ This may cause the affected plugins to malfunction. - Clone Suffix added to cloned entries - - Копия + - клон @@ -1817,7 +1817,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - Вы уверены, что вы хотите удалить %n вложение?Вы уверены, что вы хотите удалить %n вложений?Вы уверены, что вы хотите удалить %n вложений?Вы уверены, что вы хотите удалить %n вложений? + Вы уверены, что вы хотите удалить %n вложения?Вы уверены, что вы хотите удалить %n вложения?Вы уверены, что вы хотите удалить %n вложения?Действительно хотите удалить вложения (%n)? Confirm Remove @@ -1830,11 +1830,11 @@ This may cause the affected plugins to malfunction. Unable to create directory: %1 - Не удается создать каталог: %1 + Не удаётся создать папку: %1 Are you sure you want to overwrite the existing file "%1" with the attachment? - Вы уверены, что вы хотите, перезаписать существующий файл "%1" с вложением? + Действительно хотите перезаписать имеющийся файл "%1" с вложением? Confirm overwrite @@ -1843,25 +1843,25 @@ This may cause the affected plugins to malfunction. Unable to save attachments: %1 - Не удается сохранить вложение: + Невозможно сохранить вложение: %1 Unable to open attachment: %1 - Не удается открыть вложение: + Невозможно открыть вложение: %1 Unable to open attachments: %1 - Не удается открыть вложение: + Невозможно открыть вложения: %1 Unable to open files: %1 - Не удается открыть файлы: + Невозможно открыть файлы: %1 @@ -1888,7 +1888,7 @@ This may cause the affected plugins to malfunction. URL - Адрес + URL-адрес @@ -1912,7 +1912,7 @@ This may cause the affected plugins to malfunction. URL - Адрес + URL-адрес Never @@ -1975,7 +1975,7 @@ This may cause the affected plugins to malfunction. Attachments (icon) - Вложения (иконки) + Вложения (значки) @@ -1989,11 +1989,11 @@ This may cause the affected plugins to malfunction. HostInstaller KeePassXC: Cannot save file! - KeePassXC: Не удается сохранить файл! + KeePassXC: Невозможно сохранить файл! Cannot save the native messaging script file. - Не удается сохранить файл сценария для механизма native messaging. + Невозможно сохранить файл сценария для механизма native messaging. @@ -2040,11 +2040,11 @@ This may cause the affected plugins to malfunction. Exclude look-alike characters - Исключить визуально схожие символы + Не использовать визуально схожие символы Ensure that the password contains characters from every group - Убедиться, что пароль содержит символы из каждой группы + Формировать пароль с символами из каждой группы Extended ASCII @@ -2074,7 +2074,7 @@ This may cause the affected plugins to malfunction. Wrong key or database file is corrupt. - Неверный ключ или файл базы данных повреждён. + Неверный ключ, либо повреждён файл базы данных. @@ -2108,11 +2108,11 @@ This may cause the affected plugins to malfunction. Wrong key or database file is corrupt. (HMAC mismatch) - Неправильный ключ или файл базы данных поврежден. (несоответствие HMAC) + Неверный ключ, либо повреждён файл базы данных (несоответствие HMAC). Unknown cipher - Неизвестные шифр + Неизвестный шифр Invalid header id size @@ -2128,15 +2128,15 @@ This may cause the affected plugins to malfunction. Failed to open buffer for KDF parameters in header - Не удалось открыть буфер для ФФК параметров в заголовке + Не удалось открыть буфер для параметров ФФК в заголовке Unsupported key derivation function (KDF) or invalid parameters - Функция формирования ключа (ФФК) не поддерживается или неверные параметры + Функция формирования ключа (ФФК) не поддерживается, либо неверные параметры Legacy header fields found in KDBX4 file. - Устаревшие поля заголовков найденные в файле KDBX4. + В файле KDBX4 обнаружены устаревшие поля заголовков. Invalid inner header id size @@ -2240,7 +2240,7 @@ This may cause the affected plugins to malfunction. Unsupported cipher - Неподдерживаемые шифр + Неподдерживаемый шифр Invalid compression flags length @@ -2283,10 +2283,10 @@ This may cause the affected plugins to malfunction. You can import it by clicking on Database > 'Import KeePass 1 database...'. This is a one-way migration. You won't be able to open the imported database with the old KeePassX 0.4 version. - Выбранный файл от старой KeePass 1 базы данных (.kdb). + Выбран файл старой базы данных KeePass 1 (.kdb). -Вы можете импортировать его, нажав на База Данных > «Импорт KeePass 1 базы данных...». -Это одностороннее перемещение. Вы не сможете открыть импортированную базу данных на старой версии KeePassX 0,4. +Вы можете импортировать его, нажав «База данных -> Импорт базы данных KeePass 1...». +Это необратимая процедура: импортированную базу нельзя будет открыть в старой версии KeePassX 0.4. Unsupported KeePass 2 database version. @@ -2301,7 +2301,7 @@ This is a one-way migration. You won't be able to open the imported databas No root group - Нет root группы + Нет корневой группы Missing icon uuid or data @@ -2321,7 +2321,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid group icon number - Недопустимый номер значка группы. + Недопустимый номер значка группы Invalid EnableAutoType value @@ -2365,11 +2365,11 @@ This is a one-way migration. You won't be able to open the imported databas Unable to decrypt entry string - Не удается расшифровать строку записи + Невозможно расшифровать строку записи Duplicate custom attribute found - Найден повторяющиеся пользовательский атрибут + Обнаружен повторяющиеся пользовательский атрибут Entry string key or value missing @@ -2377,7 +2377,7 @@ This is a one-way migration. You won't be able to open the imported databas Duplicate attachment found - Найден дубликат во вложениях + Обнаружен дубликат во вложениях Entry binary key or value missing @@ -2414,7 +2414,7 @@ This is a one-way migration. You won't be able to open the imported databas Unable to decompress binary Translator meant is a binary data inside an entry - Не удается распаковать двоичные данные + Невозможно распаковать двоичные данные @@ -2432,7 +2432,7 @@ This is a one-way migration. You won't be able to open the imported databas KeePass1Reader Unable to read keyfile. - Невозможно прочесть файл-ключ. + Невозможно прочитать ключевой файл. Not a KeePass database. @@ -2461,7 +2461,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid content hash size - Недопустимый размер хэша содержимого + Недопустимый размер хеша содержимого Invalid transform seed size @@ -2485,7 +2485,7 @@ This is a one-way migration. You won't be able to open the imported databas Wrong key or database file is corrupt. - Неверный ключ или файл базы данных повреждён. + Неверный ключ, либо повреждён файл базы данных. Key transformation failed @@ -2580,42 +2580,15 @@ This is a one-way migration. You won't be able to open the imported databas Недопустимый тип поля записи - - KeePass2 - - AES: 256-bit - AES: 256-бит - - - Twofish: 256-bit - Twofish: 256-бит - - - ChaCha20: 256-bit - ChaCha20: 256-бит - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – рекомендуемый) - - Main Existing single-instance lock file is invalid. Launching new instance. - Запускается новый экземпляр программы, т.к. файл блокировки запуска повреждён. + Повреждён файл блокировки запуска, запуск нового экземпляра программы. The lock file could not be created. Single-instance mode disabled. - Файл блокировки не может быть создан. Режим недублирующего хранения отключен. + Невозможно создать файл блокировки. Режим недублирующего хранения отключён. Another instance of KeePassXC is already running. @@ -2623,7 +2596,7 @@ This is a one-way migration. You won't be able to open the imported databas Fatal error while testing the cryptographic functions. - Неисправимая ошибка в процессе тестирования криптографических функций. + Неустранимая ошибка при тестировании криптографических функций. KeePassXC - Error @@ -2646,7 +2619,7 @@ This is a one-way migration. You won't be able to open the imported databas &Help - Помощь + Справка E&ntries @@ -2702,7 +2675,7 @@ This is a one-way migration. You won't be able to open the imported databas &View/Edit entry - &Открыть/править запись + &Открыть/изменить запись &Delete entry @@ -2714,7 +2687,7 @@ This is a one-way migration. You won't be able to open the imported databas &Edit group - &Править группу + &Изменить группу &Delete group @@ -2726,11 +2699,11 @@ This is a one-way migration. You won't be able to open the imported databas Change &master key... - Изменение &мастер-пароля... + Изменить &мастер-пароль... &Database settings - Настройки базы данных + Параметры базы данных Database settings @@ -2762,7 +2735,7 @@ This is a one-way migration. You won't be able to open the imported databas &Settings - &Настройки + &Параметры Password Generator @@ -2770,11 +2743,11 @@ This is a one-way migration. You won't be able to open the imported databas &Perform Auto-Type - &Произвести автоввод + Вы&полнить автоввод &Open URL - &Открыть адрес + &Открыть URL-адрес &Lock databases @@ -2782,7 +2755,7 @@ This is a one-way migration. You won't be able to open the imported databas &Title - &Имя записи + Назван&ие Copy title to clipboard @@ -2790,7 +2763,7 @@ This is a one-way migration. You won't be able to open the imported databas &URL - &Адрес + &URL-адрес Copy URL to clipboard @@ -2822,19 +2795,19 @@ This is a one-way migration. You won't be able to open the imported databas Show TOTP - Показать ВРП + Показать TOTP Set up TOTP... - Установить ВРП... + Установить TOTP... Copy &TOTP - Копировать &ВРП + Скопировать &TOTP E&mpty recycle bin - &Пустая корзина + О&чистить корзину Clear history @@ -2846,7 +2819,7 @@ This is a one-way migration. You won't be able to open the imported databas <p>It looks like you are using KeePassHTTP for browser integration. This feature has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a> (warning %1 of 3).</p> - <p>Похоже вы используете KeePassHTTP для интеграции с браузером. Эта функция является устаревшей и будет удалена в будущем.<br>Пожалуйста, перейдите на KeePassXC-Browser! Чтобы получить помощь прочтите наше <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">руководство по миграции</a> (предупреждение %1 of 3).</p> + <p>Похоже вы используете KeePassHTTP для интеграции с браузером. Эта функция является устаревшей и будет удалена в будущем.<br>Пожалуйста перейдите на KeePassXC-Browser! Чтобы получить помощь прочтите наше <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">руководство по миграции</a> (предупреждение %1 of 3).</p> read-only @@ -2854,7 +2827,7 @@ This is a one-way migration. You won't be able to open the imported databas Settings - Настройки + Параметры Toggle window @@ -2892,16 +2865,16 @@ This is a one-way migration. You won't be able to open the imported databas WARNING: You are using an unstable build of KeePassXC! There is a high risk of corruption, maintain a backup of your databases. This version is not meant for production use. - Предупреждение: Вы используете нестабильную сборку KeePassXC! -Существует высокий риск повреждения базы данных, сделайте резервную копию базы данных. -Эта версия не предназначен для повседневного использования. + ВНИМАНИЕ: Используется нестабильная сборка KeePassXC! +Весьма вероятно повреждение базы данных, сделайте её резервную копию. +Эта версия не предназначена для повседневного использования. OpenSSHKey Invalid key file, expecting an OpenSSH key - Недопустимый файл ключа, ожидается OpenSSH ключ + Недопустимый ключевой файл, ожидается ключ OpenSSH PEM boundary mismatch @@ -2917,23 +2890,23 @@ This version is not meant for production use. Key file magic header id invalid - Недопустимый идентификатор заголовка файла ключа + Недопустимый идентификатор заголовка ключевого файла Found zero keys - Найдены пустые ключи + Обнаружены пустые ключи Failed to read public key. - Не удалось прочесть открытый ключ. + Не удалось прочитать публичный ключ. Corrupted key file, reading private key failed - Поврежденный файл ключа, чтение закрытого ключа не удалось + Поврежденный ключевой файл, ошибка чтения частного ключа No private key payload to decrypt - В закрытом ключе нет сведений для дешифрования + Нет сведений для дешифрования в частном ключе Trying to run KDF without cipher @@ -2941,47 +2914,47 @@ This version is not meant for production use. Passphrase is required to decrypt this key - Пароль требуется для расшифровки этого ключа + Для расшифровки этого ключа требуется пароль Key derivation failed, key file corrupted? - Формирования ключа не удалось, ключевой файл поврежден? + Ошибка формирования ключа, повреждён ключевой файл? Decryption failed, wrong passphrase? - Расшифровка не удалась, неправильный пароль? + Ошибка расшифровки, неверный пароль? Unexpected EOF while reading public key - Неожиданное EOF при чтении открытого ключа + Неожиданный конец файла при чтении публичного ключа Unexpected EOF while reading private key - Неожиданный EOF при чтении закрытого ключа + Неожиданный конец файла при чтении частного ключа Can't write public key as it is empty - Не удается записать открытый ключ, так как он пуст + Невозможно записать публичный ключ, так как он пуст Unexpected EOF when writing public key - Неожиданный EOF при записе открытого ключа + Неожиданный конец файла при записи публичного ключа Can't write private key as it is empty - Нельзя записать закрытый ключ, поскольку он пустой + Невозможно записать частный ключ, так как он пуст Unexpected EOF when writing private key - Неожиданный EOF при записи закрытого ключа + Неожиданный конец файла при записи частного ключа Unsupported key type: %1 - Неизвестный тип ключа: %1 + Неподдерживаемый тип ключа: %1 Unknown cipher: %1 - Неизвестный шифр: %1 + Неподдерживаемый шифр: %1 Cipher IV is too short for MD5 kdf @@ -3017,15 +2990,15 @@ This version is not meant for production use. Sh&ow a notification when credentials are requested Credentials mean login data requested via browser extension - Показывать уведомление при запросе учётных данных + Уведомлять при запросе учётных данных Only returns the best matches for a specific URL instead of all entries for the whole domain. - Возвращает только лучшие совпадения для определенного URL вместо всех записей для всего домена. + Возвращает только лучшие совпадения для определённого URL-адреса, а не все записи для целого домена. &Return only best matching entries - &Показывать только лучшие совпадения + Только лучшие сов&падения Re&quest to unlock the database if it is locked @@ -3037,7 +3010,7 @@ This version is not meant for production use. &Match URL schemes - &Проверять протокол + &Проверять протокол URL Sort matching entries by &username @@ -3053,7 +3026,7 @@ This version is not meant for production use. Re&move all stored permissions from entries in active database - Удалить все сохранённые права доступа из записей активной базы данных + Удалить все сохранённые разрешения из записей активной базы данных Password Generator @@ -3061,7 +3034,7 @@ This version is not meant for production use. Advanced - Продвинутые + Дополнительно Always allow &access to entries @@ -3085,7 +3058,7 @@ This version is not meant for production use. &Return advanced string fields which start with "KPH: " - Возвращать продвинутые стро&ковые поля, начинающиеся с «KPH: » + Возвращать дополнительные стро&ковые поля, начинающиеся с «KPH: » HTTP Port: @@ -3101,11 +3074,11 @@ This version is not meant for production use. <b>Warning:</b> The following options can be dangerous! - <b>Предупреждение:</b> Следующие параметры могут быть опасны! + <b>ВНИМАНИЕ:</b> Следующие параметры могут быть опасны! <p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p> - <p>KeePassHTTP устарел и будет удален в будущем.<br>Пожалуйста, перейдите на KeePassXC-Browser! Чтобы получить помощь прочтите наше <a href="https://keepassxc.org/docs/keepassxc-browser-migration">руководство по миграции</a>.</p> + <p>KeePassHTTP устарел и в будущем будет удалён.<br>Перейдите на KeePassXC-Browser! Если вам нужна помощь, прочтите <a href="https://keepassxc.org/docs/keepassxc-browser-migration">руководство по переходу</a>.</p> Cannot bind to privileged ports @@ -3114,7 +3087,7 @@ This version is not meant for production use. Cannot bind to privileged ports below 1024! Using default port 19455. - Не удаётся привязать к привилегированным портам с номерами меньше 1024! + Не удаётся выполнить привязку к привилегированным портам с номерами меньше 1024! Используется порт по умолчанию: 19455. @@ -3159,7 +3132,7 @@ Using default port 19455. Special Characters - Особые символы + Специальные символы Extended ASCII @@ -3167,7 +3140,7 @@ Using default port 19455. Exclude look-alike characters - Исключить похожие символы + Не использовать визуально схожие символы Pick characters from every group @@ -3224,22 +3197,22 @@ Using default port 19455. Poor Password quality - Плохое + Плохой Weak Password quality - Слабое + Слабый Good Password quality - Хорошее + Хороший Excellent Password quality - Отличное + Отличный @@ -3250,19 +3223,19 @@ Using default port 19455. Database hash not available - Отсутствует хэш базы данных + Отсутствует хеш базы данных Client public key not received - Не получен открытый ключ клиента + Не получен публичный ключ клиента Cannot decrypt message - Не удается расшифровать сообщение + Невозможно расшифровать сообщение Timeout or cannot connect to KeePassXC - Истекло время ожидания или невозможно подключиться к KeePassXC + Истекло время ожидания, либо невозможно подключиться к KeePassXC Action cancelled or denied @@ -3270,23 +3243,23 @@ Using default port 19455. Cannot encrypt message or public key not found. Is Native Messaging enabled in KeePassXC? - Не удается зашифровать сообщение или открытый ключ не найден. Механизм Native Messaging включен в KeePassXC? + Невозможно зашифровать сообщение, либо не найден публичный ключ. Включён ли в KeePassXC механизм Native Messaging? KeePassXC association failed, try again - KeePassXC объединение не удалось, попробуйте еще раз + Ассоциирование KeePassXC не выполнено, попробуйте ещё раз Key change was not successful - Изменение ключа не было успешным + Изменить ключ не удалось Encryption key is not recognized - Ключ шифрования не распознан + Не распознан ключ шифрования No saved databases found - Сохраненная база данных не найдена + Нет сохранённых баз данных Incorrect action @@ -3318,7 +3291,7 @@ Using default port 19455. Key file of the database. - Ключ-файл базы данных. + Ключевой файл базы данных. path @@ -3338,7 +3311,7 @@ Using default port 19455. URL - Адрес + URL-адрес Prompt for the entry's password. @@ -3358,7 +3331,7 @@ Using default port 19455. Path of the entry to add. - Путь к записе для добавления. + Путь к записи для добавления. Copy an entry's password to the clipboard. @@ -3367,7 +3340,7 @@ Using default port 19455. Path of the entry to clip. clip = copy to clipboard - Скопировать путь до записи. + Скопировать путь к записи. Timeout in seconds before clearing the clipboard. @@ -3379,7 +3352,7 @@ Using default port 19455. Title for the entry. - Название для записи. + Название записи. title @@ -3387,7 +3360,7 @@ Using default port 19455. Path of the entry to edit. - Путь к записе для редактирования. + Путь к записи для редактирования. Estimate the entropy of a password. @@ -3415,17 +3388,17 @@ Using default port 19455. Failed to load key file %1 : %2 - Не удалось загрузить ключ-файл %1: %2 + Не удалось загрузить ключевой файл %1: %2 WARNING: You are using a legacy key file format which may become unsupported in the future. Please consider generating a new key file. - ПРЕДУПРЕЖДЕНИЕ: Вы используете устаревший формат файл ключа, поддержка которого + ВНИМАНИЕ: Вы используете ключевой файл устаревшего формата, поддержка которого может быть прекращена в будущем. -Рассмотрите возможность создания нового файла ключа. +По возможности создайте новый ключевой файл. @@ -3451,7 +3424,7 @@ Available commands: Find entries quickly. - Найти записи быстро. + Быстрый поиск записей. Search term. @@ -3463,11 +3436,11 @@ Available commands: Path of the database to merge into. - Путь к базе данных для объединения в. + Путь к базе-приёмнику для объединения. Path of the database to merge from. - Путь к базе данных для слияния. + Путь к базе-источнику для объединения. Use the same credentials for both database files. @@ -3475,7 +3448,7 @@ Available commands: Key file of the database to merge from. - Файл ключа базы данных для слияния. + Ключевой файл базы данных для объединения. Show an entry's information. @@ -3483,7 +3456,7 @@ Available commands: Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given. - Имена атрибутов для отображения. Эта опция может быть указана более одного раза, причем каждый атрибут будет показан по одному в строке в заданном порядке. Если атрибуты не указаны, дается сводка атрибутов по умолчанию. + Имена атрибутов для отображения. Эта опция может быть указана более одного раза - каждый атрибут будет показан по одному в строке в заданном порядке. Если атрибуты не указаны, даётся сводка атрибутов по умолчанию. attribute @@ -3513,7 +3486,31 @@ Available commands: missing closing quote - Отсутствует закрывающая цитата + Отсутствует закрывающая кавычка + + + AES: 256-bit + AES: 256-бит + + + Twofish: 256-bit + Twofish: 256-бит + + + ChaCha20: 256-bit + ChaCha20: 256-бит + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – рекомендуется) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) Group @@ -3521,7 +3518,7 @@ Available commands: Title - Имя записи + Название Username @@ -3537,7 +3534,7 @@ Available commands: Last Modified - Последнее изменение + Изменён Created @@ -3553,7 +3550,7 @@ Available commands: YubiKey[%1] Challenge Response - Slot %2 - %3 - YubiKey[%1] Запрос ответа - слот %2 - %3 + YubiKey[%1] Вызов-ответ - слот %2 - %3 Press @@ -3565,7 +3562,7 @@ Available commands: SSH Agent - SSH Агент + SSH-агент Generate a new random diceware passphrase. @@ -3595,7 +3592,7 @@ Available commands: Use lowercase characters in the generated password. - Использовать символы в нижнем регистре в генерируемом пароле. + Использовать строчные буквы в генерируемом пароле. Use uppercase characters in the generated password. @@ -3611,7 +3608,7 @@ Available commands: Use extended ASCII in the generated password. - Использовать расширенный ASCII в генерируемом пароле. + Использовать расширенные символы ASCII в генерируемом пароле. @@ -3664,11 +3661,11 @@ Available commands: Case Sensitive - Чувствительно к регистру + Различать регистр Limit search to selected group - Ограничить поиск выбранной группой + Поиск только в выбранной группе @@ -3727,11 +3724,11 @@ Please unlock the selected database or choose another one which is unlocked. No shared encryption-keys found in KeePassHttp Settings. - Не найдено общих ключей шифрования в настройках KeePassHttp. + Совместно используемые ключи шифрования не заданы в параметрах KeePassHttp. KeePassXC: Settings not available! - KeePassXC: Настройки недоступны! + KeePassXC: Параметры недоступны! The active database does not contain an entry of KeePassHttp Settings. @@ -3739,7 +3736,7 @@ Please unlock the selected database or choose another one which is unlocked. Removing stored permissions... - Удаляю сохранённые права доступа... + Удаление сохранённых разрешений... Abort @@ -3747,19 +3744,19 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Removed permissions - KeePassXC: Права доступа удалены + KeePassXC: Разрешения удалены Successfully removed permissions from %n entries. - Успешно удалены доступы для %n записи.Успешно удалены доступы для %n записей.Успешно удалены доступы для %n записей.Успешно удалены доступы для %n записей. + Успешно удалены разрешения %n записей.Успешно удалены разрешения %n записей.Успешно удалены разрешения %n записей.Успешно удалены разрешения для %n записей. KeePassXC: No entry with permissions found! - KeePassXC: Не найдена запись с правами доступа! + KeePassXC: Не найдена запись с разрешениями! The active database does not contain an entry with permissions. - Активная база данных не содержит записей с назначенными правами доступа. + Активная база данных не содержит записей с разрешениями. @@ -3797,7 +3794,7 @@ Please unlock the selected database or choose another one which is unlocked. Remember last key files and security dongles - Запоминать последние использованные файлы ключей и устройства + Запоминать последние использованные ключевые файлы и устройства Load previous databases on startup @@ -3829,7 +3826,7 @@ Please unlock the selected database or choose another one which is unlocked. Don't mark database as modified for non-data changes (e.g., expanding groups) - Не помечать базу данных изменённой при действиях, не связанных с изменением данных (например, при распахивании групп) + Не помечать базу данных изменённой при действиях, не связанных с изменением данных (например при распахивании групп) Hide the Details view @@ -3837,19 +3834,19 @@ Please unlock the selected database or choose another one which is unlocked. Show a system tray icon - Показывать значок в системном лотке + Значок в области уведомлений Hide window to system tray when minimized - При сворачивании скрывать окно в системный лоток + При сворачивании скрывать окно в область уведомлений Hide window to system tray instead of app exit - Скрывать окно в системный лоток вместо выхода + Скрывать окно в область уведомлений вместо выхода Dark system tray icon - Темная иконка в системном трее + Тёмный значок в области уведомлений Language @@ -3861,7 +3858,7 @@ Please unlock the selected database or choose another one which is unlocked. Use entry title to match windows for global Auto-Type - Использовать название записи, для глобального автоввода + Использовать название записи для глобального автоввода Use entry URL to match windows for global Auto-Type @@ -3869,11 +3866,11 @@ Please unlock the selected database or choose another one which is unlocked. Always ask before performing Auto-Type - Всегда спрашивать перед выполнением автоввода + Всегда спрашивать перед автовводом Global Auto-Type shortcut - Глобальная комбинация клавиш для автоввода + Комбинация клавиш для глобального автоввода Auto-Type delay @@ -3894,7 +3891,7 @@ Please unlock the selected database or choose another one which is unlocked. Safely save database files (may be incompatible with Dropbox, etc) - Безопасное сохранение файлов базы данных (может быть несовместимо с Dropbox и другими) + Безопасное сохранение файлов базы данных (возможна несовместимость с Dropbox и др.) Backup database file before saving @@ -3926,7 +3923,7 @@ Please unlock the selected database or choose another one which is unlocked. Lock databases after inactivity of - Блокировать базу данных при отсутствии активности длительностью + Блокировать базу данных при отсутствии активности в течение Convenience @@ -3934,11 +3931,11 @@ Please unlock the selected database or choose another one which is unlocked. Lock databases when session is locked or lid is closed - Блокировать базу данных при закрытии сеанса или закрытии крышки + Блокировать базу данных при блокировке сеанса или закрытии крышки ноутбука Lock databases after minimizing the window - Блокировать базу данных при сворачивания окна + Блокировать базу данных при сворачивании окна Don't require password repeat when it is visible @@ -3962,7 +3959,7 @@ Please unlock the selected database or choose another one which is unlocked. Use Google as fallback for downloading website icons - Использовать Google в качестве резервного варианта для получения значков веб-сайтов + Использовать Google как резервный вариант для загрузки значков сайтов Re-lock previously locked database after performing Auto-Type @@ -3973,7 +3970,7 @@ Please unlock the selected database or choose another one which is unlocked.SetupTotpDialog Setup TOTP - Настроить ВРП + Настроить TOTP Key: @@ -3989,15 +3986,15 @@ Please unlock the selected database or choose another one which is unlocked. Use custom settings - Использовать пользовательские настройки + Использовать пользовательские параметры Note: Change these settings only if you know what you are doing. - Примечание. Изменяйте эти параметры, только если знаете, что делаете. + Примечание: изменяйте эти параметры, только если знаете, что делаете. Time step: - Шаг по времени: + Временной шаг: 8 digits @@ -4033,7 +4030,7 @@ Please unlock the selected database or choose another one which is unlocked. Expires in - Истекает в + Истекает через seconds @@ -4059,7 +4056,7 @@ Please unlock the selected database or choose another one which is unlocked. Open existing database - Открыть существующую базу данных + Открыть имеющуюся базу данных Import from KeePass 1 @@ -4075,7 +4072,7 @@ Please unlock the selected database or choose another one which is unlocked. Welcome to KeePassXC %1 - Добро пожаловать в KeePassXC %1 + Вас приветствует KeePassXC %1 @@ -4090,7 +4087,7 @@ Please unlock the selected database or choose another one which is unlocked. Path of the entry to remove. - Путь к записи, для удаления. + Путь к записи для удаления. KeePassXC - cross-platform password manager @@ -4106,11 +4103,11 @@ Please unlock the selected database or choose another one which is unlocked. key file of the database - файл-ключ базы данных + ключевой файл базы данных read password of the database from stdin - читать пароли базы данных из стандартного ввода «stdin» + читать пароли базы данных с stdin Parent window handle diff --git a/share/translations/keepassx_sk.ts b/share/translations/keepassx_sk.ts index 0e73dd894..1cc602356 100644 --- a/share/translations/keepassx_sk.ts +++ b/share/translations/keepassx_sk.ts @@ -19,7 +19,7 @@ Contributors - Prispievatelia + K vývoju prispeli <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> @@ -130,7 +130,7 @@ Prosím, zvoľte, či chcete povoliť prístup. The Syntax of your Auto-Type statement is incorrect! - Syntax Vášho Automatického vypĺňania nie je správna! + Syntax Vášho Automatického vypĺňania nieje správna! This Auto-Type command contains a very long delay. Do you really want to proceed? @@ -138,7 +138,7 @@ Prosím, zvoľte, či chcete povoliť prístup. This Auto-Type command contains very slow key presses. Do you really want to proceed? - Tento príkaz Automatického vypĺňania obsahuje príliš pomalé stlačenia kláves. Naozaj ho chcete vykonať? + Tento príkaz Automatického vypĺňania obsahuje príliš pomalé stlačenia kláves. Do you really want to proceed? This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed? @@ -329,7 +329,7 @@ Prosím, zvoľte, či chcete povoliť prístup. Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup. - Pri štarte automaticky aktualizovať cestu spustiteľného súboru s KeePassXC alebo keepassxc-proxy na skripty posielania správ medzi prehliadačom a KeePassXC (native messaging). + Pri spúšťaní automaticky aktualizovať cestu spustiteľného súboru s KeePassXC alebo keepassxc-proxy na skripty posielania správ medzi prehliadačom a KeePassXC (native messaging). Update &native messaging manifest files at startup @@ -617,7 +617,7 @@ Prosím, zvážte vygenerovanie nového súboru kľúča. Comments start with - Komentáre začínajú znakom + Komentáre začínajú First record has field names @@ -861,7 +861,7 @@ Ak ponecháte toto číslo, môže byť prelomenie ochrany databázy príliš je MiB Abbreviation for Mebibytes (KDF settings) - MiBMiBMiBMiB + MiB MiB MiB MiB thread(s) @@ -877,7 +877,7 @@ Ak ponecháte toto číslo, môže byť prelomenie ochrany databázy príliš je AES: 256 Bit (default) - AES: 256 bitov (predvolené) + AES: 256 bit (predvolené) Twofish: 256 Bit @@ -1375,7 +1375,7 @@ Chcete zlúčiť svoje zmeny? %n month(s) - %n mesiac%n mesiace%n mesiacov%n mesiacov + %n mesiacoch%n mesiacoch%n mesiacoch%n mesiacoch 1 year @@ -1692,7 +1692,7 @@ Chcete zlúčiť svoje zmeny? Unable to fetch favicon. - Nemožno stiahnuť ikonu stránky. + Nemožno stiahnuť ikonu stránky Hint: You can enable Google as a fallback under Tools>Settings>Security @@ -1887,7 +1887,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov. Username - Použ. meno: + Používateľské meno URL @@ -1911,7 +1911,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov. Username - Použ. meno: + Používateľské meno URL @@ -2015,7 +2015,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov. A-Z - A-Ž + A-Z Lower Case Letters @@ -2023,7 +2023,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov. a-z - a-ž + a-z Numbers @@ -2161,22 +2161,23 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov. Invalid variant map entry name length Translation: variant map = data structure for storing meta data - Neplatná dĺžka názvu položky meta-dát + Neplatná dĺžka názvu položky mapy varianty Invalid variant map entry name data Translation: variant map = data structure for storing meta data - Neplatné dáta názvu položky meta-dát + Neplatné dáta názvu položky mapy varianty Invalid variant map entry value length Translation: variant map = data structure for storing meta data - Neplatná dĺžka hodnoty položky meta-dát + Neplatná dĺžka hodnoty položky mapy varianty + Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - Neplatné dáta hodnoty položky meta-dát + Neplatné dáta hodnoty položky mapy varianty Invalid variant map Bool entry value length @@ -2247,7 +2248,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov. Invalid compression flags length - Neplatná dĺžka príznakov komprimácie + Nepodporovaný komprimačný algoritmus Unsupported compression algorithm @@ -2259,7 +2260,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov. Invalid transform seed size - Neplatná transformácia náhodnosti (seed) + Neplatná transformácia hlavnej náhodnosti (seed) Invalid transform rounds size @@ -2320,11 +2321,11 @@ Je to jednosmerná migrácia. Importovanú databázu už nebude možné otvoriť Null group uuid - Žiadne UUID skupiny + Nulový UUID skupiny Invalid group icon number - Neplatné číslo ikony skupiny + Neplatný počet ikon skupiny Invalid EnableAutoType value @@ -2340,7 +2341,7 @@ Je to jednosmerná migrácia. Importovanú databázu už nebude možné otvoriť Null DeleteObject uuid - Žiadne UUID DeleteObject + Nulový UUID DeleteObject Missing DeletedObject uuid or time @@ -2348,11 +2349,11 @@ Je to jednosmerná migrácia. Importovanú databázu už nebude možné otvoriť Null entry uuid - Žiadne UUID položky + Nulový UUID položky Invalid entry icon number - Neplatné číslo ikony položky + Neplatný počet ikon položky History element in history entry @@ -2376,7 +2377,7 @@ Je to jednosmerná migrácia. Importovanú databázu už nebude možné otvoriť Entry string key or value missing - Chýba textový kľúč alebo hodnota položky + Chýba kľúč alebo hodnota reťazca položky Duplicate attachment found @@ -2404,7 +2405,7 @@ Je to jednosmerná migrácia. Importovanú databázu už nebude možné otvoriť Invalid color rgb part - Neplatná časť RGB farby + neplatná časť RGB farby Invalid number value @@ -2583,33 +2584,6 @@ Je to jednosmerná migrácia. Importovanú databázu už nebude možné otvoriť Neplatný typ poľa položky - - KeePass2 - - AES: 256-bit - AES: 256-b - - - Twofish: 256-bit - Twofish: 256-b - - - ChaCha20: 256-bit - ChaCha20: 256-b - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – odporúčané) - - Main @@ -3080,7 +3054,7 @@ Táto verzia nie je určená na produkčné použitie. Searc&h in all opened databases for matching entries - &Hľadať vyhovujúce položky vo všetkých otvorených databázach + &Hľadať vyhovujúce položky vo všetkých databázach Automatically creating or updating string fields is not supported. @@ -3227,7 +3201,7 @@ Použitý predvolený port 19455. Poor Password quality - Biedne + Slabé Weak @@ -3305,7 +3279,7 @@ Použitý predvolený port 19455. No logins found - Neboli nájdené prihlásenia + Nebolo nájdené prihlásenie Unknown error @@ -3361,7 +3335,7 @@ Použitý predvolený port 19455. Path of the entry to add. - Cesta pridávanej položky. + Cesta pridávanej položky Copy an entry's password to the clipboard. @@ -3507,8 +3481,7 @@ Dostupné príkazy: file empty ! - súbor je prázdny! - + súbor je prázdny! malformed string @@ -3516,7 +3489,31 @@ Dostupné príkazy: missing closing quote - chýbajúca koncová úvodzovka + chýba koncová úvodzovka + + + AES: 256-bit + AES: 256-b + + + Twofish: 256-bit + Twofish: 256-b + + + ChaCha20: 256-bit + ChaCha20: 256-b + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – odporúčané) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) Group @@ -3722,7 +3719,7 @@ Prosím, odomknite zvolenú databázu alebo zvoľte inú, ktorá nie je zamknut Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - Úspešne odstránený %n šifrovací kľúč z nastavenia KeePassX/Http.Úspešne odstránené %n šifrovacie kľúče z nastavenia KeePassX/Http.Úspešne odstránených %n šifrovacích kľúčov z nastavenia KeePassX/Http.Úspešne odstránených %n šifrovacích kľúčov z nastavenia KeePassX/Http. + Úspešne odstránený %n šifrovací kľúč z nastavenia KeePassX/Http.Úspešne odstránené %n šifrovacie kľúče z nastavenia KeePassX/Http.Úspešne odstránených %n šifrovacích kľúčov z nastavenia KeePassX/HttÚspešne odstránených %n šifrovacích kľúčov z nastavenia KeePassX/Http. KeePassXC: No keys found @@ -3856,7 +3853,7 @@ Prosím, odomknite zvolenú databázu alebo zvoľte inú, ktorá nie je zamknut Language - Jazyk + jazyk Auto-Type diff --git a/share/translations/keepassx_sr.ts b/share/translations/keepassx_sr.ts index b86e3c8bc..cba368b8d 100644 --- a/share/translations/keepassx_sr.ts +++ b/share/translations/keepassx_sr.ts @@ -3,11 +3,11 @@ AboutDialog About KeePassXC - О KeePassXC + O KeePassXC About - О апликацији + O aplikaciji Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> @@ -40,8 +40,7 @@ Version %1 - Верзија %1 - + Верзија %1 Revision: %1 @@ -102,7 +101,7 @@ Kernel: %3 %4 %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 тражи приступ лозинкама за следећу ставку (или ставке). + %1 тражи приступ лозинкама за следеће ставке. Молим вас одаберите да ли желите да одобрите приступ. @@ -152,7 +151,7 @@ Please select whether you want to allow access. Sequence - Редослед + Секвенца Default sequence @@ -210,7 +209,7 @@ Please select whether you want to allow access. %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 тражи приступ лозинкама за следећу ставку (или ставке). + %1 тражи приступ лозинкама за следеће ставке. Молим вас одаберите да ли желите да одобрите приступ. @@ -489,7 +488,7 @@ Please unlock the selected database or choose another one which is unlocked. Browse - Преглед + Разгледај Create @@ -505,7 +504,7 @@ Please unlock the selected database or choose another one which is unlocked. Key files - Кључ-Датотеке + Датотеке са кључем All files @@ -513,15 +512,15 @@ Please unlock the selected database or choose another one which is unlocked. Create Key File... - Креирај Кључ-Датотеку... + Креирај датотеку са кључем... Unable to create Key File : - Није могуће креирати Кључ-Датотеку: + Није могуће креирати Датотеку са Кључем: Select a key file - Одаберите кључ-датотеку + Одаберите датотеку са кључем Empty password @@ -538,7 +537,7 @@ Please unlock the selected database or choose another one which is unlocked. Failed to set %1 as the Key file: %2 - Неуспешно постављање %1 као Кључ-Датотеку: + Неуспешно постављање %1 као Датотеке са Кључем: %2 @@ -569,7 +568,7 @@ Please consider generating a new key file. Replace username and password with references - Замени корисничко име и лозинку са референцама + Замените корисничко име и лозинку са референцама Copy history @@ -624,7 +623,7 @@ Please consider generating a new key file. Preview - Преглед + Приказ Column layout @@ -684,15 +683,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - %n бајт(ова),%n бајт(ова),%n бајт(ова), + %n row(s), - %n ред(ова),%n ред(ова),%n ред(ова), + %n column(s) - %n колона%n колона%n колона + @@ -984,7 +983,7 @@ If you keep this number, your database may be too easy to crack! Open KeePass 1 database - Отвори KeePass 1 базу података + Отвори KeePass 1  базу података KeePass 1 database @@ -1106,7 +1105,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - Да ли сте сигурни да желите да преместите %n ставку у корпу за отпатке?Да ли сте сигурни да желите да преместите %n ставке у корпу за отпатке?Да ли сте сигурни да желите да преместите %n ставку(ставке) у корпу за отпатке? + Execute command? @@ -1234,7 +1233,7 @@ Do you want to merge your changes? Sequence - Редослед + Секвенца Search @@ -1353,11 +1352,11 @@ Do you want to merge your changes? %n week(s) - %n недеља%n недеље(а)%n недеље(а) + %n month(s) - %n месец%n месеца(и)%n месеца(и) + 1 year @@ -1647,7 +1646,7 @@ Do you want to merge your changes? Set default Auto-Type se&quence - Подеси као подразумевану секвенцу за Аутоматско-Куцање + Унеси подразумевану секвенцу за Аутоматско-Куцање @@ -1662,19 +1661,19 @@ Do you want to merge your changes? Add custom icon - Додај посебну иконицу + Додај посебну икону Delete custom icon - Обриши посебну иконицу + Обриши посебну икону Download favicon - Преузми иконицу са сајта + Преузми икону са сајта Unable to fetch favicon. - Није могуће добавити иконицу са сајта. + Неуспело добављање иконе са сајта. Hint: You can enable Google as a fallback under Tools>Settings>Security @@ -2557,33 +2556,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - - - - Twofish: 256-bit - - - - ChaCha20: 256-bit - - - - AES-KDF (KDBX 4) - - - - AES-KDF (KDBX 3.1) - - - - Argon2 (KDBX 4 – recommended) - - - Main @@ -3157,7 +3129,7 @@ Using default port 19455. Wordlist: - Листа фраза: + Листа речи: Word Count: @@ -3198,7 +3170,7 @@ Using default port 19455. Poor Password quality - Бедан + Слаб Weak @@ -3486,6 +3458,30 @@ Available commands: missing closing quote недостаје завршни наводник + + AES: 256-bit + + + + Twofish: 256-bit + + + + ChaCha20: 256-bit + + + + Argon2 (KDBX 4 – recommended) + + + + AES-KDF (KDBX 4) + + + + AES-KDF (KDBX 3.1) + + Group Група diff --git a/share/translations/keepassx_sv.ts b/share/translations/keepassx_sv.ts index f66e51d36..d8b0cd0e0 100644 --- a/share/translations/keepassx_sv.ts +++ b/share/translations/keepassx_sv.ts @@ -19,15 +19,15 @@ Contributors - Medverkande + Bidragsgivare <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> - <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Se Bidragsgivare på GitHub</a> + <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Se alla bidrag på GitHub</a> Debug Info - Felsöknings Information + Felsökningsinformation Include the following information whenever you report a bug: @@ -59,8 +59,8 @@ Operating system: %1 CPU architecture: %2 Kernel: %3 %4 - Operativ system: %1 -CPU-arkitektur: %2 + Operativsystem: %1 +Processorarkitektur: %2 Kärna: %3 %4 @@ -89,7 +89,7 @@ Kärna: %3 %4 Remember this decision - Kom ihåg detta val + Kom ihåg det här valet Allow @@ -102,15 +102,15 @@ Kärna: %3 %4 %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 har begärt åtkomst till lösenord för följande objekt(en). -Vänligen välj om du vill tillåta åtkomst. + %1 har begärt åtkomst till lösenorden för följande objekt. +Vill du tillåta det? AgentSettingsWidget Enable SSH Agent (requires restart) - Aktivera SSH Agent (kräver omstart) + Aktivera SSH-agenten (kräver omstart) @@ -156,7 +156,7 @@ Vänligen välj om du vill tillåta åtkomst. Default sequence - Standard sekvens + Standardsekvens @@ -186,7 +186,7 @@ Vänligen välj om du vill tillåta åtkomst. Select entry to Auto-Type: - Välj post att auto-skriva + Välj post att autoskriva: @@ -302,7 +302,7 @@ Vill du tillåta det? Never &ask before accessing credentials Credentials mean login data requested via browser extension - &Fråga aldrig innan åtkomst till autentiseringsuppgifter + &Fråga aldrig innan åtkomst till autentisieringsuppgifter Never ask before &updating credentials @@ -430,7 +430,7 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: No keys found - KeePassXC: Hittade inga nycklar + KeePassXC: Inga nycklar hittade No shared encryption keys found in KeePassXC Settings. @@ -565,7 +565,7 @@ Please consider generating a new key file. Append ' - Clone' to title - + Lägg till ' - Klon' i titel Replace username and password with references @@ -620,7 +620,7 @@ Please consider generating a new key file. Consider '\' an escape character - Överväg '\' som ändelse tecken + Preview @@ -632,7 +632,7 @@ Please consider generating a new key file. Not present in CSV file - Finns inte i CSV fil + Finns inte i CSV filen Empty fieldname @@ -683,11 +683,11 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - %n byte(s),%n byte(s), + %n byte(s), %n byte(s), %n row(s), - %n rad(er),%n rad(er), + %n rad(er),%n rad(er), %n column(s) @@ -726,7 +726,7 @@ Please consider generating a new key file. Can't open key file - Kan inte öppna nyckel-fil + Kan inte öppna nyckelfilen Legacy key file format @@ -772,7 +772,7 @@ Please consider generating a new key file. Unable to open the database. - Misslyckades att öppna databasen. + Kunde inte öppna databas. Database opened fine. Nothing to do. @@ -797,7 +797,7 @@ Du kan nu spara den. DatabaseSettingsWidget General - Allmänt + Allmän Encryption @@ -844,7 +844,7 @@ If you keep this number, your database may be too easy to crack! MiB Abbreviation for Mebibytes (KDF settings) - MiBMiB + MiB MiB thread(s) @@ -943,7 +943,7 @@ If you keep this number, your database may be too easy to crack! KeePass 2 Database - KeePass 2 Databas + KeePass 2 databas All files @@ -1011,7 +1011,7 @@ Spara ändringarna? Writing the database failed. - Kunde inte skriva till databasen. + Misslyckades med att skriva till databasen. Passwords @@ -1093,7 +1093,7 @@ Disable safe saves and try again? Move entry to recycle bin? - Flytta post till papperskorgen? + Flytta post till soptunnan? Do you really want to move entry "%1" to the recycle bin? @@ -1105,7 +1105,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - Vill du verkligen flytta %n post till papperskorgen?Vill du verkligen flytta %n poster till papperskorgen? + Execute command? @@ -1117,7 +1117,7 @@ Disable safe saves and try again? Remember my choice - Kom ihåg mitt val + Delete group? @@ -1356,7 +1356,7 @@ Do you want to merge your changes? %n month(s) - %n månad%n månader + %n månad(er)%n månad(er) 1 year @@ -1364,7 +1364,7 @@ Do you want to merge your changes? Apply generated password? - + Använd genererat lösenord? Do you want to apply the generated password to this entry? @@ -1399,7 +1399,7 @@ Do you want to merge your changes? Reveal - + Visa Attachments @@ -1511,7 +1511,7 @@ Do you want to merge your changes? EditEntryWidgetSSHAgent Form - + Formulär Remove key from agent after @@ -1555,7 +1555,7 @@ Do you want to merge your changes? Private key - + Private key External file @@ -1657,7 +1657,7 @@ Do you want to merge your changes? Use custo&m icon - + Använd egen ikon Add custom icon @@ -1775,7 +1775,7 @@ This may cause the affected plugins to malfunction. EntryAttachmentsWidget Form - + Formulär Add @@ -1825,17 +1825,20 @@ This may cause the affected plugins to malfunction. Unable to save attachments: %1 - + Kunde inte spara bilagor: +%1 Unable to open attachment: %1 - + Kunde inte öppna bilaga: +%1 Unable to open attachments: %1 - + Kunde inte öppna bilagor: +%1 Unable to open files: @@ -1915,11 +1918,11 @@ This may cause the affected plugins to malfunction. Modified - + Ändrad Accessed - + Läst Attachments @@ -1930,7 +1933,7 @@ This may cause the affected plugins to malfunction. EntryView Customize View - + Anpassa vy Hide Usernames @@ -2412,7 +2415,7 @@ This is a one-way migration. You won't be able to open the imported databas Not a KeePass database. - Inte en KeePass databas + Inte en KeePass databas. Unsupported encryption algorithm. @@ -2556,33 +2559,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - AES: 256-bitar - - - Twofish: 256-bit - Twofish: 256-bitar - - - ChaCha20: 256-bit - ChaCha20: 256-bitar - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – rekommenderad) - - Main @@ -3063,7 +3039,7 @@ This version is not meant for production use. HTTP Port: - Http-Port: + HTTP Port: Default port: 19455 @@ -3164,7 +3140,7 @@ Using default port 19455. Word Separator: - Ord separator: + Ord separerare: Generate @@ -3465,7 +3441,7 @@ Tillgängliga kommandon: NULL device - + NULL device error reading from device @@ -3479,12 +3455,36 @@ Tillgängliga kommandon: malformed string - felaktigt uppbyggd sträng + felaktigt uppbyggd textsträng missing closing quote saknar avslutande citattecken + + AES: 256-bit + AES: 256-bitar + + + Twofish: 256-bit + Twofish: 256-bitar + + + ChaCha20: 256-bit + ChaCha20: 256-bitar + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – rekommenderad) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Grupp @@ -3564,23 +3564,23 @@ Tillgängliga kommandon: Use lowercase characters in the generated password. - + Använd gemener i det genererade lösenordet. Use uppercase characters in the generated password. - + Använd versaler i det genererade lösenordet. Use numbers in the generated password. - + Använd siffror i det genererade lösenordet. Use special characters in the generated password. - + Använd specialtecken i det genererade lösenordet. Use extended ASCII in the generated password. - + Använd utökad ASCII i det genererade lösenordet. @@ -3688,7 +3688,7 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: No keys found - KeePassXC: Inga nycklar hittade + KeePassXC: Hittade inga nycklar No shared encryption-keys found in KeePassHttp Settings. @@ -3716,7 +3716,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entries. - + Framgångsrikt bort behörigheter från %n poster.Lyckades ta bort behörigheter från %n poster. KeePassXC: No entry with permissions found! @@ -3754,7 +3754,7 @@ Please unlock the selected database or choose another one which is unlocked. Start only a single instance of KeePassXC - Tillåt endast en öppen instans av KeePassXC + Tillåt endast en samtidig instans av KeePassXC Remember last databases @@ -3871,7 +3871,7 @@ Please unlock the selected database or choose another one which is unlocked. General - Allmänt + Allmän diff --git a/share/translations/keepassx_th.ts b/share/translations/keepassx_th.ts index ac86a9466..39920a742 100644 --- a/share/translations/keepassx_th.ts +++ b/share/translations/keepassx_th.ts @@ -412,7 +412,7 @@ Do you want to overwrite it? KeePassXC: Database locked! - KeePassXC: ฐานข้อมูลถูกล็อก! + KeePassXC: ฐานข้อมูลล็อกอยู่! The active database is locked! @@ -437,7 +437,7 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Removed keys from database - KeePassXC: กุญแจถูกนำออกจากฐานข้อมูล + KeePassXC: เอากุญแจออกจากฐานข้อมูล Successfully removed %n encryption key(s) from KeePassXC settings. @@ -682,15 +682,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - + %n ไบต์, %n row(s), - + %n แถว, %n column(s) - + %n คอลัมน์ @@ -721,7 +721,7 @@ Please consider generating a new key file. Unable to open the database. - ไม่สามารถเปิดฐานข้อมูล + ไม่สามารถเปิดฐานข้อมูลดังกล่าว Can't open key file @@ -958,7 +958,7 @@ If you keep this number, your database may be too easy to crack! Unable to open the database. - ไม่สามารถเปิดฐานข้อมูล + ไม่สามารถเปิดฐานข้อมูลดังกล่าว File opened in read only mode. @@ -1101,7 +1101,7 @@ Disable safe saves and try again? Do you really want to move %n entry(s) to the recycle bin? - คุณต้องการจะย้ายรายการ %1 รายการไปยังถังขยะจริงๆ? + Execute command? @@ -2400,7 +2400,7 @@ This is a one-way migration. You won't be able to open the imported databas Unable to open the database. - ไม่สามารถเปิดฐานข้อมูล + ไม่สามารถเปิดฐานข้อมูลดังกล่าว @@ -2555,33 +2555,6 @@ This is a one-way migration. You won't be able to open the imported databas - - KeePass2 - - AES: 256-bit - AES: 256 บิต - - - Twofish: 256-bit - Twofish: 256 บิต - - - ChaCha20: 256-bit - ChaCha20: 256 บิต - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – แนะนำ) - - Main @@ -3119,7 +3092,7 @@ Using default port 19455. Upper Case Letters - อักษรพิมพ์ใหญ่ + อักษรตัวพิมพ์ใหญ่ Lower Case Letters @@ -3375,7 +3348,7 @@ Using default port 19455. Extract and print the content of a database. - สกัดและแสดงเนื้อหาของฐานข้อมูล + สกัดและพิมพ์เนื้อหาของฐานข้อมูล Path of the database to extract. @@ -3481,6 +3454,30 @@ Available commands: missing closing quote ไม่มีเครื่องหมายคำพูดปิด + + AES: 256-bit + AES: 256 บิต + + + Twofish: 256-bit + Twofish: 256 บิต + + + ChaCha20: 256-bit + ChaCha20: 256 บิต + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – แนะนำ) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group กลุ่ม @@ -3667,7 +3664,7 @@ Do you want to overwrite it? KeePassXC: Database locked! - KeePassXC: ฐานข้อมูลถูกล็อก! + KeePassXC: ฐานข้อมูลล็อกอยู่! The active database is locked! @@ -3676,7 +3673,7 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Removed keys from database - KeePassXC: กุญแจถูกนำออกจากฐานข้อมูล + KeePassXC: เอากุญแจออกจากฐานข้อมูล Successfully removed %n encryption-key(s) from KeePassX/Http Settings. diff --git a/share/translations/keepassx_tr.ts b/share/translations/keepassx_tr.ts index b10711c9d..ac8603c9b 100644 --- a/share/translations/keepassx_tr.ts +++ b/share/translations/keepassx_tr.ts @@ -19,7 +19,7 @@ Contributors - Katkı Verenler + Katkıcılar <a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a> @@ -90,7 +90,7 @@ MİB mimarisi: %2 Remember this decision - Bu kararı hatırla + Bu kararı anımsa Allow @@ -164,7 +164,7 @@ Lütfen erişime izin vermek istediklerinizi seçin. AutoTypeMatchModel Group - Grup + Küme Title @@ -187,7 +187,7 @@ Lütfen erişime izin vermek istediklerinizi seçin. Select entry to Auto-Type: - Oto-Yazım için girdi seçin: + Oto-Yazım için girdi seçiniz: @@ -198,7 +198,7 @@ Lütfen erişime izin vermek istediklerinizi seçin. Remember this decision - Bu kararı hatırla + Bu kararı anımsa Allow @@ -211,8 +211,8 @@ Lütfen erişime izin vermek istediklerinizi seçin. %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1, şu öge(ler) için şifrelere erişim izni istedi. -Lütfen erişime izin vermek isteyip istemediğinizi belirtin. + %1, şu öge(ler) için parolalara erişim izni istedi. +Lütfen erişime izin vermek istediklerinizi seçin. @@ -260,11 +260,11 @@ Lütfen erişime izin vermek isteyip istemediğinizi belirtin. Re&quest to unlock the database if it is locked - Eğer kilitliyse veritabanını açmayı is&te + Eğer kilitliyse veri tabanını açmayı is&te Only entries with the same scheme (http://, https://, ...) are returned. - Sadece aynı şemaya sahip girişler (http://, https://, ...) döndürülür. + Yalnızca aynı şemadaki girdiler (http://, https://, ...) döndürülür. &Match URL scheme (e.g., https://...) @@ -272,7 +272,7 @@ Lütfen erişime izin vermek isteyip istemediğinizi belirtin. Only returns the best matches for a specific URL instead of all entries for the whole domain. - Yalnızca tüm alan adı için tüm girdiler yerine belirli bir URL için en iyi eşleşenleri döndürür. + Tüm alan adı için tüm girdilerin yerine belirli bir URL için yalnızca en iyi eşleşmeyi döndürür. &Return only best-matching credentials @@ -312,7 +312,7 @@ Lütfen erişime izin vermek isteyip istemediğinizi belirtin. Only the selected database has to be connected with a client. - Sadece seçilen veritabanının bir istemci ile bağlı olması gerekir. + Yalnızca seçilen veri tabanı istemciyle bağlanmış olmalıdır. Searc&h in all opened databases for matching credentials @@ -321,11 +321,11 @@ Lütfen erişime izin vermek isteyip istemediğinizi belirtin. Automatically creating or updating string fields is not supported. - Dizi alanlarını otomatik oluşturma veya güncelleme desteklenmiyor. + Dizge alanlarını kendiliğinden oluşturma ve güncelleme desteklenmiyor. &Return advanced string fields which start with "KPH: " - "KPH: " ile başlayan gelişmiş dizi alanları &döndür + "KPH: " ile başlayan gelişmiş dizge alanları &döndür Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup. @@ -333,7 +333,7 @@ Lütfen erişime izin vermek isteyip istemediğinizi belirtin. Update &native messaging manifest files at startup - Başlangıçta yerel mesajlaşma &manifesto dosyalarını güncelle + Başlangıçta yerel mesajlaşma &amp;manifesto dosyalarını güncelle Support a proxy application between KeePassXC and browser extension. @@ -363,7 +363,7 @@ Lütfen erişime izin vermek isteyip istemediğinizi belirtin. Executable Files (*.exe);;All Files (*.*) - Yürütülebilir Dosyalar (*.exe);;Bütün Dosyalar (*.*) + Yürütülebilir Dosyalar (*.exe);;Tüm Dosyalar (*.*) Executable Files (*) @@ -400,7 +400,7 @@ onu tanımlamak için benzersiz bir isim ver ve kabul et. KeePassXC: Overwrite existing key? - KeePassXC: Mevcut anahtarın üzerine yazılsın mı? + KeePassXC: Var olan anahtarın üstüne yaz? A shared encryption key with the name "%1" already exists. @@ -410,7 +410,7 @@ Do you want to overwrite it? KeePassXC: Update Entry - KeePassXC: Giriş Güncelleme + KeePassXC: Girdi Güncelle Do you want to update the information in %1 - %2? @@ -418,21 +418,21 @@ Do you want to overwrite it? KeePassXC: Database locked! - KeePassXC: Veritabanı kilitli! + KeePassXC: Veri tabanı kilitli! The active database is locked! Please unlock the selected database or choose another one which is unlocked. - Etkin veritabanı kilitli! -Lütfen seçili veritabanının kilidini açın veya kilidi açılan başka bir tane seçin. + Etkin veri tabanı kilitli! +Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birini seçin. KeePassXC: Settings not available! - KeePassXC: Ayarlar mevcut değil! + KeePassXC: Ayarlar kullanılabilir değil! The active database does not contain a settings entry. - Aktif veritabanı bir ayar girişi içermiyor. + Aktif veritabanı ayar girdisi içermiyor. KeePassXC: No keys found @@ -444,11 +444,11 @@ Lütfen seçili veritabanının kilidini açın veya kilidi açılan başka bir KeePassXC: Removed keys from database - KeePassXC: Anahtarlar veritabanından kaldırıldı + KeePassXC: Anahtarlar veri tabanından kaldırıldı Successfully removed %n encryption key(s) from KeePassXC settings. - Başarıyla %n şifreleme anahtarları KeePassXC ayarlarından kaldırıldı.%n şifreleme anahtarı(lar), KeePassXC ayarlarından başarıyla kaldırıldı. + %n şifreleme anahtarı KeePassXC ayarlarından başarıyla kaldırıldı.%n şifreleme anahtarı KeePassXC ayarlarından başarıyla kaldırıldı. Removing stored permissions… @@ -460,11 +460,11 @@ Lütfen seçili veritabanının kilidini açın veya kilidi açılan başka bir KeePassXC: Removed permissions - KeePassXC: Kaldırılan izinler + KeePassXC: Silinen yetkiler Successfully removed permissions from %n entry(s). - Başarıyla izinleri %n entry(s) kaldırıldı.%n giriş(ler)den izinler başarıyla kaldırıldı. + %n girdiden izinler başarıyla kaldırıldı.%n girdiden izinler başarıyla kaldırıldı. KeePassXC: No entry with permissions found! @@ -472,22 +472,22 @@ Lütfen seçili veritabanının kilidini açın veya kilidi açılan başka bir The active database does not contain an entry with permissions. - Etkin veritabanı izinli bir giriş içermiyor. + Etkin veri tabanı izinleri olan girdi barındırmıyor. ChangeMasterKeyWidget Password - Şifre + Parola Enter password: - Şifre gir: + Parolayı gir: Repeat password: - Şifreyi yinele: + Parolayı yinele: &Key file @@ -515,7 +515,7 @@ Lütfen seçili veritabanının kilidini açın veya kilidi açılan başka bir All files - Bütün dosyalar + Tüm dosyalar Create Key File... @@ -535,16 +535,16 @@ Lütfen seçili veritabanının kilidini açın veya kilidi açılan başka bir Do you really want to use an empty string as password? - Boş bir dizeyi gerçekten şifre olarak kullanmak ister misiniz? + Parola olarak boş bir dizgeyi kullanmak istediğinize emin misiniz? Different passwords supplied. - Farklı şifreler sağlandı. + Farklı parolalar sağlandı. Failed to set %1 as the Key file: %2 - %1 anahtar dosyası olarak ayarlanamadı: + %1, Anahtar dosyası olarak belirlenemedi: %2 @@ -563,7 +563,7 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Changing master key failed: no YubiKey inserted. - Ana anahtar değiştirme başarısız: YubiKey eklenmedi. + Ana anahtar değiştirme başarısız: YubiKey yerleştirilmedi. @@ -578,7 +578,7 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Replace username and password with references - Kullanıcı adı ve şifreyi referanslarla değiştir + Kullanıcı adı ve parolayı referanslarla değiştir Copy history @@ -609,11 +609,11 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Text is qualified by - tarafından metin yetkili + Şu tarafından metin yetkilendirildi Fields are separated by - tarafından alanlar ayrıldı + Şu tarafından alanlar bölümlendi Comments start with @@ -621,11 +621,11 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. First record has field names - İlk kaydın alan adlarını var + İlk kayıt alan adlarını içerir Number of headers line to discard - Vazgeçilecek başlık satırı sayısı + Vazgeçilecek başlık satırı adedi Consider '\' an escape character @@ -633,15 +633,15 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Preview - Ön izleme + Ön izle Column layout - Sütun düzeni + Kolon dizimi Not present in CSV file - CSV dosyasında mevcut değil + CSV içerisinde mevcut değil Empty fieldname @@ -649,7 +649,7 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. column - sütun + kolon Imported from CSV file @@ -657,11 +657,11 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Original data: - Orijinal veri: + Özgün veri: Error(s) detected in CSV file ! - CSV dosyasında hata(lar) tespit edildi ! + CSV dosyasında hata(lar) saptandı ! more messages skipped] @@ -674,7 +674,7 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. CSV import: writer has errors: - CSV içe aktarma: yazarda hatalar var: + CSV içe aktarma: yazıcıda hatalar: @@ -693,22 +693,22 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. CsvParserModel %n byte(s), - %n bayt, %n bayt(lar), + %n bayt, %n bayt,  %n row(s), - %n satır, %n satır(lar), + %n satır, %n satır, %n column(s) - %n sütun%n sütun(lar) + %n sütun%n sütun DatabaseOpenWidget Enter master key - Ana anahtarı gir + Ana anahtar gir Key File: @@ -716,7 +716,7 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Password: - Şifre: + Parola: Browse @@ -732,11 +732,11 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Unable to open the database. - Veritabanı açılamıyor. + Veri tabanı açılamıyor. Can't open key file - Anahtar dosyası açılamıyor + Anahtar dosya açılamıyor Legacy key file format @@ -758,7 +758,7 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. All files - Bütün dosyalar + Tüm dosyalar Key files @@ -773,7 +773,7 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. DatabaseRepairWidget Repair database - Veritabanını onar + Veri tabanını onar Error @@ -781,15 +781,15 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. Can't open key file - Anahtar dosyası açılamıyor + Anahtar dosya açılamıyor Unable to open the database. - Veritabanı açılamıyor. + Veri tabanı açılamıyor. Database opened fine. Nothing to do. - Veritabanı iyi açıldı. Yapılacak bir şey yok. + Veri tabanı açıldı. Yapılacak bir şey yok. Success @@ -798,12 +798,12 @@ Lütfen yeni bir anahtar dosyası oluşturmayı düşünün. The database has been successfully repaired You can now save it. - Veritabanı başarıyla onarıldı + Veri tabanı başarıyla onarıldı Artık kaydedebilirsiniz. Unable to repair the database. - Veritabanı onarılamıyor. + Veri tabanı onarılamıyor. @@ -866,7 +866,7 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır thread(s) Threads for parallel execution (KDF settings) - iş parçacıği iş parçacık(ları) + iş parçacığıiş parçacığı @@ -877,7 +877,7 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır AES: 256 Bit (default) - AES: 256 Bit (varsayılan) + AES: 256 Bit (öntanımlı) Twofish: 256 Bit @@ -912,15 +912,15 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır Database name: - Veritabanı adı: + Veri tabanı adı: Database description: - Veritabanı açıklaması: + Veri tabanı ayrıntısı: Default username: - Varsayılan kullanıcı adı: + Öntanımlı kullanıcı adı: History Settings @@ -960,15 +960,15 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır KeePass 2 Database - KeePass 2 Veritabanı + KeePass 2 Veri Tabanı All files - Bütün dosyalar + Tüm dosyalar Open database - Veritabanını aç + Veri tabanı aç File not found! @@ -976,11 +976,11 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır Unable to open the database. - Veritabanı açılamıyor. + Veri tabanı açılamıyor. File opened in read only mode. - Dosya salt okunur modda açıldı. + Dosya salt okunur kipte açıldı. Open CSV file @@ -992,19 +992,19 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır All files (*) - Bütün dosyalar (*) + Tüm dosyalar (*) Merge database - Veritabanını birleştir + Veri tabanı birleştir Open KeePass 1 database - KeePass 1 veritabanı aç + KeePass 1 veri tabanı aç KeePass 1 database - KeePass 1 veritabanı + KeePass 1 veri tabanı Close? @@ -1013,8 +1013,8 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır "%1" is in edit mode. Discard changes and close anyway? - "%1" düzenleme modunda. -Değişikliklerden vazgeçip yine de kapatılsın mı? + "%1", düzenleme kipinde. +Değişikliklerden vazgeç ve her durumda kapat? Save changes? @@ -1024,23 +1024,23 @@ Değişikliklerden vazgeçip yine de kapatılsın mı? "%1" was modified. Save changes? "%1" değiştirildi. -Değişiklikler kaydedilsin mi? +Değişiklikleri kaydet? Writing the database failed. - Veritabanına yazma başarısız. + Veri tabanına yazma başarısız. Passwords - Şifreler + Parolalar Save database as - Veritabanını farklı kaydet + Veri tabanını farklı kaydet Export database to CSV file - Veritabanını CSV dosyasına dışa aktar + Veri tabanını CSV dosyasına dışa aktar Writing the CSV file failed. @@ -1048,7 +1048,7 @@ Değişiklikler kaydedilsin mi? New database - Yeni veritabanı + Yeni veri tabanı locked @@ -1056,25 +1056,25 @@ Değişiklikler kaydedilsin mi? Lock database - Veritabanını kilitle + Veri tabanını kilitle Can't lock the database as you are currently editing it. Please press cancel to finish your changes or discard them. - Şu anda düzenlemekte olduğunuz veritabanı kilitlenemiyor. -Lütfen değişikliklerinizi sonlandırmak veya vazgeçmek için iptal tuşuna basın. + Şu anda düzenlediğiniz için veri tabanı kilitlenemez. +Lütfen değişikliklerinizi bitirmek için iptale basın veya onlardan vazgeçin. This database has been modified. Do you want to save the database before locking it? Otherwise your changes are lost. - Bu veritabanı değiştirildi. -Veritabanını kilitlemeden önce kaydetmek ister misiniz? + Veri tabanı değiştirildi. +Kilitlemeden önce veri tabanını kaydetmek ister misiniz? Aksi halde değişiklikleriniz kaybolacak. Disable safe saves? - Güvenli kaydetme devre dışı? + Güvenli kaydetme devre-dışı? KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. @@ -1099,7 +1099,7 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi? Do you really want to delete the entry "%1" for good? - "%1" girdisini gerçekten tamamen silmek istiyor musunuz? + "%1" girdisini tümüyle silmek istediğinize emin misiniz? Delete entries? @@ -1135,15 +1135,15 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi? Remember my choice - Seçimimi hatırla + Seçimimi anımsa Delete group? - Grubu sil? + Kümeyi sil? Do you really want to delete the group "%1" for good? - "%1" grubunu gerçekten tamamen silmek istiyor musunuz? + "%1" kümesini tümüyle silmek istediğinize emin misiniz? Unable to calculate master key @@ -1151,11 +1151,11 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi? No current database. - Geçerli veritabanı yok. + Geçerli veri tabanı yok. No source database, nothing to do. - Kaynak veritabanı yok, yapılacak bir şey yok. + Kaynak veri tabanı yok, yapılacak bir şey yok. Search Results (%1) @@ -1171,7 +1171,7 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi? The database file has changed. Do you want to load the changes? - Veritabanı dosyası değiştirildi. Değişiklikleri yüklemek ister misiniz? + Veri tabanı dosyası değiştirildi. Değişiklikleri yüklemek ister misiniz? Merge Request @@ -1185,7 +1185,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Could not open the new database file while attempting to autoreload this database. - Bu veritabanını otomatik olarak yeniden yüklemeye çalışırken yeni veritabanı dosyası açılamadı. + Bu veri tabanını kendiliğinden yeniden yüklenirken yeni veri tabanı dosyası açılamadı. Empty recycle bin? @@ -1212,7 +1212,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Password - Şifre + Parola URL @@ -1240,7 +1240,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Attachments - Dosya ekleri + Ekler Notes @@ -1283,7 +1283,7 @@ Değişikliklerinizi birleştirmek ister misiniz? EditEntryWidget Entry - Giriş + Girdi Advanced @@ -1311,7 +1311,7 @@ Değişikliklerinizi birleştirmek ister misiniz? n/a - + yok (encrypted) @@ -1323,7 +1323,7 @@ Değişikliklerinizi birleştirmek ister misiniz? File too large to be a private key - Dosya bir özel anahtar olmak için çok büyük + Dosya özel anahtar olmak için çok büyük Failed to open private key @@ -1331,19 +1331,19 @@ Değişikliklerinizi birleştirmek ister misiniz? Entry history - Giriş geçmişi + Girdi geçmişi Add entry - Giriş ekle + Girdi ekle Edit entry - Girişi düzenle + Girdiyi düzenle Different passwords supplied. - Farklı şifreler sağlandı. + Farklı parolalar sağlandı. New attribute @@ -1355,7 +1355,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Are you sure you want to remove this attribute? - Bu özniteliği kaldırmak istediğinizden emin misiniz? + Bu özniteliği silmek istediğinizden emin misiniz? [PROTECTED] @@ -1383,7 +1383,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Apply generated password? - Oluşturulan şifreyi uygula? + Oluşturulan parolayı uygula? Do you want to apply the generated password to this entry? @@ -1391,7 +1391,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Entry updated successfully. - Giriş başarıyla güncellendi. + Girdi güncelleme başarılı. @@ -1422,11 +1422,11 @@ Değişikliklerinizi birleştirmek ister misiniz? Attachments - Dosya ekleri + Ekler Foreground Color: - Ön Plan Rengi: + Ön plan Rengi: Background Color: @@ -1437,15 +1437,15 @@ Değişikliklerinizi birleştirmek ister misiniz? EditEntryWidgetAutoType Enable Auto-Type for this entry - Bu giriş için Oto-Yazımı etkinleştir + Bu girdi için Oto-Yazımı etkinleştir Inherit default Auto-Type sequence from the &group - &Gruptan varsayılan Oto-Yazım sırasını devral + Öntanımlı Oto-Yazım dizilişini &kümeden devral &Use custom Auto-Type sequence: - Özel Oto-Yazım sırası k&ullan: + Özel Oto-Yazım dizilişi k&ullan: Window Associations @@ -1495,7 +1495,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Password: - Şifre: + Parola: Repeat: @@ -1523,7 +1523,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Expires - Geçersiz + Biter @@ -1554,7 +1554,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Add key to agent when database is opened/unlocked - Veritabanı kapalı/kilitliyken ajana anahtar ekle + Veritabanı kapalı/kilitliyken ajana anahtarı ekle Comment @@ -1562,11 +1562,11 @@ Değişikliklerinizi birleştirmek ister misiniz? Decrypt - Şifresiz + Şifreyi Çöz n/a - + yok Copy to clipboard @@ -1587,7 +1587,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Attachment - Dosya eki + Dosya Eki Add to agent @@ -1599,14 +1599,14 @@ Değişikliklerinizi birleştirmek ister misiniz? Require user confirmation when this key is used - Bu tuş kullanıldığında kullanıcı onayı gerekir + Bu tuş kullanıldığında kullanıcı onayı iste EditGroupWidget Group - Grup + Küme Icon @@ -1618,30 +1618,30 @@ Değişikliklerinizi birleştirmek ister misiniz? Add group - Grup ekle + Küme ekle Edit group - Grubu düzenle + Kümeyi düzenle Enable - Etkin + Etkinleştir Disable - Devre dışı + Devre dışı bırak Inherit from parent group (%1) - Ana gruptan devral (%1) + Üst kümeden devral (%1) EditGroupWidgetMain Name - Adı + Ad Notes @@ -1649,11 +1649,11 @@ Değişikliklerinizi birleştirmek ister misiniz? Expires - Geçersiz + Biter Search - Arama + Ara Auto-Type @@ -1661,22 +1661,22 @@ Değişikliklerinizi birleştirmek ister misiniz? &Use default Auto-Type sequence of parent group - Ana grubun varsayılan Oto-Yazım sırasını kullan + Üst kümenin öntanımlı Oto-Yazım dizilişini k&ullan Set default Auto-Type se&quence - Varsayılan Oto-Yazım &sırasını ayarla + Öntanımlı Oto-Yazım &dizilişi belirle EditWidgetIcons &Use default icon - &Varsayılan simge kullan + &Öntanımlı simge kullan Use custo&m icon - Özel si&mge kullan + Öze&l simge kullan Add custom icon @@ -1696,7 +1696,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Hint: You can enable Google as a fallback under Tools>Settings>Security - İpucu: Google’ı Araçlar>Ayarlar>Güvenlik altından geri almak gibi etkinleştirebilirsiniz + İpucu: Araçlar>Ayarlar>Güvenlik altından Google'ı yedek olarak etkinleştirebilirsiniz Images @@ -1704,7 +1704,7 @@ Değişikliklerinizi birleştirmek ister misiniz? All files - Bütün dosyalar + Tüm dosyalar Select Image @@ -1724,7 +1724,7 @@ Değişikliklerinizi birleştirmek ister misiniz? This icon is used by %1 entries, and will be replaced by the default icon. Are you sure you want to delete it? - Bu simge %1 girişi tarafından kullanılıyor ve varsayılan simge tarafından değiştirilecek. Silmek istediğinize emin misiniz? + Bu simge %1 girdi tarafından kullanılıyor ve öntanımlı simge tarafından değiştirilecek. Silmek istediğinize emin misiniz? @@ -1747,7 +1747,7 @@ Değişikliklerinizi birleştirmek ister misiniz? Plugin Data - + Eklenti Veri Remove @@ -1755,12 +1755,14 @@ Değişikliklerinizi birleştirmek ister misiniz? Delete plugin data? - + Eklenti Veri silinsin mi? Do you really want to delete the selected plugin data? This may cause the affected plugins to malfunction. - + Seçilen eklenti verilerini gerçekten silmek istiyor musunuz? + +Bu etkilenen eklentilerin bozulmasına neden olabilir. Key @@ -1818,7 +1820,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + %n eki kaldırmak istediğinize emin misiniz?%n eki kaldırmak istediğinize emin misiniz? Confirm Remove @@ -1857,13 +1859,13 @@ This may cause the affected plugins to malfunction. Unable to open attachments: %1 - Ekler açılamıyor: + Açılamayan ekler: %1 Unable to open files: %1 - Dosyalar açılamıyor: + Açılamayan dosyalar: %1 @@ -2072,7 +2074,7 @@ This may cause the affected plugins to malfunction. Unable to issue challenge-response. - + Zorluk-tepki yanıtı verilemiyor. Wrong key or database file is corrupt. @@ -2083,7 +2085,7 @@ This may cause the affected plugins to malfunction. Kdbx3Writer Unable to issue challenge-response. - + Zorluk-tepki yanıtı verilemiyor. Unable to calculate master key @@ -2114,7 +2116,7 @@ This may cause the affected plugins to malfunction. Unknown cipher - + Bilinmeyen şifre Invalid header id size @@ -2155,74 +2157,74 @@ This may cause the affected plugins to malfunction. Unsupported KeePass variant map version. Translation: variant map = data structure for storing meta data - + Desteklenmeyen KeePass değişken harita sürümü. Invalid variant map entry name length Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita giriş adı uzunluğu Invalid variant map entry name data Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita girdi adı verisi Invalid variant map entry value length Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita girdi değeri uzunluğu Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - + Geçersiz değişken harita girdi değeri verisi Invalid variant map Bool entry value length Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita Bool girdi değeri uzunluğu Invalid variant map Int32 entry value length Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita Int32 girdi değeri uzunluğu Invalid variant map UInt32 entry value length Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita UInt32 girdi değeri uzunluğu Invalid variant map Int64 entry value length Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita Int64 girdi değeri uzunluğu Invalid variant map UInt64 entry value length Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita UInt64 girdi değeri uzunluğu Invalid variant map entry type Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita girdi tipi Invalid variant map field type size Translation: variant map = data structure for storing meta data - + Geçersiz değişken harita alan tipi boyutu Kdbx4Writer Invalid symmetric cipher algorithm. - + Geçersiz simetrik şifreleme algoritması. Invalid symmetric cipher IV size. IV = Initialization Vector for symmetric cipher - + Geçersiz simetrik şifreleme IV boyutu. Unable to calculate master key @@ -2231,18 +2233,18 @@ This may cause the affected plugins to malfunction. Failed to serialize KDF parameters variant map Translation comment: variant map = data structure for storing meta data - + KDF parametreleri değişken haritası serileştirme başarısız KdbxReader Invalid cipher uuid length - + Geçersiz şifre uuid uzunluğu Unsupported cipher - + Desteklenmeyen şifreleme Invalid compression flags length @@ -2254,27 +2256,27 @@ This may cause the affected plugins to malfunction. Invalid master seed size - + Geçersiz ana çekirdek boyutu Invalid transform seed size - + Geçersiz dönüşüm çekirdek boyutu Invalid transform rounds size - + Geçersiz dönüşüm tur boyutu Invalid start bytes size - + Geçersiz başlangıç bayt boyutu Invalid random stream id size - + Geçersiz rastgele akış kimliği boyutu Invalid inner random stream cipher - + Geçersiz dahili rastgele akış şifresi Not a KeePass database. @@ -2315,108 +2317,109 @@ Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski Ke Multiple group elements - + Çoklu grup elementleri Null group uuid - + Boş grup benzersiz tanımlayıcı Invalid group icon number - + Geçersiz grup simgesi numarası Invalid EnableAutoType value - + Geçersiz Oto-Yazım Etkin değeri Invalid EnableSearching value - + Geçersiz Arama Etkin değeri No group uuid found - + Grup evrensel benzersiz tanımlayıcı "uuid" bulunamadı Null DeleteObject uuid - + Boş "DeleteObject" "uuid" Missing DeletedObject uuid or time - + Hatalı "DeleteObject" evrensel benzersiz tanımlayıcı "uuid" veya zamanı Null entry uuid - + Boş evrensel benzersiz tanımlayıcı "uuid" girdisi Invalid entry icon number - + Geçersiz simge numarası girdisi History element in history entry - + Geçmiş girdisinde geçmiş element No entry uuid found - + +Evrensel benzersiz tanımlayıcı "uuid" girdisi bulunamadı History element with different uuid - + Farklı evrensel benzersiz tanımlayıcı "uuid" ile geçmiş öğesi Unable to decrypt entry string - + Giriş dizesi çözülemiyor Duplicate custom attribute found - + Yinelenen özel öznitelik bulundu Entry string key or value missing - + Giriş dizesi anahtarı veya değeri eksik Duplicate attachment found - + Yinelenen ek bulundu Entry binary key or value missing - + Giriş ikili anahtar veya değeri eksik Auto-type association window or sequence missing - + Oto-Yazım ilişkilendirme penceresi veya sırası eksik Invalid bool value - + Geçersiz boolean değeri Invalid date time value - + Geçersiz tarih zaman değeri Invalid color value - + Geçersiz renk değeri Invalid color rgb part - + Geçersiz renk RGB parçası Invalid number value - + Geçersiz numara değeri Invalid uuid value - + Geçersiz Evrensel Benzersiz Tanımlayıcı "uuid" değeri Unable to decompress binary Translator meant is a binary data inside an entry - + İkili dosya sıkıştırmasını açma başarısız @@ -2451,31 +2454,31 @@ Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski Ke Unable to read encryption IV IV = Initialization Vector for symmetric cipher - + Şifreleme IV okunamadı Invalid number of groups - + Geçersiz grup numarası Invalid number of entries - + Geçersiz giriş numarası Invalid content hash size - + Geçersiz içerik karma boyutu Invalid transform seed size - + Geçersiz dönüşüm çekirdek boyutu Invalid number of transform rounds - + Geçersiz dönüşüm turu sayısı Unable to construct group tree - + Grup ağacı oluşturulamıyor. Root @@ -2483,7 +2486,7 @@ Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski Ke Unable to calculate master key - Ana anahtar hesaplanamaz + Ana anahtar hesaplanamıyor Wrong key or database file is corrupt. @@ -2491,122 +2494,95 @@ Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski Ke Key transformation failed - + Anahtar dönüştürme başarısız Invalid group field type number - + Geçersiz grup alanı türü numarası Invalid group field size - + Geçersiz grup alanı boyutu Read group field data doesn't match size - + Okuma grubu alan verisi boyutuyla eşleşmiyor Incorrect group id field size - + Hatalı grup kimliği alan boyutu Incorrect group creation time field size - + Hatalı grup oluşturma zaman alanı boyutu Incorrect group modification time field size - + Hatalı grup değişiklik zaman alanı boyutu Incorrect group access time field size - + Hatalı grup erişim zamanı alan boyutu Incorrect group expiry time field size - + Hatalı grup zaman aşımı alan boyutu Incorrect group icon field size - + Geçersiz grup simge alanı boyutu Incorrect group level field size - + Geçersiz grup seviye alanı boyutu Invalid group field type - + Geçersiz grup alanı tipi Missing group id or level - + Eksik grup kimliği veya seviyesi Missing entry field type number - + Eksik girdi alanı tipi numarası Invalid entry field size - + Geçersiz girdi alanı boyutu Read entry field data doesn't match size - + Giriş alan verisi okuma boyutuyla eşleşmiyor Invalid entry uuid field size - + Geçersiz Evrensel Benzersiz Tanımlayıcı alan boyutu girişi Invalid entry group id field size - + Geçersiz grup kimliği alan boyutu girişi Invalid entry icon field size - + Geçersiz giriş simgesi alan boyutu Invalid entry creation time field size - + Geçersiz giriş oluşturma zamanı alan boyutu Invalid entry modification time field size - + Geçersiz giriş değiştirme zamanı alan boyutu Invalid entry expiry time field size - + Geçersiz giriş süre sonu alan boyutu Invalid entry field type - - - - - KeePass2 - - AES: 256-bit - AES: 256-bit - - - Twofish: 256-bit - Twofish: 256-bit - - - ChaCha20: 256-bit - ChaCha20: 256-bit - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – önerilen) + Geçersiz girdi alanı tipi @@ -2660,7 +2636,7 @@ Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski Ke Time-based one-time password - + Zamana dayalı tek kullanımlık şifre &Groups @@ -2848,7 +2824,7 @@ Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski Ke <p>It looks like you are using KeePassHTTP for browser integration. This feature has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a> (warning %1 of 3).</p> - <p>Görünüşe göre KeePassHTTP'yi tarayıcı tümleşmesi için kullanıyorsunuz. Bu özellik terk edilmiştir ve ileride kaldırılacaktır.<br>Bunun yerine KeePassXC-Browser'a geçin! Göçle ilgili yardım için <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">göç kılavuzumuzu</a> ziyaret edin (uyarı %1 / 3).</p> + <p>Görünüşe göre KeePassHTTP'yi tarayıcı tümleşmesi için kullanıyorsunuz. Bu özellik terk edilmiştir ve gelecekte kaldırılacaktır.<br>Bunun yerine KeePassXC-Browser'a geçin! Göçle ilgili yardım için <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">göç kılavuzumuzu</a> ziyaret edin (uyarı %1 / 3).</p> read-only @@ -2907,7 +2883,7 @@ Bu sürüm, üretimde kullanıma uygun değildir. PEM boundary mismatch - + PEM limitleri uyumsuz Base64 decoding failed @@ -2919,7 +2895,7 @@ Bu sürüm, üretimde kullanıma uygun değildir. Key file magic header id invalid - + Anahtar dosyası sihirli başlık kimliği geçersiz Found zero keys @@ -2935,47 +2911,47 @@ Bu sürüm, üretimde kullanıma uygun değildir. No private key payload to decrypt - + Şifresini çözmek için yüklü özel anahtar yok Trying to run KDF without cipher - + Şifre olmadan Anahtar Türetme İşlevi çalıştırma deneniyor Passphrase is required to decrypt this key - + Bu anahtarın şifresini çözmek için parola gerekiyor Key derivation failed, key file corrupted? - + Anahtar türetme başarısız, anahtar dosya bozuk mu? Decryption failed, wrong passphrase? - + Şifre çözme başarısız, parola yanlış mı? Unexpected EOF while reading public key - + Ortak anahtar okunurken beklenmeyen dosya sonu Unexpected EOF while reading private key - + Özel anahtar okunurken beklenmeyen dosya sonu Can't write public key as it is empty - + Ortak anahtar boş olduğu için yazılamıyor Unexpected EOF when writing public key - + Ortak anahtar yazılırken beklenmeyen dosya sonu Can't write private key as it is empty - + Özel anahtar boş olduğu için yazılamıyor Unexpected EOF when writing private key - + Özel anahtar yazılırken beklenmeyen dosya sonu Unsupported key type: %1 @@ -2983,15 +2959,15 @@ Bu sürüm, üretimde kullanıma uygun değildir. Unknown cipher: %1 - + Bilinmeyen şifre: %1 Cipher IV is too short for MD5 kdf - + Cipher IV, MD5 kdf için çok kısa Unknown KDF: %1 - + Bilinmeyen KDF: %1 Unknown key type: %1 @@ -3107,11 +3083,11 @@ Bu sürüm, üretimde kullanıma uygun değildir. <p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p> - <p>KeePassHTTP terk edilmiştir ve ileride kaldırılacaktır.<br>Bunun yerine KeePassXC-Browser'a geçin! Göçle ilgili yardım için <a href="https://keepassxc.org/docs/keepassxc-browser-migration">göç kılavuzumuzu</a> ziyaret edin.</p> + <p>KeePassHTTP terk edilmiştir ve gelecekte kaldırılacaktır.<br>Bunun yerine KeePassXC-Browser'a geçin! Göçle ilgili yardım için <a href="https://keepassxc.org/docs/keepassxc-browser-migration">göç kılavuzumuzu</a> ziyaret edin.</p> Cannot bind to privileged ports - + Ayrıcalıklı bağlantı noktalarına bağlanılamıyor Cannot bind to privileged ports below 1024! @@ -3252,7 +3228,7 @@ Using default port 19455. Database hash not available - + Veritabanı karma erişilemez Client public key not received @@ -3344,7 +3320,7 @@ Using default port 19455. Prompt for the entry's password. - + Giriş şifresini sor. Generate a password for the entry. @@ -3369,7 +3345,7 @@ Using default port 19455. Path of the entry to clip. clip = copy to clipboard - + Girişi kısaltmanın yolu. Timeout in seconds before clearing the clipboard. @@ -3393,7 +3369,7 @@ Using default port 19455. Estimate the entropy of a password. - Parolanın entropisini ölç + Parolanın entropisini ölç. Password for which to estimate the entropy. @@ -3477,19 +3453,19 @@ Kullanılabilir komutlar: Key file of the database to merge from. - + Birleştirilecek veritabanının anahtar dosyası. Show an entry's information. - + Bir girişin bilgilerini göster. Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given. - + Gösterilecek özniteliklerin isimleri. Bu seçenek, her bir özniteliğin verilen sıraya göre bir satırda gösterilmesiyle birden fazla kez belirtilebilir. Eğer hiçbir öznitelik belirtilmediyse, varsayılan özniteliklerin bir özeti verilir. attribute - + öznitelik Name of the entry to show. @@ -3515,7 +3491,31 @@ Kullanılabilir komutlar: missing closing quote - + kapanış tırnak işareti eksik + + + AES: 256-bit + AES: 256-bit + + + Twofish: 256-bit + Twofish: 256-bit + + + ChaCha20: 256-bit + ChaCha20: 256-bit + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – önerilen) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) Group @@ -3539,7 +3539,7 @@ Kullanılabilir komutlar: Last Modified - + Son değişiklik Created @@ -3547,7 +3547,7 @@ Kullanılabilir komutlar: Legacy Browser Integration - + Eski Tarayıcı birleşmesi Browser Integration @@ -3555,15 +3555,15 @@ Kullanılabilir komutlar: YubiKey[%1] Challenge Response - Slot %2 - %3 - + YubiKey[%1] Karşılaştırma Yanıtı - Yuva %2 - %3 Press - + Basın Passive - + Pasif SSH Agent @@ -3571,48 +3571,49 @@ Kullanılabilir komutlar: Generate a new random diceware passphrase. - + Yeni bir rastgele diceware parolası oluştur. Word count for the diceware passphrase. - + Diceware parolası için kelime sayısı. count - + sayım Wordlist for the diceware generator. [Default: EFF English] - + Diceware oluşturucu için Kelime Listesi. +[Varsayılan: EFF İngilizce] Generate a new random password. - + Yeni bir karışık şifre oluştur. Length of the generated password. - + Oluşturulan şifrenin uzunluğu. Use lowercase characters in the generated password. - + Oluşturulan şifrede küçük karakterler kullan. Use uppercase characters in the generated password. - + Oluşturulan şifrede büyük karakterler kullan. Use numbers in the generated password. - + Oluşturulan şifrede rakamlar kullan. Use special characters in the generated password. - + Oluşturulan şifrede özel karakterler kullan. Use extended ASCII in the generated password. - + Oluşturulan şifrede genişletilmiş ASCII kullan. @@ -3623,15 +3624,15 @@ Kullanılabilir komutlar: Error writing to underlying device: - + Temel aygıta yazma hatası: Error opening underlying device: - + Temel aygıtı açma hatası: Error reading data from underlying device: - + Temel aygıttan veri okuma hatası: Internal zlib error when decompressing: @@ -3720,7 +3721,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - + %n şifreleme anahtarı KeePassX/Http ayarlarından başarıyla kaldırıldı.%n şifreleme anahtarları KeePassX/Http ayarlarından başarıyla kaldırıldı. KeePassXC: No keys found @@ -3752,7 +3753,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Successfully removed permissions from %n entries. - + %n girdiden izinler başarıyla kaldırıldı.%n girişlerden izinler başarıyla kaldırıldı. KeePassXC: No entry with permissions found! @@ -3834,7 +3835,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Hide the Details view - + Ayrıntılar görünümünü gizle Show a system tray icon @@ -3850,7 +3851,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Dark system tray icon - + Koyu sistem tepsisi simgesi Language @@ -3862,11 +3863,11 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Use entry title to match windows for global Auto-Type - + Küresel Oto-Yazım için pencereleri karşılaştırmada giriş başlığını kullan Use entry URL to match windows for global Auto-Type - + Küresel Oto-Yazım için pencereleri karşılaştırmada girdi URL'sini kullan Always ask before performing Auto-Type @@ -3951,7 +3952,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Hide passwords in the preview panel - + Önizleme panelindeki şifreleri gizle Hide entry notes by default @@ -3967,7 +3968,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Re-lock previously locked database after performing Auto-Type - + Oto-Yazım gerçekleştirdikten sonra önceden kilitli veritabanını yeniden kilitle @@ -3982,11 +3983,11 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Default RFC 6238 token settings - + Varsayılan RFC 6238 anahtar ayarları Steam token settings - + Steam anahtar ayarları Use custom settings @@ -3998,7 +3999,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Time step: - + Zaman adımı: 8 digits @@ -4076,7 +4077,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Welcome to KeePassXC %1 - + KeePassXC'ye hoş geldin %1 @@ -4115,7 +4116,7 @@ Lütfen seçilen veri tabanının kilidini kaldırın veya kilitli olmayan birin Parent window handle - + Ana pencere işlemesi \ No newline at end of file diff --git a/share/translations/keepassx_uk.ts b/share/translations/keepassx_uk.ts index e112bb27c..bc3b20d37 100644 --- a/share/translations/keepassx_uk.ts +++ b/share/translations/keepassx_uk.ts @@ -11,7 +11,7 @@ Report bugs at: <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> - Повідомляйте про помилки на <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> + Повідомляйте про вади на <a href="https://github.com/keepassxreboot/keepassxc/issues" style="text-decoration: underline;">https://github.com</a> KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. @@ -49,7 +49,7 @@ Distribution: %1 - Дистрибутив: %1 + Розподіл: %1 Libraries: @@ -69,7 +69,7 @@ Kernel: %3 %4 Project Maintainers: - Супровідники проекту: + Супровідники проекту: Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. @@ -86,7 +86,7 @@ Kernel: %3 %4 AccessControlDialog KeePassXC HTTP Confirm Access - Підтвердити доступ KeePassXC до HTTP + Схвалити доступ KeePassXC до HTTP Remember this decision @@ -103,7 +103,7 @@ Kernel: %3 %4 %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 запитує доступ до паролів у цих записах. + %1 запитує доступ до паролів у таких записах. Дозволити доступ? @@ -134,15 +134,15 @@ Please select whether you want to allow access. This Auto-Type command contains a very long delay. Do you really want to proceed? - Команда Автозаповнення містить надто велику затримку. Ви впевнені, що хочете продовжити? + Команда Автозаповнення містить надто довгу затримку. Ви дійсно хочете продовжити? This Auto-Type command contains very slow key presses. Do you really want to proceed? - Команда Автозаповнення містить надто повільні натискання клавіш. Ви впевнені, що хочете продовжити? + Команда Автозаповнення містить надто повільні натискання клавіш. Ви дійсно хочете продовжити? This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed? - Команда Автозаповнення містить параметри, що надто часто повторюються. Ви впевнені, що хочете продовжити? + Команда Автозаповнення містить надто часто повторювані параметри. Ви дійсно хочете продовжити? @@ -194,7 +194,7 @@ Please select whether you want to allow access. BrowserAccessControlDialog KeePassXC-Browser Confirm Access - Підтвердження доступу для KeePassXC-Browser + Підтвердження доступу для KeePassXC-Переглядача Remember this decision @@ -211,7 +211,7 @@ Please select whether you want to allow access. %1 has requested access to passwords for the following item(s). Please select whether you want to allow access. - %1 запитує доступ до паролів у цих записах. + %1 запитує доступ до паролів у таких записах. Дозволити доступ? @@ -223,11 +223,11 @@ Please select whether you want to allow access. This is required for accessing your databases with KeePassXC-Browser - Це необхідно для надання KeePassXC-Browser доступу до Ваших сховищ + Це необхідно для надання KeePassXC-Переглядачу доступу до Ваших сховищ Enable KeepassXC browser integration - Увімкнути інтеграцію KeePassXC з браузером + Сполучити KeePassXC з переглядачами General @@ -235,7 +235,7 @@ Please select whether you want to allow access. Enable integration for these browsers: - Увімкнути інтеграцію з цими браузерами: + Сполучити з такими переглядачами: &Google Chrome @@ -290,7 +290,7 @@ Please select whether you want to allow access. &Disconnect all browsers - &Від'єднати від усіх браузерів + Від'єднати від усіх переглядачів Forget all remembered &permissions @@ -325,7 +325,7 @@ Please select whether you want to allow access. &Return advanced string fields which start with "KPH: " - Показати розширені текстові поля, що починаються з «KPH: » + Показувати розширені текстові поля, що починаються з «KPH: » Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup. @@ -337,11 +337,11 @@ Please select whether you want to allow access. Support a proxy application between KeePassXC and browser extension. - Підтримувати посередницькі додатки між KeePassXC та розширенням браузера. + Підтримувати посередницький застосунок між KeePassXC та розширенням переглядача. Use a &proxy application between KeePassXC and browser extension - Використовувати посередницький додаток між KeePassXC та розширенням браузера + Використовувати посередницький застосунок між KeePassXC та розширенням переглядача Use a custom proxy location if you installed a proxy manually. @@ -375,7 +375,7 @@ Please select whether you want to allow access. We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment. - Вибачте, але KeePassXC-Browser поки що не підтримується Snap-версіях. + Вибачте, але KeePassXC-Переглядач поки що не працює у версіях Snap. @@ -392,7 +392,7 @@ give it a unique name to identify and accept it. Ви одержали запит на прив'язку вказаного ключа. Якщо Ви бажаєте надати доступ до Вашого сховища KeePassXC, -вкажіть унікальну назву та підтвердьте прив'язку. +вкажіть унікальну назву та схваліть прив'язку. Save and allow access @@ -448,7 +448,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption key(s) from KeePassXC settings. - Успішно видалено %n шифрувальний ключ з налаштувань KeePassXC.Успішно видалено %n шифрувальні ключа з налаштувань KeePassXC.Успішно видалено %n шифрувальних ключів з налаштувань KeePassXC.Успішно видалено %n шифрувальних ключів з налаштувань KeePassXC. + Успішно видалено %n шифрувальний ключ з налаштувань KeePassXC.Успішно видалено %n шифрувальних ключі з налаштувань KeePassXC.Успішно видалено %n шифрувальних ключів з налаштувань KeePassXC.Успішно видалено %n шифрувальних ключів з налаштувань KeePassXC. Removing stored permissions… @@ -523,7 +523,7 @@ Please unlock the selected database or choose another one which is unlocked. Unable to create Key File : - Неможливо створити файл-ключ: + Неможливо створити файл-ключ: Select a key file @@ -544,7 +544,7 @@ Please unlock the selected database or choose another one which is unlocked. Failed to set %1 as the Key file: %2 - Не вдалося встановити %1 в якості файла-ключа: + Не вдалося встановити %1 як файл-ключ: %2 @@ -574,7 +574,7 @@ Please consider generating a new key file. Append ' - Clone' to title - Додавати « – клон» до заголовка + Додати « – клон» до заголовка Replace username and password with references @@ -645,11 +645,11 @@ Please consider generating a new key file. Empty fieldname - Без назви + Без назви column - колонка + колонка Imported from CSV file @@ -657,7 +657,7 @@ Please consider generating a new key file. Original data: - Початкові дані: + Початкові дані: Error(s) detected in CSV file ! @@ -665,7 +665,7 @@ Please consider generating a new key file. more messages skipped] - решта повідомлень пропущена] + решта повідомлень пропущена] Error @@ -686,14 +686,14 @@ Please consider generating a new key file. Unable to calculate master key - Неможливо вирахувати головний ключ + Неможливо обчислити головний ключ CsvParserModel %n byte(s), - %n байт,%n byte(s), %n байта, %n байтів, %n байтів, + %n байт, %n байти, %n байтів, %n байтів, %n row(s), @@ -708,7 +708,7 @@ Please consider generating a new key file. DatabaseOpenWidget Enter master key - Уведіть головний ключ + Введіть головний ключ Key File: @@ -789,7 +789,7 @@ Please consider generating a new key file. Database opened fine. Nothing to do. - Сховище відкрито без помилок. Обробка не потрібна. + Сховище відкрито без помилок. Оброблення не потрібне. Success @@ -798,7 +798,7 @@ Please consider generating a new key file. The database has been successfully repaired You can now save it. - Лагодження сховища успішно завершено. + Лагодження сховища успішно завершене. Тепер Ви можете його зберегти. @@ -819,19 +819,19 @@ You can now save it. Number of rounds too high Key transformation rounds - Кількість циклів занадто висока + Кількість циклів надто висока You are using a very high number of key transform rounds with Argon2. If you keep this number, your database may take hours or days (or even longer) to open! - Ви використовуєте занадто багато циклів перетворення для ключа у форматі Argon2. + Ви використовуєте надто багато циклів перетворення для ключа у форматі Argon2. Якщо Ви залишите таку кількість циклів, відкриття Вашого сховища може тривати кілька годин або днів (чи навіть довше)! Understood, keep number - Зрозуміло, кількість залишено + Зрозуміло, залишити таку кількість Cancel @@ -840,15 +840,15 @@ If you keep this number, your database may take hours or days (or even longer) t Number of rounds too low Key transformation rounds - Кількість циклів занадто низька + Кількість циклів надто низька You are using a very low number of key transform rounds with AES-KDF. If you keep this number, your database may be too easy to crack! - Ви використовуєте занадто мало циклів перетворення для ключа у форматі AES-KDF. + Ви використовуєте надто мало циклів перетворення для ключа у форматі AES-KDF. -Якщо Ви залишите таку кількість циклів, Ваше сховище буде легко зламати. +Якщо Ви залишите таку кількість циклів, Ваше сховище буде легко зламати! KDF unchanged @@ -856,7 +856,7 @@ If you keep this number, your database may be too easy to crack! Failed to transform key with new KDF parameters; KDF unchanged. - Спроба перетворити ключ згідно з новими налаштуваннями ФОК зазнала невдачі; ФОК залишилась без змін + Спроба перетворити ключ згідно з новими налаштуваннями ФОК зазнала невдачі; ФОК залишилась без змін. MiB @@ -936,7 +936,7 @@ If you keep this number, your database may be too easy to crack! MiB - МіБ + МіБ Use recycle bin @@ -1068,9 +1068,9 @@ Please press cancel to finish your changes or discard them. This database has been modified. Do you want to save the database before locking it? Otherwise your changes are lost. - У сховище було внесено зміни. -Ви хочете зберегти його перед блокуванням? -Інакше внесені зміни буде втрачено. + Сховище містить незбережені зміни. +Зберегти сховище перед блокуванням? +Якщо сховище не зберегти, усі внесені зміни буде втрачено. Disable safe saves? @@ -1087,7 +1087,7 @@ Disable safe saves and try again? DatabaseWidget Searching... - Шукаю… + Триває пошук… Change master key @@ -1115,15 +1115,15 @@ Disable safe saves and try again? Do you really want to move entry "%1" to the recycle bin? - Ви справді хочете перемістити запис «%1» у смітник? + Ви дійсно хочете перемістити запис «%1» у смітник? Move entries to recycle bin? - Перемістити записи до смітника? + Перемістити записи в смітник? Do you really want to move %n entry(s) to the recycle bin? - Ви дійсно хочете перемістити %n запис у смітник?Ви дійсно хочете перемістити %n записи в смітник?Ви дійсно хочете перемістити %n записів у смітник?Ви дійсно хочете перемістити %n записів у смітник? + Ви дійсно хочете перемістити %n запис у смітник?Ви дійсно хочете перемістити %n записи у смітник?Ви дійсно хочете перемістити %n записів у смітник?Ви дійсно хочете перемістити %n записів у смітник? Execute command? @@ -1131,7 +1131,7 @@ Disable safe saves and try again? Do you really want to execute the following command?<br><br>%1<br> - Ви справді хочете виконати таку команду? <br><br>%1<br> + Ви дійсно хочете виконати таку команду? <br><br>%1<br> Remember my choice @@ -1147,7 +1147,7 @@ Disable safe saves and try again? Unable to calculate master key - Неможливо вирахувати головний ключ + Неможливо обчислити головний ключ No current database. @@ -1155,7 +1155,7 @@ Disable safe saves and try again? No source database, nothing to do. - Джерельне сховище відсутнє, обробка не потрібна. + Джерельне сховище відсутнє, оброблення не потрібне. Search Results (%1) @@ -1200,7 +1200,7 @@ Do you want to merge your changes? DetailsWidget Generate TOTP Token - Створити позначку ТОГ + Створити позначку ТОП Close @@ -1220,7 +1220,7 @@ Do you want to merge your changes? Expiration - Термін чинності + Знечинюється Username @@ -1232,7 +1232,7 @@ Do you want to merge your changes? Searching - Шукаю + Пошук Attributes @@ -1311,7 +1311,7 @@ Do you want to merge your changes? n/a - n/a + немає (encrypted) @@ -1351,7 +1351,7 @@ Do you want to merge your changes? Confirm Remove - Схваліть видалення + Схвалити видалення Are you sure you want to remove this attribute? @@ -1363,7 +1363,7 @@ Do you want to merge your changes? Press reveal to view or edit - Натисніть «показати» для перегляду або редагування + Натисніть «показати» для перегляду або зміни Tomorrow @@ -1371,11 +1371,11 @@ Do you want to merge your changes? %n week(s) - %n тиждень%n тижні%n тижнів%n тижнів + %n тиждень%n тижня%n тижнів%n тижнів %n month(s) - %n місяць%n місяці%n місяців%n місяців + %n місяць%n місяця%n місяців%n місяців 1 year @@ -1430,7 +1430,7 @@ Do you want to merge your changes? Background Color: - Колір тла: + Колір тла: @@ -1445,7 +1445,7 @@ Do you want to merge your changes? &Use custom Auto-Type sequence: - Використати власну послідовність автозаповнення + Використати власну послідовність автозаповнення: Window Associations @@ -1484,7 +1484,7 @@ Do you want to merge your changes? Delete all - Видалити все + Видалити всі @@ -1499,7 +1499,7 @@ Do you want to merge your changes? Repeat: - Пароль ще раз: + Повторіть пароль: Title: @@ -1515,7 +1515,7 @@ Do you want to merge your changes? Toggle the checkbox to reveal the notes section. - Натисніть перемикач, щоб показати розділ нотаток + Натисніть перемикач, щоб показати розділ нотаток. Username: @@ -1523,7 +1523,7 @@ Do you want to merge your changes? Expires - Закінчується + Знечинюється @@ -1534,11 +1534,11 @@ Do you want to merge your changes? Remove key from agent after - Видаляти ключ з в'язки посередника після + Видаляти ключ із в'язки посередника після seconds - секунд + секунд Fingerprint @@ -1546,7 +1546,7 @@ Do you want to merge your changes? Remove key from agent when database is closed/locked - Видаляти ключ з в'язки посередника під час блокування або закриття сховища + Видаляти ключ із в'язки посередника під час блокування або закриття сховища Public key @@ -1566,7 +1566,7 @@ Do you want to merge your changes? n/a - n/a + немає Copy to clipboard @@ -1595,11 +1595,11 @@ Do you want to merge your changes? Remove from agent - Видалити з в'язкі посередника + Видалити з в'язки посередника Require user confirmation when this key is used - Запрошувати підтвердження для використання цього ключа + Запитувати підтвердження для використання цього ключа @@ -1649,7 +1649,7 @@ Do you want to merge your changes? Expires - Закінчується + Знечинюється Search @@ -1692,11 +1692,11 @@ Do you want to merge your changes? Unable to fetch favicon. - Неможливо дістати фавікон + Неможливо дістати фавікон. Hint: You can enable Google as a fallback under Tools>Settings>Security - Порада: Ви можете увімкнути Google у якості запасного варіанту шляхом вибору Інструменти>Налаштування>Безпека + Порада: Ви можете увімкнути Google як запасний варіант шляхом вибору Інструменти>Налаштування>Безпека Images @@ -1777,7 +1777,7 @@ This may cause the affected plugins to malfunction. - Clone Suffix added to cloned entries - – клон + – клон @@ -1819,11 +1819,11 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - Ви дійсно хочете видалити %n вкладення?Ви дійсно хочете видалити %n вкладення?Ви дійсно хочете видалити %n вкладень?Ви дійсно хочете видалити %n вкладень? + Ви дійсно бажаєте видалити %n вкладення?Ви дійсно бажаєте видалити %n вкладення?Ви дійсно бажаєте видалити %n вкладень?Ви дійсно бажаєте видалити %n вкладень? Confirm Remove - Схваліть видалення + Схвалити видалення Save attachments @@ -1841,7 +1841,7 @@ This may cause the affected plugins to malfunction. Confirm overwrite - Схваліть перезапис + Схвалити перезапис Unable to save attachments: @@ -1899,7 +1899,7 @@ This may cause the affected plugins to malfunction. Ref: Reference abbreviation - Пос.: + Пос.: Group @@ -1931,7 +1931,7 @@ This may cause the affected plugins to malfunction. Expires - Закінчується + Знечинюється Created @@ -1958,19 +1958,19 @@ This may cause the affected plugins to malfunction. Hide Usernames - Ховати імена користувача + Приховувати імена користувача Hide Passwords - Сховати паролі + Приховувати паролі Fit to window - Припасувати до вікна + Припасовувати до вікна Fit to contents - Припасувати до вмісту + Припасовувати до вмісту Reset to defaults @@ -2058,7 +2058,7 @@ This may cause the affected plugins to malfunction. KMessageWidget &Close - Закрити + &Закрити Close message @@ -2069,7 +2069,7 @@ This may cause the affected plugins to malfunction. Kdbx3Reader Unable to calculate master key - Неможливо вирахувати головний ключ + Неможливо обчислити головний ключ Unable to issue challenge-response. @@ -2088,7 +2088,7 @@ This may cause the affected plugins to malfunction. Unable to calculate master key - Неможливо вирахувати головний ключ + Неможливо обчислити головний ключ @@ -2099,7 +2099,7 @@ This may cause the affected plugins to malfunction. Unable to calculate master key - Неможливо вирахувати головний ключ + Неможливо обчислити головний ключ Invalid header checksum size @@ -2111,7 +2111,7 @@ This may cause the affected plugins to malfunction. Wrong key or database file is corrupt. (HMAC mismatch) - Хибний ключ або пошкоджене сховище. (невідповідність HMAC) + Неправильний ключ або пошкоджене сховище. (невідповідність HMAC) Unknown cipher @@ -2171,12 +2171,12 @@ This may cause the affected plugins to malfunction. Invalid variant map entry value length Translation: variant map = data structure for storing meta data - Хибна довжина запису в структурі метаданих + Хибна довжина значення запису в структурі метаданих Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - Хибний запису в структурі метаданих + Хибне значення запису в структурі метаданих Invalid variant map Bool entry value length @@ -2227,7 +2227,7 @@ This may cause the affected plugins to malfunction. Unable to calculate master key - Неможливо вирахувати головний ключ + Неможливо обчислити головний ключ Failed to serialize KDF parameters variant map @@ -2279,7 +2279,7 @@ This may cause the affected plugins to malfunction. Not a KeePass database. - Не сховище KeePass. + Це не сховище KeePass. The selected file is an old KeePass 1 database (.kdb). @@ -2324,7 +2324,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid group icon number - Хибний номер значка групи + Хибна кількість значків групи Invalid EnableAutoType value @@ -2344,7 +2344,7 @@ This is a one-way migration. You won't be able to open the imported databas Missing DeletedObject uuid or time - Бракує часу або uuid для DeleteObject + Бракує значення часу або uuid для DeleteObject Null entry uuid @@ -2352,7 +2352,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid entry icon number - Хибний номер значка запису + Хибна кількість значків запису History element in history entry @@ -2376,7 +2376,7 @@ This is a one-way migration. You won't be able to open the imported databas Entry string key or value missing - Бракує текстового ключа або значення запису + Запису бракує текстового ключа або значення Duplicate attachment found @@ -2384,11 +2384,11 @@ This is a one-way migration. You won't be able to open the imported databas Entry binary key or value missing - Бракує двійкового ключа або значення запису + Запису бракує двійкового ключа або значення Auto-type association window or sequence missing - Вікно або послідовність прив'язки автозаповнення відсутнє + Відсутнє вікно або послідовність прив'язки автозаповнення Invalid bool value @@ -2408,7 +2408,7 @@ This is a one-way migration. You won't be able to open the imported databas Invalid number value - Хибне цифрове значення + Хибне числове значення Invalid uuid value @@ -2443,11 +2443,11 @@ This is a one-way migration. You won't be able to open the imported databas Unsupported encryption algorithm. - Цей алгоритм шифрування не підтримується. + Непідтримуваний алгоритм шифрування. Unsupported KeePass database version. - Ця версія сховища KeePass не підтримується. + Непідтримувана версія сховища KeePass. Unable to read encryption IV @@ -2484,7 +2484,7 @@ This is a one-way migration. You won't be able to open the imported databas Unable to calculate master key - Неможливо вирахувати головний ключ + Неможливо обчислити головний ключ Wrong key or database file is corrupt. @@ -2544,7 +2544,7 @@ This is a one-way migration. You won't be able to open the imported databas Missing entry field type number - Бракує номера типу для поля запису + Бракує кількості типів для поля запису Invalid entry field size @@ -2583,38 +2583,11 @@ This is a one-way migration. You won't be able to open the imported databas Хибний тип поля запису - - KeePass2 - - AES: 256-bit - AES: 256-біт - - - Twofish: 256-bit - Twofish: 256-біт - - - ChaCha20: 256-bit - ChaCha20: 256-біт - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – рекомендовано) - - Main Existing single-instance lock file is invalid. Launching new instance. - Наявний блокувальний файл режиму одного примірника є хибним. Запускаю новий примірник. + Наявний блокувальний файл режиму одного примірника є хибним. Запускаємо новий примірник. The lock file could not be created. Single-instance mode disabled. @@ -2622,7 +2595,7 @@ This is a one-way migration. You won't be able to open the imported databas Another instance of KeePassXC is already running. - Інший примірник KeePassXC вже запущений. + Інший примірник KeePassXC вже запущено. Fatal error while testing the cryptographic functions. @@ -2657,7 +2630,7 @@ This is a one-way migration. You won't be able to open the imported databas Copy att&ribute to clipboard - Копіювати атрибут до кишені + Скопіювати атрибут у кишеню Time-based one-time password @@ -2685,7 +2658,7 @@ This is a one-way migration. You won't be able to open the imported databas &Save database - Зберігти сховище + Зберегти сховище &Close database @@ -2737,7 +2710,7 @@ This is a one-way migration. You won't be able to open the imported databas Database settings - Параметри сховища + Налаштування сховища &Clone entry @@ -2749,19 +2722,19 @@ This is a one-way migration. You won't be able to open the imported databas Copy &username - Копіювати ім'я користувача + Скопіювати ім'я користувача Copy username to clipboard - Копіювати ім’я користувача в кишеню + Скопіювати ім’я користувача в кишеню Cop&y password - Копіювати пароль + Скопіювати пароль Copy password to clipboard - Копіювати пароль в буфер обміну + Скопіювати пароль у кишеню &Settings @@ -2789,7 +2762,7 @@ This is a one-way migration. You won't be able to open the imported databas Copy title to clipboard - Скопіювати заголовок до кишені + Скопіювати заголовок у кишеню &URL @@ -2797,7 +2770,7 @@ This is a one-way migration. You won't be able to open the imported databas Copy URL to clipboard - Скопіювати URL до кишені + Скопіювати URL у кишеню &Notes @@ -2805,7 +2778,7 @@ This is a one-way migration. You won't be able to open the imported databas Copy notes to clipboard - Скопіювати нотатки до кишені + Скопіювати нотатки в кишеню &Export to CSV file... @@ -2825,15 +2798,15 @@ This is a one-way migration. You won't be able to open the imported databas Show TOTP - Показати ТОГ + Показати ТОП Set up TOTP... - Налаштувати ТОГ… + Налаштувати ТОП… Copy &TOTP - Скопіювати ТОГ + Скопіювати ТОП E&mpty recycle bin @@ -2849,7 +2822,7 @@ This is a one-way migration. You won't be able to open the imported databas <p>It looks like you are using KeePassHTTP for browser integration. This feature has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a> (warning %1 of 3).</p> - <p>Схоже, що Ви використовуєте KeePassHTTP для інтеграції з браузером. Цей засіб застарів і незабаром буде видалений. <br>Натомість, будь ласка, використовуйте KeePassXC-Browser! Інформацію з переходу Ви можете знайти у нашому <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">посібнику</a> (попередження %1 of 3).</p> + <p>Схоже, що Ви використовуєте KeePassHTTP для сполучення з переглядачем. Цей засіб застарів і буде незабаром видалений. <br>Натомість, перейдіть, будь ласка, до використання KeePassXC-Переглядача! Інформацію щодо переходу Ви можете знайти у нашому <a class="link" href="https://keepassxc.org/docs/keepassxc-browser-migration">посібнику</a> (попередження %1 of 3).</p> read-only @@ -2904,7 +2877,7 @@ This version is not meant for production use. OpenSSHKey Invalid key file, expecting an OpenSSH key - Хибний файл ключа. Ключ має бути у форматі OpenSSH. + Хибний файл ключа. Ключ має бути у форматі OpenSSH PEM boundary mismatch @@ -2944,7 +2917,7 @@ This version is not meant for production use. Passphrase is required to decrypt this key - Для розшифрування цього ключа потрібен пароль + Для розшифрування цього ключа потрібен вираз пароля Key derivation failed, key file corrupted? @@ -2952,7 +2925,7 @@ This version is not meant for production use. Decryption failed, wrong passphrase? - Розшифрувати не вдалося, можливо, через хибний пароль + Розшифрувати не вдалося, можливо, через хибний вираз пароля? Unexpected EOF while reading public key @@ -2980,7 +2953,7 @@ This version is not meant for production use. Unsupported key type: %1 - Непідтримуваний вид ключа: %1 + Непідтримуваний тип ключа: %1 Unknown cipher: %1 @@ -3024,7 +2997,7 @@ This version is not meant for production use. Only returns the best matches for a specific URL instead of all entries for the whole domain. - Показувати лише найкращі збіги для певного URL замість усіх записів для всієї області. + Показувати лише найкращі збіги для певного URL замість усіх записів для всієї області. &Return only best matching entries @@ -3052,11 +3025,11 @@ This version is not meant for production use. R&emove all shared encryption keys from active database - Видалити всі спільні шифрувальні ключі з активного сховища + Видалити всі спільні шифрувальні ключі з поточного сховища Re&move all stored permissions from entries in active database - Видалити всі збережені привілеї для записів у активному сховищу + Видалити всі збережені привілеї для записів у поточному сховищі Password Generator @@ -3096,7 +3069,7 @@ This version is not meant for production use. Default port: 19455 - Типовий шлюз: 19455 + Типовий шлюз: 19455 KeePassXC will listen to this port on 127.0.0.1 @@ -3108,7 +3081,7 @@ This version is not meant for production use. <p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p> - <p>KeePassHTTP застарів і може бути видалений у майбутньому.<br>Натомість, будь ласка, використовуйте KeePassXC-Browser! Інформацію з переходу Ви можете знайти у нашому <a href="https://keepassxc.org/docs/keepassxc-browser-migration">посібнику</a>.</p> + <p>KeePassHTTP застарів і може бути видалений у майбутньому.<br>Натомість, перейдіть, будь ласка, до використання KeePassXC-Переглядача! Інформацію щодо переходу Ви можете знайти у нашому <a href="https://keepassxc.org/docs/keepassxc-browser-migration">посібнику</a>.</p> Cannot bind to privileged ports @@ -3182,7 +3155,7 @@ Using default port 19455. Passphrase - Пароль + Вираз пароля Wordlist: @@ -3190,11 +3163,11 @@ Using default port 19455. Word Count: - Кількість слів + Кількість слів: Word Separator: - Розділювач слів + Розділювач слів: Generate @@ -3202,7 +3175,7 @@ Using default port 19455. Copy - Копіювати + Скопіювати Accept @@ -3313,7 +3286,7 @@ Using default port 19455. Add a new entry to a database. - Додати новий запис до сховища + Додати новий запис до сховища. Path of the database. @@ -3349,11 +3322,11 @@ Using default port 19455. Generate a password for the entry. - Згенерувати пароль для запису. + Створити пароль для запису. Length for the generated password. - Довжина згенерованого пароля. + Довжина створюваного пароля. length @@ -3361,24 +3334,24 @@ Using default port 19455. Path of the entry to add. - Шлях до запису, що треба додати. + Шлях до запису, що підлягає додаванню. Copy an entry's password to the clipboard. - Копіювати пароль запису до буферу обміну. + Скопіювати пароль запису в кишеню. Path of the entry to clip. clip = copy to clipboard - Шлях до запису, що треба скопіювати. + Шлях до запису, що підлягає копіюванню. Timeout in seconds before clearing the clipboard. - Час очікування перед очищенням кишені. + Час очікування в секундах перед очищенням кишені. Edit an entry. - Змінити запис + Змінити запис. Title for the entry. @@ -3398,7 +3371,7 @@ Using default port 19455. Password for which to estimate the entropy. - Пароль, для якого обчислюється ентропія. + Пароль, що підлягає обчисленню ентропії. Perform advanced analysis on the password. @@ -3410,11 +3383,11 @@ Using default port 19455. Path of the database to extract. - Шлях до сховища, щоб відкрити. + Шлях до сховища для видобування. Insert password to unlock %1: - Вставте пароль для відкриття %1: + Вставте пароль для відкриття %1: Failed to load key file %1 : %2 @@ -3502,7 +3475,7 @@ Available commands: error reading from device - помилка під час зчитування з пристрію + помилка читання з пристрою file empty ! @@ -3518,6 +3491,30 @@ Available commands: missing closing quote бракує закривальних лапок + + AES: 256-bit + AES: 256-біт + + + Twofish: 256-bit + Twofish: 256-біт + + + ChaCha20: 256-bit + ChaCha20: 256-біт + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – рекомендовано) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group Група @@ -3548,11 +3545,11 @@ Available commands: Legacy Browser Integration - Застарілий спосіб інтеграції з браузером + Застарілий тип сполучення з переглядачем Browser Integration - Інтеграція з браузером + Сполучення з переглядачем YubiKey[%1] Challenge Response - Slot %2 - %3 @@ -3572,11 +3569,11 @@ Available commands: Generate a new random diceware passphrase. - Генерувати новий пароль методом гральних кісток (diceware). + Створити новий вираз пароля методом гральних кісток (diceware). Word count for the diceware passphrase. - Кількість слів у паролі. + Кількість слів у виразі пароля. count @@ -3590,65 +3587,65 @@ Available commands: Generate a new random password. - Згенерувати новий випадковий пароль. + Створити новий випадковий пароль. Length of the generated password. - Довжина генерованого пароля. + Довжина створюваного пароля. Use lowercase characters in the generated password. - Використовувати малі літери в генерації пароля. + Використовувати малі літери для створення пароля. Use uppercase characters in the generated password. - Використовувати великі літери в генерації пароля. + Використовувати великі літери для створення пароля. Use numbers in the generated password. - Використовувати цифри в генерації пароля. + Використовувати цифри для створення пароля. Use special characters in the generated password. - Використовувати спеціальні символи в генерації пароля. + Використовувати спеціальні символи для створення пароля. Use extended ASCII in the generated password. - Використовувати розширений набір ASCII в генерації пароля. + Використовувати розширений набір ASCII для створення пароля. QtIOCompressor Internal zlib error when compressing: - Внутрішня помилка zlib при стисненні: + Внутрішня помилка zlib під час стиснення: Error writing to underlying device: - Помилка запису на основний пристрій: + Помилка запису на основний пристрій: Error opening underlying device: - Помилка відкриття основного пристрою: + Помилка відкриття основного пристрою: Error reading data from underlying device: - Помилка читання з основного пристрою: + Помилка читання з основного пристрою: Internal zlib error when decompressing: - Внутрішня помилка zlib при розпакуванні: + Внутрішня помилка zlib під час розпакування: QtIOCompressor::open The gzip format not supported in this version of zlib. - Формат gzip не підтримується в цій версії zlib. + Ця версія zlib не підтримує формат gzip. Internal zlib error: - Внутрішня помилка zlib: + Внутрішня помилка zlib: @@ -3685,8 +3682,8 @@ Available commands: If you would like to allow it access to your KeePassXC database give it a unique name to identify and accept it. Ви одержали запит на прив'язку вказаного ключа. -Якщо Ви бажаєте надати доступ до Вашого сховища KeePassXC -надайте унікальну назву та підтвердьте його. +Якщо Ви бажаєте надати доступ до Вашого сховища KeePassXC, +вкажіть унікальну назву та схваліть прив'язку. KeePassXC: Overwrite existing key? @@ -3730,7 +3727,7 @@ Please unlock the selected database or choose another one which is unlocked. No shared encryption-keys found in KeePassHttp Settings. - Не знайдено спільних ключів шифрування у налаштуваннях KeePassHttp. + Не знайдено спільних ключів шифрування у налаштуваннях KeePassHttp. KeePassXC: Settings not available! @@ -3762,14 +3759,14 @@ Please unlock the selected database or choose another one which is unlocked. The active database does not contain an entry with permissions. - Поточне сховище не містить записів з привілеями… + Поточне сховище не містить записів з привілеями. SettingsWidget Application Settings - Параметри застосунку + Налаштування застосунку General @@ -3796,7 +3793,7 @@ Please unlock the selected database or choose another one which is unlocked. Remember last databases - Пам’ятати останнє сховище + Пам’ятати останні сховища Remember last key files and security dongles @@ -3808,7 +3805,7 @@ Please unlock the selected database or choose another one which is unlocked. Automatically save on exit - Автоматично зберігати при виході + Автоматично зберігати перед виходом Automatically save after every change @@ -3820,11 +3817,11 @@ Please unlock the selected database or choose another one which is unlocked. Minimize when copying to clipboard - Згортати при копіюванні до кишені + Згортати після копіювання до кишені Minimize window at application startup - Згорнути вікно після запуску застосунку + Згортати вікно після запуску застосунку Use group icon on entry creation @@ -3840,15 +3837,15 @@ Please unlock the selected database or choose another one which is unlocked. Show a system tray icon - Показувати значок в треї + Показувати значок у системному лотку Hide window to system tray when minimized - При згортанні ховати вікно в область системних повідомлень + Після згортання ховати вікно в системний лоток Hide window to system tray instead of app exit - Ховати вікно у системний лоток замість закриття застосунку. + Ховати вікно в системний лоток замість закриття застосунку Dark system tray icon @@ -3885,7 +3882,7 @@ Please unlock the selected database or choose another one which is unlocked. ms Milliseconds - мс + мс Startup @@ -3897,7 +3894,7 @@ Please unlock the selected database or choose another one which is unlocked. Safely save database files (may be incompatible with Dropbox, etc) - Безпечно зберігати файли сховища (може бути несумісним з Dropbox та ін.) + Безпечно зберігати файли сховища (може бути несумісним з Dropbox та ін.) Backup database file before saving @@ -3916,7 +3913,7 @@ Please unlock the selected database or choose another one which is unlocked.SettingsWidgetSecurity Timeouts - Час очикування + Час очікування Clear clipboard after @@ -3925,11 +3922,11 @@ Please unlock the selected database or choose another one which is unlocked. sec Seconds - сек + сек Lock databases after inactivity of - Заблокувати сховище, неактивне протягом + Блокувати сховища, неактивні протягом Convenience @@ -3941,11 +3938,11 @@ Please unlock the selected database or choose another one which is unlocked. Lock databases after minimizing the window - Заблоковувати сховища після згортання вікна + Блокувати сховища після згортання вікна Don't require password repeat when it is visible - Не запитувати підтвердження пароля, якщо він не приховується + Не запитувати підтвердження пароля, якщо він не прихований Show passwords in cleartext by default @@ -3957,7 +3954,7 @@ Please unlock the selected database or choose another one which is unlocked. Hide entry notes by default - Типово ховати нотатки до запису + Типово приховувати нотатки до запису Privacy @@ -3969,14 +3966,14 @@ Please unlock the selected database or choose another one which is unlocked. Re-lock previously locked database after performing Auto-Type - Заблоковувати попередньо заблоковане сховище після завершення автозаповнення + Блокувати попередньо заблоковане сховище після завершення автозаповнення SetupTotpDialog Setup TOTP - Встановити ТОГ + Встановити ТОП Key: @@ -4017,7 +4014,7 @@ Please unlock the selected database or choose another one which is unlocked. sec Seconds - сек + сек @@ -4032,11 +4029,11 @@ Please unlock the selected database or choose another one which is unlocked. Copy - Копіювати + Cкопіювати Expires in - Втрачає чинність через + Знечинюється через seconds @@ -4093,15 +4090,15 @@ Please unlock the selected database or choose another one which is unlocked. Path of the entry to remove. - Шлях до запису, який треба видалити. + Шлях до запису, що підлягає видаленню. KeePassXC - cross-platform password manager - KeePassXC – кросплатформний менеджер паролів + KeePassXC – багатоплатформний розпорядник паролів filenames of the password databases to open (*.kdbx) - Назви файлів сховищ, які треба відкрити (*.kdbx) + назви файлів сховищ, які треба відкрити (*.kdbx) path to a custom config file diff --git a/share/translations/keepassx_zh_CN.ts b/share/translations/keepassx_zh_CN.ts index 25253c508..ac976bbf2 100644 --- a/share/translations/keepassx_zh_CN.ts +++ b/share/translations/keepassx_zh_CN.ts @@ -15,7 +15,7 @@ KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3. - KeePassXC 使用第 2 版 GNU 通用公共授权协议(GPL)分发,你也可以根据需要选用第 3 版。 + KeePassXC 使用第 2 版 GNU 通用公共授权协议(GPL)分发,你也可以根据需要选用第 3 版。 Contributors @@ -78,7 +78,8 @@ CPU 架构:%2 Build Type: %1 - + 构建类型:%1 + @@ -255,7 +256,7 @@ Please select whether you want to allow access. Show a &notification when credentials are requested Credentials mean login data requested via browser extension - 当请求凭据时显示通知 (&N) + 当请求凭据时显示通知 Re&quest to unlock the database if it is locked @@ -280,7 +281,7 @@ Please select whether you want to allow access. Sort &matching credentials by title Credentials mean login data requested via browser extension - 按照名称排列匹配的凭据 + 根据名称排列匹配的凭据 Sort matching credentials by &username @@ -289,7 +290,7 @@ Please select whether you want to allow access. &Disconnect all browsers - + 断开与所有浏览器的关联 Forget all remembered &permissions @@ -307,7 +308,7 @@ Please select whether you want to allow access. Never ask before &updating credentials Credentials mean login data requested via browser extension - + 更新凭据时不再询问 Only the selected database has to be connected with a client. @@ -399,7 +400,7 @@ give it a unique name to identify and accept it. KeePassXC: Overwrite existing key? - KeePassXC︰ 覆盖现有的密钥吗? + KeePassXC:覆盖现有的密钥吗? A shared encryption key with the name "%1" already exists. @@ -409,7 +410,7 @@ Do you want to overwrite it? KeePassXC: Update Entry - KeePassXC︰ 更新条目 + KeePassXC:更新条目 Do you want to update the information in %1 - %2? @@ -417,7 +418,7 @@ Do you want to overwrite it? KeePassXC: Database locked! - KeePassXC︰ 数据库被锁定 ! + KeePassXC:数据库被锁定! The active database is locked! @@ -427,7 +428,7 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Settings not available! - KeePassXC︰ 设置不可用 ! + KeePassXC:设置不可用! The active database does not contain a settings entry. @@ -435,23 +436,23 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: No keys found - KeePassXC︰ 未找到键 + KeePassXC:未找到键 No shared encryption keys found in KeePassXC Settings. - + 在KeePassXC设置中找不到共享加密密钥。 KeePassXC: Removed keys from database - KeePassXC︰ 从数据库中删除键 + KeePassXC:从数据库中删除键 Successfully removed %n encryption key(s) from KeePassXC settings. - + 已成功从KeePassXC设置中删除了%n个加密密钥。 Removing stored permissions… - + 正在删除存储的权限... Abort @@ -459,11 +460,12 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Removed permissions - KeePassXC︰ 已删除的权限 + KeePassXC:已删除的权限 Successfully removed permissions from %n entry(s). - + 已成功从%n个条目中删除权限。 + KeePassXC: No entry with permissions found! @@ -691,15 +693,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - + %n 字节, %n row(s), - + %n 行, %n column(s) - + %n 列 @@ -816,17 +818,19 @@ You can now save it. Number of rounds too high Key transformation rounds - + 回合数太高 You are using a very high number of key transform rounds with Argon2. If you keep this number, your database may take hours or days (or even longer) to open! - + 你正在对 Argon2 使用相当高的密钥转换回合数。 + +如果仍执意使用此数量,你的数据库可能需要数小时或数天(甚至更长时间)才能打开! Understood, keep number - + 了解,仍使用此数量 Cancel @@ -835,31 +839,33 @@ If you keep this number, your database may take hours or days (or even longer) t Number of rounds too low Key transformation rounds - + 回合数太低 You are using a very low number of key transform rounds with AES-KDF. If you keep this number, your database may be too easy to crack! - + 你正对AES-KDF使用过低的密钥转换回合数。 + +如果仍执意使用此数量,你的数据库可能会变得相当简单即能破解! KDF unchanged - + KDF不变 Failed to transform key with new KDF parameters; KDF unchanged. - + 无法使用新的KDF参数转换密钥; KDF不变。 MiB Abbreviation for Mebibytes (KDF settings) - + MiB thread(s) Threads for parallel execution (KDF settings) - + 线程 @@ -886,7 +892,7 @@ If you keep this number, your database may be too easy to crack! Benchmark 1-second delay - + 基准1秒延迟 Memory Usage: @@ -894,7 +900,7 @@ If you keep this number, your database may be too easy to crack! Parallelism: - + 平行运算: @@ -929,7 +935,7 @@ If you keep this number, your database may be too easy to crack! MiB - MiB + MiB Use recycle bin @@ -1055,7 +1061,7 @@ Save changes? Can't lock the database as you are currently editing it. Please press cancel to finish your changes or discard them. 不能锁定正在编辑的数据库。 -点击“取消”继续编辑, 点击“确定”丢弃更改。 +点击“取消”继续编辑,点击“确定”丢弃更改。 This database has been modified. @@ -1067,12 +1073,13 @@ Otherwise your changes are lost. Disable safe saves? - + 禁用安全保存? KeePassXC has failed to save the database multiple times. This is likely caused by file sync services holding a lock on the save file. Disable safe saves and try again? - + KeePassXC未能多次保存数据库。 这可能是由保存文件锁定的文件同步服务引起的。 +禁用安全保存并重试? @@ -1172,7 +1179,8 @@ Disable safe saves and try again? The database file has changed and you have unsaved changes. Do you want to merge your changes? - + 数据库文件改变了,你有未保存的更改。 +你想合并你的修改吗? Could not open the new database file while attempting to autoreload this database. @@ -1227,7 +1235,7 @@ Do you want to merge your changes? Attributes - 添加属性 + 附加属性 Attachments @@ -1302,7 +1310,7 @@ Do you want to merge your changes? n/a - + (encrypted) @@ -1318,7 +1326,7 @@ Do you want to merge your changes? Failed to open private key - + 无法打开私钥 Entry history @@ -1354,7 +1362,7 @@ Do you want to merge your changes? Press reveal to view or edit - + 按显示查看或编辑 Tomorrow @@ -1366,7 +1374,7 @@ Do you want to merge your changes? %n month(s) - %n 个月 + %n 月 1 year @@ -1456,7 +1464,7 @@ Do you want to merge your changes? Use a specific sequence for this association: - + 使用特定序列进行此关联: @@ -1506,7 +1514,7 @@ Do you want to merge your changes? Toggle the checkbox to reveal the notes section. - + 切换复选框以显示备注部分。 Username: @@ -1521,15 +1529,15 @@ Do you want to merge your changes? EditEntryWidgetSSHAgent Form - + 表格 Remove key from agent after - + 稍后从代理中删除密钥 seconds - + Fingerprint @@ -1537,19 +1545,19 @@ Do you want to merge your changes? Remove key from agent when database is closed/locked - + 数据库关闭/锁定时,从代理中删除密钥 Public key - + 公钥 Add key to agent when database is opened/unlocked - + 在打开/解锁数据库时向代理添加密钥 Comment - + 注解 Decrypt @@ -1557,7 +1565,7 @@ Do you want to merge your changes? n/a - + Copy to clipboard @@ -1565,11 +1573,11 @@ Do you want to merge your changes? Private key - + 私钥 External file - + 外部文件 Browse... @@ -1578,19 +1586,19 @@ Do you want to merge your changes? Attachment - + 附件 Add to agent - + 添加到代理 Remove from agent - + 从代理中删除 Require user confirmation when this key is used - + 使用此密钥时需要用户确认 @@ -1738,7 +1746,7 @@ Do you want to merge your changes? Plugin Data - + 插件数据 Remove @@ -1746,20 +1754,21 @@ Do you want to merge your changes? Delete plugin data? - + 是否删除插件数据? Do you really want to delete the selected plugin data? This may cause the affected plugins to malfunction. - + 你真的想删除所选的插件数据吗? +这可能会导致受影响的插件出现问题。 Key - + 密钥 Value - + @@ -1778,14 +1787,14 @@ This may cause the affected plugins to malfunction. Size - + 大小 EntryAttachmentsWidget Form - + 表格 Add @@ -1805,11 +1814,11 @@ This may cause the affected plugins to malfunction. Select files - + 选择文件 Are you sure you want to remove %n attachment(s)? - + 你确定要删除%n个附件吗? Confirm Remove @@ -1817,30 +1826,33 @@ This may cause the affected plugins to malfunction. Save attachments - + 保存附件 Unable to create directory: %1 - + 无法创建目录: +%1 Are you sure you want to overwrite the existing file "%1" with the attachment? - + 您确定要用附件覆盖现有文件“%1”吗? Confirm overwrite - + 确认覆盖 Unable to save attachments: %1 - + 无法保存附件: +%1 Unable to open attachment: %1 - + 无法打开附件: +%1 Unable to open attachments: @@ -1920,15 +1932,15 @@ This may cause the affected plugins to malfunction. Created - + 创建 Modified - + 已修改 Accessed - + 已读取 Attachments @@ -1939,31 +1951,31 @@ This may cause the affected plugins to malfunction. EntryView Customize View - + 自定义视图 Hide Usernames - + 隐藏用户名 Hide Passwords - + 隐藏密码 Fit to window - + 适应窗口 Fit to contents - + 适合内容 Reset to defaults - + 重置为默认值 Attachments (icon) - + 附件(图标) @@ -1977,11 +1989,11 @@ This may cause the affected plugins to malfunction. HostInstaller KeePassXC: Cannot save file! - + KeePassXC:无法保存文件! Cannot save the native messaging script file. - + 无法保存本机消息传递脚本文件。 @@ -2080,7 +2092,7 @@ This may cause the affected plugins to malfunction. Kdbx4Reader missing database headers - + 缺少数据库头 Unable to calculate master key @@ -2088,127 +2100,127 @@ This may cause the affected plugins to malfunction. Invalid header checksum size - + 无效的报头校验大小 Header SHA256 mismatch - + SHA256标头不匹配 Wrong key or database file is corrupt. (HMAC mismatch) - + 错误的密钥或数据库文件已损坏。 (HMAC不匹配) Unknown cipher - + 未知的加密 Invalid header id size - + 无效的标头ID大小 Invalid header field length - + 无效的标头字段长度 Invalid header data length - + 无效的标头数据长度 Failed to open buffer for KDF parameters in header - + 无法打开在标头中KDF参数的缓冲区 Unsupported key derivation function (KDF) or invalid parameters - + 不支持的密钥派生函数(KDF)或无效参数 Legacy header fields found in KDBX4 file. - + 在KDBX4文件中找到旧的标头字段。 Invalid inner header id size - + 无效的内部标头ID大小 Invalid inner header field length - + 无效的内部标头字段长度 Invalid inner header binary size - + 无效的内部标题二进制文件 Unsupported KeePass variant map version. Translation: variant map = data structure for storing meta data - + 不支持的KeePass变体映射版本。 Invalid variant map entry name length Translation: variant map = data structure for storing meta data - + 无效的变量映射条目名称长度 Invalid variant map entry name data Translation: variant map = data structure for storing meta data - + 无效的变量映射条目名称数据 Invalid variant map entry value length Translation: variant map = data structure for storing meta data - + 无效的变量映射条目值长度 Invalid variant map entry value data Translation comment: variant map = data structure for storing meta data - + 无效的变量映射条目值数据 Invalid variant map Bool entry value length Translation: variant map = data structure for storing meta data - + 无效的变量映射Bool条目值 Invalid variant map Int32 entry value length Translation: variant map = data structure for storing meta data - + 无效的变量映射Int32条目值长度 Invalid variant map UInt32 entry value length Translation: variant map = data structure for storing meta data - + 无效的变量映射 UInt32条目值长度 Invalid variant map Int64 entry value length Translation: variant map = data structure for storing meta data - + 无效的变量映射Int64条目值长度 Invalid variant map UInt64 entry value length Translation: variant map = data structure for storing meta data - + 无效的变量映射UInt64条目值长度 Invalid variant map entry type Translation: variant map = data structure for storing meta data - + 无效的变量映射条目类型 Invalid variant map field type size Translation: variant map = data structure for storing meta data - + 无效的变量映射字段类型大小 Kdbx4Writer Invalid symmetric cipher algorithm. - + 无效的对称密码算法。 Invalid symmetric cipher IV size. IV = Initialization Vector for symmetric cipher - + 无效的对称密码IV大小。 Unable to calculate master key @@ -2217,50 +2229,50 @@ This may cause the affected plugins to malfunction. Failed to serialize KDF parameters variant map Translation comment: variant map = data structure for storing meta data - + 无法序列化KDF参数变量映射 KdbxReader Invalid cipher uuid length - + 无效的暗文 uuid 长度 Unsupported cipher - + 不支持的加密 Invalid compression flags length - + 无效的压缩标志长度 Unsupported compression algorithm - + 不支持的压缩算法 Invalid master seed size - + 无效的主种子大小 Invalid transform seed size - + 无效的转换种子大小 Invalid transform rounds size - + 无效的转换回合数 Invalid start bytes size - + 无效的起始字节大小 Invalid random stream id size - + 无效的随机流 ID 大小 Invalid inner random stream cipher - + 无效的内部随机流密码 Not a KeePass database. @@ -2277,38 +2289,38 @@ This is a one-way migration. You won't be able to open the imported databas Unsupported KeePass 2 database version. - + 不支持的KeePass 2数据库版本。 KdbxXmlReader XML parsing failure: %1 - + XML 解析失败:%1 No root group - + 无权限群组 Missing icon uuid or data - + 缺少图标 uuid 或数据 Missing custom data key or value - + 缺少自定义数据键或值 Multiple group elements - + 多个组元素 Null group uuid - + 空的组 UUID Invalid group icon number - + 无效的组图标编号 Invalid EnableAutoType value @@ -2316,43 +2328,43 @@ This is a one-way migration. You won't be able to open the imported databas Invalid EnableSearching value - + 无效的EnableSearching值 No group uuid found - + 找不到群组 uuid Null DeleteObject uuid - + 空的 DeleteObject uuid Missing DeletedObject uuid or time - + 缺少 DeletedObject uuid 或时间 Null entry uuid - + 空的项目 uuid Invalid entry icon number - + 无效的条目图标编号 History element in history entry - + 历史条目中的历史元素 No entry uuid found - + 找不到项目 uuid History element with different uuid - + 具有不同 uuid 的历史元素 Unable to decrypt entry string - + 无法解密条目字符串 Duplicate custom attribute found @@ -2360,48 +2372,48 @@ This is a one-way migration. You won't be able to open the imported databas Entry string key or value missing - + 输入字符串键或值丢失 Duplicate attachment found - + 找到重复的附件 Entry binary key or value missing - + 缺少输入二进制密钥或值 Auto-type association window or sequence missing - + 缺少自动键入关联窗口或序列 Invalid bool value - + 无效的布尔值 Invalid date time value - + 无效的日期时间值 Invalid color value - + 无效的颜色值 Invalid color rgb part - + 无效的颜色rgb部分 Invalid number value - + 无效的数值 Invalid uuid value - + 无效的 uuid 值 Unable to decompress binary Translator meant is a binary data inside an entry - + 无法解压缩二进制文件 @@ -2436,31 +2448,31 @@ This is a one-way migration. You won't be able to open the imported databas Unable to read encryption IV IV = Initialization Vector for symmetric cipher - + 无法读取加密IV Invalid number of groups - + 无效的群组数 Invalid number of entries - + 无效的条目数 Invalid content hash size - + 无效的内容散列大小 Invalid transform seed size - + 无效的转换种子大小 Invalid number of transform rounds - + 无效的转换回合数 Unable to construct group tree - + 无法构成群组树 Root @@ -2476,122 +2488,95 @@ This is a one-way migration. You won't be able to open the imported databas Key transformation failed - + 密钥转换失败 Invalid group field type number - + 无效的组字段类型编号 Invalid group field size - + 无效组字段大小 Read group field data doesn't match size - + 读取组字段数据与大小不匹配 Incorrect group id field size - + 组ID字段大小不正确 Incorrect group creation time field size - + 组创建时间字段大小不正确 Incorrect group modification time field size - + 组修改时间字段大小不正确 Incorrect group access time field size - + 组访问时间字段大小不正确 Incorrect group expiry time field size - + 组到期时间字段大小不正确 Incorrect group icon field size - + 组图标字段大小不正确 Incorrect group level field size - + 组级别字段大小不正确 Invalid group field type - + 无效的组字段类型 Missing group id or level - + 缺少组ID或级别 Missing entry field type number - + 缺少输入字段类型编号 Invalid entry field size - + 无效的输入字段大小 Read entry field data doesn't match size - + 读取输入字段数据与大小不匹配 Invalid entry uuid field size - + 无效的条目 uuid 字段大小 Invalid entry group id field size - + 无效的条目组ID字段大小 Invalid entry icon field size - + 无效的输入图标字段大小 Invalid entry creation time field size - + 无效的条目创建时间字段大小 Invalid entry modification time field size - + 无效的条目修改时间字段大小 Invalid entry expiry time field size - + 无效的条目到期时间字段大小 Invalid entry field type - - - - - KeePass2 - - AES: 256-bit - AES:256位 - - - Twofish: 256-bit - Twofish:256位 - - - ChaCha20: 256-bit - ChaCha20:256位 - - - AES-KDF (KDBX 4) - AES-KDF(KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF(KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2(推荐 KDBX 4) + 无效的输入字段类型 @@ -2645,7 +2630,7 @@ This is a one-way migration. You won't be able to open the imported databas Time-based one-time password - + 基于时间的一次性密码 &Groups @@ -2713,7 +2698,7 @@ This is a one-way migration. You won't be able to open the imported databas Change &master key... - 更改主密码(M),,, + 更改主密码(M)... &Database settings @@ -2821,7 +2806,7 @@ This is a one-way migration. You won't be able to open the imported databas E&mpty recycle bin - + 清空回收站 Clear history @@ -2879,106 +2864,108 @@ This is a one-way migration. You won't be able to open the imported databas WARNING: You are using an unstable build of KeePassXC! There is a high risk of corruption, maintain a backup of your databases. This version is not meant for production use. - + 警告:您正在使用不稳定的KeePassXC版本! +存在高风险的损坏,维护数据库的备份。 +此版本不适用于生产用途。 OpenSSHKey Invalid key file, expecting an OpenSSH key - + 无效的密钥文件,需要OpenSSH密钥 PEM boundary mismatch - + PEM边界不匹配 Base64 decoding failed - + Base64 解码失败 Key file way too small. - + 密钥文件太小了。 Key file magic header id invalid - + 密钥文件魔术标头ID无效 Found zero keys - + 找到零密钥 Failed to read public key. - + 无法读取公钥。 Corrupted key file, reading private key failed - + 损坏的密钥文件,读取私钥失败 No private key payload to decrypt - + 私钥中没有载体可解密 Trying to run KDF without cipher - + 尝试运行无加密的 KDF Passphrase is required to decrypt this key - + 需要密码短语解密此密钥 Key derivation failed, key file corrupted? - + 密钥派生失败,密钥文件已损坏? Decryption failed, wrong passphrase? - + 解密失败,错误的密码短语? Unexpected EOF while reading public key - + 读取公钥时意外的文件结束 Unexpected EOF while reading private key - + 读取私钥时意外的文件结束 Can't write public key as it is empty - + 无法写公钥,因为它是空的 Unexpected EOF when writing public key - + 编写公钥时意外的文件结束 Can't write private key as it is empty - + 无法写私钥,因为它是空的 Unexpected EOF when writing private key - + 编写私钥时意外的文件结束 Unsupported key type: %1 - + 不支持的密钥类型: %1 Unknown cipher: %1 - + 未知加密:%1 Cipher IV is too short for MD5 kdf - + 密码IV对于MD5 kdf来说太短了 Unknown KDF: %1 - + 未知的KDF: %1 Unknown key type: %1 - + 未知密钥类型: %1 @@ -3160,7 +3147,7 @@ Using default port 19455. &Length: - 长度(L)︰ + 长度(L): Passphrase @@ -3231,63 +3218,63 @@ Using default port 19455. QObject Database not opened - + 数据库未打开 Database hash not available - + 数据库哈希值不可用 Client public key not received - + 未收到客户端公钥 Cannot decrypt message - + 无法解密消息 Timeout or cannot connect to KeePassXC - + 超时或无法连接到KeePassXC Action cancelled or denied - + 操作被取消或被拒绝 Cannot encrypt message or public key not found. Is Native Messaging enabled in KeePassXC? - + 无法加密未找到的信息或公钥。 是否在KeePassXC中启用了Native Messaging? KeePassXC association failed, try again - + KeePassXC关联失败,请重试 Key change was not successful - + 密钥更改没有成功 Encryption key is not recognized - + 无法识别加密密钥 No saved databases found - + 找不到保存的数据库 Incorrect action - + 操作错误 Empty message received - + 收到空消息 No URL provided - + 没有提供URL No logins found - + 找不到登录信息 Unknown error @@ -3295,7 +3282,7 @@ Using default port 19455. Add a new entry to a database. - + 向数据库添加新条目。 Path of the database. @@ -3303,23 +3290,23 @@ Using default port 19455. Key file of the database. - + 数据库的密钥文件。 path - + 路径 Username for the entry. - + 项目的用户名。 username - + 用户名 URL for the entry. - + 条目的URL。 URL @@ -3327,15 +3314,15 @@ Using default port 19455. Prompt for the entry's password. - + 提示输入密码。 Generate a password for the entry. - + 为条目生成密码。 Length for the generated password. - + 生成密码的长度。 length @@ -3343,28 +3330,28 @@ Using default port 19455. Path of the entry to add. - + 要添加的条目的路径。 Copy an entry's password to the clipboard. - + 将条目的密码复制到剪贴板。 Path of the entry to clip. clip = copy to clipboard - + 剪辑条目的路径。 Timeout in seconds before clearing the clipboard. - + 清除剪贴板前超时(以秒为单位)。 Edit an entry. - + 编辑项目。 Title for the entry. - + 项目标题。 title @@ -3372,19 +3359,19 @@ Using default port 19455. Path of the entry to edit. - + 要编辑的项目的路径。 Estimate the entropy of a password. - + 估计密码的熵。 Password for which to estimate the entropy. - + 用于估计熵的密码。 Perform advanced analysis on the password. - + 对密码执行高级分析。 Extract and print the content of a database. @@ -3396,25 +3383,31 @@ Using default port 19455. Insert password to unlock %1: - + 插入密码以解锁%1: Failed to load key file %1 : %2 - + 无法加载密钥文件%1 : %2 WARNING: You are using a legacy key file format which may become unsupported in the future. Please consider generating a new key file. - + 警告:您使用的是旧密钥文件格式 +将来不受支持。 + +请考虑生成新的密钥文件。 Available commands: - + + +可用命令: + Name of the command to execute. @@ -3430,11 +3423,11 @@ Available commands: Find entries quickly. - + 快速查找项目。 Search term. - + 搜索词。 Merge two databases. @@ -3450,23 +3443,23 @@ Available commands: Use the same credentials for both database files. - + 对两个数据库文件使用相同的凭据。 Key file of the database to merge from. - + 要合并的数据库的密钥文件。 Show an entry's information. - + 显示条目的信息。 Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given. - + 要显示的属性的名称。 可以多次指定此选项,每个属性按给定顺序显示为每行一个。 如果未指定任何属性,则会给出默认属性的摘要。 attribute - + 属性 Name of the entry to show. @@ -3494,6 +3487,30 @@ Available commands: missing closing quote 缺少后引号 + + AES: 256-bit + AES:256位 + + + Twofish: 256-bit + Twofish:256位 + + + ChaCha20: 256-bit + ChaCha20:256位 + + + Argon2 (KDBX 4 – recommended) + Argon2(推荐 KDBX 4) + + + AES-KDF (KDBX 4) + AES-KDF(KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF(KDBX 3.1) + Group 群组 @@ -3516,15 +3533,15 @@ Available commands: Last Modified - + 上一次更改 Created - + 创建 Legacy Browser Integration - + 传统浏览器集成 Browser Integration @@ -3548,48 +3565,49 @@ Available commands: Generate a new random diceware passphrase. - + 生成新的随机diceware密码。 Word count for the diceware passphrase. - + diceware密码短语的字数。 count - + Wordlist for the diceware generator. [Default: EFF English] - + diceware生成器的词表。 +[默认:EFF英文] Generate a new random password. - + 生成一个新的随机密码。 Length of the generated password. - + 生成密码长度。 Use lowercase characters in the generated password. - + 在生成的密码中使用小写字符。 Use uppercase characters in the generated password. - + 在生成的密码中使用大写字符。 Use numbers in the generated password. - + 在生成的密码中使用数字。 Use special characters in the generated password. - + 在生成的密码中使用特殊字符。 Use extended ASCII in the generated password. - + 在生成的密码中使用扩展ASCII。 @@ -3665,7 +3683,7 @@ give it a unique name to identify and accept it. KeePassXC: Overwrite existing key? - KeePassXC︰ 覆盖现有的密钥吗? + KeePassXC:覆盖现有的密钥吗? A shared encryption-key with the name "%1" already exists. @@ -3675,7 +3693,7 @@ Do you want to overwrite it? KeePassXC: Update Entry - KeePassXC︰ 更新条目 + KeePassXC:更新条目 Do you want to update the information in %1 - %2? @@ -3683,7 +3701,7 @@ Do you want to overwrite it? KeePassXC: Database locked! - KeePassXC︰ 数据库被锁定 ! + KeePassXC:数据库被锁定! The active database is locked! @@ -3693,15 +3711,15 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Removed keys from database - KeePassXC︰ 从数据库中删除键 + KeePassXC:从数据库中删除键 Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - + 已成功从KeePassX / Http设置中删除%n加密密钥。 KeePassXC: No keys found - KeePassXC︰ 未找到键 + KeePassXC:未找到键 No shared encryption-keys found in KeePassHttp Settings. @@ -3709,7 +3727,7 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Settings not available! - KeePassXC︰ 设置不可用 ! + KeePassXC:设置不可用! The active database does not contain an entry of KeePassHttp Settings. @@ -3725,11 +3743,11 @@ Please unlock the selected database or choose another one which is unlocked. KeePassXC: Removed permissions - KeePassXC︰ 已删除的权限 + KeePassXC:已删除的权限 Successfully removed permissions from %n entries. - + 已成功从%n个条目中删除权限。 KeePassXC: No entry with permissions found! @@ -3811,7 +3829,7 @@ Please unlock the selected database or choose another one which is unlocked. Hide the Details view - + 隐藏详细信息视图 Show a system tray icon @@ -3827,7 +3845,7 @@ Please unlock the selected database or choose another one which is unlocked. Dark system tray icon - + 暗色系统托盘图标 Language @@ -3839,11 +3857,11 @@ Please unlock the selected database or choose another one which is unlocked. Use entry title to match windows for global Auto-Type - + 使用条目标题匹配全局自动类型的窗口 Use entry URL to match windows for global Auto-Type - + 使用条目URL匹配全局自动类型的窗口 Always ask before performing Auto-Type @@ -3864,23 +3882,23 @@ Please unlock the selected database or choose another one which is unlocked. Startup - + 启动 File Management - + 文件管理 Safely save database files (may be incompatible with Dropbox, etc) - + 安全保存数据库文件(可能与Dropbox等不兼容) Backup database file before saving - + 保存前备份数据库文件 Entry Management - + 进入管理 General @@ -3928,11 +3946,11 @@ Please unlock the selected database or choose another one which is unlocked. Hide passwords in the preview panel - + 在预览面板中隐藏密码 Hide entry notes by default - + 默认情况下隐藏条目备注 Privacy @@ -3944,7 +3962,7 @@ Please unlock the selected database or choose another one which is unlocked. Re-lock previously locked database after performing Auto-Type - + 执行自动类型后重新锁定先前锁定的数据库 @@ -3959,11 +3977,11 @@ Please unlock the selected database or choose another one which is unlocked. Default RFC 6238 token settings - + 默认RFC 6238令牌设置 Steam token settings - + Steam 令牌设置 Use custom settings @@ -4041,7 +4059,7 @@ Please unlock the selected database or choose another one which is unlocked. Import from KeePass 1 - 导入KeePass 1 数据库 + 导入 KeePass 1 数据库 Import from CSV @@ -4053,14 +4071,14 @@ Please unlock the selected database or choose another one which is unlocked. Welcome to KeePassXC %1 - + 欢迎来到 KeePassXC %1 main Remove an entry from the database. - + 从数据库中删除条目。 Path of the database. @@ -4068,7 +4086,7 @@ Please unlock the selected database or choose another one which is unlocked. Path of the entry to remove. - + 要删除的条目的路径。 KeePassXC - cross-platform password manager @@ -4092,7 +4110,7 @@ Please unlock the selected database or choose another one which is unlocked. Parent window handle - + 父窗口句柄 \ No newline at end of file diff --git a/share/translations/keepassx_zh_TW.ts b/share/translations/keepassx_zh_TW.ts index 6e9c7622c..a3ccb807f 100644 --- a/share/translations/keepassx_zh_TW.ts +++ b/share/translations/keepassx_zh_TW.ts @@ -73,12 +73,12 @@ Kernel: %3 %4 Special thanks from the KeePassXC team go to debfx for creating the original KeePassX. - + KeePassXC 團隊特別鳴謝 debfx 開發了原本的 KeePassX Build Type: %1 - + 建置型態: %1 @@ -447,7 +447,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption key(s) from KeePassXC settings. - + 成功從 KeePassXC 移除 %n 個加密金鑰。 Removing stored permissions… @@ -463,7 +463,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entry(s). - + 成功從 %n 個項目中移除權限。 KeePassXC: No entry with permissions found! @@ -691,15 +691,15 @@ Please consider generating a new key file. CsvParserModel %n byte(s), - + %n 位元組, %n row(s), - + %n 行, %n column(s) - + %n 列, @@ -857,12 +857,12 @@ If you keep this number, your database may be too easy to crack! MiB Abbreviation for Mebibytes (KDF settings) - + MiB thread(s) Threads for parallel execution (KDF settings) - + 線程 @@ -944,7 +944,7 @@ If you keep this number, your database may be too easy to crack! Enable &compression (recommended) - 啟用&壓縮 (推薦) + 啟用壓縮 (推薦) (&C) @@ -1378,7 +1378,7 @@ Do you want to merge your changes? Apply generated password? - + 使用產生的密碼? Do you want to apply the generated password to this entry? @@ -1813,7 +1813,7 @@ This may cause the affected plugins to malfunction. Are you sure you want to remove %n attachment(s)? - + 確定移除 %n 個附件? Confirm Remove @@ -2577,33 +2577,6 @@ This is a one-way migration. You won't be able to open the imported databas 無效的項目欄位類型 - - KeePass2 - - AES: 256-bit - AES: 256 位元 - - - Twofish: 256-bit - Twofish: 256 位元 - - - ChaCha20: 256-bit - ChaCha20: 256 位元 - - - AES-KDF (KDBX 4) - AES-KDF (KDBX 4) - - - AES-KDF (KDBX 3.1) - AES-KDF (KDBX 3.1) - - - Argon2 (KDBX 4 – recommended) - Argon2 (KDBX 4 – 推薦) - - Main @@ -3212,7 +3185,7 @@ Using default port 19455. Entropy: %1 bit - 資訊熵:%1 位元 + Entropy: %1 bit Password Quality: %1 @@ -3511,6 +3484,30 @@ Available commands: missing closing quote 缺少右引號 + + AES: 256-bit + AES: 256 位元 + + + Twofish: 256-bit + Twofish: 256 位元 + + + ChaCha20: 256-bit + ChaCha20: 256 位元 + + + Argon2 (KDBX 4 – recommended) + Argon2 (KDBX 4 – 推薦) + + + AES-KDF (KDBX 4) + AES-KDF (KDBX 4) + + + AES-KDF (KDBX 3.1) + AES-KDF (KDBX 3.1) + Group 群組 @@ -3714,7 +3711,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed %n encryption-key(s) from KeePassX/Http Settings. - + 成功從 KeePassX/Http Settings 移除 %n 加密金鑰。 KeePassXC: No keys found @@ -3746,7 +3743,7 @@ Please unlock the selected database or choose another one which is unlocked. Successfully removed permissions from %n entries. - + 成功從 %n 個項目中移除權限。 KeePassXC: No entry with permissions found! From 1af293847c21289520d94a39db364e202a19b689 Mon Sep 17 00:00:00 2001 From: Carlo Teubner Date: Sat, 1 Dec 2018 17:49:20 +0000 Subject: [PATCH 80/84] LayeredStream dtor: fully qualify 'close()' call `close()` is a virtual function. Since we are calling it from a destructor, make it clear that we are specifically calling the base class (`QIODevice`) implementation, as opposed to an implementation in any derived class. [Found by lgtm.com](https://lgtm.com/projects/g/keepassxreboot/keepassxc/alerts) --- src/streams/LayeredStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/streams/LayeredStream.cpp b/src/streams/LayeredStream.cpp index af62dae95..8e254efbf 100644 --- a/src/streams/LayeredStream.cpp +++ b/src/streams/LayeredStream.cpp @@ -26,7 +26,7 @@ LayeredStream::LayeredStream(QIODevice* baseDevice) LayeredStream::~LayeredStream() { - close(); + QIODevice::close(); } bool LayeredStream::isSequential() const From bdee748a6a0bbd8b9bdb461fc205177b5ed28675 Mon Sep 17 00:00:00 2001 From: Carlo Teubner <435950+c4rlo@users.noreply.github.com> Date: Sat, 1 Dec 2018 21:08:55 +0000 Subject: [PATCH 81/84] Make Entry::m_tmpHistoryItem a QScopedPointer (#2524) * Make m_tmpHistoryItem a QScopedPointer Most of the time, `m_tmpHistoryItem` should be null by the time an `Entry` is destroyed. However, if a caller ever calls `beginUpdate()` without later calling `endUpdate()` -- perhaps because an exception was throw in the meantime -- it may not be null. This change avoids a memory leak in that case. Found via https://lgtm.com/projects/g/keepassxreboot/keepassxc/alerts --- src/core/Entry.cpp | 13 +++++-------- src/core/Entry.h | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 55f56f583..9eb25c278 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -40,7 +40,6 @@ Entry::Entry() , m_attachments(new EntryAttachments(this)) , m_autoTypeAssociations(new AutoTypeAssociations(this)) , m_customData(new CustomData(this)) - , m_tmpHistoryItem(nullptr) , m_modifiedSinceBegin(false) , m_updateTimeinfo(true) { @@ -706,9 +705,9 @@ void Entry::copyDataFrom(const Entry* other) void Entry::beginUpdate() { - Q_ASSERT(!m_tmpHistoryItem); + Q_ASSERT(m_tmpHistoryItem.isNull()); - m_tmpHistoryItem = new Entry(); + m_tmpHistoryItem.reset(new Entry()); m_tmpHistoryItem->setUpdateTimeinfo(false); m_tmpHistoryItem->m_uuid = m_uuid; m_tmpHistoryItem->m_data = m_data; @@ -721,16 +720,14 @@ void Entry::beginUpdate() bool Entry::endUpdate() { - Q_ASSERT(m_tmpHistoryItem); + Q_ASSERT(!m_tmpHistoryItem.isNull()); if (m_modifiedSinceBegin) { m_tmpHistoryItem->setUpdateTimeinfo(true); - addHistoryItem(m_tmpHistoryItem); + addHistoryItem(m_tmpHistoryItem.take()); truncateHistory(); - } else { - delete m_tmpHistoryItem; } - m_tmpHistoryItem = nullptr; + m_tmpHistoryItem.reset(); return m_modifiedSinceBegin; } diff --git a/src/core/Entry.h b/src/core/Entry.h index 655cc3621..ae11abe4d 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -249,7 +249,7 @@ private: QPointer m_customData; QList m_history; // Items sorted from oldest to newest - Entry* m_tmpHistoryItem; + QScopedPointer m_tmpHistoryItem; bool m_modifiedSinceBegin; QPointer m_group; bool m_updateTimeinfo; From b6eeabab5ecddc78af41fac111e370036db0de7f Mon Sep 17 00:00:00 2001 From: Carlo Teubner <435950+c4rlo@users.noreply.github.com> Date: Sun, 2 Dec 2018 15:05:54 +0000 Subject: [PATCH 82/84] KeePass2Reader: fix error message logic (#2523) --- src/format/KeePass2Reader.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index b62e398d9..97c361abe 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -64,22 +64,25 @@ bool KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer '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 false; + } else if (!(signature1 == KeePass2::SIGNATURE_1 && signature2 == KeePass2::SIGNATURE_2)) { + raiseError(tr("Not a KeePass database.")); + return false; } + // mask out minor version + m_version &= KeePass2::FILE_VERSION_CRITICAL_MASK; + 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.")); From cb3c4893dce571c9cbfb7f32ee4580f24bb05108 Mon Sep 17 00:00:00 2001 From: louib Date: Tue, 11 Dec 2018 10:49:51 -0500 Subject: [PATCH 83/84] Move unlockDatabase to CLI/Utils (#2539) Move unlockDatabase from Database to to cli/Utils --- src/cli/Add.cpp | 8 ++++---- src/cli/Clip.cpp | 8 ++++---- src/cli/Edit.cpp | 8 ++++---- src/cli/List.cpp | 8 ++++---- src/cli/Locate.cpp | 8 ++++---- src/cli/Merge.cpp | 10 +++++----- src/cli/Remove.cpp | 8 ++++---- src/cli/Show.cpp | 8 ++++---- src/cli/Utils.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/cli/Utils.h | 8 ++++++++ src/core/Database.cpp | 41 ----------------------------------------- src/core/Database.h | 2 -- tests/TestCli.cpp | 2 +- 13 files changed, 83 insertions(+), 77 deletions(-) diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index 9c78db2fb..3eb3b1bfc 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -90,10 +90,10 @@ int Add::execute(const QStringList& arguments) const QString& databasePath = args.at(0); const QString& entryPath = args.at(1); - auto db = Database::unlockFromStdin(databasePath, - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db = Utils::unlockDatabase(databasePath, + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 7e81bcf70..0cec5b3ce 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -66,10 +66,10 @@ int Clip::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db = Utils::unlockDatabase(args.at(0), + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index 81c1e3223..48ea5d739 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -95,10 +95,10 @@ int Edit::execute(const QStringList& arguments) const QString& databasePath = args.at(0); const QString& entryPath = args.at(1); - auto db = Database::unlockFromStdin(databasePath, - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db = Utils::unlockDatabase(databasePath, + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/List.cpp b/src/cli/List.cpp index e4b06f2cc..9c4f733ef 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -64,10 +64,10 @@ int List::execute(const QStringList& arguments) bool recursive = parser.isSet(recursiveOption); - auto db = Database::unlockFromStdin(args.at(0), - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db = Utils::unlockDatabase(args.at(0), + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index 0ccf5825c..e348df117 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -59,10 +59,10 @@ int Locate::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db = Utils::unlockDatabase(args.at(0), + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index 58c7965dc..c4759fe40 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -68,17 +68,17 @@ int Merge::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db1 = Database::unlockFromStdin(args.at(0), - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db1 = Utils::unlockDatabase(args.at(0), + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db1) { return EXIT_FAILURE; } QSharedPointer db2; if (!parser.isSet("same-credentials")) { - db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFromOption), Utils::STDOUT, Utils::STDERR); + db2 = Utils::unlockDatabase(args.at(1), parser.value(keyFileFromOption), Utils::STDOUT, Utils::STDERR); } else { db2 = QSharedPointer::create(); QString errorMessage; diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 1341fc6d1..cd6275b77 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -61,10 +61,10 @@ int Remove::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db = Utils::unlockDatabase(args.at(0), + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index e900a07b9..d8cf5a4fe 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -71,10 +71,10 @@ int Show::execute(const QStringList& arguments) return EXIT_FAILURE; } - auto db = Database::unlockFromStdin(args.at(0), - parser.value(Command::KeyFileOption), - parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, - Utils::STDERR); + auto db = Utils::unlockDatabase(args.at(0), + parser.value(Command::KeyFileOption), + parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, + Utils::STDERR); if (!db) { return EXIT_FAILURE; } diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index d78c06074..26434669b 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -98,6 +98,47 @@ void setStdinEcho(bool enable = true) } } // namespace Test +QSharedPointer unlockDatabase(const QString& databaseFilename, + const QString& keyFilename, + FILE* outputDescriptor, + FILE* errorDescriptor) +{ + auto compositeKey = QSharedPointer::create(); + TextStream out(outputDescriptor); + TextStream err(errorDescriptor); + + out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename) << flush; + + QString line = Utils::getPassword(outputDescriptor); + auto passwordKey = QSharedPointer::create(); + passwordKey->setPassword(line); + compositeKey->addKey(passwordKey); + + if (!keyFilename.isEmpty()) { + auto fileKey = QSharedPointer::create(); + QString errorMessage; + // LCOV_EXCL_START + if (!fileKey->load(keyFilename, &errorMessage)) { + err << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage) << endl; + return {}; + } + + if (fileKey->type() != FileKey::Hashed) { + err << QObject::tr("WARNING: You are using a legacy key file format which may become\n" + "unsupported in the future.\n\n" + "Please consider generating a new key file.") + << endl; + } + // LCOV_EXCL_STOP + + compositeKey->addKey(fileKey); + } + + auto db = QSharedPointer::create(); + db->open(databaseFilename, compositeKey, nullptr, false); + return db; +} + /** * Read a user password from STDIN or return a password previously * set by \link setNextPassword(). diff --git a/src/cli/Utils.h b/src/cli/Utils.h index 28f260381..3d240c2c0 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -19,6 +19,10 @@ #define KEEPASSXC_UTILS_H #include "cli/TextStream.h" +#include "core/Database.h" +#include "keys/CompositeKey.h" +#include "keys/FileKey.h" +#include "keys/PasswordKey.h" #include namespace Utils @@ -31,6 +35,10 @@ namespace Utils void setStdinEcho(bool enable); QString getPassword(FILE* outputDescriptor = STDOUT); int clipText(const QString& text); + QSharedPointer unlockDatabase(const QString& databaseFilename, + const QString& keyFilename = {}, + FILE* outputDescriptor = STDOUT, + FILE* errorDescriptor = STDERR); namespace Test { diff --git a/src/core/Database.cpp b/src/core/Database.cpp index ff345c84e..69ea7b179 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -18,8 +18,6 @@ #include "Database.h" -#include "cli/Utils.h" -#include "cli/TextStream.h" #include "core/Clock.h" #include "core/Group.h" #include "core/Merger.h" @@ -701,45 +699,6 @@ QSharedPointer Database::key() const return m_data.key; } -QSharedPointer Database::unlockFromStdin(const QString& databaseFilename, const QString& keyFilename, - FILE* outputDescriptor, FILE* errorDescriptor) -{ - auto compositeKey = QSharedPointer::create(); - TextStream out(outputDescriptor); - TextStream err(errorDescriptor); - - out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename) << flush; - - QString line = Utils::getPassword(outputDescriptor); - auto passwordKey = QSharedPointer::create(); - passwordKey->setPassword(line); - compositeKey->addKey(passwordKey); - - if (!keyFilename.isEmpty()) { - auto fileKey = QSharedPointer::create(); - QString errorMessage; - // LCOV_EXCL_START - if (!fileKey->load(keyFilename, &errorMessage)) { - err << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage) << endl; - return {}; - } - - if (fileKey->type() != FileKey::Hashed) { - err << QObject::tr("WARNING: You are using a legacy key file format which may become\n" - "unsupported in the future.\n\n" - "Please consider generating a new key file.") - << endl; - } - // LCOV_EXCL_STOP - - compositeKey->addKey(fileKey); - } - - auto db = QSharedPointer::create(); - db->open(databaseFilename, compositeKey, nullptr, false); - return db; -} - QSharedPointer Database::kdf() const { return m_data.kdf; diff --git a/src/core/Database.h b/src/core/Database.h index 6d660b66d..3a4a3daf6 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -118,8 +118,6 @@ public: static Database* databaseByUuid(const QUuid& uuid); static Database* databaseByFilePath(const QString& filePath); - static QSharedPointer unlockFromStdin(const QString& databaseFilename, const QString& keyFilename = {}, - FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr); public slots: void markAsModified(); diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index 435c2f6e2..430a3ba32 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -130,7 +130,7 @@ void TestCli::cleanupTestCase() QSharedPointer TestCli::readTestDatabase() const { Utils::Test::setNextPassword("a"); - auto db = QSharedPointer(Database::unlockFromStdin(m_dbFile->fileName(), "", m_stdoutHandle)); + auto db = QSharedPointer(Utils::unlockDatabase(m_dbFile->fileName(), "", m_stdoutHandle)); m_stdoutFile->seek(ftell(m_stdoutHandle)); // re-synchronize handles return db; } From a070f1bce7efcc5ce89aea533b181b0fa8c6dea0 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Sat, 8 Dec 2018 12:12:52 +0200 Subject: [PATCH 84/84] Handle retrieving credentials from HTTP Basic Auth --- src/browser/BrowserAction.cpp | 4 +++- src/browser/BrowserOptionDialog.cpp | 2 ++ src/browser/BrowserOptionDialog.ui | 7 +++++++ src/browser/BrowserService.cpp | 18 +++++++++++++++--- src/browser/BrowserService.h | 8 ++++++-- src/browser/BrowserSettings.cpp | 10 ++++++++++ src/browser/BrowserSettings.h | 2 ++ 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 97ead6a34..4357718b3 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -251,7 +251,9 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin const QString id = decrypted.value("id").toString(); const QString submit = decrypted.value("submitUrl").toString(); - const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "", keyList); + const QString auth = decrypted.value("httpAuth").toString(); + const bool httpAuth = auth.compare("true", Qt::CaseSensitive) == 0 ? true : false; + const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "", keyList, httpAuth); if (users.isEmpty()) { return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND); diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index 27dce7994..78a51d2aa 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -100,6 +100,7 @@ void BrowserOptionDialog::loadSettings() m_ui->alwaysAllowAccess->setChecked(settings->alwaysAllowAccess()); m_ui->alwaysAllowUpdate->setChecked(settings->alwaysAllowUpdate()); + m_ui->httpAuthPermission->setChecked(settings->httpAuthPermission()); m_ui->searchInAllDatabases->setChecked(settings->searchInAllDatabases()); m_ui->supportKphFields->setChecked(settings->supportKphFields()); m_ui->supportBrowserProxy->setChecked(settings->supportBrowserProxy()); @@ -156,6 +157,7 @@ void BrowserOptionDialog::saveSettings() settings->setUpdateBinaryPath(m_ui->updateBinaryPath->isChecked()); settings->setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked()); settings->setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked()); + settings->setHttpAuthPermission(m_ui->httpAuthPermission->isChecked()); settings->setSearchInAllDatabases(m_ui->searchInAllDatabases->isChecked()); settings->setSupportKphFields(m_ui->supportKphFields->isChecked()); diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index c01be920b..2b32bb9e8 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -267,6 +267,13 @@
+ + + + Do not ask permission for HTTP &Basic Auth + + + diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 945f1303a..6b85c7864 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -207,7 +207,8 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm, - const StringPairList& keyList) + const StringPairList& keyList, + const bool httpAuth) { QJsonArray result; if (thread() != QThread::currentThread()) { @@ -219,11 +220,13 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, Q_ARG(QString, url), Q_ARG(QString, submitUrl), Q_ARG(QString, realm), - Q_ARG(StringPairList, keyList)); + Q_ARG(StringPairList, keyList), + Q_ARG(bool, httpAuth)); return result; } const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess(); + const bool ignoreHttpAuth = browserSettings()->httpAuthPermission(); const QString host = QUrl(url).host(); const QString submitHost = QUrl(submitUrl).host(); @@ -231,6 +234,12 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, QList pwEntriesToConfirm; QList pwEntries; for (Entry* entry : searchEntries(url, keyList)) { + // HTTP Basic Auth always needs a confirmation + if (!ignoreHttpAuth && httpAuth) { + pwEntriesToConfirm.append(entry); + continue; + } + switch (checkAccess(entry, host, submitHost, realm)) { case Denied: continue; @@ -642,7 +651,10 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry) } BrowserService::Access -BrowserService::checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm) +BrowserService::checkAccess(const Entry* entry, + const QString& host, + const QString& submitHost, + const QString& realm) { BrowserEntryConfig config; if (!config.load(entry)) { diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 6c84696f9..335f2a13b 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -67,7 +67,8 @@ public slots: const QString& url, const QString& submitUrl, const QString& realm, - const StringPairList& keyList); + const StringPairList& keyList, + const bool httpAuth = false); QString storeKey(const QString& key); void updateEntry(const QString& id, const QString& uuid, @@ -101,7 +102,10 @@ private: const QString& submitHost, const QString& realm); QJsonObject prepareEntry(const Entry* entry); - Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm); + Access checkAccess(const Entry* entry, + const QString& host, + const QString& submitHost, + const QString& realm); Group* findCreateAddEntryGroup(QSharedPointer selectedDb = {}); int sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const; diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index f1f9667f8..fe3d55527 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -121,6 +121,16 @@ void BrowserSettings::setAlwaysAllowUpdate(bool alwaysAllowUpdate) config()->set("Browser/AlwaysAllowUpdate", alwaysAllowUpdate); } +bool BrowserSettings::httpAuthPermission() +{ + return config()->get("Browser/HttpAuthPermission", false).toBool(); +} + +void BrowserSettings::setHttpAuthPermission(bool httpAuthPermission) +{ + config()->set("Browser/HttpAuthPermission", httpAuthPermission); +} + bool BrowserSettings::searchInAllDatabases() { return config()->get("Browser/SearchInAllDatabases", false).toBool(); diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index 0a9dc8261..b00c75b71 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -51,6 +51,8 @@ public: void setAlwaysAllowUpdate(bool alwaysAllowUpdate); bool searchInAllDatabases(); void setSearchInAllDatabases(bool searchInAllDatabases); + bool httpAuthPermission(); + void setHttpAuthPermission(bool httpAuthPermission); bool supportKphFields(); void setSupportKphFields(bool supportKphFields);