Merge branch 'release/2.4.1' into develop

This commit is contained in:
Jonathan White 2019-03-24 11:01:23 -04:00
commit d7660dad37
No known key found for this signature in database
GPG Key ID: 440FC65F2E0C6E01
51 changed files with 467 additions and 194 deletions

View File

@ -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")

View File

@ -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

View File

@ -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})

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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);

View File

@ -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()

View File

@ -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);

View File

@ -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<QString, Command*> commands;
Command::~Command()

View File

@ -40,6 +40,7 @@ public:
static const QCommandLineOption QuietOption;
static const QCommandLineOption KeyFileOption;
static const QCommandLineOption NoPasswordOption;
};
#endif // KEEPASSXC_COMMAND_H

View File

@ -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);

View File

@ -47,6 +47,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);
@ -55,8 +56,11 @@ int Extract::execute(const QStringList& arguments)
errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli extract");
return EXIT_FAILURE;
}
auto compositeKey = QSharedPointer<CompositeKey>::create();
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);

View File

@ -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);

View File

@ -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);

View File

@ -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<Database> 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);

View File

@ -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);

View File

@ -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);

View File

@ -98,6 +98,7 @@ namespace Utils
} // namespace Test
QSharedPointer<Database> 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<PasswordKey>::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<PasswordKey>::create();
passwordKey->setPassword(line);
compositeKey->addKey(passwordKey);
}
if (!keyFilename.isEmpty()) {
auto fileKey = QSharedPointer<FileKey>::create();

View File

@ -36,6 +36,7 @@ namespace Utils
QString getPassword(FILE* outputDescriptor = STDOUT);
int clipText(const QString& text);
QSharedPointer<Database> unlockDatabase(const QString& databaseFilename,
const bool isPasswordProtected = true,
const QString& keyFilename = {},
FILE* outputDescriptor = STDOUT,
FILE* errorDescriptor = STDERR);

View File

@ -56,9 +56,15 @@ 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 <path>"
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 <path>"
Silence password prompt and other secondary outputs.
@ -66,7 +72,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"
@ -74,6 +80,9 @@ Shows the program version.
.IP "-f, --key-file-from <path>"
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.

View File

@ -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();
}

View File

@ -112,6 +112,10 @@ namespace Bootstrap
mainWindow.openDatabase(filename);
}
}
auto lastActiveFile = config()->get("LastActiveDatabase").toString();
if (!lastActiveFile.isEmpty()) {
mainWindow.openDatabase(lastActiveFile);
}
}
}

View File

@ -18,6 +18,8 @@
*/
#include "Tools.h"
#include "config-keepassx.h"
#include "core/Config.h"
#include "core/Translator.h"
@ -28,8 +30,10 @@
#include <QLocale>
#include <QRegularExpression>
#include <QStringList>
#include <QSysInfo>
#include <QUuid>
#include <cctype>
#include "git-info.h"
#ifdef Q_OS_WIN
#include <windows.h> // 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;

View File

@ -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);

View File

@ -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()

View File

@ -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;
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -8,7 +8,7 @@
#endif
#include <sys/types.h>
#ifdef _WIN32
#if defined(_WIN32) || defined(__HAIKU__)
#include <stdint.h>
typedef uint32_t u_int32_t;

View File

