From e909d1b5945f0e82ac4a6a9edb6784b83a547e7e Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Tue, 19 Mar 2019 21:12:39 -0400 Subject: [PATCH 01/11] Correct invalid conditional check in release-tool --- release-tool | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/release-tool b/release-tool index a04ad5de..ab4128a3 100755 --- a/release-tool +++ b/release-tool @@ -810,10 +810,6 @@ build() { shift done - if [[ ${build_appsign} && ! -f ${build_key} ]]; then - exitError "--appsign specified with invalid key file\n" - fi - init OUTPUT_DIR="$(realpath "$OUTPUT_DIR")" @@ -908,7 +904,7 @@ build() { make ${MAKE_OPTIONS} package # Appsign the executables if desired - if [[ ${build_appsign} ]]; then + if ${build_appsign}; then logInfo "Signing executable files" appsign "-f" "./${APP_NAME}-${RELEASE_NAME}.dmg" "-k" "${build_key}" fi @@ -924,7 +920,7 @@ build() { mingw32-make ${MAKE_OPTIONS} preinstall # Appsign the executables if desired - if [[ ${build_appsign} ]]; then + if ${build_appsign} && [ -f ${build_key} ]; then logInfo "Signing executable files" appsign "-f" $(find src | grep -P '\.exe$|\.dll$') "-k" "${build_key}" fi From eb9ff677d0c681a86a9ef296f5a363549b94c42c Mon Sep 17 00:00:00 2001 From: Lars Wendler Date: Wed, 20 Mar 2019 11:53:21 +0100 Subject: [PATCH 02/11] src/gui/DatabaseWidget.cpp: Build fails without WITH_XC_KEESHARE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /var/tmp/portage/app-admin/keepassxc-2.4.0/work/keepassxc-2.4.0/src/gui/DatabaseWidget.cpp: In member function ‘void DatabaseWidget::search(const QString&)’: /var/tmp/portage/app-admin/keepassxc-2.4.0/work/keepassxc-2.4.0/src/gui/DatabaseWidget.cpp:1115:5: error: ‘m_shareLabel’ was not declared in this scope m_shareLabel->setVisible(false); ^~~~~~~~~~~~ /var/tmp/portage/app-admin/keepassxc-2.4.0/work/keepassxc-2.4.0/src/gui/DatabaseWidget.cpp:1115:5: note: suggested alternative: ‘m_searchingLabel’ m_shareLabel->setVisible(false); ^~~~~~~~~~~~ m_searchingLabel --- src/gui/DatabaseWidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 8728c331..e4196734 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1112,7 +1112,9 @@ void DatabaseWidget::search(const QString& searchtext) } m_searchingLabel->setVisible(true); +#ifdef WITH_XC_KEESHARE m_shareLabel->setVisible(false); +#endif emit searchModeActivated(); } From 4a0bb32c2ec13e142b5eec80423678f466a7b1a1 Mon Sep 17 00:00:00 2001 From: Lars Wendler Date: Fri, 8 Mar 2019 14:48:13 +0100 Subject: [PATCH 03/11] Don't call mandb There are other man implementations beside man-db so it is not even sure that the "mandb" binary even exists on all unices. Other than that, usually there's a cron job running "mandb" on a daily basis. --- src/cli/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index e59a911a..c3f97a2c 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -94,5 +94,4 @@ endif() if(APPLE OR UNIX) install(FILES keepassxc-cli.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/) - execute_process(COMMAND mandb -q) endif() From 76913a5dd1411fc911fd9a346a2d230d5cb6d18e Mon Sep 17 00:00:00 2001 From: Oirio Joshi Date: Thu, 21 Mar 2019 23:53:50 +0100 Subject: [PATCH 04/11] Snap: improve cursor theme --- snapcraft.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/snapcraft.yaml b/snapcraft.yaml index 16eb0890..6dd3ea2c 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -9,6 +9,12 @@ description: | confinement: strict base: core18 +plugs: + icon-themes: # fix mouse cursor theme + interface: content + target: $SNAP/data-dir/icons + default-provider: gtk-common-themes + apps: keepassxc: command: desktop-launch keepassxc @@ -67,6 +73,7 @@ parts: - libquazip5-1 - libusb-1.0-0 - qtwayland5 + - qt5-style-plugins # for mouse cursor theme fix override-build: | snapcraftctl build sed -i 's|Icon=keepassxc|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/keepassxc.png|g' $SNAPCRAFT_PART_INSTALL/usr/share/applications/org.keepassxc.KeePassXC.desktop From a58e3d5ee0113019adea182830b2fcc9e5387184 Mon Sep 17 00:00:00 2001 From: louib Date: Mon, 18 Feb 2019 20:17:28 -0500 Subject: [PATCH 05/11] Adding debug info to CLI. Adding debug info to the CLI and the general option of the main Qt app. Also took time to: * use `EXIT_SUCCESS`/`EXIT_FAILURE` constants for main.cpp (this is what is used in `src/cli`); * fixed `m_initalized` typo; * added info on debugging mode being disabled or not; * regrouped Qt related stuff in the debug output. --- src/cli/keepassxc-cli.1 | 5 +- src/cli/keepassxc-cli.cpp | 8 +++ src/core/Tools.cpp | 76 ++++++++++++++++++++++++++++ src/core/Tools.h | 1 + src/crypto/Crypto.cpp | 22 ++++---- src/crypto/Crypto.h | 6 +-- src/crypto/CryptoHash.cpp | 2 +- src/crypto/Random.cpp | 2 +- src/crypto/SymmetricCipherGcrypt.cpp | 2 +- src/gui/AboutDialog.cpp | 66 ++---------------------- src/main.cpp | 18 +++++-- 11 files changed, 126 insertions(+), 82 deletions(-) diff --git a/src/cli/keepassxc-cli.1 b/src/cli/keepassxc-cli.1 index 22cf88a3..4cee3177 100644 --- a/src/cli/keepassxc-cli.1 +++ b/src/cli/keepassxc-cli.1 @@ -56,6 +56,9 @@ Shows the title, username, password, URL and notes of a database entry. Can also .SS "General options" +.IP "--debug-info" +Displays debugging information. + .IP "-k, --key-file " Specifies a path to a key file for unlocking the database. In a merge operation this option is used to specify the key file path for the first database. @@ -66,7 +69,7 @@ Silence password prompt and other secondary outputs. Displays help information. .IP "-v, --version" -Shows the program version. +Displays the program version. .SS "Merge options" diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index b9e3853f..2c6ce466 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -26,6 +26,7 @@ #include "config-keepassx.h" #include "core/Bootstrap.h" +#include "core/Tools.h" #include "crypto/Crypto.h" #if defined(WITH_ASAN) && defined(WITH_LSAN) @@ -60,6 +61,9 @@ int main(int argc, char** argv) parser.addPositionalArgument("command", QObject::tr("Name of the command to execute.")); + QCommandLineOption debugInfoOption(QStringList() << "debug-info", + QObject::tr("Displays debugging information.")); + parser.addOption(debugInfoOption); parser.addHelpOption(); parser.addVersionOption(); // TODO : use the setOptionsAfterPositionalArgumentsMode (Qt 5.6) function @@ -72,6 +76,10 @@ int main(int argc, char** argv) // Switch to parser.showVersion() when available (QT 5.4). out << KEEPASSXC_VERSION << endl; return EXIT_SUCCESS; + } else if (parser.isSet(debugInfoOption)) { + QString debugInfo = Tools::debugInfo().append("\n").append(Crypto::debugInfo()); + out << debugInfo << endl; + return EXIT_SUCCESS; } parser.showHelp(); } diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index b77a460d..09938b82 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -18,6 +18,8 @@ */ #include "Tools.h" + +#include "config-keepassx.h" #include "core/Config.h" #include "core/Translator.h" @@ -28,8 +30,10 @@ #include #include #include +#include #include #include +#include "git-info.h" #ifdef Q_OS_WIN #include // for Sleep() @@ -41,6 +45,78 @@ namespace Tools { + QString debugInfo() + { + QString debugInfo = "KeePassXC - "; + debugInfo.append(QObject::tr("Version %1").arg(KEEPASSXC_VERSION).append("\n")); +#ifndef KEEPASSXC_BUILD_TYPE_RELEASE + debugInfo.append(QObject::tr("Build Type: %1").arg(KEEPASSXC_BUILD_TYPE).append("\n")); +#endif + + QString commitHash; + if (!QString(GIT_HEAD).isEmpty()) { + commitHash = GIT_HEAD; + } + if (!commitHash.isEmpty()) { + debugInfo.append(QObject::tr("Revision: %1").arg(commitHash.left(7)).append("\n")); + } + +#ifdef KEEPASSXC_DIST + debugInfo.append(QObject::tr("Distribution: %1").arg(KEEPASSXC_DIST_TYPE).append("\n")); +#endif + + // Qt related debugging information. + debugInfo.append("\n"); + debugInfo.append("Qt ").append(QString::fromLocal8Bit(qVersion())).append("\n"); +#ifdef QT_NO_DEBUG + debugInfo.append(QObject::tr("Debugging mode is disabled.").append("\n")); +#else + debugInfo.append(QObject::tr("Debugging mode is enabled.").append("\n")); +#endif + debugInfo.append("\n"); + + +#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) + debugInfo.append(QObject::tr("Operating system: %1\nCPU architecture: %2\nKernel: %3 %4") + .arg(QSysInfo::prettyProductName(), + QSysInfo::currentCpuArchitecture(), + QSysInfo::kernelType(), + QSysInfo::kernelVersion())); + + debugInfo.append("\n\n"); +#endif + + QString extensions; +#ifdef WITH_XC_AUTOTYPE + extensions += "\n- " + QObject::tr("Auto-Type"); +#endif +#ifdef WITH_XC_BROWSER + extensions += "\n- " + QObject::tr("Browser Integration"); +#endif +#ifdef WITH_XC_SSHAGENT + extensions += "\n- " + QObject::tr("SSH Agent"); +#endif +#if defined(WITH_XC_KEESHARE_SECURE) && defined(WITH_XC_KEESHARE_INSECURE) + extensions += "\n- " + QObject::tr("KeeShare (signed and unsigned sharing)"); +#elif defined(WITH_XC_KEESHARE_SECURE) + extensions += "\n- " + QObject::tr("KeeShare (only signed sharing)"); +#elif defined(WITH_XC_KEESHARE_INSECURE) + extensions += "\n- " + QObject::tr("KeeShare (only unsigned sharing)"); +#endif +#ifdef WITH_XC_YUBIKEY + extensions += "\n- " + QObject::tr("YubiKey"); +#endif +#ifdef WITH_XC_TOUCHID + extensions += "\n- " + QObject::tr("TouchID"); +#endif + + if (extensions.isEmpty()) + extensions = " " + QObject::tr("None"); + + debugInfo.append(QObject::tr("Enabled extensions:").append(extensions).append("\n")); + return debugInfo; + } + QString humanReadableFileSize(qint64 bytes, quint32 precision) { constexpr auto kibibyte = 1024; diff --git a/src/core/Tools.h b/src/core/Tools.h index a2c09efe..1fa5e6a9 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -32,6 +32,7 @@ class QRegularExpression; namespace Tools { + QString debugInfo(); QString humanReadableFileSize(qint64 bytes, quint32 precision = 2); bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384); bool readAllFromDevice(QIODevice* device, QByteArray& data); diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index ab97322c..4f54ac1d 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -25,7 +25,7 @@ #include "crypto/CryptoHash.h" #include "crypto/SymmetricCipher.h" -bool Crypto::m_initalized(false); +bool Crypto::m_initialized(false); QString Crypto::m_errorStr; QString Crypto::m_backendVersion; @@ -35,8 +35,8 @@ Crypto::Crypto() bool Crypto::init() { - if (m_initalized) { - qWarning("Crypto::init: already initalized"); + if (m_initialized) { + qWarning("Crypto::init: already initialized"); return true; } @@ -48,19 +48,19 @@ bool Crypto::init() } // has to be set before testing Crypto classes - m_initalized = true; + m_initialized = true; if (!backendSelfTest() || !selfTest()) { - m_initalized = false; + m_initialized = false; return false; } return true; } -bool Crypto::initalized() +bool Crypto::initialized() { - return m_initalized; + return m_initialized; } QString Crypto::errorString() @@ -68,9 +68,13 @@ QString Crypto::errorString() return m_errorStr; } -QString Crypto::backendVersion() +QString Crypto::debugInfo() { - return QString("libgcrypt ").append(m_backendVersion); + Q_ASSERT(Crypto::initialized()); + + QString debugInfo = QObject::tr("Cryptographic libraries:").append("\n"); + debugInfo.append(" libgcrypt ").append(m_backendVersion).append("\n"); + return debugInfo; } bool Crypto::backendSelfTest() diff --git a/src/crypto/Crypto.h b/src/crypto/Crypto.h index 379068eb..4346f505 100644 --- a/src/crypto/Crypto.h +++ b/src/crypto/Crypto.h @@ -24,10 +24,10 @@ class Crypto { public: static bool init(); - static bool initalized(); + static bool initialized(); static bool backendSelfTest(); static QString errorString(); - static QString backendVersion(); + static QString debugInfo(); private: Crypto(); @@ -42,7 +42,7 @@ private: static bool testSalsa20(); static bool testChaCha20(); - static bool m_initalized; + static bool m_initialized; static QString m_errorStr; static QString m_backendVersion; }; diff --git a/src/crypto/CryptoHash.cpp b/src/crypto/CryptoHash.cpp index 07bf5efc..5eab8567 100644 --- a/src/crypto/CryptoHash.cpp +++ b/src/crypto/CryptoHash.cpp @@ -33,7 +33,7 @@ CryptoHash::CryptoHash(Algorithm algo, bool hmac) { Q_D(CryptoHash); - Q_ASSERT(Crypto::initalized()); + Q_ASSERT(Crypto::initialized()); int algoGcrypt = -1; unsigned int flagsGcrypt = GCRY_MD_FLAG_SECURE; diff --git a/src/crypto/Random.cpp b/src/crypto/Random.cpp index 4203b6c0..024a82f9 100644 --- a/src/crypto/Random.cpp +++ b/src/crypto/Random.cpp @@ -93,7 +93,7 @@ Random::Random(RandomBackend* backend) void RandomBackendGcrypt::randomize(void* data, int len) { - Q_ASSERT(Crypto::initalized()); + Q_ASSERT(Crypto::initialized()); gcry_randomize(data, len, GCRY_STRONG_RANDOM); } diff --git a/src/crypto/SymmetricCipherGcrypt.cpp b/src/crypto/SymmetricCipherGcrypt.cpp index fb436522..4d12d25a 100644 --- a/src/crypto/SymmetricCipherGcrypt.cpp +++ b/src/crypto/SymmetricCipherGcrypt.cpp @@ -90,7 +90,7 @@ void SymmetricCipherGcrypt::setError(const gcry_error_t& err) bool SymmetricCipherGcrypt::init() { - Q_ASSERT(Crypto::initalized()); + Q_ASSERT(Crypto::initialized()); gcry_error_t error; diff --git a/src/gui/AboutDialog.cpp b/src/gui/AboutDialog.cpp index 0de79fcc..f37baedc 100644 --- a/src/gui/AboutDialog.cpp +++ b/src/gui/AboutDialog.cpp @@ -21,11 +21,11 @@ #include "config-keepassx.h" #include "core/FilePath.h" +#include "core/Tools.h" #include "crypto/Crypto.h" -#include "git-info.h" #include -#include + static const QString aboutMaintainers = R"(

    @@ -175,67 +175,7 @@ AboutDialog::AboutDialog(QWidget* parent) m_ui->iconLabel->setPixmap(filePath()->applicationIcon().pixmap(48)); - QString commitHash; - if (!QString(GIT_HEAD).isEmpty()) { - commitHash = GIT_HEAD; - } - - QString debugInfo = "KeePassXC - "; - debugInfo.append(tr("Version %1").arg(KEEPASSXC_VERSION).append("\n")); -#ifndef KEEPASSXC_BUILD_TYPE_RELEASE - debugInfo.append(tr("Build Type: %1").arg(KEEPASSXC_BUILD_TYPE).append("\n")); -#endif - if (!commitHash.isEmpty()) { - debugInfo.append(tr("Revision: %1").arg(commitHash.left(7)).append("\n")); - } - -#ifdef KEEPASSXC_DIST - debugInfo.append(tr("Distribution: %1").arg(KEEPASSXC_DIST_TYPE).append("\n")); -#endif - - debugInfo.append("\n").append( - QString("%1\n- Qt %2\n- %3\n\n") - .arg(tr("Libraries:"), QString::fromLocal8Bit(qVersion()), Crypto::backendVersion())); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) - debugInfo.append(tr("Operating system: %1\nCPU architecture: %2\nKernel: %3 %4") - .arg(QSysInfo::prettyProductName(), - QSysInfo::currentCpuArchitecture(), - QSysInfo::kernelType(), - QSysInfo::kernelVersion())); - - debugInfo.append("\n\n"); -#endif - - QString extensions; -#ifdef WITH_XC_AUTOTYPE - extensions += "\n- " + tr("Auto-Type"); -#endif -#ifdef WITH_XC_BROWSER - extensions += "\n- " + tr("Browser Integration"); -#endif -#ifdef WITH_XC_SSHAGENT - extensions += "\n- " + tr("SSH Agent"); -#endif -#if defined(WITH_XC_KEESHARE_SECURE) && defined(WITH_XC_KEESHARE_INSECURE) - extensions += "\n- " + tr("KeeShare (signed and unsigned sharing)"); -#elif defined(WITH_XC_KEESHARE_SECURE) - extensions += "\n- " + tr("KeeShare (only signed sharing)"); -#elif defined(WITH_XC_KEESHARE_INSECURE) - extensions += "\n- " + tr("KeeShare (only unsigned sharing)"); -#endif -#ifdef WITH_XC_YUBIKEY - extensions += "\n- " + tr("YubiKey"); -#endif -#ifdef WITH_XC_TOUCHID - extensions += "\n- " + tr("TouchID"); -#endif - - if (extensions.isEmpty()) - extensions = " " + tr("None"); - - debugInfo.append(tr("Enabled extensions:").append(extensions)); - + QString debugInfo = Tools::debugInfo().append("\n").append(Crypto::debugInfo()); m_ui->debugInfo->setPlainText(debugInfo); m_ui->maintainers->setText(aboutMaintainers); diff --git a/src/main.cpp b/src/main.cpp index a546c049..e975a6b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,16 +82,19 @@ int main(int argc, char** argv) QCommandLineOption helpOption = parser.addHelpOption(); QCommandLineOption versionOption = parser.addVersionOption(); + QCommandLineOption debugInfoOption(QStringList() << "debug-info", + QObject::tr("Displays debugging information.")); parser.addOption(configOption); parser.addOption(keyfileOption); parser.addOption(pwstdinOption); parser.addOption(parentWindowOption); + parser.addOption(debugInfoOption); parser.process(app); // Don't try and do anything with the application if we're only showing the help / version if (parser.isSet(versionOption) || parser.isSet(helpOption)) { - return 0; + return EXIT_SUCCESS; } const QStringList fileNames = parser.positionalArguments(); @@ -101,7 +104,7 @@ int main(int argc, char** argv) app.sendFileNamesToRunningInstance(fileNames); } qWarning() << QObject::tr("Another instance of KeePassXC is already running.").toUtf8().constData(); - return 0; + return EXIT_SUCCESS; } QApplication::setQuitOnLastWindowClosed(false); @@ -111,7 +114,16 @@ int main(int argc, char** argv) error.append("\n"); error.append(Crypto::errorString()); MessageBox::critical(nullptr, QObject::tr("KeePassXC - Error"), error); - return 1; + return EXIT_FAILURE; + } + + // Displaying the debugging informations must be done after Crypto::init, + // to make sure we know which libgcrypt version is used. + if (parser.isSet(debugInfoOption)) { + QTextStream out(stdout, QIODevice::WriteOnly); + QString debugInfo = Tools::debugInfo().append("\n").append(Crypto::debugInfo()); + out << debugInfo << endl; + return EXIT_SUCCESS; } if (parser.isSet(configOption)) { From 13a9ac8f576539aace93a2d5faa2c4d35474f95e Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sun, 24 Mar 2019 08:51:40 -0400 Subject: [PATCH 06/11] Adding --no-password option to CLI I also added tests for the --key-file option, which was untested. --- src/cli/Add.cpp | 2 + src/cli/Clip.cpp | 2 + src/cli/Command.cpp | 4 + src/cli/Command.h | 1 + src/cli/Edit.cpp | 2 + src/cli/Extract.cpp | 21 +++-- src/cli/List.cpp | 2 + src/cli/Locate.cpp | 2 + src/cli/Merge.cpp | 7 ++ src/cli/Remove.cpp | 2 + src/cli/Show.cpp | 3 + src/cli/Utils.cpp | 14 ++-- src/cli/Utils.h | 1 + src/cli/keepassxc-cli.1 | 6 ++ tests/TestCli.cpp | 91 +++++++++++++++++++-- tests/TestCli.h | 6 ++ tests/data/KeyFileProtected.kdbx | Bin 0 -> 1637 bytes tests/data/KeyFileProtected.key | Bin 0 -> 128 bytes tests/data/KeyFileProtectedNoPassword.kdbx | Bin 0 -> 1589 bytes tests/data/KeyFileProtectedNoPassword.key | Bin 0 -> 128 bytes 20 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 tests/data/KeyFileProtected.kdbx create mode 100644 tests/data/KeyFileProtected.key create mode 100644 tests/data/KeyFileProtectedNoPassword.kdbx create mode 100644 tests/data/KeyFileProtectedNoPassword.key diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index dd9d0b50..395b8491 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -50,6 +50,7 @@ int Add::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); QCommandLineOption username(QStringList() << "u" << "username", @@ -91,6 +92,7 @@ int Add::execute(const QStringList& arguments) const QString& entryPath = args.at(1); auto db = Utils::unlockDatabase(databasePath, + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 6e346f25..31b421de 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -49,6 +49,7 @@ int Clip::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); QCommandLineOption totp(QStringList() << "t" << "totp", @@ -67,6 +68,7 @@ int Clip::execute(const QStringList& arguments) } auto db = Utils::unlockDatabase(args.at(0), + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Command.cpp b/src/cli/Command.cpp index 79d56c36..e64dd4aa 100644 --- a/src/cli/Command.cpp +++ b/src/cli/Command.cpp @@ -46,6 +46,10 @@ const QCommandLineOption Command::KeyFileOption = QCommandLineOption(QStringList QObject::tr("Key file of the database."), QObject::tr("path")); +const QCommandLineOption Command::NoPasswordOption = + QCommandLineOption(QStringList() << "no-password", + QObject::tr("Deactivate password key for the database.")); + QMap commands; Command::~Command() diff --git a/src/cli/Command.h b/src/cli/Command.h index b74a312d..30af6170 100644 --- a/src/cli/Command.h +++ b/src/cli/Command.h @@ -40,6 +40,7 @@ public: static const QCommandLineOption QuietOption; static const QCommandLineOption KeyFileOption; + static const QCommandLineOption NoPasswordOption; }; #endif // KEEPASSXC_COMMAND_H diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index 7954648c..76e996c9 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -49,6 +49,7 @@ int Edit::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); QCommandLineOption username(QStringList() << "u" << "username", @@ -95,6 +96,7 @@ int Edit::execute(const QStringList& arguments) const QString& entryPath = args.at(1); auto db = Utils::unlockDatabase(databasePath, + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index be5abb92..729687fe 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -51,6 +51,7 @@ int Extract::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database to extract.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); parser.addHelpOption(); parser.process(arguments); @@ -59,17 +60,19 @@ int Extract::execute(const QStringList& arguments) errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli extract"); return EXIT_FAILURE; } - - if (!parser.isSet(Command::QuietOption)) { - outputTextStream << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush; - } - + auto compositeKey = QSharedPointer::create(); - QString line = Utils::getPassword(parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT); - auto passwordKey = QSharedPointer::create(); - passwordKey->setPassword(line); - compositeKey->addKey(passwordKey); + if (!parser.isSet(Command::NoPasswordOption)) { + if (!parser.isSet(Command::QuietOption)) { + outputTextStream << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush; + } + + QString line = Utils::getPassword(parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT); + auto passwordKey = QSharedPointer::create(); + passwordKey->setPassword(line); + compositeKey->addKey(passwordKey); + } QString keyFilePath = parser.value(Command::KeyFileOption); if (!keyFilePath.isEmpty()) { diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 11650d40..ebf7bfda 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -48,6 +48,7 @@ int List::execute(const QStringList& arguments) parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), "[group]"); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); QCommandLineOption recursiveOption(QStringList() << "R" << "recursive", @@ -65,6 +66,7 @@ int List::execute(const QStringList& arguments) bool recursive = parser.isSet(recursiveOption); auto db = Utils::unlockDatabase(args.at(0), + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index f25ce79a..81bbdd55 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -50,6 +50,7 @@ int Locate::execute(const QStringList& arguments) parser.addPositionalArgument("term", QObject::tr("Search term.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); parser.addHelpOption(); parser.process(arguments); @@ -60,6 +61,7 @@ int Locate::execute(const QStringList& arguments) } auto db = Utils::unlockDatabase(args.at(0), + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index b0e4cabc..a7357394 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -52,6 +52,7 @@ int Merge::execute(const QStringList& arguments) QObject::tr("Use the same credentials for both database files.")); parser.addOption(samePasswordOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); QCommandLineOption keyFileFromOption(QStringList() << "f" << "key-file-from", @@ -59,6 +60,10 @@ int Merge::execute(const QStringList& arguments) QObject::tr("path")); parser.addOption(keyFileFromOption); + QCommandLineOption noPasswordFromOption(QStringList() << "no-password-from", + QObject::tr("Deactivate password key for the database to merge from.")); + parser.addOption(noPasswordFromOption); + parser.addHelpOption(); parser.process(arguments); @@ -69,6 +74,7 @@ int Merge::execute(const QStringList& arguments) } auto db1 = Utils::unlockDatabase(args.at(0), + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); @@ -79,6 +85,7 @@ int Merge::execute(const QStringList& arguments) QSharedPointer db2; if (!parser.isSet("same-credentials")) { db2 = Utils::unlockDatabase(args.at(1), + !parser.isSet(noPasswordFromOption), parser.value(keyFileFromOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index 4663d83e..07da23b7 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -51,6 +51,7 @@ int Remove::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); parser.addPositionalArgument("entry", QObject::tr("Path of the entry to remove.")); parser.addHelpOption(); parser.process(arguments); @@ -62,6 +63,7 @@ int Remove::execute(const QStringList& arguments) } auto db = Utils::unlockDatabase(args.at(0), + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index 9ae3f4d0..d16fbfe3 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -48,6 +48,8 @@ int Show::execute(const QStringList& arguments) parser.addPositionalArgument("database", QObject::tr("Path of the database.")); parser.addOption(Command::QuietOption); parser.addOption(Command::KeyFileOption); + parser.addOption(Command::NoPasswordOption); + QCommandLineOption totp(QStringList() << "t" << "totp", QObject::tr("Show the entry's current TOTP.")); @@ -72,6 +74,7 @@ int Show::execute(const QStringList& arguments) } auto db = Utils::unlockDatabase(args.at(0), + !parser.isSet(Command::NoPasswordOption), parser.value(Command::KeyFileOption), parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT, Utils::STDERR); diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index 344e2b7f..06d2bcf2 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -98,6 +98,7 @@ namespace Utils } // namespace Test QSharedPointer unlockDatabase(const QString& databaseFilename, + const bool isPasswordProtected, const QString& keyFilename, FILE* outputDescriptor, FILE* errorDescriptor) @@ -106,12 +107,13 @@ namespace Utils TextStream out(outputDescriptor); TextStream err(errorDescriptor); - out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename) << flush; - - QString line = Utils::getPassword(outputDescriptor); - auto passwordKey = QSharedPointer::create(); - passwordKey->setPassword(line); - compositeKey->addKey(passwordKey); + if (isPasswordProtected) { + out << QObject::tr("Insert password to unlock %1: ").arg(databaseFilename) << flush; + QString line = Utils::getPassword(outputDescriptor); + auto passwordKey = QSharedPointer::create(); + passwordKey->setPassword(line); + compositeKey->addKey(passwordKey); + } if (!keyFilename.isEmpty()) { auto fileKey = QSharedPointer::create(); diff --git a/src/cli/Utils.h b/src/cli/Utils.h index bb4d8f09..bd89a2a5 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -36,6 +36,7 @@ namespace Utils QString getPassword(FILE* outputDescriptor = STDOUT); int clipText(const QString& text); QSharedPointer unlockDatabase(const QString& databaseFilename, + const bool isPasswordProtected = true, const QString& keyFilename = {}, FILE* outputDescriptor = STDOUT, FILE* errorDescriptor = STDERR); diff --git a/src/cli/keepassxc-cli.1 b/src/cli/keepassxc-cli.1 index 4cee3177..bd7f5d5c 100644 --- a/src/cli/keepassxc-cli.1 +++ b/src/cli/keepassxc-cli.1 @@ -62,6 +62,9 @@ Displays debugging information. .IP "-k, --key-file " Specifies a path to a key file for unlocking the database. In a merge operation this option is used to specify the key file path for the first database. +.IP "--no-password" +Deactivate password key for the database. + .IP "-q, --quiet " Silence password prompt and other secondary outputs. @@ -77,6 +80,9 @@ Displays the program version. .IP "-f, --key-file-from " Path of the key file for the second database. +.IP "--no-password-from" +Deactivate password key for the database to merge from. + .IP "-s, --same-credentials" Use the same credentials for unlocking both database. diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index e2e66c2a..3ba40b90 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -70,11 +70,23 @@ void TestCli::initTestCase() QVERIFY(Tools::readAllFromDevice(&sourceDbFile, m_dbData)); sourceDbFile.close(); - // Load the NewDatabase.kdbx file into temporary storage + // Load the NewDatabase2.kdbx file into temporary storage QFile sourceDbFile2(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase2.kdbx")); QVERIFY(sourceDbFile2.open(QIODevice::ReadOnly)); QVERIFY(Tools::readAllFromDevice(&sourceDbFile2, m_dbData2)); sourceDbFile2.close(); + + // Load the KeyFileProtected.kdbx file into temporary storage + QFile sourceDbFile3(QString(KEEPASSX_TEST_DATA_DIR).append("/KeyFileProtected.kdbx")); + QVERIFY(sourceDbFile3.open(QIODevice::ReadOnly)); + QVERIFY(Tools::readAllFromDevice(&sourceDbFile3, m_keyFileProtectedDbData)); + sourceDbFile3.close(); + + // Load the KeyFileProtectedNoPassword.kdbx file into temporary storage + QFile sourceDbFile4(QString(KEEPASSX_TEST_DATA_DIR).append("/KeyFileProtectedNoPassword.kdbx")); + QVERIFY(sourceDbFile4.open(QIODevice::ReadOnly)); + QVERIFY(Tools::readAllFromDevice(&sourceDbFile4, m_keyFileProtectedNoPasswordDbData)); + sourceDbFile4.close(); } void TestCli::init() @@ -89,6 +101,16 @@ void TestCli::init() m_dbFile2->write(m_dbData2); m_dbFile2->close(); + m_keyFileProtectedDbFile.reset(new TemporaryFile()); + m_keyFileProtectedDbFile->open(); + m_keyFileProtectedDbFile->write(m_keyFileProtectedDbData); + m_keyFileProtectedDbFile->close(); + + m_keyFileProtectedNoPasswordDbFile.reset(new TemporaryFile()); + m_keyFileProtectedNoPasswordDbFile->open(); + m_keyFileProtectedNoPasswordDbFile->write(m_keyFileProtectedNoPasswordDbData); + m_keyFileProtectedNoPasswordDbFile->close(); + m_stdinFile.reset(new TemporaryFile()); m_stdinFile->open(); m_stdinHandle = fdopen(m_stdinFile->handle(), "r+"); @@ -131,7 +153,7 @@ void TestCli::cleanupTestCase() QSharedPointer TestCli::readTestDatabase() const { Utils::Test::setNextPassword("a"); - auto db = QSharedPointer(Utils::unlockDatabase(m_dbFile->fileName(), "", m_stdoutHandle)); + auto db = QSharedPointer(Utils::unlockDatabase(m_dbFile->fileName(), true, "", m_stdoutHandle)); m_stdoutFile->seek(ftell(m_stdoutHandle)); // re-synchronize handles return db; } @@ -320,7 +342,7 @@ void TestCli::testCreate() QCOMPARE(m_stdoutFile->readLine(), QByteArray("Successfully created new database.\n")); Utils::Test::setNextPassword("a"); - auto db = QSharedPointer(Utils::unlockDatabase(databaseFilename, "", Utils::DEVNULL)); + auto db = QSharedPointer(Utils::unlockDatabase(databaseFilename, true, "", Utils::DEVNULL)); QVERIFY(db); // Should refuse to create the database if it already exists. @@ -349,7 +371,7 @@ void TestCli::testCreate() QCOMPARE(m_stdoutFile->readLine(), QByteArray("Successfully created new database.\n")); Utils::Test::setNextPassword("a"); - auto db2 = QSharedPointer(Utils::unlockDatabase(databaseFilename2, keyfilePath, Utils::DEVNULL)); + auto db2 = QSharedPointer(Utils::unlockDatabase(databaseFilename2, true, keyfilePath, Utils::DEVNULL)); QVERIFY(db2); // Testing with existing keyfile @@ -366,7 +388,7 @@ void TestCli::testCreate() QCOMPARE(m_stdoutFile->readLine(), QByteArray("Successfully created new database.\n")); Utils::Test::setNextPassword("a"); - auto db3 = QSharedPointer(Utils::unlockDatabase(databaseFilename3, keyfilePath, Utils::DEVNULL)); + auto db3 = QSharedPointer(Utils::unlockDatabase(databaseFilename3, true, keyfilePath, Utils::DEVNULL)); QVERIFY(db3); } @@ -659,6 +681,65 @@ void TestCli::testGenerate() } } +void TestCli::testKeyFileOption() +{ + List listCmd; + + QString keyFilePath(QString(KEEPASSX_TEST_DATA_DIR).append("/KeyFileProtected.key")); + Utils::Test::setNextPassword("a"); + listCmd.execute({"ls", "-k", keyFilePath, m_keyFileProtectedDbFile->fileName()}); + m_stdoutFile->reset(); + m_stdoutFile->readLine(); // skip password prompt + QCOMPARE(m_stdoutFile->readAll(), QByteArray("entry1\n" + "entry2\n")); + + // Should raise an error with no key file. + qint64 pos = m_stdoutFile->pos(); + qint64 posErr = m_stderrFile->pos(); + Utils::Test::setNextPassword("a"); + listCmd.execute({"ls", m_keyFileProtectedDbFile->fileName()}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->seek(posErr); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll(), + QByteArray("Error while reading the database: Wrong key or database file is corrupt. (HMAC mismatch)\n")); + + // Should raise an error if key file path is invalid. + pos = m_stdoutFile->pos(); + posErr = m_stderrFile->pos(); + Utils::Test::setNextPassword("a"); + listCmd.execute({"ls", "-k", "invalidpath", m_keyFileProtectedDbFile->fileName()}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->seek(posErr); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll().split(':').at(0), + QByteArray("Failed to load key file invalidpath")); +} + +void TestCli::testNoPasswordOption() +{ + List listCmd; + + QString keyFilePath(QString(KEEPASSX_TEST_DATA_DIR).append("/KeyFileProtectedNoPassword.key")); + listCmd.execute({"ls", "-k", keyFilePath, "--no-password", m_keyFileProtectedNoPasswordDbFile->fileName()}); + m_stdoutFile->reset(); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("entry1\n" + "entry2\n")); + + // Should raise an error with no key file. + qint64 pos = m_stdoutFile->pos(); + qint64 posErr = m_stderrFile->pos(); + listCmd.execute({"ls", "--no-password", m_keyFileProtectedNoPasswordDbFile->fileName()}); + m_stdoutFile->seek(pos); + m_stdoutFile->readLine(); // skip password prompt + m_stderrFile->seek(posErr); + QCOMPARE(m_stdoutFile->readAll(), QByteArray("")); + QCOMPARE(m_stderrFile->readAll(), + QByteArray("Error while reading the database: Wrong key or database file is corrupt. (HMAC mismatch)\n")); +} + void TestCli::testList() { List listCmd; diff --git a/tests/TestCli.h b/tests/TestCli.h index f3655e6c..cd8ebacf 100644 --- a/tests/TestCli.h +++ b/tests/TestCli.h @@ -51,6 +51,8 @@ private slots: void testExtract(); void testGenerate_data(); void testGenerate(); + void testKeyFileOption(); + void testNoPasswordOption(); void testList(); void testLocate(); void testMerge(); @@ -61,8 +63,12 @@ private slots: private: QByteArray m_dbData; QByteArray m_dbData2; + QByteArray m_keyFileProtectedDbData; + QByteArray m_keyFileProtectedNoPasswordDbData; QScopedPointer m_dbFile; QScopedPointer m_dbFile2; + QScopedPointer m_keyFileProtectedDbFile; + QScopedPointer m_keyFileProtectedNoPasswordDbFile; QScopedPointer m_stdoutFile; QScopedPointer m_stderrFile; QScopedPointer m_stdinFile; diff --git a/tests/data/KeyFileProtected.kdbx b/tests/data/KeyFileProtected.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..eeda44d58a1451e17b6a3eee343ab939ec1e137e GIT binary patch literal 1637 zcmV-r2AcT;*`k_f`%AR|00aO65C8xGF~RcYzi~rQzE}kzYW!ON0|Wp70096100bZa z002jncjk5vEsMs9hPYAu5btjdtpn-lj3ZI_=w!Ry3X}&B00007q?JYcNPOA~M}0A@ zvD{A!ivR!s00BY;0000aRaHqu5C8xG?_+J>j44D*k@u;j1LFz|1pxp607(b{000mG z00000000F60000@2mk;800008000001OWg508j(~000O8002S(0000}AOHXW?}x9V zLVxS16a<)17Q8TCRi9-mD&4n^M@VKUiJIkVkX%?Tpp%ntDLgfAYFR&@d zacNShCSu+*>&f!&6+hA?{lcyc6+)1FXvmn<2{&-eCRs{dDMBDHz++w6-~<2w1s!j- zu%JYO{^yNmAr}Ab;xVVpwObOm*wf6!w{fbrx=kXcq3T=OM?##s1tMs4Pdbu}`f-nULVftc4;Bff+>lY2@5TXX6dRoltae-Tzo2%V%PP;!Fqf z#_bN zZnI?ux1vW#s=PT4x``{@>=rdxjK|2{l2*nk`#(`3aFPJ)UH2=?*avrxHK*l{{v##p znlLxKFJ){M(=A)4Azq)>>sWMI=$tWMch=_V>ytoaeXhL()t|3g&B9hrzep{IxIP0qLPx{Xh-+hxL zA*E+pb+7`Vnr##?!a}#jc&i=XQg>Ekf(tv@(D*KYLr9 zo$ZY-Etr^AEp;$Iwb%Kbrda|x*FJ#}9VIusoPG=CE2rNAs_?%gU*lErFJUY(?R52 zeho-gfqAc&3qmKR6%S7R#s0AblY!|)OdJ7U!*b8nX4SweN~37#2<5H8Hy~LW&BZ=x zr$ui;psEj&B_4AhkLuuIDgtO1FE6`qf(rZXl87K{2m`dkDgj}V`hZDPRpYE^-UF3S zIqf#{YLlbVpxPFH(7^Cwea1rge|jjG}Sl4$0%Fn%NG$F zkYX1tGVotS4-|pbA0N0QOv!jTN6=(ZZlHw-t7#-Tv41nHvb=GN+yTl=MbWO-SIPW& z>TaccBZN=_rsrJli9sQ@eA;`0?$7kp(Q>vGMI&vn12DFg)&qOQx>o<{(R!k-J|1!Q z;!@saBUNfMgR1SE<=<6fB`znUi@ww4hV~iDN1<@`ST**4JHX37!WjcXvf(ihxoI!a z(J7g|yC)e=oi=PZ?3 zU*P)bdQ4TCN*ME$8}{WVOue>c0SPLHg&H|MH#s1$9gf?GmD%_2epJGZFbebq7ap)o jH1A2&vD!{QN}ojF@i0h;)_)N3bt=KmHx34p00000?EUyp literal 0 HcmV?d00001 diff --git a/tests/data/KeyFileProtected.key b/tests/data/KeyFileProtected.key new file mode 100644 index 0000000000000000000000000000000000000000..31314b95318f07b2498bde8fd9d9496842a9d052 GIT binary patch literal 128 zcmV-`0Du1i3x;>{)6@0u+AzK@WB1H(>1342=tSEKGiwUPBm}23!4+Zyw-|5EWX)3t z^%mKQ{pMjzAx|^749=@U4%qXJ9UoAd;0GAX>o5moY&qWS!HAgvjecLW+$Zt&sJ+ApOF)?b literal 0 HcmV?d00001 diff --git a/tests/data/KeyFileProtectedNoPassword.kdbx b/tests/data/KeyFileProtectedNoPassword.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..6ee188da88ccf499663d0ba81c1643e299ce63ca GIT binary patch literal 1589 zcmV-52Fm#Z*`k_f`%AR|00aO65C8xGF~RcYzi~rQzE}kzYW!ON0|Wp70096100bZa z004NI3Yz|ujSg|>{`eSY^KlG8z;KK0SThCb8eM{omHY<~0002PD?eKRh|(dn6#(f& z;|f>{ivR!s00BY;0000aRaHqu5C8xG?_+J>j44D*k@u;j1LFz|1pxp607(b{00093 z00000000F60000@2mk;800008000001OWg508j(~000C4002S(0000}AOHXW35|w= zKl2zXlAnc@J9q?VaQ6_9*fadcz;LX_^J<=|1OWg509FJ5000vJ000001ONa44GIkk z7J*|W29)88{HK(IH){CWPdS`6tHmU;a2{CnP|R-Bmt?bw3KGuObcTTmwT~j z3%W2s#v`{q`2c*kmYT(7N%GV{7AJfM@gIC@G=V;BB*wl*`NkB)XuPlkumk`A*3n!e z9)%fFBM|g){+HpaE#GP2DQZ$r#{rnDgwxbqFVWQuG#gt~3DWYNCIepW1Mj5+mZ7-o zC@8ZEKf(+^NJClJp9eDN{%Q;Y^$&s<755JHKj9cZOL zVcA$ZMVBc^gV#hgoW^e)wPvWx!QBni!-*vWxYAgthogX=Kv@oQPG~gF;duPvIb(ec zrJlgB2$${-SR2||!j3>vxItol`?)Ro7+#uRHHt(1;zXs5dOJ3?WSo>8cOH8;By22= z*4Ybk>OSRLJsf^;8D4fzel)DnDfxDQz0x_EP2r{Y*Db6PpS<_ZiUv_J$vwA4h68VU+?prgwqw?J1}AEi7XG2Gyz{YDAUZ>>+|opm zF|ZSKt4l+4Ju$5pTUmVzwaG^)!d_QJ6}DLJaY*c6#4jX7SjQc0C>)^*Q;>JfO$Oa3 zcOwU*`0iOhh%=CzFRgkN4I>3@5C1K7NF6L`pfw46xQlLaD$Vq9#tCWDj!=Id;IXsF z-zkotQ9+3@&-i@(%roQ=P$K$Q6NtV>*xuHhX(0O_8Tp$#O`-DR27;;n?SA@m05n0s zoMH@p=hzGM8god&hTzmn#jLNKB#H3EP{8;98RN@!)gbokwzt}m-D|GN9;8pJ#%@Q3+d`+Qs)32EzZ0*@Mb2F%n@1;bT*R!Vq)6H|Rj11TFk=v>G6zFm#mS#=rQD z)VZLspoB#J{|Lauv>wSftP>(F$35ew687I60Y&yEZq-%GebZ=@=>k!3h z3m5uxYjIC3>sii|1oL%dllz}vU;mHj{90!RkDfr&ux}`(D zboO2o!5GcfH*PN1EY4%tUX&}rNHz)$%;Te(UP4`s2=R4fVMR9G0ad-$m**6B^=~Dy z?~#+KI&5&~O!Q(Yow7p&jYwgTirw*lshfXRpwP+P6(SvgngWRZmYCUpssqcFQE`p% zsp6n9J3s^bborFyES zy#Ws6;KShxc}F2iIRyq@cIFjmvJ|Oio|ZVIP^^W+mWab9I{xK7Mb^2=4NHUn>MP5R nPJjxbaMQufWkfKk_K?c8QhaJE6B306Rw%*~P00000j;Pag literal 0 HcmV?d00001 diff --git a/tests/data/KeyFileProtectedNoPassword.key b/tests/data/KeyFileProtectedNoPassword.key new file mode 100644 index 0000000000000000000000000000000000000000..a6131f09ed17e60906a0d5de0f55bee655472903 GIT binary patch literal 128 zcmV-`0Du1y;JWU2Yb2`-qo>j<}MI%KC>Cde=3ES#wAa0Hh?ZZqkU1s7`w?K|%P literal 0 HcmV?d00001 From 86e5dbda4fe267ef237ba68c791c56dd1e95475a Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sun, 24 Mar 2019 09:26:34 -0400 Subject: [PATCH 07/11] Add Haiku support --- CMakeLists.txt | 2 ++ share/CMakeLists.txt | 4 ++-- src/CMakeLists.txt | 3 +++ src/autotype/CMakeLists.txt | 2 +- src/crypto/ssh/includes.h | 2 +- src/gui/MainWindow.cpp | 2 ++ 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 658548f7..969b3727 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,11 +192,13 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") add_gcc_compiler_flags("-Werror") endif() +if (NOT HAIKU) if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8.999) OR CMAKE_COMPILER_IS_CLANGXX) add_gcc_compiler_flags("-fstack-protector-strong") else() add_gcc_compiler_flags("-fstack-protector --param=ssp-buffer-size=4") endif() +endif() add_gcc_compiler_cxxflags("-fno-exceptions -fno-rtti") add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual") diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index 214c0ec9..635ee596 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -23,7 +23,7 @@ file(GLOB DATABASE_ICONS icons/database/*.png) install(FILES ${DATABASE_ICONS} DESTINATION ${DATA_INSTALL_DIR}/icons/database) -if(UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE AND NOT HAIKU) install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor FILES_MATCHING PATTERN "keepassx*.png" PATTERN "keepassx*.svg" PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE PATTERN "categories" EXCLUDE) @@ -33,7 +33,7 @@ if(UNIX AND NOT APPLE) install(FILES linux/org.keepassxc.KeePassXC.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) install(FILES linux/org.keepassxc.KeePassXC.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) install(FILES linux/keepassxc.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages) -endif(UNIX AND NOT APPLE) +endif(UNIX AND NOT APPLE AND NOT HAIKU) if(APPLE) install(FILES macosx/keepassxc.icns DESTINATION ${DATA_INSTALL_DIR}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 110dc606..b2cd2723 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -293,6 +293,9 @@ if(APPLE) target_link_libraries(keepassx_core "-framework LocalAuthentication") endif() endif() +if(HAIKU) + target_link_libraries(keepassx_core network) +endif() if(UNIX AND NOT APPLE) target_link_libraries(keepassx_core Qt5::DBus) endif() diff --git a/src/autotype/CMakeLists.txt b/src/autotype/CMakeLists.txt index df0483a0..6b9b8b67 100644 --- a/src/autotype/CMakeLists.txt +++ b/src/autotype/CMakeLists.txt @@ -1,5 +1,5 @@ if(WITH_XC_AUTOTYPE) - if(UNIX AND NOT APPLE) + if(UNIX AND NOT APPLE AND NOT HAIKU) find_package(X11) find_package(Qt5X11Extras 5.2) if(PRINT_SUMMARY) diff --git a/src/crypto/ssh/includes.h b/src/crypto/ssh/includes.h index 23b4aeeb..ab29d77d 100644 --- a/src/crypto/ssh/includes.h +++ b/src/crypto/ssh/includes.h @@ -8,7 +8,7 @@ #endif #include -#ifdef _WIN32 +#if defined(_WIN32) || defined(__HAIKU__) #include typedef uint32_t u_int32_t; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 6e3c96af..e91f35ed 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -391,8 +391,10 @@ MainWindow::MainWindow() connect(m_ui->tabWidget, SIGNAL(messageDismissGlobal()), this, SLOT(hideGlobalMessage())); +#ifndef Q_OS_HAIKU m_screenLockListener = new ScreenLockListener(this); connect(m_screenLockListener, SIGNAL(screenLocked()), SLOT(handleScreenLock())); +#endif updateTrayIcon(); From 28a3824d2d594429b38ca441ff0a718b6ec7092a Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Thu, 21 Mar 2019 17:36:57 -0400 Subject: [PATCH 08/11] Use existing database open function on startup * Fix #2828 --- src/gui/DatabaseTabWidget.cpp | 13 +++++++++---- src/gui/DatabaseTabWidget.h | 5 ++++- src/gui/MainWindow.cpp | 23 ++--------------------- src/gui/MainWindow.h | 2 +- 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index c908a82e..7693c901 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -137,10 +137,15 @@ void DatabaseTabWidget::openDatabase() * database has been opened already. * * @param filePath database file path - * @param password optional, password to unlock database * @param inBackground optional, don't focus tab after opening + * @param password optional, password to unlock database + * @param keyfile optional, path to keyfile to unlock database + * */ -void DatabaseTabWidget::addDatabaseTab(const QString& filePath, bool inBackground, const QString& password) +void DatabaseTabWidget::addDatabaseTab(const QString& filePath, + bool inBackground, + const QString& password, + const QString& keyfile) { QFileInfo fileInfo(filePath); QString canonicalFilePath = fileInfo.canonicalFilePath(); @@ -154,7 +159,7 @@ void DatabaseTabWidget::addDatabaseTab(const QString& filePath, bool inBackgroun Q_ASSERT(dbWidget); if (dbWidget && dbWidget->database()->filePath() == canonicalFilePath) { if (!password.isEmpty()) { - dbWidget->performUnlockDatabase(password); + dbWidget->performUnlockDatabase(password, keyfile); } if (!inBackground) { // switch to existing tab if file is already open @@ -167,7 +172,7 @@ void DatabaseTabWidget::addDatabaseTab(const QString& filePath, bool inBackgroun auto* dbWidget = new DatabaseWidget(QSharedPointer::create(filePath), this); addDatabaseTab(dbWidget, inBackground); if (!password.isEmpty()) { - dbWidget->performUnlockDatabase(password); + dbWidget->performUnlockDatabase(password, keyfile); } updateLastDatabases(filePath); } diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index eda28839..bafbfa37 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -48,7 +48,10 @@ public: bool hasLockableDatabases() const; public slots: - void addDatabaseTab(const QString& filePath, bool inBackground = false, const QString& password = {}); + void addDatabaseTab(const QString& filePath, + bool inBackground = false, + const QString& password = {}, + const QString& keyfile = {}); void addDatabaseTab(DatabaseWidget* dbWidget, bool inBackground = false); bool closeDatabaseTab(int index); bool closeDatabaseTab(DatabaseWidget* dbWidget); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e91f35ed..03fa5494 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -496,28 +496,9 @@ void MainWindow::clearLastDatabases() } } -void MainWindow::openDatabase(const QString& filePath, const QString& pw, const QString& keyFile) +void MainWindow::openDatabase(const QString& filePath, const QString& password, const QString& keyfile) { - if (pw.isEmpty() && keyFile.isEmpty()) { - m_ui->tabWidget->addDatabaseTab(filePath); - return; - } - - auto db = QSharedPointer::create(); - auto key = QSharedPointer::create(); - if (!pw.isEmpty()) { - key->addKey(QSharedPointer::create(pw)); - } - if (!keyFile.isEmpty()) { - auto fileKey = QSharedPointer::create(); - fileKey->load(keyFile); - key->addKey(fileKey); - } - if (db->open(filePath, key, nullptr, false)) { - auto* dbWidget = new DatabaseWidget(db, this); - m_ui->tabWidget->addDatabaseTab(dbWidget); - dbWidget->switchToMainView(true); - } + m_ui->tabWidget->addDatabaseTab(filePath, false, password, keyfile); } void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index cd7b1a39..14083da7 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -56,7 +56,7 @@ public: }; public slots: - void openDatabase(const QString& filePath, const QString& pw = {}, const QString& keyFile = {}); + void openDatabase(const QString& filePath, const QString& password = {}, const QString& keyfile = {}); void appExit(); void displayGlobalMessage(const QString& text, MessageWidget::MessageType type, From 72f0e9ba77ab2fc4f3d563d3fc43ee6f7a064c1b Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Thu, 21 Mar 2019 17:39:02 -0400 Subject: [PATCH 09/11] Fix saving order of open databases * Order of previously open databases are preserved when closing the application * The active database on closing remains active after startup * Nested open previous databases and remember key files under the remember previously open databases setting * Fix #1675 --- src/core/Bootstrap.cpp | 4 + src/gui/ApplicationSettingsWidget.cpp | 22 +++- src/gui/ApplicationSettingsWidget.h | 3 +- src/gui/ApplicationSettingsWidgetGeneral.ui | 128 ++++++++++++++------ src/gui/MainWindow.cpp | 38 +++--- src/gui/MainWindow.h | 2 - 6 files changed, 135 insertions(+), 62 deletions(-) diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp index 1950735a..a06bf74c 100644 --- a/src/core/Bootstrap.cpp +++ b/src/core/Bootstrap.cpp @@ -112,6 +112,10 @@ namespace Bootstrap mainWindow.openDatabase(filename); } } + auto lastActiveFile = config()->get("LastActiveDatabase").toString(); + if (!lastActiveFile.isEmpty()) { + mainWindow.openDatabase(lastActiveFile); + } } } diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 90b851bd..849df03a 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -81,7 +81,8 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) // clang-format off connect(m_generalUi->autoSaveAfterEveryChangeCheckBox, SIGNAL(toggled(bool)), SLOT(autoSaveToggled(bool))); connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), SLOT(systrayToggled(bool))); - connect(m_generalUi->toolbarHideCheckBox, SIGNAL(toggled(bool)), SLOT(enableToolbarSettings(bool))); + connect(m_generalUi->toolbarHideCheckBox, SIGNAL(toggled(bool)), SLOT(toolbarSettingsToggled(bool))); + connect(m_generalUi->rememberLastDatabasesCheckBox, SIGNAL(toggled(bool)), SLOT(rememberDatabasesToggled(bool))); connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)), m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool))); @@ -294,11 +295,13 @@ void ApplicationSettingsWidget::saveSettings() // Security: clear storage if related settings are disabled if (!config()->get("RememberLastDatabases").toBool()) { - config()->set("LastDatabases", QVariant()); + config()->set("LastDatabases", {}); + config()->set("OpenPreviousDatabasesOnStartup", {}); + config()->set("LastActiveDatabase", {}); } if (!config()->get("RememberLastKeyFiles").toBool()) { - config()->set("LastKeyFiles", QVariant()); + config()->set("LastKeyFiles", {}); config()->set("LastDir", ""); } @@ -330,9 +333,20 @@ void ApplicationSettingsWidget::systrayToggled(bool checked) m_generalUi->systrayMinimizeToTrayCheckBox->setEnabled(checked); } -void ApplicationSettingsWidget::enableToolbarSettings(bool checked) +void ApplicationSettingsWidget::toolbarSettingsToggled(bool checked) { m_generalUi->toolbarMovableCheckBox->setEnabled(!checked); m_generalUi->toolButtonStyleComboBox->setEnabled(!checked); m_generalUi->toolButtonStyleLabel->setEnabled(!checked); } + +void ApplicationSettingsWidget::rememberDatabasesToggled(bool checked) +{ + if (!checked) { + m_generalUi->rememberLastKeyFilesCheckBox->setChecked(false); + m_generalUi->openPreviousDatabasesOnStartupCheckBox->setChecked(false); + } + + m_generalUi->rememberLastKeyFilesCheckBox->setEnabled(checked); + m_generalUi->openPreviousDatabasesOnStartupCheckBox->setEnabled(checked); +} diff --git a/src/gui/ApplicationSettingsWidget.h b/src/gui/ApplicationSettingsWidget.h index ffcfea2b..85b3b470 100644 --- a/src/gui/ApplicationSettingsWidget.h +++ b/src/gui/ApplicationSettingsWidget.h @@ -55,7 +55,8 @@ private slots: void reject(); void autoSaveToggled(bool checked); void systrayToggled(bool checked); - void enableToolbarSettings(bool checked); + void toolbarSettingsToggled(bool checked); + void rememberDatabasesToggled(bool checked); private: QWidget* const m_secWidget; diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index 798971bf..8885ef7c 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -49,33 +49,6 @@ - - - - Remember last databases - - - true - - - - - - - Remember last key files and security dongles - - - true - - - - - - - Load previous databases on startup - - - @@ -83,6 +56,88 @@ + + + + Remember previously used databases + + + true + + + + + + + 0 + + + QLayout::SetMaximumSize + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Load previously open databases on startup + + + true + + + + + + + + + 0 + + + QLayout::SetMaximumSize + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Remember database key files and security dongles + + + true + + + + + @@ -218,7 +273,7 @@ - 40 + 20 20 @@ -245,7 +300,7 @@ - 15 + 0 @@ -257,7 +312,7 @@ - 40 + 20 20 @@ -274,8 +329,11 @@ 0 + + margin-right: 5px + - Button style + Button style: @@ -326,7 +384,7 @@ - 40 + 20 20 @@ -377,7 +435,7 @@ - 40 + 20 20 @@ -407,7 +465,7 @@ - 15 + 8 @@ -418,7 +476,7 @@ - Language + Language: diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 03fa5494..9e60b53e 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -903,24 +903,27 @@ void MainWindow::saveWindowInformation() bool MainWindow::saveLastDatabases() { - bool accept; - m_openDatabases.clear(); - bool openPreviousDatabasesOnStartup = config()->get("OpenPreviousDatabasesOnStartup").toBool(); + if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) { + auto currentDbWidget = m_ui->tabWidget->currentDatabaseWidget(); + if (currentDbWidget) { + config()->set("LastActiveDatabase", currentDbWidget->database()->filePath()); + } else { + config()->set("LastActiveDatabase", {}); + } - if (openPreviousDatabasesOnStartup) { - connect( - m_ui->tabWidget, SIGNAL(databaseClosed(const QString&)), this, SLOT(rememberOpenDatabases(const QString&))); + QStringList openDatabases; + for (int i=0; i < m_ui->tabWidget->count(); ++i) { + auto dbWidget = m_ui->tabWidget->databaseWidgetFromIndex(i); + openDatabases.append(dbWidget->database()->filePath()); + } + + config()->set("LastOpenedDatabases", openDatabases); + } else { + config()->set("LastActiveDatabase", {}); + config()->set("LastOpenedDatabases", {}); } - accept = m_ui->tabWidget->closeAllDatabaseTabs(); - - if (openPreviousDatabasesOnStartup) { - disconnect( - m_ui->tabWidget, SIGNAL(databaseClosed(const QString&)), this, SLOT(rememberOpenDatabases(const QString&))); - config()->set("LastOpenedDatabases", m_openDatabases); - } - - return accept; + return m_ui->tabWidget->closeAllDatabaseTabs(); } void MainWindow::updateTrayIcon() @@ -985,11 +988,6 @@ void MainWindow::setShortcut(QAction* action, QKeySequence::StandardKey standard } } -void MainWindow::rememberOpenDatabases(const QString& filePath) -{ - m_openDatabases.prepend(filePath); -} - void MainWindow::applySettingsChanges() { int timeout = config()->get("security/lockdatabaseidlesec").toInt() * 1000; diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 14083da7..5a72d6f0 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -105,7 +105,6 @@ private slots: void updateCopyAttributesMenu(); void showEntryContextMenu(const QPoint& globalPos); void showGroupContextMenu(const QPoint& globalPos); - void rememberOpenDatabases(const QString& filePath); void applySettingsChanges(); void trayIconTriggered(QSystemTrayIcon::ActivationReason reason); void lockDatabasesAfterInactivity(); @@ -137,7 +136,6 @@ private: QAction* m_searchWidgetAction; QActionGroup* m_lastDatabasesActions; QActionGroup* m_copyAdditionalAttributeActions; - QStringList m_openDatabases; InactivityTimer* m_inactivityTimer; InactivityTimer* m_touchIDinactivityTimer; int m_countDefaultAttributes; From 38826a851da650609b7456a8a931835423189ad7 Mon Sep 17 00:00:00 2001 From: Julius Bullinger <303458+bllngr@users.noreply.github.com> Date: Sun, 24 Mar 2019 15:13:50 +0100 Subject: [PATCH 10/11] Add button to open Custom Auto-type sequence documentation (#2733) This change adds a button next to the text edit field which opens the following wiki page: https://github.com/keepassxreboot/keepassxc/wiki/Autotype-Custom-Sequence --- src/gui/entry/EditEntryWidget.cpp | 11 +++++++++++ src/gui/entry/EditEntryWidget.h | 1 + src/gui/entry/EditEntryWidgetAutoType.ui | 17 +++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index e57bc97d..9a4c1600 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -198,11 +198,18 @@ void EditEntryWidget::setupIcon() connect(this, SIGNAL(rejected()), m_iconsWidget, SLOT(abortRequests())); } +void EditEntryWidget::openAutotypeHelp() +{ + QDesktopServices::openUrl(QUrl("https://github.com/keepassxreboot/keepassxc/wiki/Autotype-Custom-Sequence")); +} + void EditEntryWidget::setupAutoType() { m_autoTypeUi->setupUi(m_autoTypeWidget); addPage(tr("Auto-Type"), FilePath::instance()->icon("actions", "key-enter"), m_autoTypeWidget); + m_autoTypeUi->openHelpButton->setIcon(filePath()->icon("actions", "system-help")); + m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->inheritSequenceButton); m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->customSequenceButton); m_autoTypeAssocModel->setAutoTypeAssociations(m_autoTypeAssoc); @@ -213,6 +220,9 @@ void EditEntryWidget::setupAutoType() connect(m_autoTypeUi->enableButton, SIGNAL(toggled(bool)), SLOT(updateAutoTypeEnabled())); connect(m_autoTypeUi->customSequenceButton, SIGNAL(toggled(bool)), m_autoTypeUi->sequenceEdit, SLOT(setEnabled(bool))); + connect(m_autoTypeUi->customSequenceButton, SIGNAL(toggled(bool)), + m_autoTypeUi->openHelpButton, SLOT(setEnabled(bool))); + connect(m_autoTypeUi->openHelpButton, SIGNAL(clicked()), SLOT(openAutotypeHelp())); connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(toggled(bool)), m_autoTypeUi->windowSequenceEdit, SLOT(setEnabled(bool))); connect(m_autoTypeUi->assocAddButton, SIGNAL(clicked()), SLOT(insertAutoTypeAssoc())); @@ -1185,6 +1195,7 @@ void EditEntryWidget::updateAutoTypeEnabled() m_autoTypeUi->inheritSequenceButton->setEnabled(!m_history && autoTypeEnabled); m_autoTypeUi->customSequenceButton->setEnabled(!m_history && autoTypeEnabled); m_autoTypeUi->sequenceEdit->setEnabled(autoTypeEnabled && m_autoTypeUi->customSequenceButton->isChecked()); + m_autoTypeUi->openHelpButton->setEnabled(autoTypeEnabled && m_autoTypeUi->customSequenceButton->isChecked()); m_autoTypeUi->assocView->setEnabled(autoTypeEnabled); m_autoTypeUi->assocAddButton->setEnabled(!m_history); diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index 07a4b789..4c687059 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -90,6 +90,7 @@ private slots: void protectCurrentAttribute(bool state); void revealCurrentAttribute(); void updateAutoTypeEnabled(); + void openAutotypeHelp(); void insertAutoTypeAssoc(); void removeAutoTypeAssoc(); void loadCurrentAssoc(const QModelIndex& current); diff --git a/src/gui/entry/EditEntryWidgetAutoType.ui b/src/gui/entry/EditEntryWidgetAutoType.ui index 3d4ec7a3..81261394 100644 --- a/src/gui/entry/EditEntryWidgetAutoType.ui +++ b/src/gui/entry/EditEntryWidgetAutoType.ui @@ -85,6 +85,22 @@ + + + + false + + + Open AutoType help webpage + + + AutoType help button + + + + + + @@ -268,6 +284,7 @@ inheritSequenceButton customSequenceButton sequenceEdit + openHelpButton assocView windowTitleCombo customWindowSequenceButton From bc3a7522a2e95ba3689e8f0ccfaa9a09c36981b9 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Fri, 22 Mar 2019 09:01:41 -0400 Subject: [PATCH 11/11] Prevent crash when editing master key --- src/gui/masterkey/PasswordEditWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/masterkey/PasswordEditWidget.cpp b/src/gui/masterkey/PasswordEditWidget.cpp index 6f85bb19..86d629da 100644 --- a/src/gui/masterkey/PasswordEditWidget.cpp +++ b/src/gui/masterkey/PasswordEditWidget.cpp @@ -65,7 +65,7 @@ bool PasswordEditWidget::isPasswordVisible() const bool PasswordEditWidget::isEmpty() const { - return m_compUi->enterPasswordEdit->text().isEmpty(); + return (visiblePage() == Page::Edit) && m_compUi->enterPasswordEdit->text().isEmpty(); } QWidget* PasswordEditWidget::componentEditWidget()