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 00000000..e6a46635 --- /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 c48bc955..4176408d 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 36fb656f..e0c6582e 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 419028ab..d6d48c5e 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 b535a9b8..d2b4e9e0 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 e0c6582e..98bd5aee 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 706fd2d5..e904426a 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 f71f80bc..eb79ec96 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 b5f056aa..2a1df439 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 80b81082..92aae5cd 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 c48bc955..133a03a9 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 ee308268..ca0f74cb 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 d0980593..2aea2636 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 f9aa8b68..3d89c68e 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 47ac0909..0b2f3b25 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 fba7fde1..d46a5596 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 133a03a9..76f0e6f8 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 0b2f3b25..c09a8366 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 d6d48c5e..c70348a9 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 d2b4e9e0..22e3992d 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 eb79ec96..4691ed65 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 2a1df439..ba2919e9 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 8c2ba06d..e529cc42 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 c70348a9..72fe0ef7 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 22e3992d..b159cbd1 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 e529cc42..c15d16e5 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 9261a065..8e8fceb3 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 72fe0ef7..0d0ca737 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 b159cbd1..155942a5 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 0d0ca737..ba7ca747 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 155942a5..5e77f01b 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 98bd5aee..c83f3167 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 d57153e5..9af937ea 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 00000000..60cfaea4 --- /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 00000000..56ed7e4e --- /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 4c04b484..9ebf7bb8 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 4e08f5b3..715403c0 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 ba7ca747..fc533a3f 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 8df0050a..be37a5de 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 00000000..308a26e6 --- /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 00000000..d261d5dd --- /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 86b55b70..816f7ce7 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 895c2cc5..2e401990 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 308a26e6..564d71a7 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 d261d5dd..3e9d4f66 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 55f54328..4f977915 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 c2c2938c..ae07ed45 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 60cfaea4..82a553e3 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 56ed7e4e..246538cb 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 564d71a7..fec2a4ae 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 3e9d4f66..194e29f0 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 fec2a4ae..b37992df 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 194e29f0..7c45451d 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 9af937ea..b09501cf 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 9ebf7bb8..517f8cb0 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 715403c0..7391f886 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 00000000..0601ca4b --- /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 00000000..bd57a4b1 --- /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 be37a5de..d3f51ac4 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 00000000..73bf407e --- /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 00000000..15f9a7c3 --- /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 816f7ce7..0b4564f2 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 2e401990..c612a3ac 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 00000000..dedb1c8a --- /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 0601ca4b..1f76fb74 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 bd57a4b1..58c5efeb 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 73bf407e..16b4b23c 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 c09a8366..4f4350e7 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 4f258a17..d189d456 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 914096ce..5967e58e 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 15f398f1..481250a2 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 fab63db6..5347a3c5 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 b37992df..9f7ca139 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 0b4564f2..32398f01 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 a16386c7..fe7018e8 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 ab7d386a..b062908f 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 3ec4e783..423087c9 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 74a15406..328decb3 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 6b1ee1e9..3dd87c4f 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 bbc4992a..f00b38e8 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 ca57db95..d9935804 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 ec9a35b4..d5cba4ab 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 864ea1c3..85a6bf23 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 d487862c..4e2c6350 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 bccee0ec..6be714ef 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 8ac570e1..40ab702d 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 b47a0057..9f05db1b 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 e06125b8..dc9991db 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 d3f51ac4..c094f823 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 fc533a3f..c42dd5e0 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 c42dd5e0..c111f8d5 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 4691ed65..6c43df3b 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 ba2919e9..c11d0417 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 c111f8d5..00125a1c 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 c3d59b8d..ce4845dc 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 b09501cf..3de2422a 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 c15d16e5..7f998461 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 8e8fceb3..25d34f30 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 00125a1c..8173fe96 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 5e77f01b..628a344e 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 00000000..2a071e30 --- /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 00000000..574519bd --- /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 8173fe96..f3738c73 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 628a344e..4a5765df 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 a4d04c5d..51dc39e5 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 4a5765df..53906f34 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 53906f34..2e2077bc 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 f3738c73..131776e4 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 131776e4..9a09814b 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 c83f3167..8c72bbff 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 9a09814b..5379e1a1 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 2e2077bc..981784d4 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 2a071e30..c9e60bb9 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 574519bd..26911837 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 97319252..37bbce74 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 5f23d807..96ddf13f 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 4a674332..4a866680 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 5379e1a1..0779297e 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 981784d4..cbab175e 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 c9e60bb9..66b8492e 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 26911837..f6a87cd9 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 342f191a..5c75ef9e 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 0779297e..cc9c5fd1 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 26314d36..9e857453 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 4a866680..465f5d40 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 6c43df3b..cd2c6fba 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 00000000..015acf4b --- /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 7069c6c4..0e2b7fa9 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 00000000..b1aa8785 --- /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 00000000..fe074ae6 --- /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 00000000..eccd5575 --- /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 00000000..006f6f6e --- /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 00000000..6828dc82 --- /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 3de2422a..7ffc168b 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 9a3f4951..805700a9 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 76f0e6f8..8a455e27 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 00000000..bc4d2b62 --- /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 00000000..4bc4fca3 --- /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 2aea2636..374e52d9 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 3d89c68e..f3dc079e 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 abe7ceb4..d5e64b92 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 92aae5cd..aac0c0cf 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 6be714ef..443db529 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 eb77d2b3..7d8f71fa 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 bc2a19d3..8b6ea69f 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 35642ebc..280fb776 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 1e28002b..13c3c20e 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 63f11777..9926f14b 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 d5e64b92..b9659e45 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 4f4350e7..818f57c9 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 d189d456..eb26ca83 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 5967e58e..277dbcb6 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 481250a2..477e83b8 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 5347a3c5..7ba886bc 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 16b4b23c..d703e02f 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 32398f01..507cf155 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 fe7018e8..32a4b8e9 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 b062908f..09179fef 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 423087c9..249a3657 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 328decb3..7963e9af 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 3dd87c4f..d6cb70c7 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 f00b38e8..cf4ab1c5 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 d9935804..8e87d674 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 d5cba4ab..d6758d65 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 85a6bf23..e275e837 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 9f05db1b..6d4e94f6 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 51dc39e5..326c3497 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 401f68bc..87e3f248 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 beee71dc..f5d2a19a 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 028ccff2..bde991bb 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 8c72bbff..101121b0 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 374e52d9..929db377 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 101121b0..48baa4cf 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 06a1e329..6a175c72 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 d47541ed..03b5129e 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 48baa4cf..dd77989c 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 e904426a..b966703d 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 929db377..a7863eac 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 f3dc079e..cbad7e5e 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 b9659e45..2bdef5bd 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 af8e9949..f11311a9 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 85326961..b68eef68 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 6a175c72..843fbfa4 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 00000000..751dbc28 --- /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 fe074ae6..5433c3ce 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 eccd5575..a7b5b109 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 00000000..4f91b75a --- /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 00000000..aa6320ee --- /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 00000000..2a3ba790 --- /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 280fb776..3532c46f 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 751dbc28..15af85bf 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 cc9c5fd1..df321424 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 cbab175e..821a21d5 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 dd77989c..1933ef4d 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 13c5d679..838bb5f3 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 326c3497..592f57ec 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 df321424..dd63b4df 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 821a21d5..45de7462 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 1933ef4d..d48d0950 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 838bb5f3..09e1c412 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 592f57ec..26c8be07 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 03b5129e..48ea9f33 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 517f8cb0..e96b857b 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 a7863eac..33789ae7 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 cbad7e5e..04c603a3 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 e6a46635..5096b11f 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 5096b11f..493c6334 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 493c6334..ac69c572 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 ac69c572..9621448b 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 9621448b..d4d02cd0 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 383269f6..f3a7c854 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 4520d42b..ca311b0e 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 277dbcb6..cf9e3d10 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 8e87d674..e88e990a 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 00000000..89506aa6 --- /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 00000000..b3207e1a --- /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 38ab96f2..595f836f 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 37bbce74..81c19571 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 d4d02cd0..9314383b 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 465f5d40..fcb53d6a 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 4e3d82ef..90302987 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 ff7ccbb7..551ea4af 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 fcb53d6a..b7e2fdbe 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 90302987..14434762 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 551ea4af..93e15260 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 e39bf418..7bd080bf 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 b482f42e..3446b315 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 a950390c..01dcf962 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 0eba4332..334eb0ab 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 f3a7c854..d1737d52 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 b7e2fdbe..9621ca9e 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()) {