mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Merge branch 'release/2.4.1' into develop
This commit is contained in:
commit
d7660dad37
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -40,6 +40,7 @@ public:
|
||||
|
||||
static const QCommandLineOption QuietOption;
|
||||
static const QCommandLineOption KeyFileOption;
|
||||
static const QCommandLineOption NoPasswordOption;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_COMMAND_H
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
@ -56,7 +57,10 @@ int Extract::execute(const QStringList& arguments)
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
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();
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -112,6 +112,10 @@ namespace Bootstrap
|
||||
mainWindow.openDatabase(filename);
|
||||
}
|
||||
}
|
||||
auto lastActiveFile = config()->get("LastActiveDatabase").toString();
|
||||
if (!lastActiveFile.isEmpty()) {
|
||||
mainWindow.openDatabase(lastActiveFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 (openPreviousDatabasesOnStartup) {
|
||||
connect(
|
||||
m_ui->tabWidget, SIGNAL(databaseClosed(const QString&)), this, SLOT(rememberOpenDatabases(const QString&)));
|
||||
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
|
||||
auto currentDbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
||||
if (currentDbWidget) {
|
||||
config()->set("LastActiveDatabase", currentDbWidget->database()->filePath());
|
||||
} else {
|
||||
config()->set("LastActiveDatabase", {});
|
||||
}
|
||||
|
||||
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);
|
||||
QStringList openDatabases;
|
||||
for (int i=0; i < m_ui->tabWidget->count(); ++i) {
|
||||
auto dbWidget = m_ui->tabWidget->databaseWidgetFromIndex(i);
|
||||
openDatabases.append(dbWidget->database()->filePath());
|
||||
}
|
||||
|
||||
return accept;
|
||||
config()->set("LastOpenedDatabases", openDatabases);
|
||||
} else {
|
||||
config()->set("LastActiveDatabase", {});
|
||||
config()->set("LastOpenedDatabases", {});
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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()
|
||||
|
18
src/main.cpp
18
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)) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
BIN
tests/data/KeyFileProtected.kdbx
Normal file
BIN
tests/data/KeyFileProtected.kdbx
Normal file
Binary file not shown.
BIN
tests/data/KeyFileProtected.key
Normal file
BIN
tests/data/KeyFileProtected.key
Normal file
Binary file not shown.
BIN
tests/data/KeyFileProtectedNoPassword.kdbx
Normal file
BIN
tests/data/KeyFileProtectedNoPassword.kdbx
Normal file
Binary file not shown.
BIN
tests/data/KeyFileProtectedNoPassword.key
Normal file
BIN
tests/data/KeyFileProtectedNoPassword.key
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user