From 7c7f0b93ae73785ade3b8016a056023a42a9c3fc Mon Sep 17 00:00:00 2001 From: Hutson Betts Date: Fri, 18 Apr 2014 12:44:47 -0500 Subject: [PATCH 01/71] Add README.md file. Add a dedicated README.md to the KeePassX repository to explain the purpose of KeePassX, and to inform it's audience as to how they can contribute. --- README.md | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..e6a466358 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# KeePassX + +## About + +KeePassX is an application for people with extremely high demands on secure personal data management. It has a light interface, is cross platform and published under the terms of the GNU General Public License. + +KeePassX saves many different information e.g. user names, passwords, urls, attachments and comments in one single database. For a better management user-defined titles and icons can be specified for each single entry. Furthermore the entries are sorted in groups, which are customizable as well. The integrated search function allows to search in a single group or the complete database. +KeePassX offers a little utility for secure password generation. The password generator is very customizable, fast and easy to use. Especially someone who generates passwords frequently will appreciate this feature. + +The complete database is always encrypted either with AES (alias Rijndael) or Twofish encryption algorithm using a 256 bit key. Therefore the saved information can be considered as quite safe. KeePassX uses a database format that is compatible with [KeePass Password Safe](http://keepass.info/). This makes the use of that application even more favorable. + +## Install + +KeePassX can be downloaded and installed using an assortment of installers available on the main [KeePassX website](http://www.keepassx.org).KeePassX can also be installed from the official repositories of many Linux repositories. If you wish to build KeePassX from source, rather than rely on the pre-compiled binaries, you may wish to read up on the _From Source_ section. + +### Debian + +To install KeePassX from the Debian repository: + +```bash +sudo apt-get update +sudo apt-get install keepassx +``` + +### Red Hat + +Install KeePassX from the Red Hat (or CentOS) repository: + +```bash +sudo yum install keepassx +``` + +### Windows + +Download the Windows installer from the KeePassX [download](https://www.keepassx.org/downloads) page. Once downloaded, double click on the file to execute the installer. + +### From Source + +#### Build Dependencies + +The following tools must exist within your PATH: + +* make +* cmake +* g++ or clang++ + +#### Build Steps + +To compile from source: + +```bash +mkdir build +cd build +cmake .. +make [-jX] +``` + +You will have the compiled KeePassX binary inside the `./build/src/` directory. + +To install this binary execute the following: + +```bash +sudo make install +``` + +More detailed instructions available in the INSTALL file. + +## Contribute + +Coordination of work between developers is handled through the [KeePassX development](https://www.keepassx.org/dev) site. Requests for enhancements, or reports of bugs encountered, can also be reported through the KeePassX development site. However, members of the open-source community are encouraged to submit pull requests directly through GitLab. + +### Clone Repository + +Clone the repository to a suitable location where you can extend and build this project. + +```bash +git clone https://github.com/keepassx/keepassx.git +``` + +**Note:** This will clone the entire contents of the repository at the HEAD revision. + +To update the project from within the project's folder you can run the following command: + +```bash +git pull +``` + +### Feature Requests + +We're always looking for suggestions to improve our application. If you have a suggestion for improving an existing feature, or would like to suggest a completely new feature for KeePassX, please file a ticket on the [KeePassX development](https://www.keepassx.org/dev) site. + +### Bug Reports + +Our software isn't always perfect, but we strive to always improve our work. You may file bug reports on the [KeePassX development](https://www.keepassx.org/dev) site. + +### Pull Requests + +Along with our desire to hear your feedback and suggestions, we're also interested in accepting direct assistance in the form of code. + +Issue merge requests against our [GitHub repository](https://github.com/keepassx/keepassx). From d6c30b088631906bce69570553b484262e6d6933 Mon Sep 17 00:00:00 2001 From: Tim Gion Date: Wed, 30 Apr 2014 22:26:39 -0400 Subject: [PATCH 02/71] Fixed location of config file on Mac (and probably Windows). --- src/core/Config.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index c48bc9553..4176408d0 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -71,7 +71,8 @@ Config::Config(QObject* parent) userPath += "/keepassx/"; #else userPath = QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); - // storageLocation() appends the application name ("/keepassx/") to the end + // storageLocation() appends the application name ("/keepassx") to the end + userPath += "/"; #endif userPath += "keepassx2.ini"; From b718e9d8f28be186effc87e33eba044889949e75 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Sat, 3 May 2014 08:28:56 +0200 Subject: [PATCH 03/71] Make sure copy actions are disabled when database is locked. Closes #189 --- src/gui/MainWindow.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 36fb656f2..e0c6582e0 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -313,6 +313,11 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) Q_FOREACH (QAction* action, m_ui->menuGroups->actions()) { 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->actionSearch->setEnabled(false); @@ -335,6 +340,11 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) Q_FOREACH (QAction* action, m_ui->menuGroups->actions()) { 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->actionSearch->setEnabled(false); From 9363d23e09a9981de4c8f799a91ae02eb9747aa8 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 18:05:58 +0200 Subject: [PATCH 04/71] Remove dependency to Group- and EntryView from MainWindow. --- src/gui/DatabaseWidget.cpp | 20 ++++++++++++++++++++ src/gui/DatabaseWidget.h | 3 +++ src/gui/MainWindow.cpp | 19 ++++++++----------- src/gui/MainWindow.h | 1 + src/gui/entry/EntryView.cpp | 7 ++++++- src/gui/entry/EntryView.h | 1 + 6 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 419028ab8..d6d48c5e0 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -811,3 +811,23 @@ void DatabaseWidget::updateFilename(const QString& fileName) { m_filename = fileName; } + +int DatabaseWidget::numberOfSelectedEntries() +{ + return m_entryView->numberOfSelectedEntries(); +} + +QStringList DatabaseWidget::customEntryAttributes() +{ + Entry* entry = m_entryView->currentEntry(); + if (!entry) { + return QStringList(); + } + + return entry->attributes()->customKeys(); +} + +bool DatabaseWidget::isGroupSelected() +{ + return m_groupView->currentGroup() != Q_NULLPTR; +} diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index b535a9b8f..d2b4e9e05 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -71,6 +71,9 @@ public: DatabaseWidget::Mode currentMode(); void lock(); void updateFilename(const QString& filename); + int numberOfSelectedEntries(); + QStringList customEntryAttributes(); + bool isGroupSelected(); Q_SIGNALS: void closeRequest(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e0c6582e0..98bd5aee1 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -23,15 +23,11 @@ #include "autotype/AutoType.h" #include "core/Config.h" -#include "core/Database.h" -#include "core/Entry.h" #include "core/FilePath.h" #include "core/InactivityTimer.h" #include "core/Metadata.h" #include "gui/AboutDialog.h" #include "gui/DatabaseWidget.h" -#include "gui/entry/EntryView.h" -#include "gui/group/GroupView.h" const QString MainWindow::BaseWindowTitle = "KeePassX"; @@ -40,6 +36,8 @@ MainWindow::MainWindow() { m_ui->setupUi(this); + m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size(); + restoreGeometry(config()->get("window/Geometry").toByteArray()); setWindowIcon(filePath()->applicationIcon()); @@ -229,17 +227,16 @@ void MainWindow::updateCopyAttributesMenu() return; } - Entry* entry = dbWidget->entryView()->currentEntry(); - if (!entry || !dbWidget->entryView()->isSingleEntrySelected()) { + if (!dbWidget->numberOfSelectedEntries() == 1) { return; } QList actions = m_ui->menuEntryCopyAttribute->actions(); - for (int i = EntryAttributes::DefaultAttributes.size() + 1; i < actions.size(); i++) { + for (int i = m_countDefaultAttributes + 1; i < actions.size(); i++) { delete actions[i]; } - Q_FOREACH (const QString& key, entry->attributes()->customKeys()) { + Q_FOREACH (const QString& key, dbWidget->customEntryAttributes()) { QAction* action = m_ui->menuEntryCopyAttribute->addAction(key); m_copyAdditionalAttributeActions->addAction(action); } @@ -276,9 +273,9 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) switch (mode) { case DatabaseWidget::ViewMode: { bool inSearch = dbWidget->isInSearchMode(); - bool singleEntrySelected = dbWidget->entryView()->isSingleEntrySelected(); - bool entriesSelected = !dbWidget->entryView()->selectionModel()->selectedRows().isEmpty(); - bool groupSelected = dbWidget->groupView()->currentGroup(); + bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1; + bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0; + bool groupSelected = dbWidget->isGroupSelected(); m_ui->actionEntryNew->setEnabled(!inSearch); m_ui->actionEntryClone->setEnabled(singleEntrySelected && !inSearch); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 706fd2d50..e904426ae 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -77,6 +77,7 @@ private: QActionGroup* m_copyAdditionalAttributeActions; QStringList m_openDatabases; InactivityTimer* m_inactivityTimer; + int m_countDefaultAttributes; Q_DISABLE_COPY(MainWindow) }; diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index f71f80bc6..eb79ec966 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -100,9 +100,14 @@ Entry* EntryView::currentEntry() } } +int EntryView::numberOfSelectedEntries() +{ + return selectionModel()->selectedRows().size(); +} + bool EntryView::isSingleEntrySelected() { - return (selectionModel()->selectedRows().size() == 1); + return (numberOfSelectedEntries() == 1); } void EntryView::setCurrentEntry(Entry* entry) diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index b5f056aa0..2a1df439c 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -42,6 +42,7 @@ public: Entry* entryFromIndex(const QModelIndex& index); void setEntryList(const QList& entries); bool inEntryListMode(); + int numberOfSelectedEntries(); public Q_SLOTS: void setGroup(Group* group); From 147cd4ed7bc6457a2610210dd00af88a1bad8293 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 18:26:01 +0200 Subject: [PATCH 05/71] Add option to use the entry title for auto-type window matching. --- src/autotype/AutoType.cpp | 6 +++ src/core/Config.cpp | 3 +- src/core/Config.h | 2 +- src/gui/SettingsWidget.cpp | 3 ++ src/gui/SettingsWidgetGeneral.ui | 52 ++++++++++++++--------- tests/TestAutoType.cpp | 73 ++++++++++++++++++++++---------- tests/TestAutoType.h | 6 ++- 7 files changed, 98 insertions(+), 47 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 80b810821..92aae5cd7 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -503,6 +503,12 @@ QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitl } } + if (!match && config()->get("AutoTypeEntryTitleMatch").toBool() && !entry->title().isEmpty() + && windowTitle.contains(entry->title(), Qt::CaseInsensitive)) { + sequence = entry->defaultAutoTypeSequence(); + match = true; + } + if (!match) { return QString(); } diff --git a/src/core/Config.cpp b/src/core/Config.cpp index c48bc9553..133a03a9e 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -95,6 +95,7 @@ void Config::init(const QString& fileName) m_defaults.insert("ShowToolbar", true); m_defaults.insert("MinimizeOnCopy", false); m_defaults.insert("UseGroupIconOnEntryCreation", false); + m_defaults.insert("AutoTypeEntryTitleMatch", false); m_defaults.insert("security/clearclipboard", true); m_defaults.insert("security/clearclipboardtimeout", 10); m_defaults.insert("security/lockdatabaseidle", false); @@ -112,7 +113,7 @@ Config* Config::instance() return m_instance; } -void Config::createConfigFromFile(QString file) +void Config::createConfigFromFile(const QString& file) { Q_ASSERT(!m_instance); m_instance = new Config(file, qApp); diff --git a/src/core/Config.h b/src/core/Config.h index ee3082685..ca0f74cb5 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -36,7 +36,7 @@ public: void set(const QString& key, const QVariant& value); static Config* instance(); - static void createConfigFromFile(QString file); + static void createConfigFromFile(const QString& file); static void createTempFileInstance(); private: diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index d09805939..2aea2636e 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -67,6 +67,7 @@ void SettingsWidget::loadSettings() m_generalUi->autoSaveOnExitCheckBox->setChecked(config()->get("AutoSaveOnExit").toBool()); m_generalUi->minimizeOnCopyCheckBox->setChecked(config()->get("MinimizeOnCopy").toBool()); m_generalUi->useGroupIconOnEntryCreationCheckBox->setChecked(config()->get("UseGroupIconOnEntryCreation").toBool()); + m_generalUi->autoTypeEntryTitleMatchCheckBox->setChecked(config()->get("AutoTypeEntryTitleMatch").toBool()); if (autoType()->isAvailable()) { m_globalAutoTypeKey = static_cast(config()->get("GlobalAutoTypeKey").toInt()); @@ -102,6 +103,8 @@ void SettingsWidget::saveSettings() config()->set("MinimizeOnCopy", m_generalUi->minimizeOnCopyCheckBox->isChecked()); config()->set("UseGroupIconOnEntryCreation", m_generalUi->useGroupIconOnEntryCreationCheckBox->isChecked()); + config()->set("AutoTypeEntryTitleMatch", + m_generalUi->autoTypeEntryTitleMatchCheckBox->isChecked()); if (autoType()->isAvailable()) { config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key()); config()->set("GlobalAutoTypeModifiers", diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui index f9aa8b680..3d89c68e8 100644 --- a/src/gui/SettingsWidgetGeneral.ui +++ b/src/gui/SettingsWidgetGeneral.ui @@ -7,10 +7,13 @@ 0 0 456 - 185 + 230 + + QFormLayout::AllNonFixedFieldsGrow + @@ -21,6 +24,13 @@ + + + + Open previous databases on startup + + + @@ -31,13 +41,6 @@ - - - - Automatically save after every change - - - @@ -45,20 +48,10 @@ - - + + - Global Auto-Type shortcut - - - - - - - - - - Open previous databases on startup + Automatically save after every change @@ -76,6 +69,23 @@ + + + + Global Auto-Type shortcut + + + + + + + + + + Use entry title to match windows for global auto-type + + + diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index 47ac0909b..0b2f3b250 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -21,6 +21,7 @@ #include #include "tests.h" +#include "core/Config.h" #include "core/FilePath.h" #include "core/Entry.h" #include "core/Group.h" @@ -33,7 +34,7 @@ void TestAutoType::initTestCase() { Crypto::init(); - + Config::createTempFileInstance(); AutoType::createTestInstance(); QPluginLoader loader(filePath()->pluginPath("keepassx-autotype-test")); @@ -54,12 +55,24 @@ void TestAutoType::init() m_test->clearActions(); m_db = new Database(); + m_dbList.clear(); + m_dbList.append(m_db); m_group = new Group(); m_db->setRootGroup(m_group); - m_entry = new Entry(); - m_entry->setGroup(m_group); - m_entry->setUsername("myuser"); - m_entry->setPassword("mypass"); + + m_entry1 = new Entry(); + m_entry1->setGroup(m_group); + m_entry1->setUsername("myuser"); + m_entry1->setPassword("mypass"); + AutoTypeAssociations::Association association; + association.window = "custom window"; + association.sequence = "{username}association{password}"; + m_entry1->autoTypeAssociations()->add(association); + + m_entry2 = new Entry(); + m_entry2->setGroup(m_group); + m_entry2->setPassword("myuser"); + m_entry2->setTitle("entry title"); } void TestAutoType::cleanup() @@ -77,7 +90,7 @@ void TestAutoType::testInternal() void TestAutoType::testAutoTypeWithoutSequence() { - m_autoType->performAutoType(m_entry, Q_NULLPTR); + m_autoType->performAutoType(m_entry1, Q_NULLPTR); QCOMPARE(m_test->actionCount(), 14); QCOMPARE(m_test->actionChars(), @@ -88,42 +101,56 @@ void TestAutoType::testAutoTypeWithoutSequence() void TestAutoType::testAutoTypeWithSequence() { - m_autoType->performAutoType(m_entry, Q_NULLPTR, "{Username}abc{PaSsWoRd}"); + m_autoType->performAutoType(m_entry1, Q_NULLPTR, "{Username}abc{PaSsWoRd}"); QCOMPARE(m_test->actionCount(), 15); QCOMPARE(m_test->actionChars(), QString("%1abc%2") - .arg(m_entry->username()) - .arg(m_entry->password())); + .arg(m_entry1->username()) + .arg(m_entry1->password())); } void TestAutoType::testGlobalAutoTypeWithNoMatch() { - QList dbList; - dbList.append(m_db); - + m_test->setActiveWindowTitle("nomatch"); MessageBox::setNextAnswer(QMessageBox::Ok); - m_autoType->performGlobalAutoType(dbList); + m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString()); } void TestAutoType::testGlobalAutoTypeWithOneMatch() { - QList dbList; - dbList.append(m_db); - AutoTypeAssociations::Association association; - association.window = "custom window"; - association.sequence = "{username}association{password}"; - m_entry->autoTypeAssociations()->add(association); - m_test->setActiveWindowTitle("custom window"); - m_autoType->performGlobalAutoType(dbList); + m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("%1association%2") - .arg(m_entry->username()) - .arg(m_entry->password())); + .arg(m_entry1->username()) + .arg(m_entry1->password())); +} + +void TestAutoType::testGlobalAutoTypeTitleMatch() +{ + config()->set("AutoTypeEntryTitleMatch", true); + + m_test->setActiveWindowTitle("An Entry Title!"); + m_autoType->performGlobalAutoType(m_dbList); + + QCOMPARE(m_test->actionChars(), + QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter))); +} + +void TestAutoType::testGlobalAutoTypeTitleMatchDisabled() +{ + config()->set("AutoTypeEntryTitleMatch", false); + + m_test->setActiveWindowTitle("An Entry Title!"); + MessageBox::setNextAnswer(QMessageBox::Ok); + m_autoType->performGlobalAutoType(m_dbList); + + QCOMPARE(m_test->actionChars(), QString()); + } QTEST_GUILESS_MAIN(TestAutoType) diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h index fba7fde1c..d46a55969 100644 --- a/tests/TestAutoType.h +++ b/tests/TestAutoType.h @@ -41,14 +41,18 @@ private Q_SLOTS: void testAutoTypeWithSequence(); void testGlobalAutoTypeWithNoMatch(); void testGlobalAutoTypeWithOneMatch(); + void testGlobalAutoTypeTitleMatch(); + void testGlobalAutoTypeTitleMatchDisabled(); private: AutoTypePlatformInterface* m_platform; AutoTypeTestInterface* m_test; AutoType* m_autoType; Database* m_db; + QList m_dbList; Group* m_group; - Entry* m_entry; + Entry* m_entry1; + Entry* m_entry2; }; #endif // KEEPASSX_TESTAUTOTYPE_H From bf39d0b1bedf1edcfe5a69aa3b836579cdc8417d Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 18:27:53 +0200 Subject: [PATCH 06/71] Enable entry title matching but always ask before performing auto-type. --- src/core/Config.cpp | 4 ++-- tests/TestAutoType.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 133a03a9e..76f0e6f8e 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -95,13 +95,13 @@ void Config::init(const QString& fileName) m_defaults.insert("ShowToolbar", true); m_defaults.insert("MinimizeOnCopy", false); m_defaults.insert("UseGroupIconOnEntryCreation", false); - m_defaults.insert("AutoTypeEntryTitleMatch", false); + m_defaults.insert("AutoTypeEntryTitleMatch", true); m_defaults.insert("security/clearclipboard", true); m_defaults.insert("security/clearclipboardtimeout", 10); m_defaults.insert("security/lockdatabaseidle", false); m_defaults.insert("security/lockdatabaseidlesec", 10); m_defaults.insert("security/passwordscleartext", false); - m_defaults.insert("security/autotypeask", false); + m_defaults.insert("security/autotypeask", true); } Config* Config::instance() diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index 0b2f3b250..c09a8366c 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -36,6 +36,8 @@ void TestAutoType::initTestCase() Crypto::init(); Config::createTempFileInstance(); AutoType::createTestInstance(); + config()->set("AutoTypeEntryTitleMatch", false); + config()->set("security/autotypeask", false); QPluginLoader loader(filePath()->pluginPath("keepassx-autotype-test")); loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); From 75d3e6261bf2476e6ec9a67dab9e798e0239b70c Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 18:41:11 +0200 Subject: [PATCH 07/71] Coding style fix. --- src/gui/DatabaseWidget.cpp | 4 ++-- src/gui/DatabaseWidget.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index d6d48c5e0..c70348a9e 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -595,7 +595,7 @@ void DatabaseWidget::unlockDatabase(bool accepted) Q_ASSERT(accepted); Q_UNUSED(accepted); - setCurrentWidget(widgetBeforeLock); + setCurrentWidget(m_widgetBeforeLock); Q_EMIT unlockedDatabase(); } @@ -802,7 +802,7 @@ void DatabaseWidget::lock() { Q_ASSERT(currentMode() != DatabaseWidget::LockedMode); - widgetBeforeLock = currentWidget(); + m_widgetBeforeLock = currentWidget(); m_unlockDatabaseWidget->load(m_filename, m_db); setCurrentWidget(m_unlockDatabaseWidget); } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index d2b4e9e05..22e3992d2 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -153,7 +153,7 @@ private: Group* m_newParent; Group* m_lastGroup; QTimer* m_searchTimer; - QWidget* widgetBeforeLock; + QWidget* m_widgetBeforeLock; QString m_filename; }; From 50cbd80925a1a7940ac32cc8373d033fa67bd924 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 18:47:36 +0200 Subject: [PATCH 08/71] Remove obsolete method in EntryView. --- src/gui/entry/EntryView.cpp | 5 ----- src/gui/entry/EntryView.h | 1 - 2 files changed, 6 deletions(-) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index eb79ec966..4691ed655 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -105,11 +105,6 @@ int EntryView::numberOfSelectedEntries() return selectionModel()->selectedRows().size(); } -bool EntryView::isSingleEntrySelected() -{ - return (numberOfSelectedEntries() == 1); -} - void EntryView::setCurrentEntry(Entry* entry) { selectionModel()->setCurrentIndex(m_sortModel->mapFromSource(m_model->indexFromEntry(entry)), diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 2a1df439c..ba2919e91 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -37,7 +37,6 @@ public: explicit EntryView(QWidget* parent = Q_NULLPTR); void setModel(QAbstractItemModel* model) Q_DECL_OVERRIDE; Entry* currentEntry(); - bool isSingleEntrySelected(); void setCurrentEntry(Entry* entry); Entry* entryFromIndex(const QModelIndex& index); void setEntryList(const QList& entries); From cda5e990ac8ceb40aa4612e56f46d22d5558e3d7 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 22:51:13 +0200 Subject: [PATCH 09/71] Show in-edit-mode warning when database is locked. --- src/gui/DatabaseTabWidget.cpp | 2 +- src/gui/DatabaseWidget.cpp | 15 ++++++++++++++- src/gui/DatabaseWidget.h | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 8c2ba06d9..e529cc428 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -189,7 +189,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db) if (dbName.right(1) == "*") { dbName.chop(1); } - if (dbStruct.dbWidget->currentMode() == DatabaseWidget::EditMode && db->hasKey()) { + if (dbStruct.dbWidget->isInEditMode() && db->hasKey()) { QMessageBox::StandardButton result = MessageBox::question( this, tr("Close?"), diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index c70348a9e..72fe0ef7f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -167,7 +167,7 @@ DatabaseWidget::~DatabaseWidget() { } -DatabaseWidget::Mode DatabaseWidget::currentMode() +DatabaseWidget::Mode DatabaseWidget::currentMode() const { if (currentWidget() == Q_NULLPTR) { return DatabaseWidget::None; @@ -183,6 +183,19 @@ DatabaseWidget::Mode DatabaseWidget::currentMode() } } +bool DatabaseWidget::isInEditMode() const +{ + if (currentMode() == DatabaseWidget::LockedMode) { + return m_widgetBeforeLock != Q_NULLPTR + && m_widgetBeforeLock != m_mainWidget + && m_widgetBeforeLock != m_unlockDatabaseWidget; + } + else { + return currentMode() == DatabaseWidget::EditMode; + } +} + + void DatabaseWidget::emitCurrentModeChanged() { Q_EMIT currentModeChanged(currentMode()); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 22e3992d2..b159cbd16 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -68,12 +68,13 @@ public: int addWidget(QWidget* w); void setCurrentIndex(int index); void setCurrentWidget(QWidget* widget); - DatabaseWidget::Mode currentMode(); + DatabaseWidget::Mode currentMode() const; void lock(); void updateFilename(const QString& filename); int numberOfSelectedEntries(); QStringList customEntryAttributes(); bool isGroupSelected(); + bool isInEditMode() const; Q_SIGNALS: void closeRequest(); From ce7e01a1b131ccecb28972d1728401a43421ae42 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 22:53:59 +0200 Subject: [PATCH 10/71] const-ify several methods. --- src/gui/DatabaseTabWidget.cpp | 2 +- src/gui/DatabaseTabWidget.h | 2 +- src/gui/DatabaseWidget.cpp | 12 ++++++------ src/gui/DatabaseWidget.h | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index e529cc428..c15d16e5a 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -503,7 +503,7 @@ DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget() } } -bool DatabaseTabWidget::hasLockableDatabases() +bool DatabaseTabWidget::hasLockableDatabases() const { QHashIterator i(m_dbList); while (i.hasNext()) { diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 9261a065c..8e8fceb38 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -53,7 +53,7 @@ public: void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString()); DatabaseWidget* currentDatabaseWidget(); - bool hasLockableDatabases(); + bool hasLockableDatabases() const; static const int LastDatabasesCount; diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 72fe0ef7f..0d0ca7375 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -786,19 +786,19 @@ void DatabaseWidget::emitEntryContextMenuRequested(const QPoint& pos) Q_EMIT entryContextMenuRequested(m_entryView->viewport()->mapToGlobal(pos)); } -bool DatabaseWidget::dbHasKey() +bool DatabaseWidget::dbHasKey() const { return m_db->hasKey(); } -bool DatabaseWidget::canDeleteCurrentGoup() +bool DatabaseWidget::canDeleteCurrentGoup() const { bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup(); bool isRecycleBin = m_db->metadata()->recycleBin() == m_groupView->currentGroup(); return !isRootGroup && !isRecycleBin; } -bool DatabaseWidget::isInSearchMode() +bool DatabaseWidget::isInSearchMode() const { return m_entryView->inEntryListMode(); } @@ -825,12 +825,12 @@ void DatabaseWidget::updateFilename(const QString& fileName) m_filename = fileName; } -int DatabaseWidget::numberOfSelectedEntries() +int DatabaseWidget::numberOfSelectedEntries() const { return m_entryView->numberOfSelectedEntries(); } -QStringList DatabaseWidget::customEntryAttributes() +QStringList DatabaseWidget::customEntryAttributes() const { Entry* entry = m_entryView->currentEntry(); if (!entry) { @@ -840,7 +840,7 @@ QStringList DatabaseWidget::customEntryAttributes() return entry->attributes()->customKeys(); } -bool DatabaseWidget::isGroupSelected() +bool DatabaseWidget::isGroupSelected() const { return m_groupView->currentGroup() != Q_NULLPTR; } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index b159cbd16..155942a57 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -62,18 +62,18 @@ public: GroupView* groupView(); EntryView* entryView(); Database* database(); - bool dbHasKey(); - bool canDeleteCurrentGoup(); - bool isInSearchMode(); + bool dbHasKey() const; + bool canDeleteCurrentGoup() const; + bool isInSearchMode() const; int addWidget(QWidget* w); void setCurrentIndex(int index); void setCurrentWidget(QWidget* widget); DatabaseWidget::Mode currentMode() const; void lock(); void updateFilename(const QString& filename); - int numberOfSelectedEntries(); - QStringList customEntryAttributes(); - bool isGroupSelected(); + int numberOfSelectedEntries() const; + QStringList customEntryAttributes() const; + bool isGroupSelected()const ; bool isInEditMode() const; Q_SIGNALS: From e361b0dd81a72c41f4516929c83fc61217094489 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 22:56:36 +0200 Subject: [PATCH 11/71] Fix typo canDeleteCurrentGoup() -> canDeleteCurrentGroup(). --- src/gui/DatabaseWidget.cpp | 4 ++-- src/gui/DatabaseWidget.h | 2 +- src/gui/MainWindow.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 0d0ca7375..ba7ca747c 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -440,7 +440,7 @@ void DatabaseWidget::createGroup() void DatabaseWidget::deleteGroup() { Group* currentGroup = m_groupView->currentGroup(); - if (!currentGroup || !canDeleteCurrentGoup()) { + if (!currentGroup || !canDeleteCurrentGroup()) { Q_ASSERT(false); return; } @@ -791,7 +791,7 @@ bool DatabaseWidget::dbHasKey() const return m_db->hasKey(); } -bool DatabaseWidget::canDeleteCurrentGoup() const +bool DatabaseWidget::canDeleteCurrentGroup() const { bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup(); bool isRecycleBin = m_db->metadata()->recycleBin() == m_groupView->currentGroup(); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 155942a57..5e77f01b3 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -63,7 +63,7 @@ public: EntryView* entryView(); Database* database(); bool dbHasKey() const; - bool canDeleteCurrentGoup() const; + bool canDeleteCurrentGroup() const; bool isInSearchMode() const; int addWidget(QWidget* w); void setCurrentIndex(int index); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 98bd5aee1..c83f3167f 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -291,7 +291,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionEntryOpenUrl->setEnabled(singleEntrySelected); m_ui->actionGroupNew->setEnabled(groupSelected); m_ui->actionGroupEdit->setEnabled(groupSelected); - m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGoup()); + m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup()); m_ui->actionSearch->setEnabled(true); // TODO: get checked state from db widget m_ui->actionSearch->setChecked(inSearch); From 8bf48260034e3ffe27f3b78c128123ef7b22faa5 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 23:48:54 +0200 Subject: [PATCH 12/71] Move search into separate class. --- src/CMakeLists.txt | 1 + src/core/EntrySearcher.cpp | 47 ++++++++++++ src/core/EntrySearcher.h | 35 +++++++++ src/core/Group.cpp | 27 ------- src/core/Group.h | 3 - src/gui/DatabaseWidget.cpp | 3 +- tests/CMakeLists.txt | 3 + tests/TestEntrySearcher.cpp | 141 ++++++++++++++++++++++++++++++++++++ tests/TestEntrySearcher.h | 35 +++++++++ tests/TestGroup.cpp | 96 ------------------------ tests/TestGroup.h | 2 - 11 files changed, 264 insertions(+), 129 deletions(-) create mode 100644 src/core/EntrySearcher.cpp create mode 100644 src/core/EntrySearcher.h create mode 100644 tests/TestEntrySearcher.cpp create mode 100644 tests/TestEntrySearcher.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d57153e5f..9af937ea3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ set(keepassx_SOURCES core/Entry.cpp core/EntryAttachments.cpp core/EntryAttributes.cpp + core/EntrySearcher.cpp core/FilePath.cpp core/Global.h core/Group.cpp diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp new file mode 100644 index 000000000..60cfaea45 --- /dev/null +++ b/src/core/EntrySearcher.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Florian 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 "EntrySearcher.h" + +#include "core/Group.h" + +QList EntrySearcher::search(const QString &searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) +{ + if (!group->resolveSearchingEnabled()) { + return QList(); + } + + return searchEntries(searchTerm, group, caseSensitivity); +} + +QList EntrySearcher::searchEntries(const QString &searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) +{ + QList searchResult; + + Q_FOREACH (Entry* entry, group->entries()) { + if (entry->match(searchTerm, caseSensitivity)) { + searchResult.append(entry); + } + } + Q_FOREACH (Group* childGroup, group->children()) { + if (childGroup->searchingEnabled() != Group::Disable) { + searchResult.append(searchEntries(searchTerm, childGroup, caseSensitivity)); + } + } + + return searchResult; +} diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h new file mode 100644 index 000000000..56ed7e4e5 --- /dev/null +++ b/src/core/EntrySearcher.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Florian 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_ENTRYSEARCHER_H +#define KEEPASSX_ENTRYSEARCHER_H + +#include + + +class Group; +class Entry; + +class EntrySearcher +{ +public: + QList search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); +private: + QList searchEntries(const QString &searchTerm, const Group *group, Qt::CaseSensitivity caseSensitivity); +}; + +#endif // KEEPASSX_ENTRYSEARCHER_H diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 4c04b4846..9ebf7bb86 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -612,33 +612,6 @@ void Group::recCreateDelObjects() } } -QList Group::search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity, - bool resolveInherit) -{ - QList searchResult; - bool search; - if (resolveInherit) { - search = resolveSearchingEnabled(); - } - else if (searchingEnabled() == Disable) { - search = false; - } - else { - search = true; - } - if (search) { - Q_FOREACH (Entry* entry, m_entries) { - if (entry->match(searchTerm, caseSensitivity)) { - searchResult.append(entry); - } - } - Q_FOREACH (Group* group, m_children) { - searchResult.append(group->search(searchTerm, caseSensitivity, false)); - } - } - return searchResult; -} - bool Group::resolveSearchingEnabled() const { switch (m_data.searchingEnabled) { diff --git a/src/core/Group.h b/src/core/Group.h index 4e08f5b3b..715403c03 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -113,9 +113,6 @@ public: void copyDataFrom(const Group* other); Database* exportToDb(); - QList search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity, - bool resolveInherit = true); - Q_SIGNALS: void dataChanged(Group* group); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index ba7ca747c..fc533a3f3 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -28,6 +28,7 @@ #include "autotype/AutoType.h" #include "core/Config.h" +#include "core/EntrySearcher.h" #include "core/FilePath.h" #include "core/Group.h" #include "core/Metadata.h" @@ -754,8 +755,8 @@ void DatabaseWidget::search() else { sensitivity = Qt::CaseInsensitive; } - QList searchResult = searchGroup->search(m_searchUi->searchEdit->text(), sensitivity); + QList searchResult = EntrySearcher().search(m_searchUi->searchEdit->text(), searchGroup, sensitivity); m_entryView->setEntryList(searchResult); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8df0050aa..be37a5def 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -165,6 +165,9 @@ add_unit_test(NAME testqcommandlineparser SOURCES TestQCommandLineParser.cpp MOC add_unit_test(NAME testrandom SOURCES TestRandom.cpp MOCS TestRandom.h LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp MOCS TestEntrySearcher.h + LIBS ${TEST_LIBRARIES}) + if(WITH_GUI_TESTS) add_subdirectory(gui) endif(WITH_GUI_TESTS) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp new file mode 100644 index 000000000..308a26e6d --- /dev/null +++ b/tests/TestEntrySearcher.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014 Florian 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 "TestEntrySearcher.h" + +#include + +#include "tests.h" +#include "core/EntrySearcher.h" +#include "core/Group.h" + +QTEST_GUILESS_MAIN(TestEntrySearcher) + +void TestEntrySearcher::initTestCase() +{ + +} + +void TestEntrySearcher::testSearch() +{ + Group* groupRoot = new Group(); + Group* group1 = new Group(); + Group* group2 = new Group(); + Group* group3 = new Group(); + + group1->setParent(groupRoot); + group2->setParent(groupRoot); + group3->setParent(groupRoot); + + Group* group11 = new Group(); + + group11->setParent(group1); + + Group* group21 = new Group(); + Group* group211 = new Group(); + Group* group2111 = new Group(); + + group21->setParent(group2); + group211->setParent(group21); + group2111->setParent(group211); + + group1->setSearchingEnabled(Group::Disable); + group11->setSearchingEnabled(Group::Enable); + + Entry* eRoot = new Entry(); + eRoot->setNotes("test search term test"); + eRoot->setGroup(groupRoot); + + Entry* eRoot2 = new Entry(); + eRoot2->setNotes("test term test"); + eRoot2->setGroup(groupRoot); + + Entry* e1 = new Entry(); + e1->setNotes("test search term test"); + e1->setGroup(group1); + + Entry* e11 = new Entry(); + e11->setNotes("test search term test"); + e11->setGroup(group11); + + Entry* e2111 = new Entry(); + e2111->setNotes("test search term test"); + e2111->setGroup(group2111); + + Entry* e2111b = new Entry(); + e2111b->setNotes("test search test"); + e2111b->setGroup(group2111); + + Entry* e3 = new Entry(); + e3->setNotes("test search term test"); + e3->setGroup(group3); + + Entry* e3b = new Entry(); + e3b->setNotes("test search test"); + e3b->setGroup(group3); + + QList searchResult; + + EntrySearcher entrySearcher; + + searchResult = entrySearcher.search("search term", groupRoot, Qt::CaseInsensitive); + + QCOMPARE(searchResult.count(), 3); + + searchResult = entrySearcher.search("search term", group211, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("search term", group11, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("search term", group1, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 0); + + delete groupRoot; +} + +void TestEntrySearcher::testAndConcatenationInSearch() +{ + Group* group = new Group(); + Entry* entry = new Entry(); + entry->setNotes("abc def ghi"); + entry->setTitle("jkl"); + entry->setGroup(group); + + EntrySearcher entrySearcher; + QList searchResult; + + searchResult = entrySearcher.search("", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("def", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search(" abc ghi ", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("ghi ef", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("abc ef xyz", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 0); + + searchResult = entrySearcher.search("abc kl", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + delete group; +} diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h new file mode 100644 index 000000000..d261d5dd6 --- /dev/null +++ b/tests/TestEntrySearcher.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Florian 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_TESTENTRYSEARCHER_H +#define KEEPASSX_TESTENTRYSEARCHER_H + +#include + +class TestEntrySearcher : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + + void testAndConcatenationInSearch(); + void testSearch(); +}; + +#endif // KEEPASSX_TESTENTRYSEARCHER_H diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 86b55b706..816f7ce70 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -334,102 +334,6 @@ void TestGroup::testCopyCustomIcon() delete dbTarget; } -void TestGroup::testSearch() -{ - Group* groupRoot = new Group(); - Group* group1 = new Group(); - Group* group2 = new Group(); - Group* group3 = new Group(); - - group1->setParent(groupRoot); - group2->setParent(groupRoot); - group3->setParent(groupRoot); - - Group* group11 = new Group(); - - group11->setParent(group1); - - Group* group21 = new Group(); - Group* group211 = new Group(); - Group* group2111 = new Group(); - - group21->setParent(group2); - group211->setParent(group21); - group2111->setParent(group211); - - group1->setSearchingEnabled(Group::Disable); - group11->setSearchingEnabled(Group::Enable); - - Entry* eRoot = new Entry(); - eRoot->setNotes("test search term test"); - eRoot->setGroup(groupRoot); - - Entry* eRoot2 = new Entry(); - eRoot2->setNotes("test term test"); - eRoot2->setGroup(groupRoot); - - Entry* e1 = new Entry(); - e1->setNotes("test search term test"); - e1->setGroup(group1); - - Entry* e2111 = new Entry(); - e2111->setNotes("test search term test"); - e2111->setGroup(group2111); - - Entry* e2111b = new Entry(); - e2111b->setNotes("test search test"); - e2111b->setGroup(group2111); - - Entry* e3 = new Entry(); - e3->setNotes("test search term test"); - e3->setGroup(group3); - - Entry* e3b = new Entry(); - e3b->setNotes("test search test"); - e3b->setGroup(group3); - - QList searchResult; - - searchResult = groupRoot->search("search term", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 3); - - searchResult = group211->search("search term", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - delete groupRoot; -} - -void TestGroup::testAndConcatenationInSearch() -{ - Group* group = new Group(); - Entry* entry = new Entry(); - entry->setNotes("abc def ghi"); - entry->setTitle("jkl"); - entry->setGroup(group); - - QList searchResult; - - searchResult = group->search("", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search("def", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search(" abc ghi ", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search("ghi ef", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search("abc ef xyz", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 0); - - searchResult = group->search("abc kl", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - delete group; -} - void TestGroup::testClone() { Database* db = new Database(); diff --git a/tests/TestGroup.h b/tests/TestGroup.h index 895c2cc5a..2e4019901 100644 --- a/tests/TestGroup.h +++ b/tests/TestGroup.h @@ -31,8 +31,6 @@ private Q_SLOTS: void testEntries(); void testDeleteSignals(); void testCopyCustomIcon(); - void testSearch(); - void testAndConcatenationInSearch(); void testClone(); void testCopyCustomIcons(); void testExportToDb(); From c90ac914bb7eca4908ac46fa0fd3516e355431e1 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 23:59:26 +0200 Subject: [PATCH 13/71] Refactor TestEntrySearcher. --- tests/TestEntrySearcher.cpp | 25 ++++++++++++------------- tests/TestEntrySearcher.h | 5 +++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 308a26e6d..564d71a77 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -27,12 +27,16 @@ QTEST_GUILESS_MAIN(TestEntrySearcher) void TestEntrySearcher::initTestCase() { + groupRoot = new Group(); +} +void TestEntrySearcher::cleanupTestCase() +{ + delete groupRoot; } void TestEntrySearcher::testSearch() { - Group* groupRoot = new Group(); Group* group1 = new Group(); Group* group2 = new Group(); Group* group3 = new Group(); @@ -104,38 +108,33 @@ void TestEntrySearcher::testSearch() searchResult = entrySearcher.search("search term", group1, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 0); - - delete groupRoot; } void TestEntrySearcher::testAndConcatenationInSearch() { - Group* group = new Group(); Entry* entry = new Entry(); entry->setNotes("abc def ghi"); entry->setTitle("jkl"); - entry->setGroup(group); + entry->setGroup(groupRoot); EntrySearcher entrySearcher; QList searchResult; - searchResult = entrySearcher.search("", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search("def", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("def", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search(" abc ghi ", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search(" abc ghi ", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search("ghi ef", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("ghi ef", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search("abc ef xyz", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("abc ef xyz", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 0); - searchResult = entrySearcher.search("abc kl", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("abc kl", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - - delete group; } diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index d261d5dd6..3e9d4f66d 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -21,15 +21,20 @@ #include +class Group; + class TestEntrySearcher : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); + void cleanupTestCase(); void testAndConcatenationInSearch(); void testSearch(); +private: + Group* groupRoot; }; #endif // KEEPASSX_TESTENTRYSEARCHER_H From 819cfd459a63edd4baeb6eb394dd2558c6bb1d94 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 00:19:58 +0200 Subject: [PATCH 14/71] Move match method out of entry class. --- src/core/Entry.cpp | 19 ------------------- src/core/Entry.h | 2 -- src/core/EntrySearcher.cpp | 26 ++++++++++++++++++++++---- src/core/EntrySearcher.h | 4 +++- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 55f543285..4f977915b 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -579,25 +579,6 @@ const Database* Entry::database() const } } -bool Entry::match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity) -{ - QStringList wordList = searchTerm.split(QRegExp("\\s"), QString::SkipEmptyParts); - Q_FOREACH (const QString& word, wordList) { - if (!wordMatch(word, caseSensitivity)) { - return false; - } - } - return true; -} - -bool Entry::wordMatch(const QString& word, Qt::CaseSensitivity caseSensitivity) -{ - return title().contains(word, caseSensitivity) || - username().contains(word, caseSensitivity) || - url().contains(word, caseSensitivity) || - notes().contains(word, caseSensitivity); -} - QString Entry::resolvePlaceholders(const QString& str) const { QString result = str; diff --git a/src/core/Entry.h b/src/core/Entry.h index c2c2938c1..ae07ed453 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -141,7 +141,6 @@ public: void setGroup(Group* group); void setUpdateTimeinfo(bool value); - bool match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity); Q_SIGNALS: /** @@ -157,7 +156,6 @@ private Q_SLOTS: void updateModifiedSinceBegin(); private: - bool wordMatch(const QString& word, Qt::CaseSensitivity caseSensitivity); const Database* database() const; template bool set(T& property, const T& value); diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index 60cfaea45..82a553e3a 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -28,14 +28,12 @@ QList EntrySearcher::search(const QString &searchTerm, const Group* grou return searchEntries(searchTerm, group, caseSensitivity); } -QList EntrySearcher::searchEntries(const QString &searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) +QList EntrySearcher::searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) { QList searchResult; Q_FOREACH (Entry* entry, group->entries()) { - if (entry->match(searchTerm, caseSensitivity)) { - searchResult.append(entry); - } + searchResult.append(matchEntry(searchTerm, entry, caseSensitivity)); } Q_FOREACH (Group* childGroup, group->children()) { if (childGroup->searchingEnabled() != Group::Disable) { @@ -45,3 +43,23 @@ QList EntrySearcher::searchEntries(const QString &searchTerm, const Grou return searchResult; } + +QList EntrySearcher::matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity) +{ + QStringList wordList = searchTerm.split(QRegExp("\\s"), QString::SkipEmptyParts); + Q_FOREACH (const QString& word, wordList) { + if (!wordMatch(word, entry, caseSensitivity)) { + return QList(); + } + } + + return QList() << entry; +} + +bool EntrySearcher::wordMatch(const QString& word, Entry* entry, Qt::CaseSensitivity caseSensitivity) +{ + return entry->title().contains(word, caseSensitivity) || + entry->username().contains(word, caseSensitivity) || + entry->url().contains(word, caseSensitivity) || + entry->notes().contains(word, caseSensitivity); +} diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h index 56ed7e4e5..246538cbe 100644 --- a/src/core/EntrySearcher.h +++ b/src/core/EntrySearcher.h @@ -29,7 +29,9 @@ class EntrySearcher 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 searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); + QList matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity); + bool wordMatch(const QString &word, Entry *entry, Qt::CaseSensitivity caseSensitivity); }; #endif // KEEPASSX_ENTRYSEARCHER_H From 4f60df029d28f7e6f89d2b17405e878be03c307e Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 00:26:09 +0200 Subject: [PATCH 15/71] Refactor TestEntrySearcher. --- tests/TestEntrySearcher.cpp | 66 ++++++++++++++++--------------------- tests/TestEntrySearcher.h | 7 ++-- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 564d71a77..fec2a4ae2 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -20,19 +20,18 @@ #include #include "tests.h" -#include "core/EntrySearcher.h" -#include "core/Group.h" + QTEST_GUILESS_MAIN(TestEntrySearcher) void TestEntrySearcher::initTestCase() { - groupRoot = new Group(); + m_groupRoot = new Group(); } void TestEntrySearcher::cleanupTestCase() { - delete groupRoot; + delete m_groupRoot; } void TestEntrySearcher::testSearch() @@ -41,9 +40,9 @@ void TestEntrySearcher::testSearch() Group* group2 = new Group(); Group* group3 = new Group(); - group1->setParent(groupRoot); - group2->setParent(groupRoot); - group3->setParent(groupRoot); + group1->setParent(m_groupRoot); + group2->setParent(m_groupRoot); + group3->setParent(m_groupRoot); Group* group11 = new Group(); @@ -62,11 +61,11 @@ void TestEntrySearcher::testSearch() Entry* eRoot = new Entry(); eRoot->setNotes("test search term test"); - eRoot->setGroup(groupRoot); + eRoot->setGroup(m_groupRoot); Entry* eRoot2 = new Entry(); eRoot2->setNotes("test term test"); - eRoot2->setGroup(groupRoot); + eRoot2->setGroup(m_groupRoot); Entry* e1 = new Entry(); e1->setNotes("test search term test"); @@ -92,22 +91,18 @@ void TestEntrySearcher::testSearch() e3b->setNotes("test search test"); e3b->setGroup(group3); - QList searchResult; + m_searchResult = m_entrySearcher.search("search term", m_groupRoot, Qt::CaseInsensitive); - EntrySearcher entrySearcher; + QCOMPARE(m_searchResult.count(), 3); - searchResult = entrySearcher.search("search term", groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("search term", group211, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - QCOMPARE(searchResult.count(), 3); + m_searchResult = m_entrySearcher.search("search term", group11, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("search term", group211, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = entrySearcher.search("search term", group11, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = entrySearcher.search("search term", group1, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 0); + m_searchResult = m_entrySearcher.search("search term", group1, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 0); } void TestEntrySearcher::testAndConcatenationInSearch() @@ -115,26 +110,23 @@ void TestEntrySearcher::testAndConcatenationInSearch() Entry* entry = new Entry(); entry->setNotes("abc def ghi"); entry->setTitle("jkl"); - entry->setGroup(groupRoot); + entry->setGroup(m_groupRoot); - EntrySearcher entrySearcher; - QList searchResult; + m_searchResult = m_entrySearcher.search("", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("def", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("def", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search(" abc ghi ", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search(" abc ghi ", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("ghi ef", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("ghi ef", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("abc ef xyz", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 0); - searchResult = entrySearcher.search("abc ef xyz", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 0); - - searchResult = entrySearcher.search("abc kl", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("abc kl", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); } diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index 3e9d4f66d..194e29f0f 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -21,7 +21,8 @@ #include -class Group; +#include "core/EntrySearcher.h" +#include "core/Group.h" class TestEntrySearcher : public QObject { @@ -34,7 +35,9 @@ private Q_SLOTS: void testAndConcatenationInSearch(); void testSearch(); private: - Group* groupRoot; + Group* m_groupRoot; + EntrySearcher m_entrySearcher; + QList m_searchResult; }; #endif // KEEPASSX_TESTENTRYSEARCHER_H From c2940a8f18070b502eca96fc2b616d219c558332 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 10:51:22 +0200 Subject: [PATCH 16/71] Extend TestEntrySearcher. --- tests/TestEntrySearcher.cpp | 15 ++++++++++++++- tests/TestEntrySearcher.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index fec2a4ae2..b37992df9 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -92,7 +92,6 @@ void TestEntrySearcher::testSearch() e3b->setGroup(group3); m_searchResult = m_entrySearcher.search("search term", m_groupRoot, Qt::CaseInsensitive); - QCOMPARE(m_searchResult.count(), 3); m_searchResult = m_entrySearcher.search("search term", group211, Qt::CaseInsensitive); @@ -130,3 +129,17 @@ void TestEntrySearcher::testAndConcatenationInSearch() m_searchResult = m_entrySearcher.search("abc kl", m_groupRoot, Qt::CaseInsensitive); QCOMPARE(m_searchResult.count(), 1); } + +void TestEntrySearcher::testAllAttributesAreSearched() +{ + Entry* entry = new Entry(); + entry->setGroup(m_groupRoot); + + 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); + QCOMPARE(m_searchResult.count(), 1); +} diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index 194e29f0f..7c45451dc 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -34,6 +34,8 @@ private Q_SLOTS: void testAndConcatenationInSearch(); void testSearch(); + void testAllAttributesAreSearched(); + private: Group* m_groupRoot; EntrySearcher m_entrySearcher; From 204cd8d9714201a7db54f971c9e1349bd19466a3 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 11:48:30 +0200 Subject: [PATCH 17/71] Move exporter to separate class. --- src/CMakeLists.txt | 1 + src/core/Group.cpp | 16 -------- src/core/Group.h | 1 - src/core/ToDbExporter.cpp | 39 +++++++++++++++++++ src/core/ToDbExporter.h | 31 +++++++++++++++ tests/CMakeLists.txt | 3 ++ tests/TestExporter.cpp | 82 +++++++++++++++++++++++++++++++++++++++ tests/TestExporter.h | 33 ++++++++++++++++ tests/TestGroup.cpp | 44 --------------------- tests/TestGroup.h | 1 - 10 files changed, 189 insertions(+), 62 deletions(-) create mode 100644 src/core/ToDbExporter.cpp create mode 100644 src/core/ToDbExporter.h create mode 100644 tests/TestExporter.cpp create mode 100644 tests/TestExporter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9af937ea3..b09501cf7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ set(keepassx_SOURCES core/SignalMultiplexer.cpp core/TimeDelta.cpp core/TimeInfo.cpp + core/ToDbExporter.cpp core/Tools.cpp core/Uuid.cpp core/qcommandlineoption.cpp diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 9ebf7bb86..517f8cb0c 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -500,22 +500,6 @@ void Group::copyDataFrom(const Group* other) m_lastTopVisibleEntry = other->m_lastTopVisibleEntry; } -Database* Group::exportToDb() -{ - Q_ASSERT(database()); - - Database* db = new Database(); - Group* clonedGroup = clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory); - clonedGroup->setParent(db->rootGroup()); - - QSet customIcons = customIconsRecursive(); - db->metadata()->copyCustomIcons(customIcons, database()->metadata()); - - db->copyAttributesFrom(database()); - - return db; -} - void Group::addEntry(Entry* entry) { Q_ASSERT(entry); diff --git a/src/core/Group.h b/src/core/Group.h index 715403c03..7391f886d 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -111,7 +111,6 @@ public: */ Group* clone(Entry::CloneFlags entryFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo) const; void copyDataFrom(const Group* other); - Database* exportToDb(); Q_SIGNALS: void dataChanged(Group* group); diff --git a/src/core/ToDbExporter.cpp b/src/core/ToDbExporter.cpp new file mode 100644 index 000000000..0601ca4b8 --- /dev/null +++ b/src/core/ToDbExporter.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian 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 "ToDbExporter.h" +#include "core/Database.h" +#include "core/Group.h" +#include "core/Metadata.h" + +Database* ToDbExporter::exportToDb(Group* group) +{ + Database* oldDb = group->database(); + Q_ASSERT(oldDb); + + Database* db = new Database(); + Group* clonedGroup = group->clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory); + clonedGroup->setParent(db->rootGroup()); + + QSet customIcons = group->customIconsRecursive(); + db->metadata()->copyCustomIcons(customIcons, oldDb->metadata()); + + db->copyAttributesFrom(oldDb); + + return db; +} diff --git a/src/core/ToDbExporter.h b/src/core/ToDbExporter.h new file mode 100644 index 000000000..bd57a4b19 --- /dev/null +++ b/src/core/ToDbExporter.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian 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_TODBEXPORTER_H +#define KEEPASSX_TODBEXPORTER_H + +class Database; +class Group; + +class ToDbExporter +{ +public: + Database* exportToDb(Group* group); +}; + +#endif // KEEPASSX_TODBEXPORTER_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index be37a5def..d3f51ac41 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -168,6 +168,9 @@ add_unit_test(NAME testrandom SOURCES TestRandom.cpp MOCS TestRandom.h add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp MOCS TestEntrySearcher.h LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testexporter SOURCES TestExporter.cpp MOCS TestExporter.h + LIBS ${TEST_LIBRARIES}) + if(WITH_GUI_TESTS) add_subdirectory(gui) endif(WITH_GUI_TESTS) diff --git a/tests/TestExporter.cpp b/tests/TestExporter.cpp new file mode 100644 index 000000000..73bf407e9 --- /dev/null +++ b/tests/TestExporter.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian 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 "TestExporter.h" + +#include + +#include "tests.h" +#include "core/ToDbExporter.h" +#include "core/Group.h" +#include "core/Metadata.h" +#include "crypto/Crypto.h" + +QTEST_GUILESS_MAIN(TestExporter) + +void TestExporter::initTestCase() +{ + Crypto::init(); +} + +void TestExporter::testToDbExporter() +{ + QImage iconImage(1, 1, QImage::Format_RGB32); + iconImage.setPixel(0, 0, qRgb(1, 2, 3)); + Uuid iconUuid = Uuid::random(); + + QImage iconUnusedImage(1, 1, QImage::Format_RGB32); + iconUnusedImage.setPixel(0, 0, qRgb(1, 2, 3)); + Uuid iconUnusedUuid = Uuid::random(); + + Database* dbOrg = new Database(); + Group* groupOrg = new Group(); + groupOrg->setParent(dbOrg->rootGroup()); + groupOrg->setName("GTEST"); + Entry* entryOrg = new Entry(); + entryOrg->setGroup(groupOrg); + entryOrg->setTitle("ETEST"); + dbOrg->metadata()->addCustomIcon(iconUuid, iconImage); + dbOrg->metadata()->addCustomIcon(iconUnusedUuid, iconUnusedImage); + entryOrg->setIcon(iconUuid); + entryOrg->beginUpdate(); + entryOrg->setIcon(Entry::DefaultIconNumber); + entryOrg->endUpdate(); + + Database* dbExp = ToDbExporter().exportToDb(groupOrg); + + QCOMPARE(dbExp->rootGroup()->children().size(), 1); + Group* groupExp = dbExp->rootGroup()->children().first(); + QVERIFY(groupExp != groupOrg); + QCOMPARE(groupExp->name(), groupOrg->name()); + QCOMPARE(groupExp->entries().size(), 1); + + Entry* entryExp = groupExp->entries().first(); + QCOMPARE(entryExp->title(), entryOrg->title()); + QCOMPARE(dbExp->metadata()->customIcons().size(), 1); + QVERIFY(dbExp->metadata()->containsCustomIcon(iconUuid)); + QCOMPARE(entryExp->iconNumber(), entryOrg->iconNumber()); + + QCOMPARE(entryExp->historyItems().size(), 1); + QCOMPARE(entryExp->historyItems().first()->iconUuid(), iconUuid); + + delete dbOrg; + delete dbExp; +} + + + diff --git a/tests/TestExporter.h b/tests/TestExporter.h new file mode 100644 index 000000000..15f9a7c33 --- /dev/null +++ b/tests/TestExporter.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian 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_TESTEXPORTER_H +#define KEEPASSX_TESTEXPORTER_H + +#include + +class TestExporter : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testToDbExporter(); +}; + +#endif // KEEPASSX_TESTEXPORTER_H diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 816f7ce70..0b4564f2b 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -441,48 +441,4 @@ void TestGroup::testCopyCustomIcons() QCOMPARE(metaTarget->customIcon(group2Icon).pixel(0, 0), qRgb(4, 5, 6)); } -void TestGroup::testExportToDb() -{ - QImage iconImage(1, 1, QImage::Format_RGB32); - iconImage.setPixel(0, 0, qRgb(1, 2, 3)); - Uuid iconUuid = Uuid::random(); - - QImage iconUnusedImage(1, 1, QImage::Format_RGB32); - iconUnusedImage.setPixel(0, 0, qRgb(1, 2, 3)); - Uuid iconUnusedUuid = Uuid::random(); - - Database* dbOrg = new Database(); - Group* groupOrg = new Group(); - groupOrg->setParent(dbOrg->rootGroup()); - groupOrg->setName("GTEST"); - Entry* entryOrg = new Entry(); - entryOrg->setGroup(groupOrg); - entryOrg->setTitle("ETEST"); - dbOrg->metadata()->addCustomIcon(iconUuid, iconImage); - dbOrg->metadata()->addCustomIcon(iconUnusedUuid, iconUnusedImage); - entryOrg->setIcon(iconUuid); - entryOrg->beginUpdate(); - entryOrg->setIcon(Entry::DefaultIconNumber); - entryOrg->endUpdate(); - - Database* dbExp = groupOrg->exportToDb(); - QCOMPARE(dbExp->rootGroup()->children().size(), 1); - Group* groupExp = dbExp->rootGroup()->children().first(); - QVERIFY(groupExp != groupOrg); - QCOMPARE(groupExp->name(), groupOrg->name()); - QCOMPARE(groupExp->entries().size(), 1); - - Entry* entryExp = groupExp->entries().first(); - QCOMPARE(entryExp->title(), entryOrg->title()); - QCOMPARE(dbExp->metadata()->customIcons().size(), 1); - QVERIFY(dbExp->metadata()->containsCustomIcon(iconUuid)); - QCOMPARE(entryExp->iconNumber(), entryOrg->iconNumber()); - - QCOMPARE(entryExp->historyItems().size(), 1); - QCOMPARE(entryExp->historyItems().first()->iconUuid(), iconUuid); - - delete dbOrg; - delete dbExp; -} - QTEST_GUILESS_MAIN(TestGroup) diff --git a/tests/TestGroup.h b/tests/TestGroup.h index 2e4019901..c612a3ac6 100644 --- a/tests/TestGroup.h +++ b/tests/TestGroup.h @@ -33,7 +33,6 @@ private Q_SLOTS: void testCopyCustomIcon(); void testClone(); void testCopyCustomIcons(); - void testExportToDb(); }; #endif // KEEPASSX_TESTGROUP_H From ea3375490c76ae410a7d1202cc8ce7a5e6d5f602 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 12:30:47 +0200 Subject: [PATCH 18/71] Introduce interface for exporter. --- src/core/Exporter.h | 14 ++++++++++++++ src/core/ToDbExporter.cpp | 2 +- src/core/ToDbExporter.h | 6 ++++-- tests/TestExporter.cpp | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/core/Exporter.h diff --git a/src/core/Exporter.h b/src/core/Exporter.h new file mode 100644 index 000000000..dedb1c8a5 --- /dev/null +++ b/src/core/Exporter.h @@ -0,0 +1,14 @@ +#ifndef KEEPASSX_EXPORTER_H +#define KEEPASSX_EXPORTER_H + +class Database; +class Group; + +class Exporter +{ +public: + virtual Database* exportGroup(Group* group) = 0; + virtual ~Exporter() {} +}; + +#endif // KEEPASSX_EXPORTER_H diff --git a/src/core/ToDbExporter.cpp b/src/core/ToDbExporter.cpp index 0601ca4b8..1f76fb744 100644 --- a/src/core/ToDbExporter.cpp +++ b/src/core/ToDbExporter.cpp @@ -21,7 +21,7 @@ #include "core/Group.h" #include "core/Metadata.h" -Database* ToDbExporter::exportToDb(Group* group) +Database* ToDbExporter::exportGroup(Group* group) { Database* oldDb = group->database(); Q_ASSERT(oldDb); diff --git a/src/core/ToDbExporter.h b/src/core/ToDbExporter.h index bd57a4b19..58c5efeb3 100644 --- a/src/core/ToDbExporter.h +++ b/src/core/ToDbExporter.h @@ -19,13 +19,15 @@ #ifndef KEEPASSX_TODBEXPORTER_H #define KEEPASSX_TODBEXPORTER_H +#include "core/Exporter.h" + class Database; class Group; -class ToDbExporter +class ToDbExporter : Exporter { public: - Database* exportToDb(Group* group); + Database* exportGroup(Group* group); }; #endif // KEEPASSX_TODBEXPORTER_H diff --git a/tests/TestExporter.cpp b/tests/TestExporter.cpp index 73bf407e9..16b4b23cb 100644 --- a/tests/TestExporter.cpp +++ b/tests/TestExporter.cpp @@ -57,7 +57,7 @@ void TestExporter::testToDbExporter() entryOrg->setIcon(Entry::DefaultIconNumber); entryOrg->endUpdate(); - Database* dbExp = ToDbExporter().exportToDb(groupOrg); + Database* dbExp = ToDbExporter().exportGroup(groupOrg); QCOMPARE(dbExp->rootGroup()->children().size(), 1); Group* groupExp = dbExp->rootGroup()->children().first(); From 77af79498c566be89787d206c2fa88985463a932 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 12:32:52 +0200 Subject: [PATCH 19/71] Move QTEST_GUILESS_MAIN statements before test cases. --- tests/TestAutoType.cpp | 4 ++-- tests/TestCryptoHash.cpp | 4 ++-- tests/TestDeletedObjects.cpp | 4 ++-- tests/TestEntry.cpp | 4 ++-- tests/TestEntryModel.cpp | 4 ++-- tests/TestEntrySearcher.cpp | 1 - tests/TestGroup.cpp | 4 ++-- tests/TestGroupModel.cpp | 4 ++-- tests/TestHashedBlockStream.cpp | 4 ++-- tests/TestKeePass1Reader.cpp | 4 ++-- tests/TestKeePass2RandomStream.cpp | 4 ++-- tests/TestKeePass2Reader.cpp | 4 ++-- tests/TestKeePass2Writer.cpp | 4 ++-- tests/TestKeePass2XmlReader.cpp | 4 ++-- tests/TestKeys.cpp | 4 ++-- tests/TestModified.cpp | 4 ++-- tests/TestQCommandLineParser.cpp | 4 ++-- tests/TestQSaveFile.cpp | 4 ++-- tests/TestRandom.cpp | 4 ++-- tests/TestSymmetricCipher.cpp | 4 ++-- tests/TestWildcardMatcher.cpp | 4 ++-- 21 files changed, 40 insertions(+), 41 deletions(-) diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index c09a8366c..4f4350e71 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -31,6 +31,8 @@ #include "autotype/test/AutoTypeTestInterface.h" #include "gui/MessageBox.h" +QTEST_GUILESS_MAIN(TestAutoType) + void TestAutoType::initTestCase() { Crypto::init(); @@ -154,5 +156,3 @@ void TestAutoType::testGlobalAutoTypeTitleMatchDisabled() QCOMPARE(m_test->actionChars(), QString()); } - -QTEST_GUILESS_MAIN(TestAutoType) diff --git a/tests/TestCryptoHash.cpp b/tests/TestCryptoHash.cpp index 4f258a179..d189d456c 100644 --- a/tests/TestCryptoHash.cpp +++ b/tests/TestCryptoHash.cpp @@ -23,6 +23,8 @@ #include "crypto/Crypto.h" #include "crypto/CryptoHash.h" +QTEST_GUILESS_MAIN(TestCryptoHash) + void TestCryptoHash::initTestCase() { Crypto::init(); @@ -47,5 +49,3 @@ void TestCryptoHash::test() QCOMPARE(cryptoHash3.result(), QByteArray::fromHex("0b56e5f65263e747af4a833bd7dd7ad26a64d7a4de7c68e52364893dca0766b4")); } - -QTEST_GUILESS_MAIN(TestCryptoHash) diff --git a/tests/TestDeletedObjects.cpp b/tests/TestDeletedObjects.cpp index 914096cec..5967e58ee 100644 --- a/tests/TestDeletedObjects.cpp +++ b/tests/TestDeletedObjects.cpp @@ -26,6 +26,8 @@ #include "format/KeePass2XmlReader.h" #include "config-keepassx-tests.h" +QTEST_GUILESS_MAIN(TestDeletedObjects) + void TestDeletedObjects::initTestCase() { Crypto::init(); @@ -158,5 +160,3 @@ void TestDeletedObjects::testDatabaseChange() delete db; delete db2; } - -QTEST_GUILESS_MAIN(TestDeletedObjects) diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp index 15f398f18..481250a20 100644 --- a/tests/TestEntry.cpp +++ b/tests/TestEntry.cpp @@ -23,6 +23,8 @@ #include "core/Entry.h" #include "crypto/Crypto.h" +QTEST_GUILESS_MAIN(TestEntry) + void TestEntry::initTestCase() { Crypto::init(); @@ -121,5 +123,3 @@ void TestEntry::testClone() QCOMPARE(entryCloneHistory->historyItems().first()->title(), QString("Original Title")); QCOMPARE(entryCloneHistory->timeInfo().creationTime(), entryOrg->timeInfo().creationTime()); } - -QTEST_GUILESS_MAIN(TestEntry) diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp index fab63db68..5347a3c52 100644 --- a/tests/TestEntryModel.cpp +++ b/tests/TestEntryModel.cpp @@ -33,6 +33,8 @@ #include "gui/entry/EntryAttachmentsModel.h" #include "gui/entry/EntryAttributesModel.h" +QTEST_GUILESS_MAIN(TestEntryModel) + void TestEntryModel::initTestCase() { qRegisterMetaType("QModelIndex"); @@ -341,5 +343,3 @@ void TestEntryModel::testDatabaseDelete() delete modelTest; delete model; } - -QTEST_GUILESS_MAIN(TestEntryModel) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index b37992df9..9f7ca1397 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -21,7 +21,6 @@ #include "tests.h" - QTEST_GUILESS_MAIN(TestEntrySearcher) void TestEntrySearcher::initTestCase() diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 0b4564f2b..32398f01f 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -27,6 +27,8 @@ #include "core/Metadata.h" #include "crypto/Crypto.h" +QTEST_GUILESS_MAIN(TestGroup) + void TestGroup::initTestCase() { qRegisterMetaType("Entry*"); @@ -440,5 +442,3 @@ void TestGroup::testCopyCustomIcons() QCOMPARE(metaTarget->customIcon(group1Icon).pixel(0, 0), qRgb(1, 2, 3)); QCOMPARE(metaTarget->customIcon(group2Icon).pixel(0, 0), qRgb(4, 5, 6)); } - -QTEST_GUILESS_MAIN(TestGroup) diff --git a/tests/TestGroupModel.cpp b/tests/TestGroupModel.cpp index a16386c7d..fe7018e84 100644 --- a/tests/TestGroupModel.cpp +++ b/tests/TestGroupModel.cpp @@ -27,6 +27,8 @@ #include "crypto/Crypto.h" #include "gui/group/GroupModel.h" +QTEST_GUILESS_MAIN(TestGroupModel) + void TestGroupModel::initTestCase() { qRegisterMetaType("QModelIndex"); @@ -149,5 +151,3 @@ void TestGroupModel::test() delete modelTest; delete model; } - -QTEST_GUILESS_MAIN(TestGroupModel) diff --git a/tests/TestHashedBlockStream.cpp b/tests/TestHashedBlockStream.cpp index ab7d386a6..b062908f7 100644 --- a/tests/TestHashedBlockStream.cpp +++ b/tests/TestHashedBlockStream.cpp @@ -24,6 +24,8 @@ #include "crypto/Crypto.h" #include "streams/HashedBlockStream.h" +QTEST_GUILESS_MAIN(TestHashedBlockStream) + void TestHashedBlockStream::initTestCase() { Crypto::init(); @@ -69,5 +71,3 @@ void TestHashedBlockStream::testWriteRead() buffer.reset(); buffer.buffer().clear(); } - -QTEST_GUILESS_MAIN(TestHashedBlockStream) diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index 3ec4e7830..423087c91 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -33,6 +33,8 @@ #include "keys/FileKey.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeePass1Reader) + void TestKeePass1Reader::initTestCase() { Crypto::init(); @@ -292,5 +294,3 @@ void TestKeePass1Reader::reopenDatabase(Database* db, const QString& password, c QVERIFY(!reader.hasError()); delete newDb; } - -QTEST_GUILESS_MAIN(TestKeePass1Reader) diff --git a/tests/TestKeePass2RandomStream.cpp b/tests/TestKeePass2RandomStream.cpp index 74a154069..328decb30 100644 --- a/tests/TestKeePass2RandomStream.cpp +++ b/tests/TestKeePass2RandomStream.cpp @@ -26,6 +26,8 @@ #include "format/KeePass2.h" #include "format/KeePass2RandomStream.h" +QTEST_GUILESS_MAIN(TestKeePass2RandomStream) + void TestKeePass2RandomStream::initTestCase() { Crypto::init(); @@ -77,5 +79,3 @@ void TestKeePass2RandomStream::test() QCOMPARE(cipherData, cipherDataEncrypt); QCOMPARE(randomStreamData, cipherData); } - -QTEST_GUILESS_MAIN(TestKeePass2RandomStream) diff --git a/tests/TestKeePass2Reader.cpp b/tests/TestKeePass2Reader.cpp index 6b1ee1e95..3dd87c4f4 100644 --- a/tests/TestKeePass2Reader.cpp +++ b/tests/TestKeePass2Reader.cpp @@ -28,6 +28,8 @@ #include "format/KeePass2Reader.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeePass2Reader) + void TestKeePass2Reader::initTestCase() { Crypto::init(); @@ -154,5 +156,3 @@ void TestKeePass2Reader::testFormat300() delete db; } - -QTEST_GUILESS_MAIN(TestKeePass2Reader) diff --git a/tests/TestKeePass2Writer.cpp b/tests/TestKeePass2Writer.cpp index bbc4992a0..f00b38e86 100644 --- a/tests/TestKeePass2Writer.cpp +++ b/tests/TestKeePass2Writer.cpp @@ -29,6 +29,8 @@ #include "format/KeePass2Writer.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeePass2Writer) + void TestKeePass2Writer::initTestCase() { Crypto::init(); @@ -104,5 +106,3 @@ void TestKeePass2Writer::cleanupTestCase() delete m_dbOrg; delete m_dbTest; } - -QTEST_GUILESS_MAIN(TestKeePass2Writer) diff --git a/tests/TestKeePass2XmlReader.cpp b/tests/TestKeePass2XmlReader.cpp index ca57db958..d9935804d 100644 --- a/tests/TestKeePass2XmlReader.cpp +++ b/tests/TestKeePass2XmlReader.cpp @@ -28,6 +28,8 @@ #include "format/KeePass2XmlReader.h" #include "config-keepassx-tests.h" +QTEST_GUILESS_MAIN(TestKeePass2XmlReader) + namespace QTest { template<> char* toString(const Uuid& uuid) @@ -378,5 +380,3 @@ void TestKeePass2XmlReader::cleanupTestCase() { delete m_db; } - -QTEST_GUILESS_MAIN(TestKeePass2XmlReader) diff --git a/tests/TestKeys.cpp b/tests/TestKeys.cpp index ec9a35b4b..d5cba4abc 100644 --- a/tests/TestKeys.cpp +++ b/tests/TestKeys.cpp @@ -31,6 +31,8 @@ #include "keys/FileKey.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeys) + void TestKeys::initTestCase() { Crypto::init(); @@ -184,5 +186,3 @@ void TestKeys::benchmarkTransformKey() compositeKey.transform(seed, 1e6); } } - -QTEST_GUILESS_MAIN(TestKeys) diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp index 864ea1c36..85a6bf23e 100644 --- a/tests/TestModified.cpp +++ b/tests/TestModified.cpp @@ -27,6 +27,8 @@ #include "core/Tools.h" #include "crypto/Crypto.h" +QTEST_GUILESS_MAIN(TestModified) + void TestModified::initTestCase() { Crypto::init(); @@ -466,5 +468,3 @@ void TestModified::testHistoryItem() delete db; } - -QTEST_GUILESS_MAIN(TestModified) diff --git a/tests/TestQCommandLineParser.cpp b/tests/TestQCommandLineParser.cpp index d487862c0..4e2c63505 100644 --- a/tests/TestQCommandLineParser.cpp +++ b/tests/TestQCommandLineParser.cpp @@ -46,6 +46,8 @@ #include "tests.h" #include "core/qcommandlineparser.h" +QTEST_GUILESS_MAIN(TestQCommandLineParser) + Q_DECLARE_METATYPE(char**) static char *empty_argv[] = { 0 }; @@ -412,5 +414,3 @@ void TestQCommandLineParser::testSingleDashWordOptionModes() QCOMPARE(parser.value(parser.optionNames().at(i)), expectedOptionValues.at(i)); QCOMPARE(parser.unknownOptionNames(), QStringList()); } - -QTEST_GUILESS_MAIN(TestQCommandLineParser) diff --git a/tests/TestQSaveFile.cpp b/tests/TestQSaveFile.cpp index bccee0ec4..6be714efd 100644 --- a/tests/TestQSaveFile.cpp +++ b/tests/TestQSaveFile.cpp @@ -29,6 +29,8 @@ #include "tests.h" #include "core/qsavefile.h" +QTEST_GUILESS_MAIN(TestQSaveFile) + class DirCleanup { public: @@ -197,5 +199,3 @@ QString TestQSaveFile::tmpDir() return dirName; } - -QTEST_GUILESS_MAIN(TestQSaveFile) diff --git a/tests/TestRandom.cpp b/tests/TestRandom.cpp index 8ac570e1c..40ab702db 100644 --- a/tests/TestRandom.cpp +++ b/tests/TestRandom.cpp @@ -22,6 +22,8 @@ #include +QTEST_GUILESS_MAIN(TestRandom) + void TestRandom::initTestCase() { m_backend = new RandomBackendTest(); @@ -93,5 +95,3 @@ void RandomBackendTest::setNextBytes(const QByteArray& nextBytes) m_nextBytes = nextBytes; m_bytesIndex = 0; } - -QTEST_GUILESS_MAIN(TestRandom) diff --git a/tests/TestSymmetricCipher.cpp b/tests/TestSymmetricCipher.cpp index b47a00576..9f05db1bd 100644 --- a/tests/TestSymmetricCipher.cpp +++ b/tests/TestSymmetricCipher.cpp @@ -25,6 +25,8 @@ #include "crypto/SymmetricCipher.h" #include "streams/SymmetricCipherStream.h" +QTEST_GUILESS_MAIN(TestSymmetricCipher) + void TestSymmetricCipher::initTestCase() { Crypto::init(); @@ -192,5 +194,3 @@ void TestSymmetricCipher::testPadding() QByteArray decrypted = streamDec.readAll(); QCOMPARE(decrypted, plainText); } - -QTEST_GUILESS_MAIN(TestSymmetricCipher) diff --git a/tests/TestWildcardMatcher.cpp b/tests/TestWildcardMatcher.cpp index e06125b86..dc9991db9 100644 --- a/tests/TestWildcardMatcher.cpp +++ b/tests/TestWildcardMatcher.cpp @@ -22,6 +22,8 @@ #include "tests.h" #include "autotype/WildcardMatcher.h" +QTEST_GUILESS_MAIN(TestWildcardMatcher) + const QString TestWildcardMatcher::DefaultText = QString("some text"); const QString TestWildcardMatcher::AlternativeText = QString("some other text"); @@ -82,5 +84,3 @@ void TestWildcardMatcher::verifyNoMatch(QString pattern) bool matchResult = m_matcher->match(pattern); QVERIFY(!matchResult); } - -QTEST_GUILESS_MAIN(TestWildcardMatcher) From a6d44034a4bb826a7256fdd56727d9f05f5e87f4 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Fri, 16 May 2014 12:33:10 +0200 Subject: [PATCH 20/71] Put test executables into their default location. --- tests/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d3f51ac41..c094f8235 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src) add_definitions(-DQT_TEST_LIB) From 2d8ba2b394e2988ca7b6b0115c4588bb9a213a06 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Fri, 16 May 2014 12:33:29 +0200 Subject: [PATCH 21/71] Focus the search field instead of closing it when pressing the shortcut. Closes #124 --- src/gui/DatabaseWidget.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index fc533a3f3..c42dd5e07 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -681,8 +681,16 @@ void DatabaseWidget::switchToImportKeepass1(const QString& fileName) void DatabaseWidget::toggleSearch() { - if (m_entryView->inEntryListMode()) { - closeSearch(); + if (isInSearchMode()) { + if (m_searchUi->searchEdit->hasFocus()) { + closeSearch(); + } + else { + m_searchUi->searchEdit->selectAll(); + m_searchUi->searchEdit->setFocus(); + // make sure the search action is checked again + emitCurrentModeChanged(); + } } else { showSearch(); From 552ca7bf7198d75499d626c65d298d2f83a95783 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 18:56:01 +0200 Subject: [PATCH 22/71] Stop search timer when closing search. --- src/gui/DatabaseWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index c42dd5e07..c111f8d52 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -701,6 +701,7 @@ void DatabaseWidget::closeSearch() { Q_ASSERT(m_lastGroup); m_groupView->setCurrentGroup(m_lastGroup); + m_searchTimer->stop(); } void DatabaseWidget::showSearch() From 4ab887c773656737abce2c4d0225357dc45b35c5 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Fri, 16 May 2014 19:10:30 +0200 Subject: [PATCH 23/71] Initally select first entry in EntryView. Closes #104 --- src/gui/entry/EntryView.cpp | 15 +++++++++++++-- src/gui/entry/EntryView.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 4691ed655..6c43df3bf 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -62,13 +62,24 @@ void EntryView::keyPressEvent(QKeyEvent* event) void EntryView::setGroup(Group* group) { m_model->setGroup(group); - Q_EMIT entrySelectionChanged(); + setFirstEntryActive(); } void EntryView::setEntryList(const QList& entries) { m_model->setEntryList(entries); - Q_EMIT entrySelectionChanged(); + setFirstEntryActive(); +} + +void EntryView::setFirstEntryActive() +{ + if(m_model->rowCount() > 0) { + QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); + setCurrentEntry(m_model->entryFromIndex(index)); + } + else { + Q_EMIT entrySelectionChanged(); + } } bool EntryView::inEntryListMode() diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index ba2919e91..c11d0417d 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -42,6 +42,7 @@ public: void setEntryList(const QList& entries); bool inEntryListMode(); int numberOfSelectedEntries(); + void setFirstEntryActive(); public Q_SLOTS: void setGroup(Group* group); From 05de45dadb44e34f36c7898bb812379ee278acbe Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 19:49:58 +0200 Subject: [PATCH 24/71] Improve tab order. --- src/gui/DatabaseWidget.cpp | 4 ++++ src/gui/SearchWidget.ui | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index c111f8d52..00125a1c5 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -102,6 +102,10 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) rightHandSideWidget->setLayout(vLayout); + setTabOrder(m_searchUi->searchRootRadioButton, m_entryView); + setTabOrder(m_entryView, m_groupView); + setTabOrder(m_groupView, m_searchWidget); + splitter->addWidget(m_groupView); splitter->addWidget(rightHandSideWidget); diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui index c3d59b8d1..ce4845dcd 100644 --- a/src/gui/SearchWidget.ui +++ b/src/gui/SearchWidget.ui @@ -11,7 +11,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -21,6 +30,9 @@ + + Qt::ClickFocus + true @@ -38,7 +50,16 @@ - + + 0 + + + 0 + + + 0 + + 0 From d874f58a394d9ca7bfe91fb8bd9218929f1b755f Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:16:27 +0200 Subject: [PATCH 25/71] Synchronize DatabaseWidget splitter sizes. --- src/CMakeLists.txt | 2 + src/gui/DatabaseTabWidget.cpp | 9 ++++ src/gui/DatabaseTabWidget.h | 4 ++ src/gui/DatabaseWidget.cpp | 22 ++++++--- src/gui/DatabaseWidget.h | 7 +++ src/gui/DatabaseWidgetStateSync.cpp | 72 +++++++++++++++++++++++++++++ src/gui/DatabaseWidgetStateSync.h | 44 ++++++++++++++++++ 7 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 src/gui/DatabaseWidgetStateSync.cpp create mode 100644 src/gui/DatabaseWidgetStateSync.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b09501cf7..3de2422a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,7 @@ set(keepassx_SOURCES gui/DatabaseSettingsWidget.cpp gui/DatabaseTabWidget.cpp gui/DatabaseWidget.cpp + gui/DatabaseWidgetStateSync.cpp gui/DialogyWidget.cpp gui/DragTabBar.cpp gui/EditWidget.cpp @@ -156,6 +157,7 @@ set(keepassx_MOC gui/DatabaseSettingsWidget.h gui/DatabaseTabWidget.h gui/DatabaseWidget.h + gui/DatabaseWidgetStateSync.h gui/DialogyWidget.h gui/DragTabBar.h gui/EditWidget.h diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index c15d16e5a..7f9984615 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -27,6 +27,7 @@ #include "core/Metadata.h" #include "core/qsavefile.h" #include "gui/DatabaseWidget.h" +#include "gui/DatabaseWidgetStateSync.h" #include "gui/DragTabBar.h" #include "gui/FileDialog.h" #include "gui/MessageBox.h" @@ -46,12 +47,15 @@ const int DatabaseTabWidget::LastDatabasesCount = 5; DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) : QTabWidget(parent) + , m_dbWidgetSateSync(new DatabaseWidgetStateSync(this)) { DragTabBar* tabBar = new DragTabBar(this); tabBar->setDrawBase(false); setTabBar(tabBar); connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabase(int))); + connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged())); + connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetSateSync, SLOT(setActive(DatabaseWidget*))); connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType())); } @@ -584,6 +588,11 @@ void DatabaseTabWidget::changeDatabase(Database* newDb) connectDatabase(newDb, oldDb); } +void DatabaseTabWidget::emitActivateDatabaseChanged() +{ + Q_EMIT activateDatabaseChanged(currentDatabaseWidget()); +} + void DatabaseTabWidget::connectDatabase(Database* newDb, Database* oldDb) { if (oldDb) { diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 8e8fceb38..25d34f304 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -25,6 +25,7 @@ #include "gui/DatabaseWidget.h" class DatabaseWidget; +class DatabaseWidgetStateSync; class DatabaseOpenWidget; class QFile; @@ -75,6 +76,7 @@ public Q_SLOTS: Q_SIGNALS: void tabNameChanged(); void databaseWithFileClosed(QString filePath); + void activateDatabaseChanged(DatabaseWidget* dbWidget); private Q_SLOTS: void updateTabName(Database* db); @@ -83,6 +85,7 @@ private Q_SLOTS: void modified(); void toggleTabbar(); void changeDatabase(Database* newDb); + void emitActivateDatabaseChanged(); private: void saveDatabase(Database* db); @@ -99,6 +102,7 @@ private: KeePass2Writer m_writer; QHash m_dbList; + DatabaseWidgetStateSync* m_dbWidgetSateSync; }; #endif // KEEPASSX_DATABASETABWIDGET_H diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 00125a1c5..8173fe968 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -61,12 +61,12 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_mainWidget = new QWidget(this); QLayout* layout = new QHBoxLayout(m_mainWidget); - QSplitter* splitter = new QSplitter(m_mainWidget); + m_splitter = new QSplitter(m_mainWidget); - QWidget* rightHandSideWidget = new QWidget(splitter); + QWidget* rightHandSideWidget = new QWidget(m_splitter); m_searchWidget->setParent(rightHandSideWidget); - m_groupView = new GroupView(db, splitter); + m_groupView = new GroupView(db, m_splitter); m_groupView->setObjectName("groupView"); m_groupView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_groupView, SIGNAL(customContextMenuRequested(QPoint)), @@ -106,10 +106,10 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) setTabOrder(m_entryView, m_groupView); setTabOrder(m_groupView, m_searchWidget); - splitter->addWidget(m_groupView); - splitter->addWidget(rightHandSideWidget); + m_splitter->addWidget(m_groupView); + m_splitter->addWidget(rightHandSideWidget); - layout->addWidget(splitter); + layout->addWidget(m_splitter); m_mainWidget->setLayout(layout); m_editEntryWidget = new EditEntryWidget(); @@ -141,6 +141,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) addWidget(m_keepass1OpenWidget); addWidget(m_unlockDatabaseWidget); + connect(m_splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterSizesChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(clearLastGroup(Group*))); connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), m_entryView, SLOT(setGroup(Group*))); @@ -200,6 +201,15 @@ bool DatabaseWidget::isInEditMode() const } } +QList DatabaseWidget::splitterSizes() const +{ + return m_splitter->sizes(); +} + +void DatabaseWidget::setSplitterSizes(const QList& sizes) +{ + m_splitter->setSizes(sizes); +} void DatabaseWidget::emitCurrentModeChanged() { diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 5e77f01b3..628a344e9 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -38,6 +38,7 @@ class GroupView; class KeePass1OpenWidget; class QFile; class QMenu; +class QSplitter; class UnlockDatabaseWidget; namespace Ui { @@ -75,6 +76,8 @@ public: QStringList customEntryAttributes() const; bool isGroupSelected()const ; bool isInEditMode() const; + QList splitterSizes() const; + void setSplitterSizes(const QList& sizes); Q_SIGNALS: void closeRequest(); @@ -85,6 +88,9 @@ Q_SIGNALS: void groupContextMenuRequested(const QPoint& globalPos); void entryContextMenuRequested(const QPoint& globalPos); void unlockedDatabase(); + void listModeActivated(); + void searchModeActivated(); + void splitterSizesChanged(); public Q_SLOTS: void createEntry(); @@ -147,6 +153,7 @@ private: DatabaseOpenWidget* m_databaseOpenWidget; KeePass1OpenWidget* m_keepass1OpenWidget; UnlockDatabaseWidget* m_unlockDatabaseWidget; + QSplitter* m_splitter; GroupView* m_groupView; EntryView* m_entryView; Group* m_newGroup; diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp new file mode 100644 index 000000000..2a071e306 --- /dev/null +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian 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 "DatabaseWidgetStateSync.h" + +#include "core/Config.h" + +DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent) + : QObject(parent) + , m_activeDbWidget(Q_NULLPTR) +{ + QVariantList variantList = config()->get("GUI/SplitterState").toList(); + Q_FOREACH (const QVariant& var, variantList) { + bool ok; + int size = var.toInt(&ok); + if (ok) { + m_splitterSizes.append(size); + } + else { + m_splitterSizes.clear(); + break; + } + } +} + +DatabaseWidgetStateSync::~DatabaseWidgetStateSync() +{ + QVariantList variantList; + Q_FOREACH (int size, m_splitterSizes) { + variantList.append(size); + } + + config()->set("GUI/SplitterState", variantList); +} + +void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) +{ + if (m_activeDbWidget) { + disconnect(m_activeDbWidget, 0, this, 0); + } + + m_activeDbWidget = dbWidget; + + if (m_activeDbWidget) { + if (!m_splitterSizes.isEmpty()) { + m_activeDbWidget->setSplitterSizes(m_splitterSizes); + } + + connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()), + SLOT(updateSplitterSizes())); + } +} + +void DatabaseWidgetStateSync::updateSplitterSizes() +{ + m_splitterSizes = m_activeDbWidget->splitterSizes(); +} diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h new file mode 100644 index 000000000..574519bd4 --- /dev/null +++ b/src/gui/DatabaseWidgetStateSync.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian 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_HEADERVIEWSYNC_H +#define KEEPASSX_HEADERVIEWSYNC_H + +#include "gui/DatabaseWidget.h" + +class DatabaseWidgetStateSync : public QObject +{ + Q_OBJECT + +public: + explicit DatabaseWidgetStateSync(QObject* parent = Q_NULLPTR); + ~DatabaseWidgetStateSync(); + +public Q_SLOTS: + void setActive(DatabaseWidget* dbWidget); + +private Q_SLOTS: + void updateSplitterSizes(); + +private: + DatabaseWidget* m_activeDbWidget; + + QList m_splitterSizes; +}; + +#endif // KEEPASSX_HEADERVIEWSYNC_H From 9ac01c930d36ed9ff1e59f7315f1adabf0f5aa18 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:21:17 +0200 Subject: [PATCH 26/71] Drop DatabaseWidget::groupView() and entryView(). --- src/gui/DatabaseWidget.cpp | 10 ---------- src/gui/DatabaseWidget.h | 2 -- tests/gui/TestGui.cpp | 16 ++++++++-------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 8173fe968..f3738c734 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -216,16 +216,6 @@ void DatabaseWidget::emitCurrentModeChanged() Q_EMIT currentModeChanged(currentMode()); } -GroupView* DatabaseWidget::groupView() -{ - return m_groupView; -} - -EntryView* DatabaseWidget::entryView() -{ - return m_entryView; -} - Database* DatabaseWidget::database() { return m_db; diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 628a344e9..4a5765df9 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -60,8 +60,6 @@ public: explicit DatabaseWidget(Database* db, QWidget* parent = Q_NULLPTR); ~DatabaseWidget(); - GroupView* groupView(); - EntryView* entryView(); Database* database(); bool dbHasKey() const; bool canDeleteCurrentGroup() const; diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index a4d04c5de..51dc39e5d 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -83,7 +83,7 @@ void TestGui::testTabs() void TestGui::testEditEntry() { - EntryView* entryView = m_dbWidget->entryView(); + EntryView* entryView = m_dbWidget->findChild("entryView"); QModelIndex item = entryView->model()->index(0, 1); QRect itemRect = entryView->visualRect(item); QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center()); @@ -237,8 +237,8 @@ void TestGui::testSearch() void TestGui::testDeleteEntry() { - GroupView* groupView = m_dbWidget->groupView(); - EntryView* entryView = m_dbWidget->entryView(); + GroupView* groupView = m_dbWidget->findChild("groupView"); + EntryView* entryView = m_dbWidget->findChild("entryView"); QToolBar* toolBar = m_mainWindow->findChild("toolBar"); QAction* entryDeleteAction = m_mainWindow->findChild("actionEntryDelete"); QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction); @@ -274,7 +274,7 @@ void TestGui::testDeleteEntry() void TestGui::testCloneEntry() { - EntryView* entryView = m_dbWidget->entryView(); + EntryView* entryView = m_dbWidget->findChild("entryView"); QCOMPARE(entryView->model()->rowCount(), 1); @@ -292,8 +292,8 @@ void TestGui::testCloneEntry() void TestGui::testDragAndDropEntry() { - EntryView* entryView = m_dbWidget->entryView(); - GroupView* groupView = m_dbWidget->groupView(); + EntryView* entryView = m_dbWidget->findChild("entryView"); + GroupView* groupView = m_dbWidget->findChild("groupView"); QAbstractItemModel* groupModel = groupView->model(); QModelIndex sourceIndex = entryView->model()->index(0, 1); @@ -314,7 +314,7 @@ void TestGui::testDragAndDropEntry() void TestGui::testDragAndDropGroup() { - QAbstractItemModel* groupModel = m_dbWidget->groupView()->model(); + QAbstractItemModel* groupModel = m_dbWidget->findChild("groupView")->model(); QModelIndex rootIndex = groupModel->index(0, 0); dragAndDropGroup(groupModel->index(0, 0, rootIndex), @@ -453,7 +453,7 @@ void TestGui::dragAndDropGroup(const QModelIndex& sourceIndex, const QModelIndex QVERIFY(sourceIndex.isValid()); QVERIFY(targetIndex.isValid()); - GroupModel* groupModel = qobject_cast(m_dbWidget->groupView()->model()); + GroupModel* groupModel = qobject_cast(m_dbWidget->findChild("groupView")->model()); QMimeData mimeData; QByteArray encoded; From 584f4b50bff944fffba09eb6d5fcf18c9d75414e Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:21:50 +0200 Subject: [PATCH 27/71] Coding style fix. --- src/gui/DatabaseWidget.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 4a5765df9..53906f34b 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -72,7 +72,7 @@ public: void updateFilename(const QString& filename); int numberOfSelectedEntries() const; QStringList customEntryAttributes() const; - bool isGroupSelected()const ; + bool isGroupSelected() const; bool isInEditMode() const; QList splitterSizes() const; void setSplitterSizes(const QList& sizes); From 8a4100adbd5ba85eedb301220ed56ac624a00a15 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:22:45 +0200 Subject: [PATCH 28/71] Make DatabaseWidget::emit{Group,Entry}ContextMenuRequested() private. --- src/gui/DatabaseWidget.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 53906f34b..2e2077bcc 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -113,8 +113,6 @@ public Q_SLOTS: void switchToOpenDatabase(const QString& fileName, const QString& password, const QString& keyFile); void switchToImportKeepass1(const QString& fileName); void toggleSearch(); - void emitGroupContextMenuRequested(const QPoint& pos); - void emitEntryContextMenuRequested(const QPoint& pos); private Q_SLOTS: void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column); @@ -124,6 +122,8 @@ private Q_SLOTS: void switchToEntryEdit(Entry* entry); void switchToEntryEdit(Entry* entry, bool create); void switchToGroupEdit(Group* entry, bool create); + void emitGroupContextMenuRequested(const QPoint& pos); + void emitEntryContextMenuRequested(const QPoint& pos); void updateMasterKey(bool accepted); void openDatabase(bool accepted); void unlockDatabase(bool accepted); From 76da4a6cd48ce61eb519a375d4c1aed8abe74161 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:25:45 +0200 Subject: [PATCH 29/71] Use QSplitter::setStretchFactor() convenience method. --- src/gui/DatabaseWidget.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index f3738c734..131776e41 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -79,14 +79,6 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_entryView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(emitEntryContextMenuRequested(QPoint))); - QSizePolicy policy; - policy = m_groupView->sizePolicy(); - policy.setHorizontalStretch(30); - m_groupView->setSizePolicy(policy); - policy = rightHandSideWidget->sizePolicy(); - policy.setHorizontalStretch(70); - rightHandSideWidget->setSizePolicy(policy); - QAction* closeAction = new QAction(m_searchWidget); QIcon closeIcon = filePath()->icon("actions", "dialog-close"); closeAction->setIcon(closeIcon); @@ -109,6 +101,9 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_splitter->addWidget(m_groupView); m_splitter->addWidget(rightHandSideWidget); + m_splitter->setStretchFactor(0, 30); + m_splitter->setStretchFactor(1, 70); + layout->addWidget(m_splitter); m_mainWidget->setLayout(layout); From 0e75e6ff03d27685330358806d3886590c020d7e Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:27:04 +0200 Subject: [PATCH 30/71] Make DatabaseWidget splitter non-collapsible. --- src/gui/DatabaseWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 131776e41..9a09814b7 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -62,6 +62,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_mainWidget = new QWidget(this); QLayout* layout = new QHBoxLayout(m_mainWidget); m_splitter = new QSplitter(m_mainWidget); + m_splitter->setChildrenCollapsible(false); QWidget* rightHandSideWidget = new QWidget(m_splitter); m_searchWidget->setParent(rightHandSideWidget); From a25b28ffee309f51c64a131925dac3ff047db592 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:38:48 +0200 Subject: [PATCH 31/71] Rename config option window/Geometry to GUI/MainWindowGeometry. --- src/gui/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c83f3167f..8c72bbff9 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -38,7 +38,7 @@ MainWindow::MainWindow() m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size(); - restoreGeometry(config()->get("window/Geometry").toByteArray()); + restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray()); setWindowIcon(filePath()->applicationIcon()); QAction* toggleViewAction = m_ui->toolBar->toggleViewAction(); @@ -437,7 +437,7 @@ void MainWindow::closeEvent(QCloseEvent* event) void MainWindow::saveWindowInformation() { - config()->set("window/Geometry", saveGeometry()); + config()->set("GUI/MainWindowGeometry", saveGeometry()); } bool MainWindow::saveLastDatabases() From e776de8eebed90af6b41c9df34e977795bcb4468 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 12:51:16 +0200 Subject: [PATCH 32/71] Remember and synchronize entry column sizes. Closes #159 --- src/gui/DatabaseWidget.cpp | 28 +++++++++ src/gui/DatabaseWidget.h | 3 + src/gui/DatabaseWidgetStateSync.cpp | 92 +++++++++++++++++++++++------ src/gui/DatabaseWidgetStateSync.h | 8 +++ 4 files changed, 113 insertions(+), 18 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 9a09814b7..5379e1a1f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -138,6 +138,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) addWidget(m_unlockDatabaseWidget); connect(m_splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterSizesChanged())); + connect(m_entryView->header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(entryColumnSizesChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(clearLastGroup(Group*))); connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), m_entryView, SLOT(setGroup(Group*))); @@ -207,6 +208,29 @@ void DatabaseWidget::setSplitterSizes(const QList& sizes) m_splitter->setSizes(sizes); } +QList DatabaseWidget::entryHeaderViewSizes() const +{ + QList sizes; + + for (int i = 0; i < m_entryView->header()->count(); i++) { + sizes.append(m_entryView->header()->sectionSize(i)); + } + + return sizes; +} + +void DatabaseWidget::setEntryViewHeaderSizes(const QList& sizes) +{ + if (sizes.size() != m_entryView->header()->count()) { + Q_ASSERT(false); + return; + } + + for (int i = 0; i < sizes.size(); i++) { + m_entryView->header()->resizeSection(i, sizes[i]); + } +} + void DatabaseWidget::emitCurrentModeChanged() { Q_EMIT currentModeChanged(currentMode()); @@ -702,6 +726,8 @@ void DatabaseWidget::closeSearch() Q_ASSERT(m_lastGroup); m_groupView->setCurrentGroup(m_lastGroup); m_searchTimer->stop(); + + Q_EMIT listModeActivated(); } void DatabaseWidget::showSearch() @@ -739,6 +765,8 @@ void DatabaseWidget::showSearch() m_searchWidget->show(); search(); m_searchUi->searchEdit->setFocus(); + + Q_EMIT searchModeActivated(); } void DatabaseWidget::search() diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 2e2077bcc..981784d48 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -76,6 +76,8 @@ public: bool isInEditMode() const; QList splitterSizes() const; void setSplitterSizes(const QList& sizes); + QList entryHeaderViewSizes() const; + void setEntryViewHeaderSizes(const QList& sizes); Q_SIGNALS: void closeRequest(); @@ -89,6 +91,7 @@ Q_SIGNALS: void listModeActivated(); void searchModeActivated(); void splitterSizesChanged(); + void entryColumnSizesChanged(); public Q_SLOTS: void createEntry(); diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp index 2a071e306..c9e60bb93 100644 --- a/src/gui/DatabaseWidgetStateSync.cpp +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -24,28 +24,16 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent) : QObject(parent) , m_activeDbWidget(Q_NULLPTR) { - QVariantList variantList = config()->get("GUI/SplitterState").toList(); - Q_FOREACH (const QVariant& var, variantList) { - bool ok; - int size = var.toInt(&ok); - if (ok) { - m_splitterSizes.append(size); - } - else { - m_splitterSizes.clear(); - break; - } - } + m_splitterSizes = variantToIntList(config()->get("GUI/SplitterState")); + m_columnSizesList = variantToIntList(config()->get("GUI/EntryListColumnSizes")); + m_columnSizesSearch = variantToIntList(config()->get("GUI/EntrySearchColumnSizes")); } DatabaseWidgetStateSync::~DatabaseWidgetStateSync() { - QVariantList variantList; - Q_FOREACH (int size, m_splitterSizes) { - variantList.append(size); - } - - config()->set("GUI/SplitterState", variantList); + config()->set("GUI/SplitterState", intListToVariant(m_splitterSizes)); + config()->set("GUI/EntryListColumnSizes", intListToVariant(m_columnSizesList)); + config()->set("GUI/EntrySearchColumnSizes", intListToVariant(m_columnSizesSearch)); } void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) @@ -61,8 +49,35 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) m_activeDbWidget->setSplitterSizes(m_splitterSizes); } + if (m_activeDbWidget->isGroupSelected()) { + restoreListView(); + } + else { + restoreSearchView(); + } + connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()), SLOT(updateSplitterSizes())); + connect(m_activeDbWidget, SIGNAL(entryColumnSizesChanged()), + SLOT(updateColumnSizes())); + connect(m_activeDbWidget, SIGNAL(listModeActivated()), + SLOT(restoreListView())); + connect(m_activeDbWidget, SIGNAL(searchModeActivated()), + SLOT(restoreSearchView())); + } +} + +void DatabaseWidgetStateSync::restoreListView() +{ + if (!m_columnSizesList.isEmpty()) { + m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesList); + } +} + +void DatabaseWidgetStateSync::restoreSearchView() +{ + if (!m_columnSizesSearch.isEmpty()) { + m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesSearch); } } @@ -70,3 +85,44 @@ void DatabaseWidgetStateSync::updateSplitterSizes() { m_splitterSizes = m_activeDbWidget->splitterSizes(); } + +void DatabaseWidgetStateSync::updateColumnSizes() +{ + if (m_activeDbWidget->isGroupSelected()) { + m_columnSizesList = m_activeDbWidget->entryHeaderViewSizes(); + } + else { + m_columnSizesSearch = m_activeDbWidget->entryHeaderViewSizes(); + } +} + +QList DatabaseWidgetStateSync::variantToIntList(const QVariant& variant) +{ + QVariantList list = variant.toList(); + QList result; + + Q_FOREACH (const QVariant& var, list) { + bool ok; + int size = var.toInt(&ok); + if (ok) { + result.append(size); + } + else { + result.clear(); + break; + } + } + + return result; +} + +QVariant DatabaseWidgetStateSync::intListToVariant(const QList& list) +{ + QVariantList result; + + Q_FOREACH (int value, list) { + result.append(value); + } + + return result; +} diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h index 574519bd4..26911837c 100644 --- a/src/gui/DatabaseWidgetStateSync.h +++ b/src/gui/DatabaseWidgetStateSync.h @@ -31,14 +31,22 @@ public: public Q_SLOTS: void setActive(DatabaseWidget* dbWidget); + void restoreListView(); + void restoreSearchView(); private Q_SLOTS: void updateSplitterSizes(); + void updateColumnSizes(); private: + static QList variantToIntList(const QVariant& variant); + static QVariant intListToVariant(const QList& list); + DatabaseWidget* m_activeDbWidget; QList m_splitterSizes; + QList m_columnSizesList; + QList m_columnSizesSearch; }; #endif // KEEPASSX_HEADERVIEWSYNC_H From c806f9ebf4f594f1caf194d61c4fbfaeafdcef13 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Sat, 17 May 2014 18:13:22 +0200 Subject: [PATCH 33/71] Correct tr-calls. --- src/gui/DatabaseOpenWidget.cpp | 6 +++--- src/gui/KeePass1OpenWidget.cpp | 4 ++-- src/gui/entry/EditEntryWidget.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 973192523..37bbce747 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -117,8 +117,8 @@ void DatabaseOpenWidget::openDatabase() Q_EMIT editFinished(true); } else { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.\n%1") - .arg(reader.errorString())); + MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") + .append(reader.errorString())); m_ui->editPassword->clear(); } } @@ -138,7 +138,7 @@ CompositeKey DatabaseOpenWidget::databaseKey() QString keyFilename = m_ui->comboKeyFile->currentText(); QString errorMsg; if (!key.load(keyFilename, &errorMsg)) { - MessageBox::warning(this, tr("Error"), tr("Can't open key file:\n%1").arg(errorMsg)); + MessageBox::warning(this, tr("Error"), tr("Can't open key file").append(":\n").append(errorMsg)); return CompositeKey(); } masterKey.addKey(key); diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp index 5f23d8070..96ddf13f2 100644 --- a/src/gui/KeePass1OpenWidget.cpp +++ b/src/gui/KeePass1OpenWidget.cpp @@ -64,8 +64,8 @@ void KeePass1OpenWidget::openDatabase() Q_EMIT editFinished(true); } else { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.\n%1") - .arg(reader.errorString())); + MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") + .append(reader.errorString())); m_ui->editPassword->clear(); } } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 4a6743324..4a866680b 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -590,14 +590,14 @@ void EditEntryWidget::insertAttachment() QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { MessageBox::warning(this, tr("Error"), - tr("Unable to open file:\n").append(file.errorString())); + tr("Unable to open file").append(":\n").append(file.errorString())); return; } QByteArray data; if (!Tools::readAllFromDevice(&file, data)) { MessageBox::warning(this, tr("Error"), - tr("Unable to open file:\n").append(file.errorString())); + tr("Unable to open file").append(":\n").append(file.errorString())); return; } From 9391de74c7117ad40d8bd1d530e2620cedc101c1 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 18:05:02 +0200 Subject: [PATCH 34/71] Block non-user updates in DatabaseWidgetStateSync. --- src/gui/DatabaseWidget.cpp | 5 +++++ src/gui/DatabaseWidget.h | 2 ++ src/gui/DatabaseWidgetStateSync.cpp | 26 ++++++++++++++++++++++++++ src/gui/DatabaseWidgetStateSync.h | 2 ++ 4 files changed, 35 insertions(+) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 5379e1a1f..0779297ef 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -724,6 +724,9 @@ void DatabaseWidget::toggleSearch() void DatabaseWidget::closeSearch() { Q_ASSERT(m_lastGroup); + + Q_EMIT listModeAboutToActivate(); + m_groupView->setCurrentGroup(m_lastGroup); m_searchTimer->stop(); @@ -732,6 +735,8 @@ void DatabaseWidget::closeSearch() void DatabaseWidget::showSearch() { + Q_EMIT searchModeAboutToActivate(); + m_searchUi->searchEdit->blockSignals(true); m_searchUi->searchEdit->clear(); m_searchUi->searchEdit->blockSignals(false); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 981784d48..cbab175e4 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -88,7 +88,9 @@ Q_SIGNALS: void groupContextMenuRequested(const QPoint& globalPos); void entryContextMenuRequested(const QPoint& globalPos); void unlockedDatabase(); + void listModeAboutToActivate(); void listModeActivated(); + void searchModeAboutToActivate(); void searchModeActivated(); void splitterSizesChanged(); void entryColumnSizesChanged(); diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp index c9e60bb93..66b8492e2 100644 --- a/src/gui/DatabaseWidgetStateSync.cpp +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -23,6 +23,7 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent) : QObject(parent) , m_activeDbWidget(Q_NULLPTR) + , m_blockUpdates(false) { m_splitterSizes = variantToIntList(config()->get("GUI/SplitterState")); m_columnSizesList = variantToIntList(config()->get("GUI/EntryListColumnSizes")); @@ -45,6 +46,8 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) m_activeDbWidget = dbWidget; if (m_activeDbWidget) { + m_blockUpdates = true; + if (!m_splitterSizes.isEmpty()) { m_activeDbWidget->setSplitterSizes(m_splitterSizes); } @@ -56,6 +59,8 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) restoreSearchView(); } + m_blockUpdates = false; + connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()), SLOT(updateSplitterSizes())); connect(m_activeDbWidget, SIGNAL(entryColumnSizesChanged()), @@ -64,6 +69,10 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) SLOT(restoreListView())); connect(m_activeDbWidget, SIGNAL(searchModeActivated()), SLOT(restoreSearchView())); + connect(m_activeDbWidget, SIGNAL(listModeAboutToActivate()), + SLOT(blockUpdates())); + connect(m_activeDbWidget, SIGNAL(searchModeAboutToActivate()), + SLOT(blockUpdates())); } } @@ -72,6 +81,8 @@ void DatabaseWidgetStateSync::restoreListView() if (!m_columnSizesList.isEmpty()) { m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesList); } + + m_blockUpdates = false; } void DatabaseWidgetStateSync::restoreSearchView() @@ -79,15 +90,30 @@ void DatabaseWidgetStateSync::restoreSearchView() if (!m_columnSizesSearch.isEmpty()) { m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesSearch); } + + m_blockUpdates = false; +} + +void DatabaseWidgetStateSync::blockUpdates() +{ + m_blockUpdates = true; } void DatabaseWidgetStateSync::updateSplitterSizes() { + if (m_blockUpdates) { + return; + } + m_splitterSizes = m_activeDbWidget->splitterSizes(); } void DatabaseWidgetStateSync::updateColumnSizes() { + if (m_blockUpdates) { + return; + } + if (m_activeDbWidget->isGroupSelected()) { m_columnSizesList = m_activeDbWidget->entryHeaderViewSizes(); } diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h index 26911837c..f6a87cd9a 100644 --- a/src/gui/DatabaseWidgetStateSync.h +++ b/src/gui/DatabaseWidgetStateSync.h @@ -35,6 +35,7 @@ public Q_SLOTS: void restoreSearchView(); private Q_SLOTS: + void blockUpdates(); void updateSplitterSizes(); void updateColumnSizes(); @@ -44,6 +45,7 @@ private: DatabaseWidget* m_activeDbWidget; + bool m_blockUpdates; QList m_splitterSizes; QList m_columnSizesList; QList m_columnSizesSearch; From 910788c038178dc7a1ef49d08870b3ffdaaabafc Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 18:14:13 +0200 Subject: [PATCH 35/71] Mark some strings as untranslatable. --- src/gui/PasswordGeneratorWidget.ui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/PasswordGeneratorWidget.ui b/src/gui/PasswordGeneratorWidget.ui index 342f191a8..5c75ef9ea 100644 --- a/src/gui/PasswordGeneratorWidget.ui +++ b/src/gui/PasswordGeneratorWidget.ui @@ -100,7 +100,7 @@ Upper Case Letters - A-Z + A-Z true @@ -116,7 +116,7 @@ Lower Case Letters - a-z + a-z true @@ -132,7 +132,7 @@ Numbers - 0-9 + 0-9 true @@ -148,7 +148,7 @@ Special Characters - /*_& ... + /*_& ... true From 8cc1e6008eff3394afd365805c9aca6a2ab295b8 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 18:15:06 +0200 Subject: [PATCH 36/71] Use plurals in translations. --- src/gui/DatabaseWidget.cpp | 3 +-- src/gui/EditWidgetIcons.cpp | 3 +-- src/gui/entry/EditEntryWidget.cpp | 12 ++++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 0779297ef..cc9c5fd18 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -332,8 +332,7 @@ void DatabaseWidget::deleteEntries() if (selected.size() > 1) { QMessageBox::StandardButton result = MessageBox::question( this, tr("Move entries to recycle bin?"), - tr("Do you really want to move %1 entries to the recycle bin?") - .arg(selected.size()), + tr("Do you really want to move %n entry(s) to the recycle bin?", 0, selected.size()), QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::No) { return; diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 26314d360..9e8574535 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -194,8 +194,7 @@ void EditWidgetIcons::removeCustomIcon() } else { MessageBox::information(this, tr("Can't delete icon!"), - tr("Can't delete icon. Still used by %1 items.") - .arg(iconUsedCount)); + tr("Can't delete icon. Still used by %n item(s).", 0, iconUsedCount)); } } } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 4a866680b..465f5d401 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -783,13 +783,13 @@ QMenu* EditEntryWidget::createPresetsMenu() QMenu* expirePresetsMenu = new QMenu(this); expirePresetsMenu->addAction(tr("Tomorrow"))->setData(QVariant::fromValue(TimeDelta::fromDays(1))); expirePresetsMenu->addSeparator(); - expirePresetsMenu->addAction(tr("1 week"))->setData(QVariant::fromValue(TimeDelta::fromDays(7))); - expirePresetsMenu->addAction(tr("2 weeks"))->setData(QVariant::fromValue(TimeDelta::fromDays(14))); - expirePresetsMenu->addAction(tr("3 weeks"))->setData(QVariant::fromValue(TimeDelta::fromDays(21))); + 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->addSeparator(); - expirePresetsMenu->addAction(tr("1 month"))->setData(QVariant::fromValue(TimeDelta::fromMonths(1))); - expirePresetsMenu->addAction(tr("3 months"))->setData(QVariant::fromValue(TimeDelta::fromMonths(3))); - expirePresetsMenu->addAction(tr("6 months"))->setData(QVariant::fromValue(TimeDelta::fromMonths(6))); + 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->addSeparator(); expirePresetsMenu->addAction(tr("1 year"))->setData(QVariant::fromValue(TimeDelta::fromYears(1))); return expirePresetsMenu; From becd3a0019505eaa264de7b5b3fe27c46cb9c5a0 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 19:01:43 +0200 Subject: [PATCH 37/71] Increase the EntryView default column size a bit. --- src/gui/entry/EntryView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 6c43df3bf..cd2c6fbad 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -17,6 +17,7 @@ #include "EntryView.h" +#include #include #include "gui/SortFilterHideProxyModel.h" @@ -40,6 +41,7 @@ EntryView::EntryView(QWidget* parent) setDragEnabled(true); setSortingEnabled(true); setSelectionMode(QAbstractItemView::ExtendedSelection); + header()->setDefaultSectionSize(150); // QAbstractItemView::startDrag() uses this property as the default drag action setDefaultDropAction(Qt::MoveAction); From 28694ae687173cf5039fd864390699310a7b0243 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 18 May 2014 01:33:22 +0200 Subject: [PATCH 38/71] Add initial support for translations. --- .tx/config | 8 + share/CMakeLists.txt | 2 + share/translations/CMakeLists.txt | 26 + share/translations/keepassx_de.ts | 1175 ++++++++++++++++++++ share/translations/keepassx_en.ts | 1189 +++++++++++++++++++++ share/translations/keepassx_en_plurals.ts | 41 + share/translations/update.sh | 8 + src/CMakeLists.txt | 1 + src/config-keepassx.h.cmake | 1 + src/core/Config.cpp | 1 + src/core/Translator.cpp | 120 +++ src/core/Translator.h | 36 + src/gui/SettingsWidget.cpp | 12 + src/gui/SettingsWidgetGeneral.ui | 12 +- src/main.cpp | 3 + 15 files changed, 2634 insertions(+), 1 deletion(-) create mode 100644 .tx/config create mode 100644 share/translations/CMakeLists.txt create mode 100644 share/translations/keepassx_de.ts create mode 100644 share/translations/keepassx_en.ts create mode 100644 share/translations/keepassx_en_plurals.ts create mode 100755 share/translations/update.sh create mode 100644 src/core/Translator.cpp create mode 100644 src/core/Translator.h diff --git a/.tx/config b/.tx/config new file mode 100644 index 000000000..015acf4b0 --- /dev/null +++ b/.tx/config @@ -0,0 +1,8 @@ +[main] +host = https://www.transifex.com + +[keepassx.keepassx_ents] +source_file = share/translations/keepassx_en.ts +file_filter = share/translations/keepassx_.ts +source_lang = en +type = QT diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index 7069c6c4b..0e2b7fa99 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(translations) + file(GLOB DATABASE_ICONS icons/database/*.png) install(FILES ${DATABASE_ICONS} DESTINATION ${DATA_INSTALL_DIR}/icons/database) diff --git a/share/translations/CMakeLists.txt b/share/translations/CMakeLists.txt new file mode 100644 index 000000000..b1aa8785b --- /dev/null +++ b/share/translations/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2014 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 . + +file(GLOB TRANSLATION_FILES *.ts) +get_filename_component(TRANSLATION_EN_ABS keepassx_en.ts ABSOLUTE) +list(REMOVE_ITEM TRANSLATION_FILES keepassx_en.ts) +list(REMOVE_ITEM TRANSLATION_FILES ${TRANSLATION_EN_ABS}) +message(STATUS ${TRANSLATION_FILES}) + +qt4_add_translation(QM_FILES ${TRANSLATION_FILES}) + +install(FILES ${QM_FILES} DESTINATION ${DATA_INSTALL_DIR}/translations) +add_custom_target(translations DEPENDS ${QM_FILES}) +add_dependencies(${PROGNAME} translations) diff --git a/share/translations/keepassx_de.ts b/share/translations/keepassx_de.ts new file mode 100644 index 000000000..fe074ae69 --- /dev/null +++ b/share/translations/keepassx_de.ts @@ -0,0 +1,1175 @@ + + + AboutDialog + + About KeePassX + Über KeePassX + + + KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. + + + + + AutoType + + Auto-Type - KeePassX + + + + Couldn't find an entry that matches the window title. + + + + + AutoTypeAssociationsModel + + Window + + + + Sequence + + + + Default sequence + + + + + AutoTypeSelectDialog + + Auto-Type - KeePassX + + + + Select entry to Auto-Type: + + + + + ChangeMasterKeyWidget + + Password + + + + Enter password: + + + + Repeat password: + + + + Key file + + + + Browse + + + + Create + + + + Key files + + + + All files + + + + Create Key File... + + + + Error + + + + Unable to create Key File : + + + + Select a key file + + + + Question + + + + Do you really want to use an empty string as password? + + + + Different passwords supplied. + + + + + DatabaseOpenWidget + + Enter master key + + + + Key File: + + + + Password: + + + + Browse + + + + Error + + + + Unable to open the database. + + + + Can't open key file + + + + All files + + + + Key files + + + + Select key file + + + + + DatabaseSettingsWidget + + Database name: + + + + Database description: + + + + Transform rounds: + + + + Default username: + + + + Use recycle bin: + + + + MiB + + + + Benchmark + + + + Max. history items: + + + + Max. history size: + + + + + DatabaseTabWidget + + Root + + + + KeePass 2 Database + + + + All files + + + + Open database + + + + Warning + + + + File not found! + + + + Open KeePass 1 database + + + + KeePass 1 database + + + + All files (*) + + + + Close? + + + + "%1" is in edit mode. +Close anyway? + + + + Save changes? + + + + "%1" was modified. +Save changes? + + + + Error + + + + Writing the database failed. + + + + Save database as + + + + New database + + + + locked + + + + + DatabaseWidget + + 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 entries to recycle bin? + + + + Do you really want to move %n entry(s) to the recycle bin? + + + + Delete group? + + + + Do you really want to delete the group "%1" for good? + + + + Current group + + + + + EditEntryWidget + + Entry + + + + Advanced + + + + Icon + + + + Auto-Type + + + + Properties + + + + History + + + + Entry history + + + + Add entry + + + + Edit entry + + + + Error + + + + Different passwords supplied. + + + + New attribute + + + + Select file + + + + Unable to open file + + + + Save attachment + + + + Unable to save the attachment: + + + + + Tomorrow + + + + %n week(s) + + + + %n month(s) + + + + 1 year + + + + + EditEntryWidgetAdvanced + + Additional attributes + + + + Add + + + + Edit + + + + Remove + + + + Attachments + + + + Save + + + + + EditEntryWidgetAutoType + + Enable Auto-Type for this entry + + + + Inherit default Auto-Type sequence from the group + + + + Use custom Auto-Type sequence: + + + + + + + + + - + + + + Window title: + + + + Use default sequence + + + + Set custom sequence: + + + + + EditEntryWidgetHistory + + Show + + + + Restore + + + + Delete + + + + Delete all + + + + + EditEntryWidgetMain + + Title: + + + + Username: + + + + Password: + + + + Repeat: + + + + Gen. + + + + URL: + + + + Expires + + + + Presets + + + + Notes: + + + + + EditGroupWidget + + Group + + + + Icon + + + + Properties + + + + Add group + + + + Edit group + + + + Enable + + + + Disable + + + + Inherit from parent group (%1) + + + + + EditGroupWidgetMain + + Name + + + + Notes + + + + Expires + + + + Search + + + + Auto-type + + + + + EditWidgetIcons + + Use default icon + + + + Use custom icon + + + + Add custom icon + + + + Delete custom icon + + + + Images + + + + All files + + + + Select Image + + + + Can't delete icon! + + + + Can't delete icon. Still used by %n item(s). + + + + + EditWidgetProperties + + Created: + + + + Modified: + + + + Accessed: + + + + Uuid: + + + + + EntryAttributesModel + + Name + + + + + EntryHistoryModel + + Last modified + + + + Title + + + + Username + + + + URL + + + + + EntryModel + + Group + + + + Title + + + + Username + + + + URL + + + + + Group + + Recycle Bin + + + + + KeePass1OpenWidget + + Import KeePass1 database + + + + Error + + + + Unable to open the database. + + + + + KeePass1Reader + + Unable to read keyfile. + + + + Not a KeePass database. + + + + Unsupported encryption algorithm. + + + + Unsupported KeePass database version. + + + + Root + + + + + KeePass2Reader + + Not a KeePass database. + + + + Unsupported KeePass database version. + + + + Wrong key or database file is corrupt. + + + + + MainWindow + + Database + Datenbank + + + Recent databases + + + + Help + Hilfe + + + Entries + Einträge + + + Copy attribute to clipboard + + + + Groups + Gruppen + + + Extras + Extras + + + View + Ansicht + + + Quit + + + + About + + + + Open database + + + + Save database + + + + Close database + + + + New database + + + + Add new entry + + + + View/Edit entry + + + + Delete entry + + + + Add new group + + + + Edit group + + + + Delete group + + + + Save database as + + + + Change master key + + + + Database settings + + + + Import KeePass 1 database + + + + Clone entry + + + + Find + + + + Username + + + + Copy username to clipboard + + + + Password + + + + Copy password to clipboard + + + + Settings + + + + Perform Auto-Type + + + + Open URL + + + + Lock databases + + + + Title + + + + URL + + + + Notes + + + + Show toolbar + + + + read-only + + + + + PasswordGeneratorWidget + + Password: + + + + Length: + + + + Character Types + + + + Upper Case Letters + + + + Lower Case Letters + + + + Numbers + + + + Special Characters + + + + Exclude look-alike characters + + + + Ensure that the password contains characters from every group + + + + Accept + + + + + QCommandLineParser + + Displays version information. + + + + Displays this help. + + + + Unknown option '%1'. + + + + Unknown options: %1. + + + + Missing value after '%1'. + + + + Unexpected value after '%1'. + + + + [options] + + + + Usage: %1 + + + + Options: + + + + Arguments: + + + + + QSaveFile + + Existing file %1 is not writable + + + + Writing canceled by application + + + + Partial write. Partition full? + + + + + QtIOCompressor + + Internal zlib error when compressing: + + + + Error writing to underlying device: + + + + Error opening underlying device: + + + + Error reading data from underlying device: + + + + Internal zlib error when decompressing: + + + + + QtIOCompressor::open + + The gzip format not supported in this version of zlib. + + + + Internal zlib error: + + + + + SearchWidget + + Find: + + + + Case sensitive + + + + Current group + + + + Root group + + + + + SettingsWidget + + Application Settings + + + + General + + + + Security + + + + + SettingsWidgetGeneral + + Remember last databases + + + + Open previous databases on startup + + + + Mark as modified on expanded state changes + + + + Automatically save on exit + + + + Automatically save after every change + + + + Minimize when copying to clipboard + + + + Use group icon on entry creation + + + + Global Auto-Type shortcut + + + + Use entry title to match windows for global auto-type + + + + + SettingsWidgetSecurity + + Clear clipboard after + + + + sec + + + + Lock databases after inactivity of + + + + Show passwords in cleartext by default + + + + Always ask before performing auto-type + + + + + UnlockDatabaseWidget + + Unlock database + + + + Error + + + + Wrong key. + + + + + WelcomeWidget + + Welcome! + + + + + main + + KeePassX - cross-platform password manager + + + + filename of the password database to open (*.kdbx) + + + + path to a custom config file + + + + password of the database (DANGEROUS!) + + + + key file of the database + + + + \ No newline at end of file diff --git a/share/translations/keepassx_en.ts b/share/translations/keepassx_en.ts new file mode 100644 index 000000000..eccd5575b --- /dev/null +++ b/share/translations/keepassx_en.ts @@ -0,0 +1,1189 @@ + + + + + AboutDialog + + About KeePassX + + + + KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. + + + + + AutoType + + Auto-Type - KeePassX + + + + Couldn't find an entry that matches the window title. + + + + + AutoTypeAssociationsModel + + Window + + + + Sequence + + + + Default sequence + + + + + AutoTypeSelectDialog + + Auto-Type - KeePassX + + + + Select entry to Auto-Type: + + + + + ChangeMasterKeyWidget + + Password + + + + Enter password: + + + + Repeat password: + + + + Key file + + + + Browse + + + + Create + + + + Key files + + + + All files + + + + Create Key File... + + + + Error + + + + Unable to create Key File : + + + + Select a key file + + + + Question + + + + Do you really want to use an empty string as password? + + + + Different passwords supplied. + + + + + DatabaseOpenWidget + + Enter master key + + + + Key File: + + + + Password: + + + + Browse + + + + Error + + + + Unable to open the database. + + + + Can't open key file + + + + All files + + + + Key files + + + + Select key file + + + + + DatabaseSettingsWidget + + Database name: + + + + Database description: + + + + Transform rounds: + + + + Default username: + + + + Use recycle bin: + + + + MiB + + + + Benchmark + + + + Max. history items: + + + + Max. history size: + + + + + DatabaseTabWidget + + Root + + + + KeePass 2 Database + + + + All files + + + + Open database + + + + Warning + + + + File not found! + + + + Open KeePass 1 database + + + + KeePass 1 database + + + + All files (*) + + + + Close? + + + + "%1" is in edit mode. +Close anyway? + + + + Save changes? + + + + "%1" was modified. +Save changes? + + + + Error + + + + Writing the database failed. + + + + Save database as + + + + New database + + + + locked + + + + + DatabaseWidget + + 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 entries to recycle bin? + + + + Do you really want to move %n entry(s) to the recycle bin? + + + + + + + Delete group? + + + + Do you really want to delete the group "%1" for good? + + + + Current group + + + + + EditEntryWidget + + Entry + + + + Advanced + + + + Icon + + + + Auto-Type + + + + Properties + + + + History + + + + Entry history + + + + Add entry + + + + Edit entry + + + + Error + + + + Different passwords supplied. + + + + New attribute + + + + Select file + + + + Unable to open file + + + + Save attachment + + + + Unable to save the attachment: + + + + + Tomorrow + + + + %n week(s) + + + + + + + %n month(s) + + + + + + + 1 year + + + + + EditEntryWidgetAdvanced + + Additional attributes + + + + Add + + + + Edit + + + + Remove + + + + Attachments + + + + Save + + + + + EditEntryWidgetAutoType + + Enable Auto-Type for this entry + + + + Inherit default Auto-Type sequence from the group + + + + Use custom Auto-Type sequence: + + + + + + + + + - + + + + Window title: + + + + Use default sequence + + + + Set custom sequence: + + + + + EditEntryWidgetHistory + + Show + + + + Restore + + + + Delete + + + + Delete all + + + + + EditEntryWidgetMain + + Title: + + + + Username: + + + + Password: + + + + Repeat: + + + + Gen. + + + + URL: + + + + Expires + + + + Presets + + + + Notes: + + + + + EditGroupWidget + + Group + + + + Icon + + + + Properties + + + + Add group + + + + Edit group + + + + Enable + + + + Disable + + + + Inherit from parent group (%1) + + + + + EditGroupWidgetMain + + Name + + + + Notes + + + + Expires + + + + Search + + + + Auto-type + + + + + EditWidgetIcons + + Use default icon + + + + Use custom icon + + + + Add custom icon + + + + Delete custom icon + + + + Images + + + + All files + + + + Select Image + + + + Can't delete icon! + + + + Can't delete icon. Still used by %n item(s). + + + + + + + + EditWidgetProperties + + Created: + + + + Modified: + + + + Accessed: + + + + Uuid: + + + + + EntryAttributesModel + + Name + + + + + EntryHistoryModel + + Last modified + + + + Title + + + + Username + + + + URL + + + + + EntryModel + + Group + + + + Title + + + + Username + + + + URL + + + + + Group + + Recycle Bin + + + + + KeePass1OpenWidget + + Import KeePass1 database + + + + Error + + + + Unable to open the database. + + + + + KeePass1Reader + + Unable to read keyfile. + + + + Not a KeePass database. + + + + Unsupported encryption algorithm. + + + + Unsupported KeePass database version. + + + + Root + + + + + KeePass2Reader + + Not a KeePass database. + + + + Unsupported KeePass database version. + + + + Wrong key or database file is corrupt. + + + + + MainWindow + + Database + + + + Recent databases + + + + Help + + + + Entries + + + + Copy attribute to clipboard + + + + Groups + + + + Extras + + + + View + + + + Quit + + + + About + + + + Open database + + + + Save database + + + + Close database + + + + New database + + + + Add new entry + + + + View/Edit entry + + + + Delete entry + + + + Add new group + + + + Edit group + + + + Delete group + + + + Save database as + + + + Change master key + + + + Database settings + + + + Import KeePass 1 database + + + + Clone entry + + + + Find + + + + Username + + + + Copy username to clipboard + + + + Password + + + + Copy password to clipboard + + + + Settings + + + + Perform Auto-Type + + + + Open URL + + + + Lock databases + + + + Title + + + + URL + + + + Notes + + + + Show toolbar + + + + read-only + + + + + PasswordGeneratorWidget + + Password: + + + + Length: + + + + Character Types + + + + Upper Case Letters + + + + Lower Case Letters + + + + Numbers + + + + Special Characters + + + + Exclude look-alike characters + + + + Ensure that the password contains characters from every group + + + + Accept + + + + + QCommandLineParser + + Displays version information. + + + + Displays this help. + + + + Unknown option '%1'. + + + + Unknown options: %1. + + + + Missing value after '%1'. + + + + Unexpected value after '%1'. + + + + [options] + + + + Usage: %1 + + + + Options: + + + + Arguments: + + + + + QSaveFile + + Existing file %1 is not writable + + + + Writing canceled by application + + + + Partial write. Partition full? + + + + + QtIOCompressor + + Internal zlib error when compressing: + + + + Error writing to underlying device: + + + + Error opening underlying device: + + + + Error reading data from underlying device: + + + + Internal zlib error when decompressing: + + + + + QtIOCompressor::open + + The gzip format not supported in this version of zlib. + + + + Internal zlib error: + + + + + SearchWidget + + Find: + + + + Case sensitive + + + + Current group + + + + Root group + + + + + SettingsWidget + + Application Settings + + + + General + + + + Security + + + + + SettingsWidgetGeneral + + Remember last databases + + + + Open previous databases on startup + + + + Mark as modified on expanded state changes + + + + Automatically save on exit + + + + Automatically save after every change + + + + Minimize when copying to clipboard + + + + Use group icon on entry creation + + + + Global Auto-Type shortcut + + + + Use entry title to match windows for global auto-type + + + + + SettingsWidgetSecurity + + Clear clipboard after + + + + sec + + + + Lock databases after inactivity of + + + + Show passwords in cleartext by default + + + + Always ask before performing auto-type + + + + + UnlockDatabaseWidget + + Unlock database + + + + Error + + + + Wrong key. + + + + + WelcomeWidget + + Welcome! + + + + + main + + KeePassX - cross-platform password manager + + + + filename of the password database to open (*.kdbx) + + + + path to a custom config file + + + + password of the database (DANGEROUS!) + + + + key file of the database + + + + diff --git a/share/translations/keepassx_en_plurals.ts b/share/translations/keepassx_en_plurals.ts new file mode 100644 index 000000000..006f6f6e4 --- /dev/null +++ b/share/translations/keepassx_en_plurals.ts @@ -0,0 +1,41 @@ + + + + + DatabaseWidget + + 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? + + + + + EditEntryWidget + + %n week(s) + + %n week + %n weeks + + + + %n month(s) + + %n month + %n months + + + + + EditWidgetIcons + + Can't delete icon. Still used by %n item(s). + + Can't delete icon. Still used by %n item. + Can't delete icon. Still used by %n items. + + + + diff --git a/share/translations/update.sh b/share/translations/update.sh new file mode 100755 index 000000000..6828dc820 --- /dev/null +++ b/share/translations/update.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +BASEDIR=$(dirname $0) + +cd $BASEDIR/../.. + +lupdate -no-ui-lines -disable-heuristic similartext -locations none -no-obsolete src -ts share/translations/keepassx_en.ts +lupdate -no-ui-lines -disable-heuristic similartext -locations none -pluralonly src -ts share/translations/keepassx_en_plurals.ts diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3de2422a4..7ffc168b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,6 +50,7 @@ set(keepassx_SOURCES core/TimeInfo.cpp core/ToDbExporter.cpp core/Tools.cpp + core/Translator.cpp core/Uuid.cpp core/qcommandlineoption.cpp core/qcommandlineparser.cpp diff --git a/src/config-keepassx.h.cmake b/src/config-keepassx.h.cmake index 9a3f49516..805700a9f 100644 --- a/src/config-keepassx.h.cmake +++ b/src/config-keepassx.h.cmake @@ -6,6 +6,7 @@ #define KEEPASSX_VERSION "${KEEPASSX_VERSION}" #define KEEPASSX_SOURCE_DIR "${CMAKE_SOURCE_DIR}" +#define KEEPASSX_BINARY_DIR "${CMAKE_BINARY_DIR}" #define KEEPASSX_PLUGIN_DIR "${PLUGIN_INSTALL_DIR}" diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 76f0e6f8e..8a455e27a 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -102,6 +102,7 @@ void Config::init(const QString& fileName) m_defaults.insert("security/lockdatabaseidlesec", 10); m_defaults.insert("security/passwordscleartext", false); m_defaults.insert("security/autotypeask", true); + m_defaults.insert("GUI/Language", "system"); } Config* Config::instance() diff --git a/src/core/Translator.cpp b/src/core/Translator.cpp new file mode 100644 index 000000000..bc4d2b629 --- /dev/null +++ b/src/core/Translator.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2014 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 "Translator.h" + +#include +#include +#include +#include +#include +#include + +#include "config-keepassx.h" +#include "core/Config.h" +#include "core/FilePath.h" + +void Translator::installTranslator() +{ + QString language = config()->get("GUI/Language").toString(); + if (language == "system" || language.isEmpty()) { + language = QLocale::system().name(); + } + + if (!installTranslator(language)) { + // English fallback still needs translations for plurals + if (!installTranslator("en_plurals")) { + qWarning("Couldn't load translations."); + } + } + + installQtTranslator(language); + + availableLanguages(); +} + +QList > Translator::availableLanguages() +{ + QStringList paths; +#ifdef QT_DEBUG + paths.append(QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR)); +#endif + paths.append(filePath()->dataPath("translations")); + + QList > languages; + languages.append(QPair("system", "System default")); + + QRegExp regExp("keepassx_([a-zA-Z_]+)\\.qm", Qt::CaseInsensitive, QRegExp::RegExp2); + Q_FOREACH (const QString& path, paths) { + Q_FOREACH (const QString& filename, QDir(path).entryList()) { + if (regExp.exactMatch(filename)) { + QString langcode = regExp.cap(1); + if (langcode == "en_plurals") { + langcode = "en"; + } + + languages.append(QPair(langcode, + QLocale::languageToString(QLocale(langcode).language()))); + } + } + } + + return languages; +} + +bool Translator::installTranslator(const QString& language) +{ + QStringList paths; +#ifdef QT_DEBUG + paths.append(QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR)); +#endif + paths.append(filePath()->dataPath("translations")); + + Q_FOREACH (const QString& path, paths) { + if (installTranslator(language, path)) { + return true; + } + } + + return false; +} + +bool Translator::installTranslator(const QString& language, const QString& path) +{ + QTranslator* translator = new QTranslator(qApp); + if (translator->load(QString("keepassx_").append(language), path)) { + QCoreApplication::installTranslator(translator); + return true; + } + else { + delete translator; + return false; + } +} + +bool Translator::installQtTranslator(const QString& language) +{ + QTranslator* qtTranslator = new QTranslator(qApp); + if (qtTranslator->load(QString("%1/qt_%2").arg(QLibraryInfo::location(QLibraryInfo::TranslationsPath), language))) { + QCoreApplication::installTranslator(qtTranslator); + return true; + } + else { + delete qtTranslator; + return false; + } +} diff --git a/src/core/Translator.h b/src/core/Translator.h new file mode 100644 index 000000000..4bc4fca3e --- /dev/null +++ b/src/core/Translator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 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_TRANSLATOR_H +#define KEEPASSX_TRANSLATOR_H + +#include +#include + +class Translator +{ +public: + static void installTranslator(); + static QList > availableLanguages(); + +private: + static bool installTranslator(const QString& language); + static bool installTranslator(const QString& language, const QString& path); + static bool installQtTranslator(const QString& language); +}; + +#endif // KEEPASSX_TRANSLATOR_H diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 2aea2636e..374e52d97 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -21,6 +21,7 @@ #include "autotype/AutoType.h" #include "core/Config.h" +#include "core/Translator.h" SettingsWidget::SettingsWidget(QWidget* parent) : EditWidget(parent) @@ -69,6 +70,15 @@ void SettingsWidget::loadSettings() m_generalUi->useGroupIconOnEntryCreationCheckBox->setChecked(config()->get("UseGroupIconOnEntryCreation").toBool()); m_generalUi->autoTypeEntryTitleMatchCheckBox->setChecked(config()->get("AutoTypeEntryTitleMatch").toBool()); + QList > languages = Translator::availableLanguages(); + for (int i = 0; i < languages.size(); i++) { + m_generalUi->languageComboBox->addItem(languages[i].second, languages[i].first); + } + int defaultIndex = m_generalUi->languageComboBox->findData(config()->get("GUI/Language")); + if (defaultIndex > 0) { + m_generalUi->languageComboBox->setCurrentIndex(defaultIndex); + } + if (autoType()->isAvailable()) { m_globalAutoTypeKey = static_cast(config()->get("GlobalAutoTypeKey").toInt()); m_globalAutoTypeModifiers = static_cast(config()->get("GlobalAutoTypeModifiers").toInt()); @@ -105,6 +115,8 @@ void SettingsWidget::saveSettings() m_generalUi->useGroupIconOnEntryCreationCheckBox->isChecked()); config()->set("AutoTypeEntryTitleMatch", m_generalUi->autoTypeEntryTitleMatchCheckBox->isChecked()); + int currentLangIndex = m_generalUi->languageComboBox->currentIndex(); + config()->set("GUI/Language", m_generalUi->languageComboBox->itemData(currentLangIndex).toString()); if (autoType()->isAvailable()) { config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key()); config()->set("GlobalAutoTypeModifiers", diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui index 3d89c68e8..f3dc079e2 100644 --- a/src/gui/SettingsWidgetGeneral.ui +++ b/src/gui/SettingsWidgetGeneral.ui @@ -7,7 +7,7 @@ 0 0 456 - 230 + 288 @@ -86,6 +86,16 @@ + + + + Language + + + + + + diff --git a/src/main.cpp b/src/main.cpp index abe7ceb4e..d5e64b92c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include "core/Config.h" #include "core/qcommandlineparser.h" #include "core/Tools.h" +#include "core/Translator.h" #include "crypto/Crypto.h" #include "gui/Application.h" #include "gui/MainWindow.h" @@ -66,6 +67,8 @@ int main(int argc, char** argv) Config::createConfigFromFile(parser.value(configOption)); } + Translator::installTranslator(); + #ifdef Q_OS_MAC // Don't show menu icons on OSX QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); From 5a31e055cfd98d476721055b9d03a36be0b929da Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 18 May 2014 12:08:31 +0200 Subject: [PATCH 39/71] Show the window title when no entry matches for auto-type. Closes #188 --- src/autotype/AutoType.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 92aae5cd7..aac0c0cf8 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -190,8 +190,10 @@ void AutoType::performGlobalAutoType(const QList& dbList) if (entryList.isEmpty()) { m_inAutoType = false; - MessageBox::information(Q_NULLPTR, tr("Auto-Type - KeePassX"), - tr("Couldn't find an entry that matches the window title.")); + QString message = tr("Couldn't find an entry that matches the window title:"); + message.append("\n\n"); + message.append(windowTitle); + MessageBox::information(Q_NULLPTR, tr("Auto-Type - KeePassX"), message); } else if ((entryList.size() == 1) && !config()->get("security/autotypeask").toBool()) { m_inAutoType = false; From 916ab99d6249f17271338ae711cb226ba1d0f4b5 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Mon, 26 May 2014 18:24:43 +0200 Subject: [PATCH 40/71] Skip TestQSaveFile::transactionalWriteErrorRenaming as user root. You can't deny root access to a file. Closes #201 --- tests/TestQSaveFile.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/TestQSaveFile.cpp b/tests/TestQSaveFile.cpp index 6be714efd..443db5299 100644 --- a/tests/TestQSaveFile.cpp +++ b/tests/TestQSaveFile.cpp @@ -156,6 +156,9 @@ void TestQSaveFile::transactionalWriteCanceled() void TestQSaveFile::transactionalWriteErrorRenaming() { #ifndef Q_OS_WIN + if (::geteuid() == 0) { + QSKIP("not valid running this test as root", SkipAll); + } const QString dir = tmpDir(); QVERIFY(!dir.isEmpty()); const QString targetFile = dir + QString::fromLatin1("/outfile"); From 7137990a214ba49c23d6ba038a95c2ced9b826bb Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Mon, 26 May 2014 18:41:48 +0200 Subject: [PATCH 41/71] Clear clipboard only if copied text is still present. Closes #198 --- src/gui/Clipboard.cpp | 11 +++++++++-- src/gui/Clipboard.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gui/Clipboard.cpp b/src/gui/Clipboard.cpp index eb77d2b3d..7d8f71faf 100644 --- a/src/gui/Clipboard.cpp +++ b/src/gui/Clipboard.cpp @@ -51,6 +51,7 @@ void Clipboard::setText(const QString& text) if (config()->get("security/clearclipboard").toBool()) { int timeout = config()->get("security/clearclipboardtimeout").toInt(); if (timeout > 0) { + m_lastCopied = text; m_timer->start(timeout * 1000); } } @@ -65,8 +66,12 @@ void Clipboard::clearClipboard() return; } - clipboard->clear(QClipboard::Clipboard); - if (clipboard->supportsSelection()) { + if (clipboard->text(QClipboard::Clipboard) == m_lastCopied) { + clipboard->clear(QClipboard::Clipboard); + } + + if (clipboard->supportsSelection() + && (clipboard->text(QClipboard::Selection) == m_lastCopied)) { clipboard->clear(QClipboard::Selection); } @@ -74,6 +79,8 @@ void Clipboard::clearClipboard() QDBusMessage message = QDBusMessage::createMethodCall("org.kde.klipper", "/klipper", "", "clearClipboardHistory"); QDBusConnection::sessionBus().send(message); #endif + + m_lastCopied.clear(); } void Clipboard::cleanup() diff --git a/src/gui/Clipboard.h b/src/gui/Clipboard.h index bc2a19d31..8b6ea69f5 100644 --- a/src/gui/Clipboard.h +++ b/src/gui/Clipboard.h @@ -43,6 +43,7 @@ private: static Clipboard* m_instance; QTimer* m_timer; + QString m_lastCopied; }; inline Clipboard* clipboard() { From b417bf91873ae760031cdc821d4e411701da71f5 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Fri, 13 Jun 2014 21:33:36 +0200 Subject: [PATCH 42/71] Enable C++11 by default. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 35642ebcf..280fb7766 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ include(CheckCXXSourceCompiles) option(WITH_TESTS "Enable building of unit tests" ON) option(WITH_GUI_TESTS "Enable building of GUI tests" OFF) option(WITH_LTO "Enable Link Time Optimization (LTO)" OFF) -option(WITH_CXX11 "Build with the C++ 11 standard" OFF) +option(WITH_CXX11 "Build with the C++ 11 standard" ON) set(KEEPASSX_VERSION "2.0 alpha 6") set(KEEPASSX_VERSION_NUM "1.9.85") From 0d6117bf4c0ac1e604c5b2fa0dcf8d5d47c46a58 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 15 Jun 2014 11:17:40 +0200 Subject: [PATCH 43/71] Do some basic self-checks when initializing the crypto backend. --- src/crypto/Crypto.cpp | 105 ++++++++++++++++++++++++++++- src/crypto/Crypto.h | 11 ++- src/main.cpp | 10 ++- tests/TestAutoType.cpp | 2 +- tests/TestCryptoHash.cpp | 4 +- tests/TestDeletedObjects.cpp | 2 +- tests/TestEntry.cpp | 2 +- tests/TestEntryModel.cpp | 2 +- tests/TestExporter.cpp | 2 +- tests/TestGroup.cpp | 2 +- tests/TestGroupModel.cpp | 2 +- tests/TestHashedBlockStream.cpp | 2 +- tests/TestKeePass1Reader.cpp | 2 +- tests/TestKeePass2RandomStream.cpp | 2 +- tests/TestKeePass2Reader.cpp | 2 +- tests/TestKeePass2Writer.cpp | 2 +- tests/TestKeePass2XmlReader.cpp | 2 +- tests/TestKeys.cpp | 2 +- tests/TestModified.cpp | 2 +- tests/TestSymmetricCipher.cpp | 2 +- tests/gui/TestGui.cpp | 2 +- tests/gui/TestGuiPixmaps.cpp | 2 +- utils/kdbx-extract.cpp | 4 +- 23 files changed, 143 insertions(+), 27 deletions(-) diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index 1e28002b6..13c3c20ee 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -21,7 +21,12 @@ #include +#include "config-keepassx.h" +#include "crypto/CryptoHash.h" +#include "crypto/SymmetricCipher.h" + bool Crypto::m_initalized(false); +QString Crypto::m_errorStr; #if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600) static int gcry_qt_mutex_init(void** p_sys) @@ -64,11 +69,11 @@ Crypto::Crypto() { } -void Crypto::init() +bool Crypto::init() { if (m_initalized) { qWarning("Crypto::init: already initalized"); - return; + return true; } // libgcrypt >= 1.6 doesn't allow custom thread callbacks anymore. @@ -78,7 +83,19 @@ void Crypto::init() gcry_check_version(0); gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + if (!checkAlgorithms()) { + return false; + } + + // has to be set before testing Crypto classes m_initalized = true; + + if (!selfTest()) { + m_initalized = false; + return false; + } + + return true; } bool Crypto::initalized() @@ -86,7 +103,89 @@ bool Crypto::initalized() return m_initalized; } -bool Crypto::selfTest() +QString Crypto::errorString() +{ + return m_errorStr; +} + +bool Crypto::backendSelfTest() { return (gcry_control(GCRYCTL_SELFTEST) == 0); } + +bool Crypto::checkAlgorithms() +{ + if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) { + m_errorStr = "GCRY_CIPHER_AES256 not found."; + qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); + return false; + } + if (gcry_cipher_algo_info(GCRY_CIPHER_TWOFISH, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) { + m_errorStr = "GCRY_CIPHER_TWOFISH not found."; + qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); + return false; + } +#ifdef GCRYPT_HAS_SALSA20 + if (gcry_cipher_algo_info(GCRY_CIPHER_SALSA20, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) { + m_errorStr = "GCRY_CIPHER_SALSA20 not found."; + qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); + return false; + } +#endif + if (gcry_md_test_algo(GCRY_MD_SHA256) != 0) { + m_errorStr = "GCRY_MD_SHA256 not found."; + qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); + return false; + } + + return true; +} + +bool Crypto::selfTest() +{ + QByteArray sha256Test = CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + CryptoHash::Sha256); + + if (sha256Test != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) { + m_errorStr = "SHA-256 mismatch."; + qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); + return false; + } + + 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")); + + SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt, key, iv); + if (aes256Encrypt.process(plainText) != cipherText) { + m_errorStr = "AES-256 encryption mismatch."; + qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); + return false; + } + + SymmetricCipher aes256Descrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, key, iv); + if (aes256Descrypt.process(cipherText) != plainText) { + m_errorStr = "AES-256 decryption mismatch."; + qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); + return false; + } + + QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112"); + QByteArray salsa20iv = QByteArray::fromHex("0000000000000000"); + QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000"); + QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F"); + + SymmetricCipher salsa20Stream(SymmetricCipher::Salsa20, SymmetricCipher::Stream, + SymmetricCipher::Encrypt, salsa20Key, salsa20iv); + + if (salsa20Stream.process(salsa20Plain) != salsa20Cipher) { + m_errorStr = "Salsa20 stream cipher mismatch."; + qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); + return false; + } + + return true; +} diff --git a/src/crypto/Crypto.h b/src/crypto/Crypto.h index 63f117771..9926f14b2 100644 --- a/src/crypto/Crypto.h +++ b/src/crypto/Crypto.h @@ -18,18 +18,25 @@ #ifndef KEEPASSX_CRYPTO_H #define KEEPASSX_CRYPTO_H +#include + #include "core/Global.h" class Crypto { public: - static void init(); + static bool init(); static bool initalized(); - static bool selfTest(); + static bool backendSelfTest(); + static QString errorString(); private: Crypto(); + static bool checkAlgorithms(); + static bool selfTest(); + static bool m_initalized; + static QString m_errorStr; }; #endif // KEEPASSX_CRYPTO_H diff --git a/src/main.cpp b/src/main.cpp index d5e64b92c..b9659e45d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,7 @@ #include "crypto/Crypto.h" #include "gui/Application.h" #include "gui/MainWindow.h" +#include "gui/MessageBox.h" int main(int argc, char** argv) { @@ -38,7 +39,14 @@ int main(int argc, char** argv) // don't set organizationName as that changes the return value of // QDesktopServices::storageLocation(QDesktopServices::DataLocation) - Crypto::init(); + if (!Crypto::init()) { + QString error = QCoreApplication::translate("Main", + "Fatal error while testing the cryptographic functions."); + error.append("\n"); + error.append(Crypto::errorString()); + MessageBox::critical(Q_NULLPTR, QCoreApplication::translate("Main", "KeePassX - Error"), error); + return 1; + } QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassX - cross-platform password manager")); diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index 4f4350e71..818f57c96 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -35,7 +35,7 @@ QTEST_GUILESS_MAIN(TestAutoType) void TestAutoType::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); Config::createTempFileInstance(); AutoType::createTestInstance(); config()->set("AutoTypeEntryTitleMatch", false); diff --git a/tests/TestCryptoHash.cpp b/tests/TestCryptoHash.cpp index d189d456c..eb26ca83f 100644 --- a/tests/TestCryptoHash.cpp +++ b/tests/TestCryptoHash.cpp @@ -27,13 +27,13 @@ QTEST_GUILESS_MAIN(TestCryptoHash) void TestCryptoHash::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestCryptoHash::test() { // TODO: move somewhere else - QVERIFY(Crypto::selfTest()); + QVERIFY(Crypto::backendSelfTest()); CryptoHash cryptoHash1(CryptoHash::Sha256); QCOMPARE(cryptoHash1.result(), diff --git a/tests/TestDeletedObjects.cpp b/tests/TestDeletedObjects.cpp index 5967e58ee..277dbcb6b 100644 --- a/tests/TestDeletedObjects.cpp +++ b/tests/TestDeletedObjects.cpp @@ -30,7 +30,7 @@ QTEST_GUILESS_MAIN(TestDeletedObjects) void TestDeletedObjects::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestDeletedObjects::createAndDelete(Database* db, int delObjectsSize) diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp index 481250a20..477e83b86 100644 --- a/tests/TestEntry.cpp +++ b/tests/TestEntry.cpp @@ -27,7 +27,7 @@ QTEST_GUILESS_MAIN(TestEntry) void TestEntry::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestEntry::testHistoryItemDeletion() diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp index 5347a3c52..7ba886bc2 100644 --- a/tests/TestEntryModel.cpp +++ b/tests/TestEntryModel.cpp @@ -38,7 +38,7 @@ QTEST_GUILESS_MAIN(TestEntryModel) void TestEntryModel::initTestCase() { qRegisterMetaType("QModelIndex"); - Crypto::init(); + QVERIFY(Crypto::init()); } void TestEntryModel::test() diff --git a/tests/TestExporter.cpp b/tests/TestExporter.cpp index 16b4b23cb..d703e02f1 100644 --- a/tests/TestExporter.cpp +++ b/tests/TestExporter.cpp @@ -30,7 +30,7 @@ QTEST_GUILESS_MAIN(TestExporter) void TestExporter::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestExporter::testToDbExporter() diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 32398f01f..507cf155d 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -33,7 +33,7 @@ void TestGroup::initTestCase() { qRegisterMetaType("Entry*"); qRegisterMetaType("Group*"); - Crypto::init(); + QVERIFY(Crypto::init()); } void TestGroup::testParenting() diff --git a/tests/TestGroupModel.cpp b/tests/TestGroupModel.cpp index fe7018e84..32a4b8e9b 100644 --- a/tests/TestGroupModel.cpp +++ b/tests/TestGroupModel.cpp @@ -32,7 +32,7 @@ QTEST_GUILESS_MAIN(TestGroupModel) void TestGroupModel::initTestCase() { qRegisterMetaType("QModelIndex"); - Crypto::init(); + QVERIFY(Crypto::init()); } void TestGroupModel::test() diff --git a/tests/TestHashedBlockStream.cpp b/tests/TestHashedBlockStream.cpp index b062908f7..09179fef2 100644 --- a/tests/TestHashedBlockStream.cpp +++ b/tests/TestHashedBlockStream.cpp @@ -28,7 +28,7 @@ QTEST_GUILESS_MAIN(TestHashedBlockStream) void TestHashedBlockStream::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestHashedBlockStream::testWriteRead() diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index 423087c91..249a3657b 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -37,7 +37,7 @@ QTEST_GUILESS_MAIN(TestKeePass1Reader) void TestKeePass1Reader::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/basic.kdb"); diff --git a/tests/TestKeePass2RandomStream.cpp b/tests/TestKeePass2RandomStream.cpp index 328decb30..7963e9af8 100644 --- a/tests/TestKeePass2RandomStream.cpp +++ b/tests/TestKeePass2RandomStream.cpp @@ -30,7 +30,7 @@ QTEST_GUILESS_MAIN(TestKeePass2RandomStream) void TestKeePass2RandomStream::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestKeePass2RandomStream::test() diff --git a/tests/TestKeePass2Reader.cpp b/tests/TestKeePass2Reader.cpp index 3dd87c4f4..d6cb70c78 100644 --- a/tests/TestKeePass2Reader.cpp +++ b/tests/TestKeePass2Reader.cpp @@ -32,7 +32,7 @@ QTEST_GUILESS_MAIN(TestKeePass2Reader) void TestKeePass2Reader::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestKeePass2Reader::testNonAscii() diff --git a/tests/TestKeePass2Writer.cpp b/tests/TestKeePass2Writer.cpp index f00b38e86..cf4ab1c56 100644 --- a/tests/TestKeePass2Writer.cpp +++ b/tests/TestKeePass2Writer.cpp @@ -33,7 +33,7 @@ QTEST_GUILESS_MAIN(TestKeePass2Writer) void TestKeePass2Writer::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); CompositeKey key; key.addKey(PasswordKey("test")); diff --git a/tests/TestKeePass2XmlReader.cpp b/tests/TestKeePass2XmlReader.cpp index d9935804d..8e87d6744 100644 --- a/tests/TestKeePass2XmlReader.cpp +++ b/tests/TestKeePass2XmlReader.cpp @@ -68,7 +68,7 @@ QDateTime TestKeePass2XmlReader::genDT(int year, int month, int day, int hour, i void TestKeePass2XmlReader::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); KeePass2XmlReader reader; QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"); diff --git a/tests/TestKeys.cpp b/tests/TestKeys.cpp index d5cba4abc..d6758d655 100644 --- a/tests/TestKeys.cpp +++ b/tests/TestKeys.cpp @@ -35,7 +35,7 @@ QTEST_GUILESS_MAIN(TestKeys) void TestKeys::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestKeys::testComposite() diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp index 85a6bf23e..e275e837b 100644 --- a/tests/TestModified.cpp +++ b/tests/TestModified.cpp @@ -31,7 +31,7 @@ QTEST_GUILESS_MAIN(TestModified) void TestModified::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestModified::testSignals() diff --git a/tests/TestSymmetricCipher.cpp b/tests/TestSymmetricCipher.cpp index 9f05db1bd..6d4e94f6f 100644 --- a/tests/TestSymmetricCipher.cpp +++ b/tests/TestSymmetricCipher.cpp @@ -29,7 +29,7 @@ QTEST_GUILESS_MAIN(TestSymmetricCipher) void TestSymmetricCipher::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestSymmetricCipher::testAes256CbcEncryption() diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 51dc39e5d..326c3497b 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -51,7 +51,7 @@ void TestGui::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); Config::createTempFileInstance(); m_mainWindow = new MainWindow(); m_tabWidget = m_mainWindow->findChild("tabWidget"); diff --git a/tests/gui/TestGuiPixmaps.cpp b/tests/gui/TestGuiPixmaps.cpp index 401f68bc3..87e3f2488 100644 --- a/tests/gui/TestGuiPixmaps.cpp +++ b/tests/gui/TestGuiPixmaps.cpp @@ -29,7 +29,7 @@ void TestGuiPixmaps::initTestCase() { - Crypto::init(); + QVERIFY(Crypto::init()); } void TestGuiPixmaps::testDatabaseIcons() diff --git a/utils/kdbx-extract.cpp b/utils/kdbx-extract.cpp index beee71dc3..f5d2a19a6 100644 --- a/utils/kdbx-extract.cpp +++ b/utils/kdbx-extract.cpp @@ -38,7 +38,9 @@ int main(int argc, char **argv) return 1; } - Crypto::init(); + if (!Crypto::init()) { + qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); + } CompositeKey key; if (QFile::exists(app.arguments().at(1))) { From 2e76385cae7b1ff3eac6aef2be4199d2702ec7fa Mon Sep 17 00:00:00 2001 From: David Kolossa Date: Mon, 16 Jun 2014 13:08:39 +0200 Subject: [PATCH 44/71] Fixed typo in INSTALL --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 028ccff2f..bde991bbd 100644 --- a/INSTALL +++ b/INSTALL @@ -2,7 +2,7 @@ Building: ========= mkdir build cd build -cmake .. [CMAKE PARAMETERS] +cmake [CMAKE PARAMETERS] .. make [-jX] Common cmake parameters: From 3a0648cf2546644a5abd560a7b5b2c1034fe162a Mon Sep 17 00:00:00 2001 From: David Kolossa Date: Mon, 16 Jun 2014 15:40:28 +0200 Subject: [PATCH 45/71] ! binds stronger than == This should just avoid useless copying if more than 1 attribute is selected (and the option to copy attributes is unavailable). This also fixes a clang warning. --- src/gui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 8c72bbff9..101121b0d 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -227,7 +227,7 @@ void MainWindow::updateCopyAttributesMenu() return; } - if (!dbWidget->numberOfSelectedEntries() == 1) { + if (dbWidget->numberOfSelectedEntries() != 1) { return; } From 72b59d541a67e664467862e881cdd3f047727b73 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 5 Sep 2014 10:04:02 +0200 Subject: [PATCH 46/71] Clear available languages when loading settings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to Victor Häggqvist for spotting this. --- src/gui/SettingsWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 374e52d97..929db377f 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -70,6 +70,7 @@ void SettingsWidget::loadSettings() m_generalUi->useGroupIconOnEntryCreationCheckBox->setChecked(config()->get("UseGroupIconOnEntryCreation").toBool()); m_generalUi->autoTypeEntryTitleMatchCheckBox->setChecked(config()->get("AutoTypeEntryTitleMatch").toBool()); + 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); From f1aa6aca2682db1515c684174689b3753057de2c Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 9 Oct 2014 21:36:08 +0200 Subject: [PATCH 47/71] Fix copy custom attributes menu. --- src/gui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 101121b0d..48baa4cf8 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -232,7 +232,7 @@ void MainWindow::updateCopyAttributesMenu() } QList actions = m_ui->menuEntryCopyAttribute->actions(); - for (int i = m_countDefaultAttributes + 1; i < actions.size(); i++) { + for (int i = m_countDefaultAttributes; i < actions.size(); i++) { delete actions[i]; } From 870d7355cabdd4ad4445bf4655965893db826f81 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 2 Nov 2014 10:00:16 +0100 Subject: [PATCH 48/71] Fix reading window title from _NET_WM_NAME. XGetWindowProperty() returns 0 on success. Closes #236 --- src/autotype/x11/AutoTypeX11.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autotype/x11/AutoTypeX11.cpp b/src/autotype/x11/AutoTypeX11.cpp index 06a1e3291..6a175c727 100644 --- a/src/autotype/x11/AutoTypeX11.cpp +++ b/src/autotype/x11/AutoTypeX11.cpp @@ -212,7 +212,7 @@ QString AutoTypePlatformX11::windowTitle(Window window, bool useBlacklist) int retVal = XGetWindowProperty(m_dpy, window, m_atomNetWmName, 0, 1000, false, m_atomUtf8String, &type, &format, &nitems, &after, &data); - if (retVal != 0 && data) { + if (retVal == 0 && data) { title = QString::fromUtf8(reinterpret_cast(data)); } else { From 4cdb9a645d8af65ea37e3af41e668540f8a1b30c Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 2 Nov 2014 10:15:44 +0100 Subject: [PATCH 49/71] Add an option to display a tray icon. Also implement "Minimize to tray" functionality. --- src/core/Config.cpp | 2 + src/gui/MainWindow.cpp | 73 ++++++++++++++++++++++++++++++++ src/gui/MainWindow.h | 7 +++ src/gui/SettingsWidget.cpp | 9 ++++ src/gui/SettingsWidgetGeneral.ui | 19 ++++++++- src/main.cpp | 2 + 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index d47541eda..03b5129ef 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -104,6 +104,8 @@ void Config::init(const QString& fileName) m_defaults.insert("security/passwordscleartext", false); m_defaults.insert("security/autotypeask", true); m_defaults.insert("GUI/Language", "system"); + m_defaults.insert("GUI/ShowTrayIcon", false); + m_defaults.insert("GUI/MinimizeToTray", false); } Config* Config::instance() diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 48baa4cf8..dd77989c3 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -33,6 +33,7 @@ const QString MainWindow::BaseWindowTitle = "KeePassX"; MainWindow::MainWindow() : m_ui(new Ui::MainWindow()) + , m_trayIcon(Q_NULLPTR) { m_ui->setupUi(this); @@ -201,6 +202,8 @@ MainWindow::MainWindow() m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()), SLOT(toggleSearch())); + + updateTrayIcon(); } MainWindow::~MainWindow() @@ -429,12 +432,26 @@ void MainWindow::closeEvent(QCloseEvent* event) saveWindowInformation(); event->accept(); + QApplication::quit(); } else { event->ignore(); } } +void MainWindow::changeEvent(QEvent *event) +{ + if ((event->type() == QEvent::WindowStateChange) && isMinimized() + && isTrayIconEnabled() && config()->get("GUI/MinimizeToTray").toBool()) + { + event->ignore(); + hide(); + } + else { + QMainWindow::changeEvent(event); + } +} + void MainWindow::saveWindowInformation() { config()->set("GUI/MainWindowGeometry", saveGeometry()); @@ -467,6 +484,35 @@ bool MainWindow::saveLastDatabases() return accept; } +void MainWindow::updateTrayIcon() +{ + if (isTrayIconEnabled()) { + if (!m_trayIcon) { + m_trayIcon = new QSystemTrayIcon(filePath()->applicationIcon(), this); + + QMenu* menu = new QMenu(this); + + QAction* actionToggle = new QAction(tr("Toggle window"), menu); + menu->addAction(actionToggle); + + menu->addAction(m_ui->actionQuit); + + connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + SLOT(trayIconTriggered(QSystemTrayIcon::ActivationReason))); + connect(actionToggle, SIGNAL(triggered()), SLOT(toggleWindow())); + + m_trayIcon->setContextMenu(menu); + m_trayIcon->show(); + } + } + else { + if (m_trayIcon) { + delete m_trayIcon; + m_trayIcon = Q_NULLPTR; + } + } +} + void MainWindow::showEntryContextMenu(const QPoint& globalPos) { m_ui->menuEntries->popup(globalPos); @@ -511,4 +557,31 @@ void MainWindow::applySettingsChanges() else { m_inactivityTimer->deactivate(); } + + updateTrayIcon(); +} + +void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::Trigger) { + toggleWindow(); + } +} + +void MainWindow::toggleWindow() +{ + if (QApplication::activeWindow() == this) { + hide(); + } + else { + show(); + raise(); + activateWindow(); + } +} + +bool MainWindow::isTrayIconEnabled() const +{ + return config()->get("GUI/ShowTrayIcon").toBool() + && QSystemTrayIcon::isSystemTrayAvailable(); } diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index e904426ae..b966703d1 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -20,6 +20,7 @@ #include #include +#include #include "core/SignalMultiplexer.h" #include "gui/DatabaseWidget.h" @@ -44,6 +45,7 @@ public Q_SLOTS: protected: void closeEvent(QCloseEvent* event) Q_DECL_OVERRIDE; + void changeEvent(QEvent* event) Q_DECL_OVERRIDE; private Q_SLOTS: void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::None); @@ -61,6 +63,8 @@ private Q_SLOTS: void saveToolbarState(bool value); void rememberOpenDatabases(const QString& filePath); void applySettingsChanges(); + void trayIconTriggered(QSystemTrayIcon::ActivationReason reason); + void toggleWindow(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); @@ -69,6 +73,8 @@ private: void saveWindowInformation(); bool saveLastDatabases(); + void updateTrayIcon(); + bool isTrayIconEnabled() const; const QScopedPointer m_ui; SignalMultiplexer m_actionMultiplexer; @@ -78,6 +84,7 @@ private: QStringList m_openDatabases; InactivityTimer* m_inactivityTimer; int m_countDefaultAttributes; + QSystemTrayIcon* m_trayIcon; Q_DISABLE_COPY(MainWindow) }; diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 929db377f..a7863eac8 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -47,6 +47,8 @@ SettingsWidget::SettingsWidget(QWidget* parent) connect(m_generalUi->autoSaveAfterEveryChangeCheckBox, SIGNAL(toggled(bool)), this, SLOT(enableAutoSaveOnExit(bool))); + connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), + m_generalUi->systrayMinimizeToTrayCheckBox, SLOT(setEnabled(bool))); connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)), m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool))); @@ -80,6 +82,9 @@ void SettingsWidget::loadSettings() m_generalUi->languageComboBox->setCurrentIndex(defaultIndex); } + m_generalUi->systrayShowCheckBox->setChecked(config()->get("GUI/ShowTrayIcon").toBool()); + m_generalUi->systrayMinimizeToTrayCheckBox->setChecked(config()->get("GUI/MinimizeToTray").toBool()); + if (autoType()->isAvailable()) { m_globalAutoTypeKey = static_cast(config()->get("GlobalAutoTypeKey").toInt()); m_globalAutoTypeModifiers = static_cast(config()->get("GlobalAutoTypeModifiers").toInt()); @@ -118,6 +123,10 @@ void SettingsWidget::saveSettings() m_generalUi->autoTypeEntryTitleMatchCheckBox->isChecked()); int currentLangIndex = m_generalUi->languageComboBox->currentIndex(); config()->set("GUI/Language", m_generalUi->languageComboBox->itemData(currentLangIndex).toString()); + + config()->set("GUI/ShowTrayIcon", m_generalUi->systrayShowCheckBox->isChecked()); + config()->set("GUI/MinimizeToTray", m_generalUi->systrayMinimizeToTrayCheckBox->isChecked()); + if (autoType()->isAvailable()) { config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key()); config()->set("GlobalAutoTypeModifiers", diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui index f3dc079e2..cbad7e5eb 100644 --- a/src/gui/SettingsWidgetGeneral.ui +++ b/src/gui/SettingsWidgetGeneral.ui @@ -7,7 +7,7 @@ 0 0 456 - 288 + 340 @@ -96,6 +96,23 @@ + + + + Show a system tray icon + + + + + + + false + + + Hide window to system tray when minimized + + + diff --git a/src/main.cpp b/src/main.cpp index b9659e45d..2bdef5bde 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,8 @@ int main(int argc, char** argv) // don't set organizationName as that changes the return value of // QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QApplication::setQuitOnLastWindowClosed(false); + if (!Crypto::init()) { QString error = QCoreApplication::translate("Main", "Fatal error while testing the cryptographic functions."); From 87468b648bbefc373990cfd9e1fc3cb6214e90c6 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 2 Nov 2014 11:18:51 +0100 Subject: [PATCH 50/71] Use specific monospace fonts as fallback on Mac OS X. Qt (4.8.6) doesn't seem to be able to resolve the generic monospace font family. Closes #214 --- src/gui/PasswordComboBox.cpp | 8 +++++++- src/gui/PasswordEdit.cpp | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/gui/PasswordComboBox.cpp b/src/gui/PasswordComboBox.cpp index af8e9949d..f11311a9a 100644 --- a/src/gui/PasswordComboBox.cpp +++ b/src/gui/PasswordComboBox.cpp @@ -45,7 +45,13 @@ void PasswordComboBox::setEcho(bool echo) // add fake item to show visual indication that a popup is available addItem(""); - setStyleSheet("QComboBox { font-family: monospace; }"); +#ifdef Q_OS_MAC + // Qt on Mac OS doesn't seem to know the generic monospace family (tested with 4.8.6) + setStyleSheet("QComboBox { font-family: monospace,Menlo,Monaco; }"); +#else + setStyleSheet("QComboBox { font-family: monospace,Courier; }"); +#endif + } else { // clear items so the combobox indicates that no popup menu is available diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 853269614..b68eef68b 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -56,7 +56,12 @@ void PasswordEdit::updateStylesheet() QString stylesheet("QLineEdit { "); if (echoMode() == QLineEdit::Normal) { +#ifdef Q_OS_MAC + // Qt on Mac OS doesn't seem to know the generic monospace family (tested with 4.8.6) + stylesheet.append("font-family: monospace,Menlo,Monaco; "); +#else stylesheet.append("font-family: monospace; "); +#endif } if (m_basePasswordEdit && !passwordsEqual()) { From 315df0b8a8ba7e76609cecbfe440805ef5de950f Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 2 Nov 2014 11:46:51 +0100 Subject: [PATCH 51/71] Coding style fixes. --- src/autotype/x11/AutoTypeX11.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/autotype/x11/AutoTypeX11.cpp b/src/autotype/x11/AutoTypeX11.cpp index 6a175c727..843fbfa40 100644 --- a/src/autotype/x11/AutoTypeX11.cpp +++ b/src/autotype/x11/AutoTypeX11.cpp @@ -209,23 +209,26 @@ QString AutoTypePlatformX11::windowTitle(Window window, bool useBlacklist) unsigned long after; unsigned char* data = Q_NULLPTR; + // the window manager spec says we should read _NET_WM_NAME first, then fall back to WM_NAME + int retVal = XGetWindowProperty(m_dpy, window, m_atomNetWmName, 0, 1000, false, m_atomUtf8String, &type, &format, &nitems, &after, &data); - if (retVal == 0 && data) { + if ((retVal == 0) && data) { title = QString::fromUtf8(reinterpret_cast(data)); } else { XTextProperty textProp; retVal = XGetTextProperty(m_dpy, window, &textProp, m_atomWmName); - if (retVal != 0 && textProp.value) { + if ((retVal != 0) && textProp.value) { char** textList = Q_NULLPTR; int count; if (textProp.encoding == m_atomUtf8String) { title = QString::fromUtf8(reinterpret_cast(textProp.value)); } - else if (XmbTextPropertyToTextList(m_dpy, &textProp, &textList, &count) == 0 && textList && count > 0) { + else if ((XmbTextPropertyToTextList(m_dpy, &textProp, &textList, &count) == 0) + && textList && (count > 0)) { title = QString::fromLocal8Bit(textList[0]); } else if (textProp.encoding == m_atomString) { From 1c365b8417f842d853430a828399da9cd333c0ff Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 2 Nov 2014 12:55:46 +0100 Subject: [PATCH 52/71] Add Travis CI config. --- .travis.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..751dbc28c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +os: + - linux + - osx +compiler: + - gcc + - clang +language: cpp +install: + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libqt4-dev libgcrypt11-dev zlib1g-dev libxtst-dev; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install cmake qt libgcrypt; fi +before_script: mkdir build && pushd build +script: + - cmake -DWITH_GUI_TESTS=ON .. + - make + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui"; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui"; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then make test; fi From 6ecb8690f2de6bb2c01dceecb87c6abffad6ead2 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 2 Nov 2014 15:42:39 +0100 Subject: [PATCH 53/71] Update translations. --- share/translations/keepassx_de.ts | 516 +++++------ share/translations/keepassx_en.ts | 29 +- share/translations/keepassx_it.ts | 1179 ++++++++++++++++++++++++++ share/translations/keepassx_nl_NL.ts | 1178 +++++++++++++++++++++++++ share/translations/keepassx_sv.ts | 1178 +++++++++++++++++++++++++ 5 files changed, 3822 insertions(+), 258 deletions(-) create mode 100644 share/translations/keepassx_it.ts create mode 100644 share/translations/keepassx_nl_NL.ts create mode 100644 share/translations/keepassx_sv.ts diff --git a/share/translations/keepassx_de.ts b/share/translations/keepassx_de.ts index fe074ae69..5433c3ced 100644 --- a/share/translations/keepassx_de.ts +++ b/share/translations/keepassx_de.ts @@ -7,734 +7,736 @@ KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. - + KeePassX ist unter der GNU General Public License (GPL) version 2 (version 3) veröffentlicht. AutoType Auto-Type - KeePassX - + Auto-Type - KeePassX Couldn't find an entry that matches the window title. - + Konnte dem Fenstertitel keinen passenden Eintrag zuordnen. AutoTypeAssociationsModel Window - + Fenster Sequence - + Reihenfolge Default sequence - + Standardreihenfolge AutoTypeSelectDialog Auto-Type - KeePassX - + Auto-Type - KeePassX Select entry to Auto-Type: - + Wählen Sie einen Eintrag für Auto-Type: ChangeMasterKeyWidget Password - + Passwort Enter password: - + Passwort eingeben: Repeat password: - + Passwort wiederholen: Key file - + Schlüsseldatei Browse - + Durchsuchen Create - + Erstellen Key files - + Schlüsseldateien All files - + Alle Dateien Create Key File... - + Erzeuge eine Schlüsseldatei... Error - + Fehler Unable to create Key File : - + Erzeugen der Schlüsseldatei nicht möglich: Select a key file - + Schlüsseldatei auswählen Question - + Frage Do you really want to use an empty string as password? - + Wollen Sie wirklich eine leere Zeichenkette als Passwort verwenden? Different passwords supplied. - + Unterschiedliche Passwörter eingegeben. DatabaseOpenWidget Enter master key - + Hauptschlüssel eingeben Key File: - + Schlüsseldatei: Password: - + Passwort: Browse - + Durchsuchen Error - + Fehler Unable to open the database. - + Öffnen der Datenbank nicht möglich. Can't open key file - + Schlüsseldatein kann nicht geöffnet werden All files - + Alle Dateien Key files - + Schlüsseldateien Select key file - + Schlüsseldatei auswählen DatabaseSettingsWidget Database name: - + Datenbankname: Database description: - + Datenbankbeschreibung: Transform rounds: - + Verschlüsselungsdurchläufe: Default username: - + Standardbenutzername: Use recycle bin: - + Verwende Papierkorb: MiB - + MiB Benchmark - + Benchmark Max. history items: - + Max Einträge im Verlauf: Max. history size: - + Max. Verlaufsgröße: DatabaseTabWidget Root - + Root KeePass 2 Database - + KeePass 2 Datenbank All files - + Alle Dateien Open database - + Datenbank öffnen Warning - + Warnung File not found! - + Datei nicht gefunden! Open KeePass 1 database - + KeePass 1 Datenbank öffnen KeePass 1 database - + KeePass 1 Datenbank All files (*) - + Alle Dateien (*) Close? - + Schließen? "%1" is in edit mode. Close anyway? - + "%1" wird bearbeitet. +Trotzdem schließen? Save changes? - + Änderungen speichern? "%1" was modified. Save changes? - + "%1" wurde geändert. +Änderungen speichern? Error - + Fehler Writing the database failed. - + Schreiben der Datenbank fehlgeschlagen. Save database as - + Datenbank speichern unter New database - + Neue Datenbank locked - + gesperrt DatabaseWidget Change master key - + Hauptschlüssel ändern Delete entry? - + Eintrag löschen? Do you really want to delete the entry "%1" for good? - + Wollen Sie den Eintrag "%1" wirklich löschen? Delete entries? - + Einträge löschen? Do you really want to delete %1 entries for good? - + Wollen Sie die Einträge "%1" wirklich löschen? Move entries to recycle bin? - + Einträge in den Papierkorb verschieben? 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? Delete group? - + Gruppe löschen? Do you really want to delete the group "%1" for good? - + Wollen Sie die Gruppe "%1" wirklich löschen? Current group - + Aktuelle Gruppe EditEntryWidget Entry - + Eintrag Advanced - + Fortgeschritten Icon - + Symbol Auto-Type - + Auto-Type Properties - + Eigenschaften History - + Verlauf Entry history - + Eintragsverlauf Add entry - + Eintrag hinzufügen Edit entry - + Eintrag bearbeiten Error - + Fehler Different passwords supplied. - + Unterschiedliche Passwörter eingegeben. New attribute - + Neue Eigenschaft Select file - + Datei wählen Unable to open file - + Öffnen der Datei nicht möglich Save attachment - + Anhang speichern Unable to save the attachment: - + Speichern des Anhangs nicht möglich: Tomorrow - + Morgen %n week(s) - + %n Woche%n Wochen %n month(s) - + %n Monat%n Monaten 1 year - + 1 Jahr EditEntryWidgetAdvanced Additional attributes - + Zusätzliche Eigenschaften Add - + Hinzufügen Edit - + Bearbeiten Remove - + Entfernen Attachments - + Anhänge Save - + Speichern EditEntryWidgetAutoType Enable Auto-Type for this entry - + Auto-Type für diesen Eintrag aktivieren Inherit default Auto-Type sequence from the group - + Standard-Auto-Type-Sequenz von der Gruppe erben Use custom Auto-Type sequence: - + Benutzerdefinierte Auto-Type-Sequenz benutzen: + - + + - - + - Window title: - + Fenstertitel: Use default sequence - + Standardsequenz benutzen Set custom sequence: - + Benutzerdefinierte Sequenz verwenden: EditEntryWidgetHistory Show - + Anzeigen Restore - + Wiederherstellen Delete - + Löschen Delete all - + Alle löschen EditEntryWidgetMain Title: - + Titel: Username: - + Benutzername: Password: - + Passwort: Repeat: - + Wiederholen: Gen. - + Gen. URL: - + URL: Expires - + Erlischt Presets - + Vorgaben Notes: - + Notizen: EditGroupWidget Group - + Gruppe Icon - + Symbol Properties - + Eigenschaften Add group - + Gruppe hinzufügen Edit group - + Gruppe bearbeiten Enable - + Aktivieren Disable - + Deaktivieren Inherit from parent group (%1) - + Von der übergeordneten Gruppe (%1) erben EditGroupWidgetMain Name - + Name Notes - + Notizen Expires - + Erlischt Search - + Suche Auto-type - + Auto-type EditWidgetIcons Use default icon - + Standardsymbol verwenden Use custom icon - + Benutzerdefiniertes Symbol verwenden Add custom icon - + Benutzerdefiniertes Symbol hinzufügen Delete custom icon - + Benutzerdefiniertes Symbol löschen Images - + Bilder All files - + Alle Dateien Select Image - + Bild auswählen Can't delete icon! - + Symbol kann nicht gelöscht werden! Can't delete icon. Still used by %n item(s). - + Symbol kann nicht gelöscht werden. Es wird von %n Eintrag verwendet.Symbol kann nicht gelöscht werden. Es wird von %n Einträgen verwendet. EditWidgetProperties Created: - + Erstellt: Modified: - + Bearbeitet: Accessed: - + Zugegriffen: Uuid: - + Uuid: EntryAttributesModel Name - + Name EntryHistoryModel Last modified - + Zuletzt geändert Title - + Titel Username - + Benutzername URL - + URL EntryModel Group - + Gruppe Title - + Titel Username - + Benutzername URL - + URL Group Recycle Bin - + Papierkorb KeePass1OpenWidget Import KeePass1 database - + KeePass 1 Datenbank importieren Error - + Fehler Unable to open the database. - + Öffnen der Datenbank nicht möglich. KeePass1Reader Unable to read keyfile. - + Lesen der Schlüsseldatei nicht möglich. Not a KeePass database. - + Keine KeePass-Datenbank. Unsupported encryption algorithm. - + Nicht unterstützter Verschlüsselungsalgorithmus. Unsupported KeePass database version. - + Nicht unterstützte KeePass-Datenbankversion. Root - + Root KeePass2Reader Not a KeePass database. - + Keine KeePass-Datenbank. Unsupported KeePass database version. - + Nicht unterstützte KeePass-Datenbankversion. Wrong key or database file is corrupt. - + Falscher Schlüssel oder die Datei ist beschädigt. @@ -745,7 +747,7 @@ Save changes? Recent databases - + Aktuelle Datenbanken Help @@ -757,7 +759,7 @@ Save changes? Copy attribute to clipboard - + Eingenschaft in die Zwischenablage kopieren Groups @@ -773,403 +775,403 @@ Save changes? Quit - + Beenden About - + Über Open database - + Datenbank öffnen Save database - + Datenbank speichern Close database - + Datenbank schließen New database - + Neue Datenbank Add new entry - + Neuen Eintrag hinzufügen View/Edit entry - + Eintrag anzeigen/bearbeiten Delete entry - + Eintrag löschen Add new group - + Neue Gruppe hinzufügen Edit group - + Gruppe bearbeiten Delete group - + Gruppe löschen Save database as - + Datenbank speichern als Change master key - + Hauptschlüssel ändern Database settings - + Datenbankeinstellungen Import KeePass 1 database - + KeePass 1 Datenbank importieren Clone entry - + Eintrag klonen Find - + Suchen Username - + Benutzername Copy username to clipboard - + Benutzername in die Zwischenablage kopieren Password - + Passwort Copy password to clipboard - + Passwort in die Zwischenablage kopieren Settings - + Einstellungen Perform Auto-Type - + Auto-Type ausführen Open URL - + URL öffnen Lock databases - + Datenbank sperren Title - + Titel URL - + URL Notes - + Notizen Show toolbar - + Symbolleiste anzeigen read-only - + Nur Lesezugriff PasswordGeneratorWidget Password: - + Passwort: Length: - + Länge: Character Types - + Zeichenarten Upper Case Letters - + Großbuchstaben Lower Case Letters - + Kleinbuchstaben Numbers - + Zahlen Special Characters - + Sonderzeichen Exclude look-alike characters - + Gleich aussehende Zeichen ausschließen Ensure that the password contains characters from every group - + Sicher stellen, dass das Passwort Zeichen aller Gruppen enthält Accept - + Akzeptieren QCommandLineParser Displays version information. - + Versionsinformationen anzeigen. Displays this help. - + Zeigt diese Hilfe an. Unknown option '%1'. - + Unbekannte Option '%1'. Unknown options: %1. - + Unbekannte Optionen: '%1'. Missing value after '%1'. - + Fehlender Wert nach '%1'. Unexpected value after '%1'. - + Unerwarteter Wert nach '%1'. [options] - + [Optionen] Usage: %1 - + Verwendung: %1 Options: - + Optionen: Arguments: - + Argumente: QSaveFile Existing file %1 is not writable - + Bestehende Datei(en) %1 ist nicht schreibbar Writing canceled by application - + Schreiben von der Applikation abgebrochen Partial write. Partition full? - + Unvollständiger Schreibvorgang. Partition voll? QtIOCompressor Internal zlib error when compressing: - + Interner Fehler in zlib beim komprimieren: Error writing to underlying device: - + Fehler beim Schreiben auf das zugrunde liegende Gerät: Error opening underlying device: - + Fehler beim Öffnen des zugrunde liegenden Gerätes: Error reading data from underlying device: - + Fehler beim Lesen von Daten auf dem zugrunde liegenden Gerät: Internal zlib error when decompressing: - + Interner Fehler in zlib beim dekomprimieren: QtIOCompressor::open The gzip format not supported in this version of zlib. - + Das gzip-Format wird von dieser zlib Version nicht unterstützt. Internal zlib error: - + Interner Fehler in zlib: SearchWidget Find: - + Suchen nach: Case sensitive - + Groß-/Kleinschreibung unterscheiden Current group - + Aktuelle Gruppe Root group - + Root-Gruppe SettingsWidget Application Settings - + Anwendungseinstellungen General - + Allgemein Security - + Sicherheit SettingsWidgetGeneral Remember last databases - + Letzte Datenbank merken Open previous databases on startup - + Letzte Datenbank beim Starten öffnen Mark as modified on expanded state changes - + Als erweiterte Zustandsänderungen makieren Automatically save on exit - + Automatisch speichern beim Schließen Automatically save after every change - + Automatisch nach jeder Änderung speichern Minimize when copying to clipboard - + Minimieren beim Kopieren in die Zwischenablage Use group icon on entry creation - + Gruppensymbol für das Erstellen neuer Einträge verwenden Global Auto-Type shortcut - + Globale Tastenkombination für Auto-Type Use entry title to match windows for global auto-type - + Verwende den Eintragstitel für entsprechende Fenster für den globale Auto-Typ SettingsWidgetSecurity Clear clipboard after - + Zwischenablage leeren nach sec - + sek Lock databases after inactivity of - + Datenbank sperren nach einer Inaktivität von Show passwords in cleartext by default - + Passwort standartmäßig in Klartext anzeigen Always ask before performing auto-type - + Immer vor einem Auto-type fragen UnlockDatabaseWidget Unlock database - + Datenbank entsperren Error - + Fehler Wrong key. - + Falscher Schlüssel. WelcomeWidget Welcome! - + Willkommen! main KeePassX - cross-platform password manager - + KeePassX - plattformübergreifender Passwortmanager filename of the password database to open (*.kdbx) - + Dateiname für die zu öffnende Passwortdatenbank (*.kdbx) path to a custom config file - + Pfad zu einer benutzerdefinierten Konfigurationsdatei password of the database (DANGEROUS!) - + Passwort der Datenbank (GEFÄHRLICH!) key file of the database - + Schlüsseldatei der Datenbank \ No newline at end of file diff --git a/share/translations/keepassx_en.ts b/share/translations/keepassx_en.ts index eccd5575b..a7b5b109d 100644 --- a/share/translations/keepassx_en.ts +++ b/share/translations/keepassx_en.ts @@ -19,7 +19,7 @@ - Couldn't find an entry that matches the window title. + Couldn't find an entry that matches the window title: @@ -751,6 +751,17 @@ Save changes? + + Main + + Fatal error while testing the cryptographic functions. + + + + KeePassX - Error + + + MainWindow @@ -909,6 +920,10 @@ Save changes? read-only + + Toggle window + + PasswordGeneratorWidget @@ -1117,6 +1132,18 @@ Save changes? Use entry title to match windows for global auto-type + + Language + + + + Show a system tray icon + + + + Hide window to system tray when minimized + + SettingsWidgetSecurity diff --git a/share/translations/keepassx_it.ts b/share/translations/keepassx_it.ts new file mode 100644 index 000000000..4f91b75ab --- /dev/null +++ b/share/translations/keepassx_it.ts @@ -0,0 +1,1179 @@ + + + AboutDialog + + About KeePassX + A proposito di KeePassX + + + KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. + KeePassX è distribuito sotto i termini della licenza +GNU General Public License (GPL) versione 2 o, a tua scelta, della versione 3. + + + + AutoType + + Auto-Type - KeePassX + Auto-Type - KeePassX + + + Couldn't find an entry that matches the window title. + Impossibile trovare una voce che corrisponda al titolo della finestra + + + + AutoTypeAssociationsModel + + Window + Finestra + + + Sequence + Sequenza + + + Default sequence + Sequenza predefinita + + + + AutoTypeSelectDialog + + Auto-Type - KeePassX + Auto-Type - KeePassX + + + Select entry to Auto-Type: + Selezionare una voce per Auto-Type: + + + + ChangeMasterKeyWidget + + Password + Password + + + Enter password: + Inserire password: + + + Repeat password: + Ripetere password: + + + Key file + File chiave + + + Browse + Sfogliare + + + Create + Creare + + + Key files + File chiave + + + All files + Tutti i file + + + Create Key File... + Creare file chiave... + + + Error + Errore + + + Unable to create Key File : + Impossibile creare file chiave: + + + Select a key file + Selezionare file chiave + + + Question + Domanda + + + Do you really want to use an empty string as password? + Vuoi veramente usare una stringa vuota come password? + + + Different passwords supplied. + Sono state fornite password differenti. + + + + DatabaseOpenWidget + + Enter master key + Inserire password + + + Key File: + File Chiave: + + + Password: + Password: + + + Browse + Sfogliare + + + Error + Errore + + + Unable to open the database. + Impossibile aprire il database. + + + Can't open key file + Impossibile aprire il file chiave + + + All files + Tutti i file + + + Key files + File chiave + + + Select key file + Selezionare file chiave + + + + DatabaseSettingsWidget + + Database name: + Nome database: + + + Database description: + Descrizione database: + + + Transform rounds: + Round di trasformazione: + + + Default username: + Nome utente predefinito: + + + Use recycle bin: + Utilizzare cestino: + + + MiB + MiB + + + Benchmark + Benchmark + + + Max. history items: + Max. oggetti nella cronologia: + + + Max. history size: + Max. grandezza della cronologia: + + + + DatabaseTabWidget + + Root + Root + + + KeePass 2 Database + Database KeePass 2 + + + All files + Tutti i file + + + Open database + Aprire database + + + Warning + Avviso + + + File not found! + File non trovato! + + + Open KeePass 1 database + Aprire database KeePass 1 + + + KeePass 1 database + Database KeePass 1 + + + All files (*) + Tutti i file (*) + + + Close? + Chiudere? + + + "%1" is in edit mode. +Close anyway? + "%1" è in modalità modifica. +Chiudere comunque? + + + Save changes? + Salvare modifiche? + + + "%1" was modified. +Save changes? + "%1" è stata modificata. +Salvare le modifiche? + + + Error + Errore + + + Writing the database failed. + Scrittura del database fallita. + + + Save database as + Salvare database come + + + New database + Nuovo database + + + locked + bloccato + + + + DatabaseWidget + + Change master key + Cambiare password principale + + + Delete entry? + Eliminare voce? + + + Do you really want to delete the entry "%1" for good? + Vuoi veramente eliminare la voce "%1"? + + + Delete entries? + Eliminare voci? + + + Do you really want to delete %1 entries for good? + Vuoi veramente eliminare %1 voci? + + + Move entries to recycle bin? + Muovere le voci nel cestino? + + + Do you really want to move %n entry(s) to the recycle bin? + Vuoi veramente spostare %n voce(i) nel cestino?Vuoi veramente spostare %n voce(i) nel cestino? + + + Delete group? + Eliminare gruppo? + + + Do you really want to delete the group "%1" for good? + Vuoi veramente eliminare il gruppo "%1"? + + + Current group + Gruppo corrente + + + + EditEntryWidget + + Entry + Voce + + + Advanced + Avanzate + + + Icon + Icona + + + Auto-Type + Auto-Type + + + Properties + Proprietà + + + History + Cronologia + + + Entry history + Cronologia voce + + + Add entry + Aggiungere voce + + + Edit entry + Modificare voce + + + Error + Errore + + + Different passwords supplied. + Sono state immesse password differenti. + + + New attribute + Nuovo attributo + + + Select file + Selezionare file + + + Unable to open file + Impossibile aprire il file + + + Save attachment + Salvare l'allegato + + + Unable to save the attachment: + + Impossibile salvare l'allegato + + + + Tomorrow + Domani + + + %n week(s) + %n settimana(e)%n settimana(e) + + + %n month(s) + %n mese(i)%n mese(i) + + + 1 year + 1 anno + + + + EditEntryWidgetAdvanced + + Additional attributes + Attributi addizionali + + + Add + Aggiungere + + + Edit + Modificare + + + Remove + Rimuovere + + + Attachments + Allegati + + + Save + Salvare + + + + EditEntryWidgetAutoType + + Enable Auto-Type for this entry + Abilitare Auto-Type per questa voce + + + Inherit default Auto-Type sequence from the group + Ereditare la sequenza predefinita di Auto-Type dal gruppo + + + Use custom Auto-Type sequence: + Usare sequenza personalizzata di Auto-Type: + + + + + + + + + - + - + + + Window title: + Titolo finestra: + + + Use default sequence + Usare sequenza predefinita + + + Set custom sequence: + Impostare sequenza personalizzata: + + + + EditEntryWidgetHistory + + Show + Mostrare + + + Restore + Ripristinare + + + Delete + Eliminare + + + Delete all + Eliminare tutti + + + + EditEntryWidgetMain + + Title: + Titolo: + + + Username: + Nome utente: + + + Password: + Password: + + + Repeat: + Ripetere: + + + Gen. + Gen. + + + URL: + URL: + + + Expires + Scade: + + + Presets + Programmare + + + Notes: + Note: + + + + EditGroupWidget + + Group + Gruppo + + + Icon + Icona + + + Properties + Proprietà + + + Add group + Aggiungere gruppo + + + Edit group + Modificare gruppo + + + Enable + Abilitare + + + Disable + Disabilitare + + + Inherit from parent group (%1) + Ereditare dal gruppo genitore (%1) + + + + EditGroupWidgetMain + + Name + Nome + + + Notes + Note + + + Expires + Scade + + + Search + Cercare + + + Auto-type + Auto-Type + + + + EditWidgetIcons + + Use default icon + Usare icona predefinita + + + Use custom icon + Usare icona personalizzata + + + Add custom icon + Aggiungere icona personalizzata + + + Delete custom icon + Rimuovere icona personalizzata + + + Images + Immagini + + + All files + Tutti i file + + + Select Image + Selezionare Immagine + + + Can't delete icon! + Impossibile eliminare icona! + + + Can't delete icon. Still used by %n item(s). + Impossibile eliminare l'icona in quanto è in uso da %n voce(i).Impossibile eliminare l'icona in quanto è in uso da %n voce(i). + + + + EditWidgetProperties + + Created: + Creato: + + + Modified: + Modificato: + + + Accessed: + Accesso: + + + Uuid: + Uuid: + + + + EntryAttributesModel + + Name + Nome + + + + EntryHistoryModel + + Last modified + Ultima modifica + + + Title + Titolo + + + Username + Nome utente + + + URL + URL + + + + EntryModel + + Group + Gruppo + + + Title + Titolo + + + Username + Nome Utente + + + URL + URL + + + + Group + + Recycle Bin + Cestino (Gruppo) + + + + KeePass1OpenWidget + + Import KeePass1 database + Importare database KeePass1 + + + Error + Errore + + + Unable to open the database. + Impossibile aprire il database. + + + + KeePass1Reader + + Unable to read keyfile. + Impossibile leggere il file chiave. + + + Not a KeePass database. + Non è un database KeePass. + + + Unsupported encryption algorithm. + Algoritmo di cifratura non supportato. + + + Unsupported KeePass database version. + Versione database non supportata + + + Root + Root (KeePass1Reader) + + + + KeePass2Reader + + Not a KeePass database. + Non è un database KeePass. + + + Unsupported KeePass database version. + Versione database non supportata + + + Wrong key or database file is corrupt. + Password errata o database corrotto. + + + + MainWindow + + Database + Database + + + Recent databases + Database recenti + + + Help + Aiuto + + + Entries + Voci + + + Copy attribute to clipboard + Copiare attributi negli appunti + + + Groups + Gruppi + + + Extras + Extra + + + View + Visualizzare + + + Quit + Uscire + + + About + A Proposito + + + Open database + Aprire database + + + Save database + Salvare database + + + Close database + Chiudere database + + + New database + Nuovo database + + + Add new entry + Aggiungere nuova voce + + + View/Edit entry + Visualizzare/Modificare voce + + + Delete entry + Eliminare voce + + + Add new group + Aggiungere nuovo gruppo + + + Edit group + Modificare gruppo + + + Delete group + Eliminare gruppo + + + Save database as + Salvare database come + + + Change master key + Cambiare password principale + + + Database settings + Impostazioni database + + + Import KeePass 1 database + Importare database KeePass 1 + + + Clone entry + Clona voce + + + Find + Trovare + + + Username + Nome Utente + + + Copy username to clipboard + Copiare nome utente negli appunti + + + Password + Password + + + Copy password to clipboard + Copiare password negli appunti + + + Settings + Impostazioni + + + Perform Auto-Type + Eseguire Auto-Type + + + Open URL + Aprire URL + + + Lock databases + Bloccare database + + + Title + Titolo + + + URL + URL + + + Notes + Note + + + Show toolbar + Mostrare barra degli strumenti + + + read-only + sola lettura + + + + PasswordGeneratorWidget + + Password: + Password: + + + Length: + Lunghezza: + + + Character Types + Tipi di carattere + + + Upper Case Letters + Lettere maiuscole + + + Lower Case Letters + Lettere minuscole + + + Numbers + Numeri + + + Special Characters + Caratteri speciali + + + Exclude look-alike characters + Escludere caratteri simili + + + Ensure that the password contains characters from every group + Assicurare che la password contenga caratteri di ogni gruppo + + + Accept + Accettare + + + + QCommandLineParser + + Displays version information. + Mostrare informazioni sulla versione. + + + Displays this help. + Mostrare questo aiuto. + + + Unknown option '%1'. + Opzione sconosciuta '%1'. + + + Unknown options: %1. + Opzioni sconosciute '%1'. + + + Missing value after '%1'. + Manca valore dopo '%1'. + + + Unexpected value after '%1'. + Valore inaspettato dopo '%1'. + + + [options] + [opzioni] + + + Usage: %1 + Uso: %1 + + + Options: + Opzioni: + + + Arguments: + Argomenti: + + + + QSaveFile + + Existing file %1 is not writable + Il file esistente %1 non è scrivibile + + + Writing canceled by application + Scrittura cancellata dall'applicazione + + + Partial write. Partition full? + Scrittura parziale. Partizione piena? + + + + QtIOCompressor + + Internal zlib error when compressing: + Errore interno di zlib durante la compressione: + + + Error writing to underlying device: + Errore durante la scrittura nel dispositivo: + + + Error opening underlying device: + Errore durante l'apertura dal dispositivo: + + + Error reading data from underlying device: + Errore durante la lettura dal dispositivo: + + + Internal zlib error when decompressing: + Errore interno di zlib durante la decompressione: + + + + QtIOCompressor::open + + The gzip format not supported in this version of zlib. + Formato gzip non supportato da questa versione di zlib. + + + Internal zlib error: + Errore interno di zlib: + + + + SearchWidget + + Find: + Trovare: + + + Case sensitive + Case sensitive + + + Current group + Gruppo corrente + + + Root group + Gruppo radice + + + + SettingsWidget + + Application Settings + Impostazioni applicazione + + + General + Generale + + + Security + Sicurezza + + + + SettingsWidgetGeneral + + Remember last databases + Ricordare ultimo database + + + Open previous databases on startup + Aprire precedente database all'avvio + + + Mark as modified on expanded state changes + Marcare come modificata quando la voce viene espansa + + + Automatically save on exit + Salvare automaticamente all'uscita + + + Automatically save after every change + Salvare automaticamente dopo ogni modifica + + + Minimize when copying to clipboard + Minimizzare quando si copia negli appunti + + + Use group icon on entry creation + Usare l'icona del gruppo alla creazione di una voce + + + Global Auto-Type shortcut + Scorciatoia Auto-Type globale + + + Use entry title to match windows for global auto-type + Utilizzare il titolo della voce per abbinare la finestra per auto-type globale + + + + SettingsWidgetSecurity + + Clear clipboard after + Pulire appunti dopo + + + sec + sec + + + Lock databases after inactivity of + Bloccare database dopo un'inattività di + + + Show passwords in cleartext by default + Mostrare la password in chiaro in maniera predefinita + + + Always ask before performing auto-type + Chiedere sempre prima di eseguire auto-type + + + + UnlockDatabaseWidget + + Unlock database + Sbloccare database + + + Error + Errore + + + Wrong key. + Password errata. + + + + WelcomeWidget + + Welcome! + Benvenuto/a! + + + + main + + KeePassX - cross-platform password manager + KeePassX - gestore di password cross-platform + + + filename of the password database to open (*.kdbx) + nome del file del database da aprire (*.kdbx) + + + path to a custom config file + percorso ad un file di configurazione personalizzato + + + password of the database (DANGEROUS!) + password del database (PERICOLOSO!) + + + key file of the database + file chiave del database + + + \ No newline at end of file diff --git a/share/translations/keepassx_nl_NL.ts b/share/translations/keepassx_nl_NL.ts new file mode 100644 index 000000000..aa6320eee --- /dev/null +++ b/share/translations/keepassx_nl_NL.ts @@ -0,0 +1,1178 @@ + + + AboutDialog + + About KeePassX + Over KeePassX + + + KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. + KeePassX wordt verspreid onder de bepalingen van de GNU General Public License (GPL) versie 2 of (als u wenst) versie 3. + + + + AutoType + + Auto-Type - KeePassX + Auto-typen - KeePassX + + + Couldn't find an entry that matches the window title. + Kon geen element vinden dat overeenkomt met de venstertitel. + + + + AutoTypeAssociationsModel + + Window + Venster + + + Sequence + Volgorde + + + Default sequence + Standaardvolgorde + + + + AutoTypeSelectDialog + + Auto-Type - KeePassX + Auto-typen - KeePassX + + + Select entry to Auto-Type: + Kies element om automatisch te typen: + + + + ChangeMasterKeyWidget + + Password + Wachtwoord + + + Enter password: + Geef wachtwoord: + + + Repeat password: + Herhaal wachtwoord: + + + Key file + Sleutelbestand + + + Browse + Bladeren + + + Create + Aanmaken + + + Key files + Sleutelbestanden + + + All files + Alle bestanden + + + Create Key File... + Genereer sleutelbestand... + + + Error + Fout + + + Unable to create Key File : + Niet mogelijk om sleutelbestand aan te maken: + + + Select a key file + Kies een sleutelbestand + + + Question + Vraag + + + Do you really want to use an empty string as password? + Weet u zeker dat u een leeg veld als wachtwoord wilt gebruiken? + + + Different passwords supplied. + Verschillende wachtwoorden opgegeven. + + + + DatabaseOpenWidget + + Enter master key + Geef hoofdsleutel + + + Key File: + Sleutelbestand: + + + Password: + Wachtwoord: + + + Browse + Bladeren + + + Error + Fout + + + Unable to open the database. + Niet mogelijk om de database te openen. + + + Can't open key file + Niet mogelijk om het sleutelbestand te openen + + + All files + Alle bestanden + + + Key files + Sleutelbestanden + + + Select key file + Kies sleutelbestand + + + + DatabaseSettingsWidget + + Database name: + Naam van de database: + + + Database description: + Beschrijving van de database: + + + Transform rounds: + Transformatierondes: + + + Default username: + Standaard gebruikersnaam: + + + Use recycle bin: + Gebruik prullenbak: + + + MiB + MiB + + + Benchmark + Test + + + Max. history items: + Max. items in geschiedenis: + + + Max. history size: + Max. grootte geschiedenis: + + + + DatabaseTabWidget + + Root + Alles + + + KeePass 2 Database + KeePass 2 Database + + + All files + Alle bestanden + + + Open database + Open database + + + Warning + Waarschuwing + + + File not found! + Bestand niet gevonden! + + + Open KeePass 1 database + Open KeePass 1 database + + + KeePass 1 database + KeePass 1 database + + + All files (*) + Alle bestanden (*) + + + Close? + Sluiten? + + + "%1" is in edit mode. +Close anyway? + "%1" is in bewerkmodus. +Toch sluiten? + + + Save changes? + Wijzigingen opslaan? + + + "%1" was modified. +Save changes? + "%1" is gewijzigd. +Opslaan? + + + Error + Fout + + + Writing the database failed. + Opslaan van de database is mislukt. + + + Save database as + Database opslaan als + + + New database + Nieuwe database + + + locked + vergrendeld + + + + DatabaseWidget + + Change master key + Wijzig hoofdsleutel + + + Delete entry? + Element verwijderen? + + + Do you really want to delete the entry "%1" for good? + Weet u zeker dat u het element "%1" wilt verwijderen? + + + Delete entries? + Elementen wissen? + + + Do you really want to delete %1 entries for good? + Weet u zeker dat u %1 elementen wilt wissen? + + + Move entries to recycle bin? + Elementen naar de prullenbak verplaatsen? + + + Do you really want to move %n entry(s) to the recycle bin? + Weet u zeker dat u %n element naar de prullenbak wilt verplaatsen?Weet u zeker dat u %n elementen naar de prullenbak wilt verplaatsen? + + + Delete group? + Groep verwijderen? + + + Do you really want to delete the group "%1" for good? + Weet u zeker dat u de groep "%1" wilt verwijderen? + + + Current group + Huidige groep + + + + EditEntryWidget + + Entry + Element + + + Advanced + Geavanceerd + + + Icon + Icoon + + + Auto-Type + Auto-typen - KeePassX + + + Properties + Eigenschappen + + + History + Geschiedenis + + + Entry history + Geschiedenis van element + + + Add entry + Element toevoegen + + + Edit entry + Element wijzigen + + + Error + Fout + + + Different passwords supplied. + Verschillende wachtwoorden opgegeven. + + + New attribute + Nieuwe eigenschap + + + Select file + Kies bestand + + + Unable to open file + Niet mogelijk om bestand te openen + + + Save attachment + Bijlage opslaan + + + Unable to save the attachment: + + Niet mogelijk om de bijlage op te slaan: + + + + Tomorrow + Morgen + + + %n week(s) + %n week%n weken + + + %n month(s) + %n maand%n maanden + + + 1 year + 1 jaar + + + + EditEntryWidgetAdvanced + + Additional attributes + Extra eigenschappen + + + Add + Toevoegen + + + Edit + Wijzigen + + + Remove + Verwijderen + + + Attachments + Bijlagen + + + Save + Opslaan + + + + EditEntryWidgetAutoType + + Enable Auto-Type for this entry + Auto-typen inschakelen voor dit element + + + Inherit default Auto-Type sequence from the group + Erf standaard auto-typevolgorde van de groep + + + Use custom Auto-Type sequence: + Gebruik aangepaste auto-typevolgorde: + + + + + + + + + - + - + + + Window title: + Venstertitel: + + + Use default sequence + Gebruik standaardvolgorde + + + Set custom sequence: + Aangepaste volgorde: + + + + EditEntryWidgetHistory + + Show + Tonen + + + Restore + Herstellen + + + Delete + Verwijderen + + + Delete all + Alles verwijderen + + + + EditEntryWidgetMain + + Title: + Titel: + + + Username: + Gebruikersnaam: + + + Password: + Wachtwoord: + + + Repeat: + Herhalen: + + + Gen. + Gen. + + + URL: + URL: + + + Expires + Verloopt + + + Presets + Ingebouwd + + + Notes: + Opmerkingen: + + + + EditGroupWidget + + Group + Groep + + + Icon + Icoon + + + Properties + Eigenschappen + + + Add group + Groep toevoegen + + + Edit group + Groep wijzigen + + + Enable + Inschakelen + + + Disable + Uitschakelen + + + Inherit from parent group (%1) + Erf van bovenliggende groep (%1) + + + + EditGroupWidgetMain + + Name + Naam + + + Notes + Opmerkingen + + + Expires + Verloopt + + + Search + Zoeken + + + Auto-type + Auto-typen + + + + EditWidgetIcons + + Use default icon + Gebruik standaardicoon + + + Use custom icon + Gebruik aangepast icoon + + + Add custom icon + Voeg icoon toe + + + Delete custom icon + Verwijder icoon + + + Images + Afbeeldingen + + + All files + Alle bestanden + + + Select Image + Kies afbeelding + + + Can't delete icon! + Kan icoon niet verwijderen! + + + Can't delete icon. Still used by %n item(s). + Kan icoon niet verwijderen. Het wordt nog gebruikt door %n element.Kan icoon niet verwijderen. Het wordt nog gebruikt door %n elementen. + + + + EditWidgetProperties + + Created: + Aangemaakt: + + + Modified: + Gewijzigd: + + + Accessed: + Gelezen: + + + Uuid: + Uuid: + + + + EntryAttributesModel + + Name + Naam + + + + EntryHistoryModel + + Last modified + Laatst gewijzigd + + + Title + Titel + + + Username + Gebruikersnaam + + + URL + URL + + + + EntryModel + + Group + Groep + + + Title + Titel + + + Username + Gebruikersnaam + + + URL + URL + + + + Group + + Recycle Bin + Prullenbak + + + + KeePass1OpenWidget + + Import KeePass1 database + Importeer Keepass 1-database + + + Error + Fout + + + Unable to open the database. + Niet mogelijk om de database te openen. + + + + KeePass1Reader + + Unable to read keyfile. + Niet mogelijk om sleutelbestand te lezen + + + Not a KeePass database. + Geen Keepass-database + + + Unsupported encryption algorithm. + Niet-ondersteund encryptie-algoritme + + + Unsupported KeePass database version. + Niet-ondersteunde versie van Keepass-database + + + Root + Alles + + + + KeePass2Reader + + Not a KeePass database. + Geen Keepass-database. + + + Unsupported KeePass database version. + Niet-ondersteunde versie van Keepass-database. + + + Wrong key or database file is corrupt. + Verkeerde sleutel of corrupte database. + + + + MainWindow + + Database + Database + + + Recent databases + Recente databases + + + Help + Help + + + Entries + Elementen + + + Copy attribute to clipboard + Kopieer eigenschap naar klembord + + + Groups + Groepen + + + Extras + Extra's + + + View + Beeld + + + Quit + Afsluiten + + + About + Over + + + Open database + Open database + + + Save database + Sla database op + + + Close database + Sluit database + + + New database + Nieuwe database + + + Add new entry + Voeg element toe + + + View/Edit entry + Bekijk/bewerk element + + + Delete entry + Verwijder element + + + Add new group + Voeg groep toe + + + Edit group + Bewerk groep + + + Delete group + Verwijder groep + + + Save database as + Database opslaan als + + + Change master key + Hoofdsleutel wijzigen + + + Database settings + Database-instellingen + + + Import KeePass 1 database + Importeer Keepass 1-database + + + Clone entry + Element klonen + + + Find + Vind + + + Username + Gebruikersnaam + + + Copy username to clipboard + Kopieer gebruikersnaam naar klembord + + + Password + Wachtwoord + + + Copy password to clipboard + Kopieer wachtwoord naar klembord + + + Settings + Instellingen + + + Perform Auto-Type + Voer auto-typen uit + + + Open URL + Open URL + + + Lock databases + Vergrendel databases + + + Title + Titel + + + URL + URL + + + Notes + Opmerkingen + + + Show toolbar + Werkbalk weergeven + + + read-only + alleen-lezen + + + + PasswordGeneratorWidget + + Password: + Wachtwoord: + + + Length: + Lengte: + + + Character Types + Tekens + + + Upper Case Letters + Hoofdletters + + + Lower Case Letters + Kleine letters + + + Numbers + Cijfers + + + Special Characters + Speciale tekens + + + Exclude look-alike characters + Geen op elkaar lijkende tekens + + + Ensure that the password contains characters from every group + Zorg dat het wachtwoord tekens uit iedere groep bevat + + + Accept + Accepteren + + + + QCommandLineParser + + Displays version information. + Toont versie-informatie. + + + Displays this help. + Toont deze helptekst. + + + Unknown option '%1'. + Onbekende optie '%1'. + + + Unknown options: %1. + Onbekende opties: %1. + + + Missing value after '%1'. + Ontbrekende waarde na '%1'. + + + Unexpected value after '%1'. + Onverwachte waarde na '%1'. + + + [options] + [opties] + + + Usage: %1 + Gebruik: %1 + + + Options: + Opties: + + + Arguments: + Argumenten: + + + + QSaveFile + + Existing file %1 is not writable + Bestaand bestand %1 is niet schrijfbaar + + + Writing canceled by application + Schrijven afgebroken door programma + + + Partial write. Partition full? + Slechts deels geschreven. Is de schijf vol? + + + + QtIOCompressor + + Internal zlib error when compressing: + Interne fout in zlib bij inpakken: + + + Error writing to underlying device: + Fout bij schrijven naar onderliggend apparaat: + + + Error opening underlying device: + Fout bij openen van onderliggend apparaat: + + + Error reading data from underlying device: + Fout bij lezen van gegevens van onderliggend apparaat: + + + Internal zlib error when decompressing: + Interne fout in zlib bij uitpakken: + + + + QtIOCompressor::open + + The gzip format not supported in this version of zlib. + Gzip wordt niet ondersteund in deze versie van zlib. + + + Internal zlib error: + Interne fout in zlib: + + + + SearchWidget + + Find: + Vind: + + + Case sensitive + Hoofdlettergevoelig + + + Current group + Huidige groep + + + Root group + Hoofdgroep + + + + SettingsWidget + + Application Settings + Programma-instellingen + + + General + Algemeen + + + Security + Beveiliging + + + + SettingsWidgetGeneral + + Remember last databases + Onthoud laatste databases + + + Open previous databases on startup + Open vorige databases bij starten + + + Mark as modified on expanded state changes + Markeer database als gewijzigd bij wijzigen van de status + + + Automatically save on exit + Automatisch opslaan bij afsluiten + + + Automatically save after every change + Automatisch opslaan na iedere wijziging + + + Minimize when copying to clipboard + Minimaliseer bij kopieeren naar klembord + + + Use group icon on entry creation + Gebruik icoon van de groep voor nieuwe elementen + + + Global Auto-Type shortcut + Globale sneltoets voor auto-typen + + + Use entry title to match windows for global auto-type + Gebruik naam van element als vensternaam voor auto-typen + + + + SettingsWidgetSecurity + + Clear clipboard after + Leeg klembord na + + + sec + sec + + + Lock databases after inactivity of + Vergrendel databases na inactiviteit van + + + Show passwords in cleartext by default + Laat wachtwoorden standaard zien + + + Always ask before performing auto-type + Altijd vragen alvorens auto-type uit te voeren + + + + UnlockDatabaseWidget + + Unlock database + Database ontgrendelen + + + Error + Fout + + + Wrong key. + Verkeerd wachtwoord + + + + WelcomeWidget + + Welcome! + Welkom! + + + + main + + KeePassX - cross-platform password manager + KeepassX - multi-platform wachtwoordbeheerder + + + filename of the password database to open (*.kdbx) + bestandsnaam van de te openen wachtwoorddatabase (*.kdbx) + + + path to a custom config file + pad naar een configuratiebestand + + + password of the database (DANGEROUS!) + wachtwoord van de database (GEVAARLIJK!) + + + key file of the database + sleutelbestand van de database + + + \ No newline at end of file diff --git a/share/translations/keepassx_sv.ts b/share/translations/keepassx_sv.ts new file mode 100644 index 000000000..2a3ba790c --- /dev/null +++ b/share/translations/keepassx_sv.ts @@ -0,0 +1,1178 @@ + + + AboutDialog + + About KeePassX + Om KeePassX + + + KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. + Keepassx distribueras enligt villkoren i GNU General Public License (GPL) version 2 eller (om du vill) version 3. + + + + AutoType + + Auto-Type - KeePassX + Auto-skriv - KeePassX + + + Couldn't find an entry that matches the window title. + Kunde inte hitta en post som matchar fönstertiteln. + + + + AutoTypeAssociationsModel + + Window + Fönster + + + Sequence + Sekvens + + + Default sequence + Standard sekvens + + + + AutoTypeSelectDialog + + Auto-Type - KeePassX + Auto-skriv - KeePassX + + + Select entry to Auto-Type: + Välj post att auto-skriva + + + + ChangeMasterKeyWidget + + Password + Lösenord + + + Enter password: + Ange lösenord: + + + Repeat password: + Repetera lösenord: + + + Key file + Nyckel-fil + + + Browse + Bläddra + + + Create + Skapa + + + Key files + Nyckel-filer + + + All files + Alla filer + + + Create Key File... + Skapa nyckel-fil... + + + Error + Fel + + + Unable to create Key File : + Kunde inte skapa nyckel-fil + + + Select a key file + Välj nyckel-fil + + + Question + Fråga + + + Do you really want to use an empty string as password? + Vill du verkligen vill använda en tom sträng som lösenord? + + + Different passwords supplied. + Olika lösenord angivna + + + + DatabaseOpenWidget + + Enter master key + Ange huvud lösenord + + + Key File: + Nyckel-fil: + + + Password: + Lösenord: + + + Browse + Bläddra + + + Error + Fel + + + Unable to open the database. + Kunde inte öppna databas. + + + Can't open key file + Kan inte öppna nyckel-fil + + + All files + Alla filer + + + Key files + Nyckel-filer + + + Select key file + Välj nyckel-fil + + + + DatabaseSettingsWidget + + Database name: + Databasnamn: + + + Database description: + Databasbeskrivning: + + + Transform rounds: + Transformerings varv: + + + Default username: + Standard användarnamn: + + + Use recycle bin: + Använd papperskorg: + + + MiB + MiB + + + Benchmark + Benchmark + + + Max. history items: + Maxantal historik poster: + + + Max. history size: + Maximal historik storlek: + + + + DatabaseTabWidget + + Root + Root + + + KeePass 2 Database + KeePass 2 Databas + + + All files + Alla filer + + + Open database + Öppna databas + + + Warning + Varning + + + File not found! + Filen kunde inte hittas! + + + Open KeePass 1 database + Öppna KeePass 1 databas + + + KeePass 1 database + KeePass 1 databas + + + All files (*) + Alla filer (*) + + + Close? + Stäng? + + + "%1" is in edit mode. +Close anyway? + "%1" är i redigerar-läge. +Stäng ändå? + + + Save changes? + Spara ändringar? + + + "%1" was modified. +Save changes? + "%1" har ändrats. +Spara ändringarna? + + + Error + Fel + + + Writing the database failed. + Kunde inte skriva till databasen. + + + Save database as + Spara databas som + + + New database + Ny databas + + + locked + låst + + + + DatabaseWidget + + Change master key + Ändra huvud lösenord + + + Delete entry? + Ta bort post? + + + Do you really want to delete the entry "%1" for good? + Vill du verkligen ta bort "%1" för gott? + + + Delete entries? + Ta bort poster? + + + Do you really want to delete %1 entries for good? + Vill du verkligen ta bort %1 poser för gott? + + + Move entries to recycle bin? + Lägg poster i papperskorgen? + + + 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? + + + Delete group? + Ta bort grupp? + + + Do you really want to delete the group "%1" for good? + Vill du verkligen ta bort gruppen "%1" för gott? + + + Current group + Nuvarande grupp + + + + EditEntryWidget + + Entry + Post + + + Advanced + Avancerat + + + Icon + Ikon + + + Auto-Type + Auto-skriv + + + Properties + Egenskaper + + + History + Historik + + + Entry history + Posthistork + + + Add entry + Lägg till post + + + Edit entry + Ändra post + + + Error + Fel + + + Different passwords supplied. + Olika lösenord angivna + + + New attribute + Nytt attribut + + + Select file + Välj fil + + + Unable to open file + Kunde inte öppna filen. + + + Save attachment + Spara bifogad fil + + + Unable to save the attachment: + + Kunde inte spara bifogad fil: + + + + Tomorrow + Imorgon + + + %n week(s) + %n vecka%n veckor + + + %n month(s) + %n månad%n månader + + + 1 year + 1 år + + + + EditEntryWidgetAdvanced + + Additional attributes + Ytterligare attribut + + + Add + Lägg till + + + Edit + Ändra + + + Remove + Ta bort + + + Attachments + Bilagor + + + Save + Spara + + + + EditEntryWidgetAutoType + + Enable Auto-Type for this entry + Slå på auto-skriv för denna post + + + Inherit default Auto-Type sequence from the group + Ärv standard auto-skriv sekvens för grupp + + + Use custom Auto-Type sequence: + Använd egen auto-skriv sekvens: + + + + + + + + + - + - + + + Window title: + Fönster titel: + + + Use default sequence + Använd standard sekvens + + + Set custom sequence: + Egen sekvens: + + + + EditEntryWidgetHistory + + Show + Visa + + + Restore + Återställ + + + Delete + Ta bort + + + Delete all + Ta bort alla + + + + EditEntryWidgetMain + + Title: + Titel: + + + Username: + Användarnamn: + + + Password: + Lösenord: + + + Repeat: + Repetera: + + + Gen. + Gen. + + + URL: + URL: + + + Expires + Går ut + + + Presets + Förinställningar + + + Notes: + Anteckningar: + + + + EditGroupWidget + + Group + Grupp + + + Icon + Ikon + + + Properties + Egenskaper + + + Add group + Lägg till grupp + + + Edit group + Ändra grupp + + + Enable + Slå på + + + Disable + Stäng av + + + Inherit from parent group (%1) + Ärv från förälder grupp (%1) + + + + EditGroupWidgetMain + + Name + Namn + + + Notes + Anteckningar + + + Expires + Går ut + + + Search + Sök + + + Auto-type + Auto-skriv + + + + EditWidgetIcons + + Use default icon + Använd standard ikon + + + Use custom icon + Använd egen ikon + + + Add custom icon + Lägg till egen ikon + + + Delete custom icon + Ta bort egen ikon + + + Images + Bilder + + + All files + Alla filer + + + Select Image + Välj bild + + + Can't delete icon! + Kan inte ta bort ikon! + + + Can't delete icon. Still used by %n item(s). + Kan inte ta bort ikonen. Den används fortfarande av %n postKan inte ta bort ikonen. Den används fortfarande av %n poster + + + + EditWidgetProperties + + Created: + Skapad: + + + Modified: + Ändrad: + + + Accessed: + Läst: + + + Uuid: + UUID: + + + + EntryAttributesModel + + Name + Namn + + + + EntryHistoryModel + + Last modified + Senast ändrad + + + Title + Titel + + + Username + Användarnamn + + + URL + URL + + + + EntryModel + + Group + Grupp + + + Title + Titel + + + Username + Användarnamn + + + URL + URL + + + + Group + + Recycle Bin + Papperskorg + + + + KeePass1OpenWidget + + Import KeePass1 database + Importera KeePass1 databas + + + Error + Fel + + + Unable to open the database. + Kunde inte öppna databas. + + + + KeePass1Reader + + Unable to read keyfile. + Kunde inte läsa nyckel-filen. + + + Not a KeePass database. + Inte en KeePass databas + + + Unsupported encryption algorithm. + Krypteringsalgoritnmen stöds ej + + + Unsupported KeePass database version. + KeePass databas versionen stöds ej. + + + Root + Root + + + + KeePass2Reader + + Not a KeePass database. + Inte en KeePass databas. + + + Unsupported KeePass database version. + KeePass databas versionen stöds ej. + + + Wrong key or database file is corrupt. + Fel lösenord eller korrupt databas-fil + + + + MainWindow + + Database + Databas + + + Recent databases + Senast använda databser + + + Help + Hjälp + + + Entries + Poster + + + Copy attribute to clipboard + Kopiera attribut + + + Groups + Grupper + + + Extras + Extra + + + View + Vy + + + Quit + Avsluta + + + About + Om + + + Open database + Öppna databas + + + Save database + Spara databas + + + Close database + Stäng databas + + + New database + Ny databas + + + Add new entry + Lägg till ny post + + + View/Edit entry + Visa/ändra post + + + Delete entry + Ta bort post + + + Add new group + Lägg till ny grupp + + + Edit group + Ändra grupp + + + Delete group + Ta bort grupp + + + Save database as + Spara databas som + + + Change master key + Ändra huvud lösenord + + + Database settings + Databasinställningar + + + Import KeePass 1 database + Importera KeePass1 databas + + + Clone entry + Klona post + + + Find + Sök + + + Username + Användarnamn + + + Copy username to clipboard + Kopiera användarnamn + + + Password + Lösenord + + + Copy password to clipboard + Kopiera lösenord + + + Settings + Inställningar + + + Perform Auto-Type + Utför auto-skriv + + + Open URL + Öppna URL + + + Lock databases + Lås databaser + + + Title + Titel + + + URL + URL + + + Notes + Anteckningar + + + Show toolbar + Visa verktygsfält + + + read-only + läs bara + + + + PasswordGeneratorWidget + + Password: + Lösenord: + + + Length: + Längd: + + + Character Types + Teckentyper + + + Upper Case Letters + Versaler + + + Lower Case Letters + Gemener + + + Numbers + Siffror + + + Special Characters + Specialtecken + + + Exclude look-alike characters + Uteslut liknande tecken + + + Ensure that the password contains characters from every group + Säkerställ att lösenordet innehåller tecken från varje grupp + + + Accept + Acceptera + + + + QCommandLineParser + + Displays version information. + Visar versionsinformation. + + + Displays this help. + Visa denna hjälp. + + + Unknown option '%1'. + Okänt alternativ: '%1' + + + Unknown options: %1. + Okända alternativ: '%1' + + + Missing value after '%1'. + Saknar värde efter '%1' + + + Unexpected value after '%1'. + Oväntat värde efter '%1' + + + [options] + [alternativ] + + + Usage: %1 + Användning: %1 + + + Options: + Alternativ: + + + Arguments: + Argument: + + + + QSaveFile + + Existing file %1 is not writable + Den existerande filen %1 är inte skrivbar + + + Writing canceled by application + Skrivning avbruten av applikation + + + Partial write. Partition full? + Delvis skrivet. Är partitionen full? + + + + QtIOCompressor + + Internal zlib error when compressing: + Internt zlib fel vid komprimering: + + + Error writing to underlying device: + Fel vid skrivning till underliggande enhet: + + + Error opening underlying device: + Fel vid öppning av underliggande enhet: + + + Error reading data from underlying device: + Fel vid läsning från underliggande enhet: + + + Internal zlib error when decompressing: + Internt zlib fel vid extrahering: + + + + QtIOCompressor::open + + The gzip format not supported in this version of zlib. + Gzip formatet stöds inte av denna version av zlib. + + + Internal zlib error: + Internt zlib fel: + + + + SearchWidget + + Find: + Sök: + + + Case sensitive + Skiftlägeskänslig + + + Current group + Nuvarande grupp + + + Root group + Root grupp + + + + SettingsWidget + + Application Settings + Applikationsinställningar + + + General + Allmän + + + Security + Säkerhet + + + + SettingsWidgetGeneral + + Remember last databases + Komihåg senaste databasen + + + Open previous databases on startup + Öppna senaste databasen är programmet startar + + + Mark as modified on expanded state changes + Markera som ändrad när utökat läge ändras + + + Automatically save on exit + Spara automatiskt är applikationen anslutas + + + Automatically save after every change + Spara automatiskt efter varje ändring + + + Minimize when copying to clipboard + Minimera vid kopiering + + + Use group icon on entry creation + Använd gruppens ikon för nya poster + + + Global Auto-Type shortcut + Globalt auto-skriv kortkommando + + + Use entry title to match windows for global auto-type + Använda postens titel till matchning med fönster för globalt auto-skriv + + + + SettingsWidgetSecurity + + Clear clipboard after + Rensa urklipp efter + + + sec + sek + + + Lock databases after inactivity of + Lås databaser efter inaktivitet i + + + Show passwords in cleartext by default + Visa lösenord i klartext som standard + + + Always ask before performing auto-type + Fråga alltid innan auto-skriv utförs + + + + UnlockDatabaseWidget + + Unlock database + Lås upp databas + + + Error + Fel + + + Wrong key. + Fel lösenord + + + + WelcomeWidget + + Welcome! + Välkommen! + + + + main + + KeePassX - cross-platform password manager + KeePassX - plattformsoberoende lösenordshanterare + + + filename of the password database to open (*.kdbx) + namn på databas fil att öppna (*.kdbx) + + + path to a custom config file + Sökväg till egen konfigurations-fil + + + password of the database (DANGEROUS!) + lösenord för databasen (FARLIGT!) + + + key file of the database + nyckel-fil för databas + + + \ No newline at end of file From 4b3a82592cd7a4bd2b18be1a4e020e05786a39a6 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Tue, 4 Nov 2014 18:50:07 +0100 Subject: [PATCH 54/71] Define QT_NO_DEBUG for build type None. Debian sets the the build type to None for package builds. Make sure we don't enable asserts there. Closes #237 --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 280fb7766..3532c46f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,6 +165,9 @@ endif() find_package(Qt4 4.6.0 REQUIRED ${QT_REQUIRED_MODULES}) include(${QT_USE_FILE}) +# Debian sets the the build type to None for package builds. +# Make sure we don't enable asserts there. +set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG) find_package(Gcrypt REQUIRED) if(NOT (${GCRYPT_VERSION_STRING} VERSION_LESS "1.6.0")) From 57107ea5608a24d925b0182ece19fc34a4605224 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Tue, 4 Nov 2014 18:51:46 +0100 Subject: [PATCH 55/71] Enable debug mode for Travis CI builds. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 751dbc28c..15af85bf8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ install: - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install cmake qt libgcrypt; fi before_script: mkdir build && pushd build script: - - cmake -DWITH_GUI_TESTS=ON .. + - cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_GUI_TESTS=ON .. - make - if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui"; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui"; fi From b1c38149729d69a0c2abf916c43bc30db6b89493 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Tue, 18 Nov 2014 16:26:04 +0900 Subject: [PATCH 56/71] Make Ctrl+F not toggle the search mode but always enable it. Switching back from other applications, the previous behavior of Ctrl+F would often bother you in that it would dismiss the search widget if it was already enabled when you meant by the key you wanted to perform a search. Making Ctrl+F always set you in search mode should save user from having to care about the mode which is persistent across application switching and database locking. --- src/gui/DatabaseWidget.cpp | 16 ++++++++++++++++ src/gui/DatabaseWidget.h | 1 + src/gui/MainWindow.cpp | 15 +++++++++------ src/gui/MainWindow.ui | 7 ++++++- tests/gui/TestGui.cpp | 33 ++++++++++++++++++++++++--------- 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index cc9c5fd18..df3214246 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -702,6 +702,22 @@ void DatabaseWidget::switchToImportKeepass1(const QString& fileName) setCurrentWidget(m_keepass1OpenWidget); } +void DatabaseWidget::openSearch() +{ + if (isInSearchMode()) { + m_searchUi->searchEdit->selectAll(); + + if (!m_searchUi->searchEdit->hasFocus()) { + m_searchUi->searchEdit->setFocus(); + // make sure the search action is checked again + emitCurrentModeChanged(); + } + } + else { + showSearch(); + } +} + void DatabaseWidget::toggleSearch() { if (isInSearchMode()) { diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index cbab175e4..821a21d5b 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -117,6 +117,7 @@ public Q_SLOTS: void switchToOpenDatabase(const QString& fileName); void switchToOpenDatabase(const QString& fileName, const QString& password, const QString& keyFile); void switchToImportKeepass1(const QString& fileName); + void openSearch(); void toggleSearch(); private Q_SLOTS: diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index dd77989c3..1933ef4db 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -119,7 +119,7 @@ MainWindow::MainWindow() m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about")); - m_ui->actionSearch->setIcon(filePath()->icon("actions", "system-search")); + m_ui->actionToggleSearch->setIcon(filePath()->icon("actions", "system-search")); m_actionMultiplexer.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(setMenuActionState(DatabaseWidget::Mode))); @@ -200,8 +200,10 @@ MainWindow::MainWindow() connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog())); - m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()), + m_actionMultiplexer.connect(m_ui->actionToggleSearch, SIGNAL(triggered()), SLOT(toggleSearch())); + m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()), + SLOT(openSearch())); updateTrayIcon(); } @@ -295,9 +297,10 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupNew->setEnabled(groupSelected); m_ui->actionGroupEdit->setEnabled(groupSelected); m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup()); - m_ui->actionSearch->setEnabled(true); // TODO: get checked state from db widget - m_ui->actionSearch->setChecked(inSearch); + m_ui->actionSearch->setEnabled(true); + m_ui->actionToggleSearch->setEnabled(true); + m_ui->actionToggleSearch->setChecked(inSearch); m_ui->actionChangeMasterKey->setEnabled(true); m_ui->actionChangeDatabaseSettings->setEnabled(true); m_ui->actionDatabaseSave->setEnabled(true); @@ -321,7 +324,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->menuEntryCopyAttribute->setEnabled(false); m_ui->actionSearch->setEnabled(false); - m_ui->actionSearch->setChecked(false); + m_ui->actionToggleSearch->setEnabled(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); @@ -348,7 +351,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->menuEntryCopyAttribute->setEnabled(false); m_ui->actionSearch->setEnabled(false); - m_ui->actionSearch->setChecked(false); + m_ui->actionToggleSearch->setEnabled(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 13c5d6796..838bb5f32 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -177,7 +177,7 @@ - + @@ -304,6 +304,11 @@ + + Find + + + true diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 326c3497b..592f57ecb 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -166,24 +166,39 @@ void TestGui::testAddEntry() void TestGui::testSearch() { - QAction* searchAction = m_mainWindow->findChild("actionSearch"); - QVERIFY(searchAction->isEnabled()); + QAction* toggleSearchAction = m_mainWindow->findChild("actionToggleSearch"); + QVERIFY(toggleSearchAction->isEnabled()); QToolBar* toolBar = m_mainWindow->findChild("toolBar"); - QWidget* searchActionWidget = toolBar->widgetForAction(searchAction); - QVERIFY(searchActionWidget->isEnabled()); - QTest::mouseClick(searchActionWidget, Qt::LeftButton); - + QWidget* toggleSearchActionWidget = toolBar->widgetForAction(toggleSearchAction); EntryView* entryView = m_dbWidget->findChild("entryView"); QLineEdit* searchEdit = m_dbWidget->findChild("searchEdit"); QToolButton* clearSearch = m_dbWidget->findChild("clearButton"); + QVERIFY(!searchEdit->hasFocus()); + + // Toggle + QTest::mouseClick(toggleSearchActionWidget, Qt::LeftButton); + QTRY_VERIFY(searchEdit->hasFocus()); + // Search for "ZZZ" QTest::keyClicks(searchEdit, "ZZZ"); - QTRY_COMPARE(entryView->model()->rowCount(), 0); - + // Escape + QTest::keyClick(m_mainWindow, Qt::Key_Escape); + QTRY_VERIFY(!searchEdit->hasFocus()); + // Toggle again + QTest::mouseClick(toggleSearchActionWidget, Qt::LeftButton); + QTRY_VERIFY(searchEdit->hasFocus()); + // Input and clear + QTest::keyClicks(searchEdit, "ZZZ"); + QTRY_COMPARE(searchEdit->text(), QString("ZZZ")); QTest::mouseClick(clearSearch, Qt::LeftButton); + QTRY_COMPARE(searchEdit->text(), QString("")); + // Ctrl+F should select the current text + QTest::keyClicks(searchEdit, "ZZZ"); + QTest::keyClick(m_mainWindow, Qt::Key_F, Qt::ControlModifier); + QTRY_VERIFY(searchEdit->hasFocus()); + // Search for "some" QTest::keyClicks(searchEdit, "some"); - QTRY_COMPARE(entryView->model()->rowCount(), 4); clickIndex(entryView->model()->index(0, 1), entryView, Qt::LeftButton); From dd79105baac83c13bb63e990489f6c66e26be308 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Wed, 19 Nov 2014 11:46:38 +0900 Subject: [PATCH 57/71] Complete remove the toggle search action. --- src/gui/DatabaseWidget.cpp | 18 ------------------ src/gui/DatabaseWidget.h | 1 - src/gui/MainWindow.cpp | 8 +------- src/gui/MainWindow.ui | 10 +--------- tests/gui/TestGui.cpp | 18 +++++++++--------- 5 files changed, 11 insertions(+), 44 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index df3214246..dd63b4df2 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -718,24 +718,6 @@ void DatabaseWidget::openSearch() } } -void DatabaseWidget::toggleSearch() -{ - if (isInSearchMode()) { - if (m_searchUi->searchEdit->hasFocus()) { - closeSearch(); - } - else { - m_searchUi->searchEdit->selectAll(); - m_searchUi->searchEdit->setFocus(); - // make sure the search action is checked again - emitCurrentModeChanged(); - } - } - else { - showSearch(); - } -} - void DatabaseWidget::closeSearch() { Q_ASSERT(m_lastGroup); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 821a21d5b..45de74629 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -118,7 +118,6 @@ public Q_SLOTS: void switchToOpenDatabase(const QString& fileName, const QString& password, const QString& keyFile); void switchToImportKeepass1(const QString& fileName); void openSearch(); - void toggleSearch(); private Q_SLOTS: void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 1933ef4db..d48d09500 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -119,7 +119,7 @@ MainWindow::MainWindow() m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about")); - m_ui->actionToggleSearch->setIcon(filePath()->icon("actions", "system-search")); + m_ui->actionSearch->setIcon(filePath()->icon("actions", "system-search")); m_actionMultiplexer.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(setMenuActionState(DatabaseWidget::Mode))); @@ -200,8 +200,6 @@ MainWindow::MainWindow() connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog())); - m_actionMultiplexer.connect(m_ui->actionToggleSearch, SIGNAL(triggered()), - SLOT(toggleSearch())); m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()), SLOT(openSearch())); @@ -299,8 +297,6 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup()); // TODO: get checked state from db widget m_ui->actionSearch->setEnabled(true); - m_ui->actionToggleSearch->setEnabled(true); - m_ui->actionToggleSearch->setChecked(inSearch); m_ui->actionChangeMasterKey->setEnabled(true); m_ui->actionChangeDatabaseSettings->setEnabled(true); m_ui->actionDatabaseSave->setEnabled(true); @@ -324,7 +320,6 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->menuEntryCopyAttribute->setEnabled(false); m_ui->actionSearch->setEnabled(false); - m_ui->actionToggleSearch->setEnabled(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); @@ -351,7 +346,6 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->menuEntryCopyAttribute->setEnabled(false); m_ui->actionSearch->setEnabled(false); - m_ui->actionToggleSearch->setEnabled(false); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 838bb5f32..09e1c412e 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -177,7 +177,7 @@ - + @@ -304,14 +304,6 @@ - - Find - - - - - true - false diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 592f57ecb..26c8be077 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -166,18 +166,18 @@ void TestGui::testAddEntry() void TestGui::testSearch() { - QAction* toggleSearchAction = m_mainWindow->findChild("actionToggleSearch"); - QVERIFY(toggleSearchAction->isEnabled()); + QAction* searchAction = m_mainWindow->findChild("actionSearch"); + QVERIFY(searchAction->isEnabled()); QToolBar* toolBar = m_mainWindow->findChild("toolBar"); - QWidget* toggleSearchActionWidget = toolBar->widgetForAction(toggleSearchAction); + QWidget* searchActionWidget = toolBar->widgetForAction(searchAction); EntryView* entryView = m_dbWidget->findChild("entryView"); QLineEdit* searchEdit = m_dbWidget->findChild("searchEdit"); QToolButton* clearSearch = m_dbWidget->findChild("clearButton"); QVERIFY(!searchEdit->hasFocus()); - // Toggle - QTest::mouseClick(toggleSearchActionWidget, Qt::LeftButton); + // Enter search + QTest::mouseClick(searchActionWidget, Qt::LeftButton); QTRY_VERIFY(searchEdit->hasFocus()); // Search for "ZZZ" QTest::keyClicks(searchEdit, "ZZZ"); @@ -185,17 +185,17 @@ void TestGui::testSearch() // Escape QTest::keyClick(m_mainWindow, Qt::Key_Escape); QTRY_VERIFY(!searchEdit->hasFocus()); - // Toggle again - QTest::mouseClick(toggleSearchActionWidget, Qt::LeftButton); + // Enter search again + QTest::mouseClick(searchActionWidget, Qt::LeftButton); QTRY_VERIFY(searchEdit->hasFocus()); // Input and clear QTest::keyClicks(searchEdit, "ZZZ"); QTRY_COMPARE(searchEdit->text(), QString("ZZZ")); QTest::mouseClick(clearSearch, Qt::LeftButton); QTRY_COMPARE(searchEdit->text(), QString("")); - // Ctrl+F should select the current text + // Triggering search should select the existing text QTest::keyClicks(searchEdit, "ZZZ"); - QTest::keyClick(m_mainWindow, Qt::Key_F, Qt::ControlModifier); + QTest::mouseClick(searchActionWidget, Qt::LeftButton); QTRY_VERIFY(searchEdit->hasFocus()); // Search for "some" QTest::keyClicks(searchEdit, "some"); From 07e4fbacd4ec334115308045f8277ea531c000ec Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 30 Nov 2014 23:04:17 +0100 Subject: [PATCH 58/71] Remove ModifiedOnExpandedStateChanges config option. I'm pretty sure noone knew what it actually does. This is the sort of option users shouldn't be bothered with. --- src/core/Config.cpp | 1 - src/core/Group.cpp | 4 +--- src/gui/SettingsWidget.cpp | 3 --- src/gui/SettingsWidgetGeneral.ui | 33 +++++++++++--------------------- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 03b5129ef..48ea9f333 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -90,7 +90,6 @@ void Config::init(const QString& fileName) m_defaults.insert("RememberLastDatabases", true); m_defaults.insert("OpenPreviousDatabasesOnStartup", true); - m_defaults.insert("ModifiedOnExpandedStateChanges", true); m_defaults.insert("AutoSaveAfterEveryChange", false); m_defaults.insert("AutoSaveOnExit", false); m_defaults.insert("ShowToolbar", true); diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 517f8cb0c..e96b857ba 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -248,9 +248,7 @@ void Group::setExpanded(bool expanded) if (m_data.isExpanded != expanded) { m_data.isExpanded = expanded; updateTimeinfo(); - if (config()->get("ModifiedOnExpandedStateChanges").toBool()) { - Q_EMIT modified(); - } + Q_EMIT modified(); } } diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index a7863eac8..33789ae7e 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -65,7 +65,6 @@ void SettingsWidget::loadSettings() m_generalUi->rememberLastDatabasesCheckBox->setChecked(config()->get("RememberLastDatabases").toBool()); m_generalUi->openPreviousDatabasesOnStartupCheckBox->setChecked( config()->get("OpenPreviousDatabasesOnStartup").toBool()); - m_generalUi->modifiedExpandedChangedCheckBox->setChecked(config()->get("ModifiedOnExpandedStateChanges").toBool()); m_generalUi->autoSaveAfterEveryChangeCheckBox->setChecked(config()->get("AutoSaveAfterEveryChange").toBool()); m_generalUi->autoSaveOnExitCheckBox->setChecked(config()->get("AutoSaveOnExit").toBool()); m_generalUi->minimizeOnCopyCheckBox->setChecked(config()->get("MinimizeOnCopy").toBool()); @@ -111,8 +110,6 @@ void SettingsWidget::saveSettings() config()->set("RememberLastDatabases", m_generalUi->rememberLastDatabasesCheckBox->isChecked()); config()->set("OpenPreviousDatabasesOnStartup", m_generalUi->openPreviousDatabasesOnStartupCheckBox->isChecked()); - config()->set("ModifiedOnExpandedStateChanges", - m_generalUi->modifiedExpandedChangedCheckBox->isChecked()); config()->set("AutoSaveAfterEveryChange", m_generalUi->autoSaveAfterEveryChangeCheckBox->isChecked()); config()->set("AutoSaveOnExit", m_generalUi->autoSaveOnExitCheckBox->isChecked()); diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui index cbad7e5eb..04c603a34 100644 --- a/src/gui/SettingsWidgetGeneral.ui +++ b/src/gui/SettingsWidgetGeneral.ui @@ -7,7 +7,7 @@ 0 0 456 - 340 + 313 @@ -32,78 +32,68 @@ - - - Mark as modified on expanded state changes - - - true - - - - Automatically save on exit - + Automatically save after every change - + Minimize when copying to clipboard - + Use group icon on entry creation - + Global Auto-Type shortcut - + - + Use entry title to match windows for global auto-type - + Language - + - + Show a system tray icon - + false @@ -125,7 +115,6 @@ rememberLastDatabasesCheckBox openPreviousDatabasesOnStartupCheckBox - modifiedExpandedChangedCheckBox autoSaveOnExitCheckBox autoSaveAfterEveryChangeCheckBox minimizeOnCopyCheckBox From e58be4452304c76c408710f156f6afbba0de6771 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 30 Nov 2014 23:23:29 +0100 Subject: [PATCH 59/71] Wrap overly long lines in README.md. --- README.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e6a466358..5096b11fb 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,24 @@ ## About -KeePassX is an application for people with extremely high demands on secure personal data management. It has a light interface, is cross platform and published under the terms of the GNU General Public License. +KeePassX is an application for people with extremely high demands on secure personal data management. +It has a light interface, is cross platform and published under the terms of the GNU General Public License. -KeePassX saves many different information e.g. user names, passwords, urls, attachments and comments in one single database. For a better management user-defined titles and icons can be specified for each single entry. Furthermore the entries are sorted in groups, which are customizable as well. The integrated search function allows to search in a single group or the complete database. -KeePassX offers a little utility for secure password generation. The password generator is very customizable, fast and easy to use. Especially someone who generates passwords frequently will appreciate this feature. +KeePassX saves many different information e.g. user names, passwords, urls, attachments and comments in one single database. +For a better management user-defined titles and icons can be specified for each single entry. +Furthermore the entries are sorted in groups, which are customizable as well. The integrated search function allows to search in a single group or the complete database. +KeePassX offers a little utility for secure password generation. The password generator is very customizable, fast and easy to use. +Especially someone who generates passwords frequently will appreciate this feature. -The complete database is always encrypted either with AES (alias Rijndael) or Twofish encryption algorithm using a 256 bit key. Therefore the saved information can be considered as quite safe. KeePassX uses a database format that is compatible with [KeePass Password Safe](http://keepass.info/). This makes the use of that application even more favorable. +The complete database is always encrypted either with AES (alias Rijndael) or Twofish encryption algorithm using a 256 bit key. +Therefore the saved information can be considered as quite safe. KeePassX uses a database format that is compatible with [KeePass Password Safe](http://keepass.info/). +This makes the use of that application even more favorable. ## Install -KeePassX can be downloaded and installed using an assortment of installers available on the main [KeePassX website](http://www.keepassx.org).KeePassX can also be installed from the official repositories of many Linux repositories. If you wish to build KeePassX from source, rather than rely on the pre-compiled binaries, you may wish to read up on the _From Source_ section. +KeePassX can be downloaded and installed using an assortment of installers available on the main [KeePassX website](http://www.keepassx.org). +KeePassX can also be installed from the official repositories of many Linux repositories. +If you wish to build KeePassX from source, rather than rely on the pre-compiled binaries, you may wish to read up on the _From Source_ section. ### Debian @@ -32,7 +40,8 @@ sudo yum install keepassx ### Windows -Download the Windows installer from the KeePassX [download](https://www.keepassx.org/downloads) page. Once downloaded, double click on the file to execute the installer. +Download the Windows installer from the KeePassX [download](https://www.keepassx.org/downloads) page. +Once downloaded, double click on the file to execute the installer. ### From Source @@ -67,7 +76,9 @@ More detailed instructions available in the INSTALL file. ## Contribute -Coordination of work between developers is handled through the [KeePassX development](https://www.keepassx.org/dev) site. Requests for enhancements, or reports of bugs encountered, can also be reported through the KeePassX development site. However, members of the open-source community are encouraged to submit pull requests directly through GitLab. +Coordination of work between developers is handled through the [KeePassX development](https://www.keepassx.org/dev) site. +Requests for enhancements, or reports of bugs encountered, can also be reported through the KeePassX development site. +However, members of the open-source community are encouraged to submit pull requests directly through GitLab. ### Clone Repository @@ -87,7 +98,8 @@ git pull ### Feature Requests -We're always looking for suggestions to improve our application. If you have a suggestion for improving an existing feature, or would like to suggest a completely new feature for KeePassX, please file a ticket on the [KeePassX development](https://www.keepassx.org/dev) site. +We're always looking for suggestions to improve our application. If you have a suggestion for improving an existing feature, +or would like to suggest a completely new feature for KeePassX, please file a ticket on the [KeePassX development](https://www.keepassx.org/dev) site. ### Bug Reports From 5cc33343256c4169a7773d33c0af8890fba77297 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 30 Nov 2014 23:25:04 +0100 Subject: [PATCH 60/71] Small README corrections. --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5096b11fb..493c63345 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ If you wish to build KeePassX from source, rather than rely on the pre-compiled To install KeePassX from the Debian repository: ```bash -sudo apt-get update sudo apt-get install keepassx ``` @@ -38,9 +37,9 @@ Install KeePassX from the Red Hat (or CentOS) repository: sudo yum install keepassx ``` -### Windows +### Windows / Mac OS X -Download the Windows installer from the KeePassX [download](https://www.keepassx.org/downloads) page. +Download the installer from the KeePassX [download](https://www.keepassx.org/downloads) page. Once downloaded, double click on the file to execute the installer. ### From Source @@ -76,9 +75,9 @@ More detailed instructions available in the INSTALL file. ## Contribute -Coordination of work between developers is handled through the [KeePassX development](https://www.keepassx.org/dev) site. +Coordination of work between developers is handled through the [KeePassX development](https://www.keepassx.org/dev/) site. Requests for enhancements, or reports of bugs encountered, can also be reported through the KeePassX development site. -However, members of the open-source community are encouraged to submit pull requests directly through GitLab. +However, members of the open-source community are encouraged to submit pull requests directly through GitHub. ### Clone Repository @@ -99,11 +98,11 @@ git pull ### Feature Requests We're always looking for suggestions to improve our application. If you have a suggestion for improving an existing feature, -or would like to suggest a completely new feature for KeePassX, please file a ticket on the [KeePassX development](https://www.keepassx.org/dev) site. +or would like to suggest a completely new feature for KeePassX, please file a ticket on the [KeePassX development](https://www.keepassx.org/dev/) site. ### Bug Reports -Our software isn't always perfect, but we strive to always improve our work. You may file bug reports on the [KeePassX development](https://www.keepassx.org/dev) site. +Our software isn't always perfect, but we strive to always improve our work. You may file bug reports on the [KeePassX development](https://www.keepassx.org/dev/) site. ### Pull Requests From 889c742a3351bd63d8bbc8a89e8071aed7d66616 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 30 Nov 2014 23:31:25 +0100 Subject: [PATCH 61/71] Expand the build-dependency section of the README. --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 493c63345..ac69c5721 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,21 @@ Once downloaded, double click on the file to execute the installer. The following tools must exist within your PATH: * make -* cmake +* cmake (>= 2.6.4) * g++ or clang++ +The following libraries are required: + +* Qt 4 (>= 4.6) +* libgcrypt +* zlib + +On Debian you can install them with: + +```bash +sudo apt-get install build-essential cmake libqt4-dev libgcrypt11-dev zlib1g-dev +``` + #### Build Steps To compile from source: From dd2fbebb8145ab012656d5cdd59da2a401ebc139 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 30 Nov 2014 23:34:16 +0100 Subject: [PATCH 62/71] Add a translations section to the README. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index ac69c5721..9621448ba 100644 --- a/README.md +++ b/README.md @@ -121,3 +121,8 @@ Our software isn't always perfect, but we strive to always improve our work. You Along with our desire to hear your feedback and suggestions, we're also interested in accepting direct assistance in the form of code. Issue merge requests against our [GitHub repository](https://github.com/keepassx/keepassx). + +### Translations + +Translations are managed on [Transifex](https://www.transifex.com/projects/p/keepassx/) which offers a web interface. +Please join an existing language team or request a new one if there is none. From 226c061c01b708023a1dcabaf4530607afa49599 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 30 Nov 2014 23:38:08 +0100 Subject: [PATCH 63/71] Remove Twofish reference from the README. It isn't supported anymore. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9621448ba..d4d02cd0d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Furthermore the entries are sorted in groups, which are customizable as well. Th KeePassX offers a little utility for secure password generation. The password generator is very customizable, fast and easy to use. Especially someone who generates passwords frequently will appreciate this feature. -The complete database is always encrypted either with AES (alias Rijndael) or Twofish encryption algorithm using a 256 bit key. +The complete database is always encrypted either with AES (aka Rijndael) encryption algorithm using a 256 bit key. Therefore the saved information can be considered as quite safe. KeePassX uses a database format that is compatible with [KeePass Password Safe](http://keepass.info/). This makes the use of that application even more favorable. From 71d39865b3a134d67675693ffe400b267cf5d15d Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Mon, 1 Dec 2014 21:52:51 +0100 Subject: [PATCH 64/71] Introduce a strict mode in KeePass2XmlReader. Many errors are now ignored when not in strict mode so we can still parse files that have been written by broken/incomplete implementations. --- src/format/KeePass2XmlReader.cpp | 61 ++++++++++++++++++++++++----- src/format/KeePass2XmlReader.h | 2 + tests/TestDeletedObjects.cpp | 1 + tests/TestKeePass2XmlReader.cpp | 31 ++++++++++++--- tests/data/BrokenDeletedObjects.xml | 27 +++++++++++++ tests/data/BrokenGroupReference.xml | 20 ++++++++++ tests/data/BrokenNoEntryUuid.xml | 1 + 7 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 tests/data/BrokenDeletedObjects.xml create mode 100644 tests/data/BrokenGroupReference.xml diff --git a/src/format/KeePass2XmlReader.cpp b/src/format/KeePass2XmlReader.cpp index 383269f68..f3a7c8541 100644 --- a/src/format/KeePass2XmlReader.cpp +++ b/src/format/KeePass2XmlReader.cpp @@ -35,9 +35,15 @@ KeePass2XmlReader::KeePass2XmlReader() , m_db(Q_NULLPTR) , m_meta(Q_NULLPTR) , m_error(false) + , m_strictMode(false) { } +void KeePass2XmlReader::setStrictMode(bool strictMode) +{ + m_strictMode = strictMode; +} + void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream) { m_error = false; @@ -493,7 +499,12 @@ Group* KeePass2XmlReader::parseGroup() if (m_xml.name() == "UUID") { Uuid uuid = readUuid(); if (uuid.isNull()) { - raiseError("Null group uuid"); + if (m_strictMode) { + raiseError("Null group uuid"); + } + else { + group->setUuid(Uuid::random()); + } } else { group->setUuid(uuid); @@ -508,7 +519,9 @@ Group* KeePass2XmlReader::parseGroup() else if (m_xml.name() == "IconID") { int iconId = readNumber(); if (iconId < 0) { - raiseError("Invalid group icon number"); + if (m_strictMode) { + raiseError("Invalid group icon number"); + } } else { if (iconId >= DatabaseIcons::IconCount) { @@ -584,6 +597,10 @@ Group* KeePass2XmlReader::parseGroup() } } + if (group->uuid().isNull() && !m_strictMode) { + group->setUuid(Uuid::random()); + } + if (!group->uuid().isNull()) { Group* tmpGroup = group; group = getGroup(tmpGroup->uuid()); @@ -630,7 +647,9 @@ void KeePass2XmlReader::parseDeletedObject() if (m_xml.name() == "UUID") { Uuid uuid = readUuid(); if (uuid.isNull()) { - raiseError("Null DeleteObject uuid"); + if (m_strictMode) { + raiseError("Null DeleteObject uuid"); + } } else { delObj.uuid = uuid; @@ -647,7 +666,7 @@ void KeePass2XmlReader::parseDeletedObject() if (!delObj.uuid.isNull() && !delObj.deletionTime.isNull()) { m_db->addDeletedObject(delObj); } - else { + else if (m_strictMode) { raiseError("Missing DeletedObject uuid or time"); } } @@ -665,7 +684,12 @@ Entry* KeePass2XmlReader::parseEntry(bool history) if (m_xml.name() == "UUID") { Uuid uuid = readUuid(); if (uuid.isNull()) { - raiseError("Null entry uuid"); + if (m_strictMode) { + raiseError("Null entry uuid"); + } + else { + entry->setUuid(Uuid::random()); + } } else { entry->setUuid(uuid); @@ -674,7 +698,9 @@ Entry* KeePass2XmlReader::parseEntry(bool history) else if (m_xml.name() == "IconID") { int iconId = readNumber(); if (iconId < 0) { - raiseError("Invalud entry icon number"); + if (m_strictMode) { + raiseError("Invalud entry icon number"); + } } else { entry->setIcon(iconId); @@ -726,6 +752,10 @@ Entry* KeePass2XmlReader::parseEntry(bool history) } } + if (entry->uuid().isNull() && !m_strictMode) { + entry->setUuid(Uuid::random()); + } + if (!entry->uuid().isNull()) { if (history) { entry->setUpdateTimeinfo(false); @@ -986,7 +1016,12 @@ QDateTime KeePass2XmlReader::readDateTime() QDateTime dt = QDateTime::fromString(str, Qt::ISODate); if (!dt.isValid()) { - raiseError("Invalid date time value"); + if (m_strictMode) { + raiseError("Invalid date time value"); + } + else { + dt = Tools::currentDateTimeUtc(); + } } return dt; @@ -1001,7 +1036,9 @@ QColor KeePass2XmlReader::readColor() } if (colorStr.length() != 7 || colorStr[0] != '#') { - raiseError("Invalid color value"); + if (m_strictMode) { + raiseError("Invalid color value"); + } return QColor(); } @@ -1011,7 +1048,9 @@ QColor KeePass2XmlReader::readColor() bool ok; int rgbPart = rgbPartStr.toInt(&ok, 16); if (!ok || rgbPart > 255) { - raiseError("Invalid color rgb part"); + if (m_strictMode) { + raiseError("Invalid color rgb part"); + } return QColor(); } @@ -1043,7 +1082,9 @@ Uuid KeePass2XmlReader::readUuid() { QByteArray uuidBin = readBinary(); if (uuidBin.length() != Uuid::Length) { - raiseError("Invalid uuid value"); + if (m_strictMode) { + raiseError("Invalid uuid value"); + } return Uuid(); } else { diff --git a/src/format/KeePass2XmlReader.h b/src/format/KeePass2XmlReader.h index 4520d42bf..ca311b0e4 100644 --- a/src/format/KeePass2XmlReader.h +++ b/src/format/KeePass2XmlReader.h @@ -47,6 +47,7 @@ public: bool hasError(); QString errorString(); QByteArray headerHash(); + void setStrictMode(bool strictMode); private: bool parseKeePassFile(); @@ -95,6 +96,7 @@ private: QByteArray m_headerHash; bool m_error; QString m_errorStr; + bool m_strictMode; }; #endif // KEEPASSX_KEEPASS2XMLREADER_H diff --git a/tests/TestDeletedObjects.cpp b/tests/TestDeletedObjects.cpp index 277dbcb6b..cf9e3d104 100644 --- a/tests/TestDeletedObjects.cpp +++ b/tests/TestDeletedObjects.cpp @@ -90,6 +90,7 @@ void TestDeletedObjects::createAndDelete(Database* db, int delObjectsSize) void TestDeletedObjects::testDeletedObjectsFromFile() { KeePass2XmlReader reader; + reader.setStrictMode(true); QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"); Database* db = reader.readDatabase(xmlFile); diff --git a/tests/TestKeePass2XmlReader.cpp b/tests/TestKeePass2XmlReader.cpp index 8e87d6744..e88e990a6 100644 --- a/tests/TestKeePass2XmlReader.cpp +++ b/tests/TestKeePass2XmlReader.cpp @@ -71,6 +71,7 @@ void TestKeePass2XmlReader::initTestCase() QVERIFY(Crypto::init()); KeePass2XmlReader reader; + reader.setStrictMode(true); QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"); m_db = reader.readDatabase(xmlFile); QVERIFY(m_db); @@ -357,23 +358,41 @@ void TestKeePass2XmlReader::testDeletedObjects() void TestKeePass2XmlReader::testBroken() { QFETCH(QString, baseName); + QFETCH(bool, strictMode); + QFETCH(bool, expectError); KeePass2XmlReader reader; + reader.setStrictMode(strictMode); QString xmlFile = QString("%1/%2.xml").arg(KEEPASSX_TEST_DATA_DIR, baseName); QVERIFY(QFile::exists(xmlFile)); QScopedPointer db(reader.readDatabase(xmlFile)); - QVERIFY(reader.hasError()); + if (reader.hasError()) { + qWarning("Reader error: %s", qPrintable(reader.errorString())); + } + QCOMPARE(reader.hasError(), expectError); } void TestKeePass2XmlReader::testBroken_data() { QTest::addColumn("baseName"); + QTest::addColumn("strictMode"); + QTest::addColumn("expectError"); - QTest::newRow("BrokenNoGroupUuid") << "BrokenNoGroupUuid"; - QTest::newRow("BrokenNoEntryUuid") << "BrokenNoEntryUuid"; - QTest::newRow("BrokenNoRootGroup") << "BrokenNoRootGroup"; - QTest::newRow("BrokenTwoRoots") << "BrokenTwoRoots"; - QTest::newRow("BrokenTwoRootGroups") << "BrokenTwoRootGroups"; + // testfile strict? error? + QTest::newRow("BrokenNoGroupUuid (strict)") << "BrokenNoGroupUuid" << true << true; + QTest::newRow("BrokenNoGroupUuid (not strict)") << "BrokenNoGroupUuid" << false << false; + QTest::newRow("BrokenNoEntryUuid (strict)") << "BrokenNoEntryUuid" << true << true; + QTest::newRow("BrokenNoEntryUuid (not strict)") << "BrokenNoEntryUuid" << false << false; + QTest::newRow("BrokenNoRootGroup (strict)") << "BrokenNoRootGroup" << true << true; + QTest::newRow("BrokenNoRootGroup (not strict)") << "BrokenNoRootGroup" << false << true; + QTest::newRow("BrokenTwoRoots (strict)") << "BrokenTwoRoots" << true << true; + QTest::newRow("BrokenTwoRoots (not strict)") << "BrokenTwoRoots" << false << true; + QTest::newRow("BrokenTwoRootGroups (strict)") << "BrokenTwoRootGroups" << true << true; + QTest::newRow("BrokenTwoRootGroups (not strict)") << "BrokenTwoRootGroups" << false << true; + QTest::newRow("BrokenGroupReference (strict)") << "BrokenGroupReference" << true << false; + QTest::newRow("BrokenGroupReference (not strict)") << "BrokenGroupReference" << false << false; + QTest::newRow("BrokenDeletedObjects (strict)") << "BrokenDeletedObjects" << true << true; + QTest::newRow("BrokenDeletedObjects (not strict)") << "BrokenDeletedObjects" << false << false; } void TestKeePass2XmlReader::cleanupTestCase() diff --git a/tests/data/BrokenDeletedObjects.xml b/tests/data/BrokenDeletedObjects.xml new file mode 100644 index 000000000..89506aa62 --- /dev/null +++ b/tests/data/BrokenDeletedObjects.xml @@ -0,0 +1,27 @@ + + + + + lmU+9n0aeESKZvcEze+bRg== + Test + + AaUYVdXsI02h4T1RiAlgtg== + + Title + Sample Entry 1 + + + + + + + 2010-08-25T16:14:12Z + + + + 5K/bzWCSmkCv5OZxYl4N/w== + + + + + diff --git a/tests/data/BrokenGroupReference.xml b/tests/data/BrokenGroupReference.xml new file mode 100644 index 000000000..b3207e1ac --- /dev/null +++ b/tests/data/BrokenGroupReference.xml @@ -0,0 +1,20 @@ + + + + True + 6w7wZdhAp0qVlXjkemuCYw== + + + + lmU+9n0aeESKZvcEze+bRg== + Test + + AaUYVdXsI02h4T1RiAlgtg== + + Title + Sample Entry 1 + + + + + diff --git a/tests/data/BrokenNoEntryUuid.xml b/tests/data/BrokenNoEntryUuid.xml index 38ab96f2e..595f836f7 100644 --- a/tests/data/BrokenNoEntryUuid.xml +++ b/tests/data/BrokenNoEntryUuid.xml @@ -9,6 +9,7 @@ Title Sample Entry 1 + From 2adc64939ff953f72d0786840d2c19469f247e05 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Mon, 1 Dec 2014 22:45:11 +0100 Subject: [PATCH 65/71] Correct handling of keyfile argument. Closes #223. --- src/gui/DatabaseOpenWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 37bbce747..81c195712 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -87,7 +87,7 @@ void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile) m_ui->editPassword->setText(pw); } if (!keyFile.isEmpty()) { - m_ui->checkKeyFile->setText(keyFile); + m_ui->comboKeyFile->setEditText(keyFile); } openDatabase(); From 7f412fbd7fab0788885e8375f66756686af4719f Mon Sep 17 00:00:00 2001 From: Rob Speller Date: Mon, 1 Dec 2014 22:21:49 +0000 Subject: [PATCH 66/71] Remove confusing grammar Sentence still had 'either' because the sentence used to include twofish --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4d02cd0d..9314383b2 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Furthermore the entries are sorted in groups, which are customizable as well. Th KeePassX offers a little utility for secure password generation. The password generator is very customizable, fast and easy to use. Especially someone who generates passwords frequently will appreciate this feature. -The complete database is always encrypted either with AES (aka Rijndael) encryption algorithm using a 256 bit key. +The complete database is always encrypted with the AES (aka Rijndael) encryption algorithm using a 256 bit key. Therefore the saved information can be considered as quite safe. KeePassX uses a database format that is compatible with [KeePass Password Safe](http://keepass.info/). This makes the use of that application even more favorable. From c39898dad95ef711e8abfb92961cef941a697b86 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Wed, 3 Dec 2014 21:50:17 +0100 Subject: [PATCH 67/71] Support opening attachments directly. --- src/gui/entry/EditEntryWidget.cpp | 39 ++++++++++++++++++++++++ src/gui/entry/EditEntryWidget.h | 2 ++ src/gui/entry/EditEntryWidgetAdvanced.ui | 9 +++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 465f5d401..fcb53d6a0 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "core/Config.h" #include "core/Database.h" @@ -107,7 +108,9 @@ void EditEntryWidget::setupAdvanced() m_attachmentsModel->setEntryAttachments(m_entryAttachments); m_advancedUi->attachmentsView->setModel(m_attachmentsModel); + connect(m_advancedUi->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex))); connect(m_advancedUi->saveAttachmentButton, SIGNAL(clicked()), SLOT(saveCurrentAttachment())); + connect(m_advancedUi->openAttachmentButton, SIGNAL(clicked()), SLOT(openCurrentAttachment())); connect(m_advancedUi->addAttachmentButton, SIGNAL(clicked()), SLOT(insertAttachment())); connect(m_advancedUi->removeAttachmentButton, SIGNAL(clicked()), SLOT(removeCurrentAttachment())); @@ -636,6 +639,42 @@ void EditEntryWidget::saveCurrentAttachment() } } +void EditEntryWidget::openAttachment(const QModelIndex& index) +{ + if (!index.isValid()) { + Q_ASSERT(false); + return; + } + + QString filename = m_attachmentsModel->keyByIndex(index); + QByteArray attachmentData = m_entryAttachments->value(filename); + + // tmp file will be removed once the database (or the application) has been closed + QString tmpFileTemplate = QDir::temp().absoluteFilePath(filename); + QTemporaryFile* file = new QTemporaryFile(tmpFileTemplate, this); + + if (!file->open()) { + MessageBox::warning(this, tr("Error"), + tr("Unable to save the attachment:\n").append(file->errorString())); + return; + } + + if (file->write(attachmentData) != attachmentData.size()) { + MessageBox::warning(this, tr("Error"), + tr("Unable to save the attachment:\n").append(file->errorString())); + return; + } + + QDesktopServices::openUrl(QUrl::fromLocalFile(file->fileName())); +} + +void EditEntryWidget::openCurrentAttachment() +{ + QModelIndex index = m_advancedUi->attachmentsView->currentIndex(); + + openAttachment(index); +} + void EditEntryWidget::removeCurrentAttachment() { Q_ASSERT(!m_history); diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index 4e3d82efa..903029872 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -76,6 +76,8 @@ private Q_SLOTS: void updateCurrentAttribute(); void insertAttachment(); void saveCurrentAttachment(); + void openAttachment(const QModelIndex& index); + void openCurrentAttachment(); void removeCurrentAttachment(); void updateAutoTypeEnabled(); void insertAutoTypeAssoc(); diff --git a/src/gui/entry/EditEntryWidgetAdvanced.ui b/src/gui/entry/EditEntryWidgetAdvanced.ui index ff7ccbb73..551ea4afe 100644 --- a/src/gui/entry/EditEntryWidgetAdvanced.ui +++ b/src/gui/entry/EditEntryWidgetAdvanced.ui @@ -7,7 +7,7 @@ 0 0 400 - 299 + 315 @@ -106,6 +106,13 @@ + + + + Open + + + From 876a75b572ea045ce119047a74b6c21db663b001 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Wed, 3 Dec 2014 23:26:42 +0100 Subject: [PATCH 68/71] Disable attachment buttons when none is selected. --- src/gui/entry/EditEntryWidget.cpp | 13 ++++++++++++- src/gui/entry/EditEntryWidget.h | 1 + src/gui/entry/EditEntryWidgetAdvanced.ui | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index fcb53d6a0..b7e2fdbe5 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -108,6 +108,8 @@ void EditEntryWidget::setupAdvanced() m_attachmentsModel->setEntryAttachments(m_entryAttachments); m_advancedUi->attachmentsView->setModel(m_attachmentsModel); + connect(m_advancedUi->attachmentsView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + SLOT(updateAttachmentButtonsEnabled(QModelIndex))); connect(m_advancedUi->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex))); connect(m_advancedUi->saveAttachmentButton, SIGNAL(clicked()), SLOT(saveCurrentAttachment())); connect(m_advancedUi->openAttachmentButton, SIGNAL(clicked()), SLOT(openCurrentAttachment())); @@ -235,6 +237,15 @@ void EditEntryWidget::useExpiryPreset(QAction* action) m_mainUi->expireDatePicker->setDateTime(expiryDateTime); } +void EditEntryWidget::updateAttachmentButtonsEnabled(const QModelIndex& current) +{ + bool enable = current.isValid(); + + m_advancedUi->saveAttachmentButton->setEnabled(enable); + m_advancedUi->openAttachmentButton->setEnabled(enable); + m_advancedUi->removeAttachmentButton->setEnabled(enable && !m_history); +} + QString EditEntryWidget::entryTitle() const { if (m_entry) { @@ -284,7 +295,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) m_mainUi->tooglePasswordGeneratorButton->setChecked(false); m_mainUi->passwordGenerator->reset(); m_advancedUi->addAttachmentButton->setEnabled(!m_history); - m_advancedUi->removeAttachmentButton->setEnabled(!m_history); + updateAttachmentButtonsEnabled(m_advancedUi->attachmentsView->currentIndex()); m_advancedUi->addAttributeButton->setEnabled(!m_history); m_advancedUi->editAttributeButton->setEnabled(false); m_advancedUi->removeAttributeButton->setEnabled(false); diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index 903029872..144347629 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -93,6 +93,7 @@ private Q_SLOTS: void histEntryActivated(const QModelIndex& index); void updateHistoryButtons(const QModelIndex& current, const QModelIndex& previous); void useExpiryPreset(QAction* action); + void updateAttachmentButtonsEnabled(const QModelIndex& current); private: void setupMain(); diff --git a/src/gui/entry/EditEntryWidgetAdvanced.ui b/src/gui/entry/EditEntryWidgetAdvanced.ui index 551ea4afe..93e15260a 100644 --- a/src/gui/entry/EditEntryWidgetAdvanced.ui +++ b/src/gui/entry/EditEntryWidgetAdvanced.ui @@ -101,6 +101,9 @@ + + false + Remove @@ -108,6 +111,9 @@ + + false + Open @@ -115,6 +121,9 @@ + + false + Save From 3ea0592b531daf2f2119ebaa379eea48aa3d948a Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Wed, 3 Dec 2014 23:36:24 +0100 Subject: [PATCH 69/71] Add hasKey() convenience methods. --- src/core/EntryAttachments.cpp | 5 +++++ src/core/EntryAttachments.h | 1 + src/core/EntryAttributes.cpp | 5 +++++ src/core/EntryAttributes.h | 1 + 4 files changed, 12 insertions(+) diff --git a/src/core/EntryAttachments.cpp b/src/core/EntryAttachments.cpp index e39bf4189..7bd080bfa 100644 --- a/src/core/EntryAttachments.cpp +++ b/src/core/EntryAttachments.cpp @@ -27,6 +27,11 @@ QList EntryAttachments::keys() const return m_attachments.keys(); } +bool EntryAttachments::hasKey(const QString& key) const +{ + return m_attachments.keys().contains(key); +} + QList EntryAttachments::values() const { return m_attachments.values(); diff --git a/src/core/EntryAttachments.h b/src/core/EntryAttachments.h index b482f42e3..3446b315c 100644 --- a/src/core/EntryAttachments.h +++ b/src/core/EntryAttachments.h @@ -30,6 +30,7 @@ class EntryAttachments : public QObject public: explicit EntryAttachments(QObject* parent = Q_NULLPTR); QList keys() const; + bool hasKey(const QString& key) const; QList values() const; QByteArray value(const QString& key) const; void set(const QString& key, const QByteArray& value); diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp index a950390cd..01dcf9624 100644 --- a/src/core/EntryAttributes.cpp +++ b/src/core/EntryAttributes.cpp @@ -36,6 +36,11 @@ QList EntryAttributes::keys() const return m_attributes.keys(); } +bool EntryAttributes::hasKey(const QString& key) const +{ + return m_attributes.keys().contains(key); +} + QList EntryAttributes::customKeys() { QList customKeys; diff --git a/src/core/EntryAttributes.h b/src/core/EntryAttributes.h index 0eba4332c..334eb0ab3 100644 --- a/src/core/EntryAttributes.h +++ b/src/core/EntryAttributes.h @@ -32,6 +32,7 @@ class EntryAttributes : public QObject public: explicit EntryAttributes(QObject* parent = Q_NULLPTR); QList keys() const; + bool hasKey(const QString& key) const; QList customKeys(); QString value(const QString& key) const; bool isProtected(const QString& key) const; From eb22f0a2d87e9401629d87e27f6d962fd4ec6f6c Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Wed, 3 Dec 2014 23:36:53 +0100 Subject: [PATCH 70/71] Raise an error when parsing duplicate attributes/attachments. --- src/format/KeePass2XmlReader.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/format/KeePass2XmlReader.cpp b/src/format/KeePass2XmlReader.cpp index f3a7c8541..d1737d52a 100644 --- a/src/format/KeePass2XmlReader.cpp +++ b/src/format/KeePass2XmlReader.cpp @@ -825,7 +825,13 @@ void KeePass2XmlReader::parseEntryString(Entry* entry) } if (keySet && valueSet) { - entry->attributes()->set(key, value, protect); + // the default attributes are always there so additionally check if it's empty + if (entry->attributes()->hasKey(key) && !entry->attributes()->value(key).isEmpty()) { + raiseError("Duplicate custom attribute found"); + } + else { + entry->attributes()->set(key, value, protect); + } } else { raiseError("Entry string key or value missing"); @@ -874,7 +880,12 @@ QPair KeePass2XmlReader::parseEntryBinary(Entry* entry) } if (keySet && valueSet) { - entry->attachments()->set(key, value); + if (entry->attachments()->hasKey(key)) { + raiseError("Duplicate attachment found"); + } + else { + entry->attachments()->set(key, value); + } } else { raiseError("Entry binary key or value missing"); From e4758c19848989b3c56d10de6aa53749cb8bbb2b Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Mon, 22 Dec 2014 23:47:16 +0900 Subject: [PATCH 71/71] Fix the temporary filename template so that the original suffix is preserved. --- 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 b7e2fdbe5..9621ca9e4 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -661,7 +661,7 @@ void EditEntryWidget::openAttachment(const QModelIndex& index) QByteArray attachmentData = m_entryAttachments->value(filename); // tmp file will be removed once the database (or the application) has been closed - QString tmpFileTemplate = QDir::temp().absoluteFilePath(filename); + QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename)); QTemporaryFile* file = new QTemporaryFile(tmpFileTemplate, this); if (!file->open()) {