@ -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 <QClipboard>
#include <QSysInfo>
static const QString aboutMaintainers = R"(
<p><ul>
@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -49,33 +49,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rememberLastDatabasesCheckBox">
<property name="text">
<string>Remember last databases</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rememberLastKeyFilesCheckBox">
<property name="text">
<string>Remember last key files and security dongles</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="openPreviousDatabasesOnStartupCheckBox">
<property name="text">
<string>Load previous databases on startup</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="systrayMinimizeOnStartup">
<property name="text">
@ -83,6 +56,88 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rememberLastDatabasesCheckBox">
<property name="text">
<string>Remember previously used databases</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="rememberDbSubLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<spacer name="toolbarMovableSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="openPreviousDatabasesOnStartupCheckBox">
<property name="text">
<string>Load previously open databases on startup</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="rememberDbSubLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<spacer name="toolbarMovableSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="rememberLastKeyFilesCheckBox">
<property name="text">
<string>Remember database key files and security dongles</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkForUpdatesOnStartupCheckBox">
<property name="text">
@ -218,7 +273,7 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>20</width>
<height>20</height>
</size>
</property>
@ -245,7 +300,7 @@
<item>
<layout class="QHBoxLayout" name="toolButtonStyleLayout">
<property name="spacing">
<number>15</number>
<number>0</number>
</property>
<item>
<spacer name="toolButtonStyleSpacer">
@ -257,7 +312,7 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>20</width>
<height>20</height>
</size>
</property>
@ -274,8 +329,11 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">margin-right: 5px</string>
</property>
<property name="text">
<string>Button style</string>
<string>Button style:</string>
</property>
</widget>
</item>
@ -326,7 +384,7 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>20</width>
<height>20</height>
</size>
</property>
@ -377,7 +435,7 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>20</width>
<height>20</height>
</size>
</property>
@ -407,7 +465,7 @@
<item>
<layout class="QHBoxLayout" name="languageLabelLayout_2">
<property name="spacing">
<number>15</number>
<number>8</number>
</property>
<item alignment="Qt::AlignRight">
<widget class="QLabel" name="languageLabel_2">
@ -418,7 +476,7 @@
</sizepolicy>
</property>
<property name="text">
<string>Language</string>
<string>Language:</string>
</property>
</widget>
</item>

View File

@ -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<Database>::create(filePath), this);
addDatabaseTab(dbWidget, inBackground);
if (!password.isEmpty()) {
dbWidget->performUnlockDatabase(password);
dbWidget->performUnlockDatabase(password, keyfile);
}
updateLastDatabases(filePath);
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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();
@ -494,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<Database>::create();
auto key = QSharedPointer<CompositeKey>::create();
if (!pw.isEmpty()) {
key->addKey(QSharedPointer<PasswordKey>::create(pw));
}
if (!keyFile.isEmpty()) {
auto fileKey = QSharedPointer<FileKey>::create();
fileKey->load(keyFile);
key->addKey(fileKey);
}
if (db->open(filePath, key, nullptr, false)) {
auto* dbWidget = new DatabaseWidget(db, this);
m_ui->tabWidget->addDatabaseTab(dbWidget);
dbWidget->switchToMainView(true);
}
m_ui->tabWidget->addDatabaseTab(filePath, false, password, keyfile);
}
void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
@ -920,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()
@ -1002,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;

View File

@ -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,
@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -85,6 +85,22 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openHelpButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Open AutoType help webpage</string>
</property>
<property name="accessibleName">
<string>AutoType help button</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -268,6 +284,7 @@
<tabstop>inheritSequenceButton</tabstop>
<tabstop>customSequenceButton</tabstop>
<tabstop>sequenceEdit</tabstop>
<tabstop>openHelpButton</tabstop>
<tabstop>assocView</tabstop>
<tabstop>windowTitleCombo</tabstop>
<tabstop>customWindowSequenceButton</tabstop>

View File

@ -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()

View File

@ -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)) {

View File

@ -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<Database> TestCli::readTestDatabase() const
{
Utils::Test::setNextPassword("a");
auto db = QSharedPointer<Database>(Utils::unlockDatabase(m_dbFile->fileName(), "", m_stdoutHandle));
auto db = QSharedPointer<Database>(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<Database>(Utils::unlockDatabase(databaseFilename, "", Utils::DEVNULL));
auto db = QSharedPointer<Database>(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<Database>(Utils::unlockDatabase(databaseFilename2, keyfilePath, Utils::DEVNULL));
auto db2 = QSharedPointer<Database>(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<Database>(Utils::unlockDatabase(databaseFilename3, keyfilePath, Utils::DEVNULL));
auto db3 = QSharedPointer<Database>(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;

View File

@ -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<TemporaryFile> m_dbFile;
QScopedPointer<TemporaryFile> m_dbFile2;
QScopedPointer<TemporaryFile> m_keyFileProtectedDbFile;
QScopedPointer<TemporaryFile> m_keyFileProtectedNoPasswordDbFile;
QScopedPointer<TemporaryFile> m_stdoutFile;
QScopedPointer<TemporaryFile> m_stderrFile;
QScopedPointer<TemporaryFile> m_stdinFile;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.