mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-15 17:27:43 -05:00
Merge branch 'release/2.5.1' into develop
This commit is contained in:
commit
eed935c923
@ -438,6 +438,14 @@ if(UNIX)
|
||||
int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }"
|
||||
HAVE_PR_SET_DUMPABLE)
|
||||
|
||||
check_cxx_source_compiles("#include <malloc.h>
|
||||
int main() { return 0; }"
|
||||
HAVE_MALLOC_H)
|
||||
|
||||
check_cxx_source_compiles("#include <malloc.h>
|
||||
int main() { malloc_usable_size(NULL, 0); return 0; }"
|
||||
HAVE_MALLOC_USABLE_SIZE)
|
||||
|
||||
check_cxx_source_compiles("#include <sys/resource.h>
|
||||
int main() {
|
||||
struct rlimit limit;
|
||||
|
@ -182,6 +182,10 @@ an error if no TOTP is configured for the entry.
|
||||
Shows the named attributes. This option can be specified more than once,
|
||||
with each attribute shown one-per-line in the given order. If no attributes are
|
||||
specified and \fI-t\fP is not specified, a summary of the default attributes is given.
|
||||
Protected attributes will be displayed in clear text if specified explicitly by this option.
|
||||
|
||||
.IP "-s, --show-protected"
|
||||
Shows the protected attributes in clear text.
|
||||
|
||||
.IP "-t, --totp"
|
||||
Also shows the current TOTP, reporting an error if no TOTP is configured for
|
||||
|
@ -33,5 +33,9 @@ endif()
|
||||
set(QM_FILES ${QM_FILES} ${QTBASE_TRANSLATIONS})
|
||||
|
||||
install(FILES ${QM_FILES} DESTINATION ${DATA_INSTALL_DIR}/translations)
|
||||
|
||||
# Add keepassx_en.qm as a fallback for uncommon english locales
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/keepassx_en_US.qm DESTINATION ${DATA_INSTALL_DIR}/translations RENAME keepassx_en.qm)
|
||||
|
||||
add_custom_target(translations DEPENDS ${QM_FILES})
|
||||
add_dependencies(${PROGNAME} translations)
|
||||
|
@ -46,7 +46,7 @@ Clip::Clip()
|
||||
int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
const QStringList args = parser->positionalArguments();
|
||||
QString entryPath = args.at(1);
|
||||
const QString& entryPath = args.at(1);
|
||||
QString timeout;
|
||||
if (args.size() == 3) {
|
||||
timeout = args.at(2);
|
||||
|
@ -60,8 +60,8 @@ int Import::execute(const QStringList& arguments)
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString xmlExportPath = args.at(0);
|
||||
const QString dbPath = args.at(1);
|
||||
const QString& xmlExportPath = args.at(0);
|
||||
const QString& dbPath = args.at(1);
|
||||
|
||||
if (QFileInfo::exists(dbPath)) {
|
||||
errorTextStream << QObject::tr("File %1 already exists.").arg(dbPath) << endl;
|
||||
|
@ -60,7 +60,7 @@ int List::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
QString groupPath = args.at(1);
|
||||
const QString& groupPath = args.at(1);
|
||||
Group* group = database->rootGroup()->findGroupByPath(groupPath);
|
||||
if (!group) {
|
||||
errorTextStream << QObject::tr("Cannot find group %1.").arg(groupPath) << endl;
|
||||
|
@ -40,7 +40,7 @@ int Locate::executeWithDatabase(QSharedPointer<Database> database, QSharedPointe
|
||||
{
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
QString searchTerm = args.at(1);
|
||||
const QString& searchTerm = args.at(1);
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
|
@ -31,6 +31,11 @@ const QCommandLineOption Show::TotpOption = QCommandLineOption(QStringList() <<
|
||||
<< "totp",
|
||||
QObject::tr("Show the entry's current TOTP."));
|
||||
|
||||
const QCommandLineOption Show::ProtectedAttributesOption =
|
||||
QCommandLineOption(QStringList() << "s"
|
||||
<< "show-protected",
|
||||
QObject::tr("Show the protected attributes in clear text."));
|
||||
|
||||
const QCommandLineOption Show::AttributesOption = QCommandLineOption(
|
||||
QStringList() << "a"
|
||||
<< "attributes",
|
||||
@ -46,6 +51,7 @@ Show::Show()
|
||||
description = QObject::tr("Show an entry's information.");
|
||||
options.append(Show::TotpOption);
|
||||
options.append(Show::AttributesOption);
|
||||
options.append(Show::ProtectedAttributesOption);
|
||||
positionalArguments.append({QString("entry"), QObject::tr("Name of the entry to show."), QString("")});
|
||||
}
|
||||
|
||||
@ -57,6 +63,7 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& entryPath = args.at(1);
|
||||
bool showTotp = parser->isSet(Show::TotpOption);
|
||||
bool showProtectedAttributes = parser->isSet(Show::ProtectedAttributesOption);
|
||||
QStringList attributes = parser->values(Show::AttributesOption);
|
||||
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
@ -78,16 +85,20 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
// Iterate over the attributes and output them line-by-line.
|
||||
bool sawUnknownAttribute = false;
|
||||
for (const QString& attribute : asConst(attributes)) {
|
||||
if (!entry->attributes()->contains(attribute)) {
|
||||
for (const QString& attributeName : asConst(attributes)) {
|
||||
if (!entry->attributes()->contains(attributeName)) {
|
||||
sawUnknownAttribute = true;
|
||||
errorTextStream << QObject::tr("ERROR: unknown attribute %1.").arg(attribute) << endl;
|
||||
errorTextStream << QObject::tr("ERROR: unknown attribute %1.").arg(attributeName) << endl;
|
||||
continue;
|
||||
}
|
||||
if (showAttributeNames) {
|
||||
outputTextStream << attribute << ": ";
|
||||
outputTextStream << attributeName << ": ";
|
||||
}
|
||||
if (entry->attributes()->isProtected(attributeName) && showAttributeNames && !showProtectedAttributes) {
|
||||
outputTextStream << "PROTECTED" << endl;
|
||||
} else {
|
||||
outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attributeName)) << endl;
|
||||
}
|
||||
outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl;
|
||||
}
|
||||
|
||||
if (showTotp) {
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
|
||||
static const QCommandLineOption TotpOption;
|
||||
static const QCommandLineOption AttributesOption;
|
||||
static const QCommandLineOption ProtectedAttributesOption;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_SHOW_H
|
||||
|
@ -23,8 +23,10 @@
|
||||
#include <malloc/malloc.h>
|
||||
#elif defined(Q_OS_FREEBSD)
|
||||
#include <malloc_np.h>
|
||||
#else
|
||||
#elif defined(HAVE_MALLOC_H)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG) && !defined(__cpp_sized_deallocation)
|
||||
@ -64,7 +66,7 @@ void operator delete(void* ptr) noexcept
|
||||
::operator delete(ptr, _msize(ptr));
|
||||
#elif defined(Q_OS_MACOS)
|
||||
::operator delete(ptr, malloc_size(ptr));
|
||||
#elif defined(Q_OS_UNIX)
|
||||
#elif defined(HAVE_MALLOC_USABLE_SIZE)
|
||||
::operator delete(ptr, malloc_usable_size(ptr));
|
||||
#else
|
||||
// whatever OS this is, give up and simply free stuff
|
||||
|
@ -158,7 +158,7 @@ bool Database::open(const QString& filePath, QSharedPointer<const CompositeKey>
|
||||
|
||||
m_initialized = true;
|
||||
emit databaseOpened();
|
||||
m_fileWatcher->start(canonicalFilePath());
|
||||
m_fileWatcher->start(canonicalFilePath(), 30, 1);
|
||||
setEmitModified(true);
|
||||
|
||||
return true;
|
||||
@ -234,7 +234,7 @@ bool Database::saveAs(const QString& filePath, QString* error, bool atomic, bool
|
||||
bool ok = performSave(canonicalFilePath, error, atomic, backup);
|
||||
if (ok) {
|
||||
setFilePath(filePath);
|
||||
m_fileWatcher->start(canonicalFilePath);
|
||||
m_fileWatcher->start(canonicalFilePath, 30, 1);
|
||||
} else {
|
||||
// Saving failed, don't rewatch file since it does not represent our database
|
||||
markAsModified();
|
||||
|
@ -42,7 +42,7 @@ FileWatcher::FileWatcher(QObject* parent)
|
||||
m_fileIgnoreDelayTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
void FileWatcher::start(const QString& filePath, int checksumInterval)
|
||||
void FileWatcher::start(const QString& filePath, int checksumIntervalSeconds, int checksumSizeKibibytes)
|
||||
{
|
||||
stop();
|
||||
|
||||
@ -63,8 +63,14 @@ void FileWatcher::start(const QString& filePath, int checksumInterval)
|
||||
|
||||
m_fileWatcher.addPath(filePath);
|
||||
m_filePath = filePath;
|
||||
|
||||
// Handle file checksum
|
||||
m_fileChecksumSizeBytes = checksumSizeKibibytes * 1024;
|
||||
m_fileChecksum = calculateChecksum();
|
||||
m_fileChecksumTimer.start(checksumInterval);
|
||||
if (checksumIntervalSeconds > 0) {
|
||||
m_fileChecksumTimer.start(checksumIntervalSeconds * 1000);
|
||||
}
|
||||
|
||||
m_ignoreFileChange = false;
|
||||
}
|
||||
|
||||
@ -131,9 +137,12 @@ QByteArray FileWatcher::calculateChecksum()
|
||||
QFile file(m_filePath);
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
QCryptographicHash hash(QCryptographicHash::Sha256);
|
||||
if (hash.addData(&file)) {
|
||||
return hash.result();
|
||||
if (m_fileChecksumSizeBytes > 0) {
|
||||
hash.addData(file.read(m_fileChecksumSizeBytes));
|
||||
} else {
|
||||
hash.addData(&file);
|
||||
}
|
||||
return hash.result();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class FileWatcher : public QObject
|
||||
public:
|
||||
explicit FileWatcher(QObject* parent = nullptr);
|
||||
|
||||
void start(const QString& path, int checksumInterval = 1000);
|
||||
void start(const QString& path, int checksumIntervalSeconds = 0, int checksumSizeKibibytes = -1);
|
||||
void stop();
|
||||
|
||||
bool hasSameFileChecksum();
|
||||
@ -56,6 +56,7 @@ private:
|
||||
QTimer m_fileChangeDelayTimer;
|
||||
QTimer m_fileIgnoreDelayTimer;
|
||||
QTimer m_fileChecksumTimer;
|
||||
int m_fileChecksumSizeBytes;
|
||||
bool m_ignoreFileChange;
|
||||
};
|
||||
|
||||
|
@ -90,7 +90,7 @@ void IconDownloader::setUrl(const QString& entryUrl)
|
||||
// searching for a match with the returned address(es).
|
||||
bool hostIsIp = false;
|
||||
QList<QHostAddress> hostAddressess = QHostInfo::fromName(fullyQualifiedDomain).addresses();
|
||||
for (auto addr : hostAddressess) {
|
||||
for (const auto& addr : hostAddressess) {
|
||||
if (addr.toString() == fullyQualifiedDomain) {
|
||||
hostIsIp = true;
|
||||
}
|
||||
|
@ -341,6 +341,8 @@ OpVaultReader::decodeB64CompositeKeys(const QString& b64, const QByteArray& encK
|
||||
result->errorStr = tr("Unable to decode masterKey: %1").arg(keyKey01.errorString());
|
||||
return result;
|
||||
}
|
||||
delete result;
|
||||
|
||||
const QByteArray keyKey = keyKey01.getClearText();
|
||||
|
||||
return decodeCompositeKeys(keyKey);
|
||||
|
@ -75,3 +75,10 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte
|
||||
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
bool YubiKey::checkSlotIsBlocking(int slot, QString& errorMessage)
|
||||
{
|
||||
Q_UNUSED(slot);
|
||||
Q_UNUSED(errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
@ -55,6 +55,10 @@ int main(int argc, char** argv)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
||||
QGuiApplication::setDesktopFileName("org.keepassxc.KeePassXC.desktop");
|
||||
#endif
|
||||
|
||||
Application app(argc, argv);
|
||||
Application::setApplicationName("keepassxc");
|
||||
Application::setApplicationVersion(KEEPASSXC_VERSION);
|
||||
|
@ -87,17 +87,7 @@ macro(add_unit_test)
|
||||
endif()
|
||||
endmacro(add_unit_test)
|
||||
|
||||
set(TEST_LIBRARIES
|
||||
keepassx_core
|
||||
${keepassxcbrowser_LIB}
|
||||
${autotype_LIB}
|
||||
Qt5::Core
|
||||
Qt5::Concurrent
|
||||
Qt5::Widgets
|
||||
Qt5::Test
|
||||
${GCRYPT_LIBRARIES}
|
||||
${GPGERROR_LIBRARIES}
|
||||
${ZLIB_LIBRARIES})
|
||||
set(TEST_LIBRARIES keepassx_core Qt5::Test)
|
||||
|
||||
set(testsupport_SOURCES
|
||||
modeltest.cpp
|
||||
@ -108,10 +98,6 @@ set(testsupport_SOURCES
|
||||
add_library(testsupport STATIC ${testsupport_SOURCES})
|
||||
target_link_libraries(testsupport Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Test)
|
||||
|
||||
if(YUBIKEY_FOUND)
|
||||
set(TEST_LIBRARIES ${TEST_LIBRARIES} ${YUBIKEY_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_unit_test(NAME testgroup SOURCES TestGroup.cpp
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
|
||||
|
@ -21,9 +21,9 @@
|
||||
#include "core/Bootstrap.h"
|
||||
#include "core/Config.h"
|
||||
#include "core/Global.h"
|
||||
#include "core/PasswordGenerator.h"
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "keys/drivers/YubiKey.h"
|
||||
#include "format/Kdbx3Reader.h"
|
||||
#include "format/Kdbx3Writer.h"
|
||||
#include "format/Kdbx4Reader.h"
|
||||
@ -1682,14 +1682,15 @@ void TestCli::testShow()
|
||||
QCOMPARE(m_stdoutFile->readAll(),
|
||||
QByteArray("Title: Sample Entry\n"
|
||||
"UserName: User Name\n"
|
||||
"Password: Password\n"
|
||||
"Password: PROTECTED\n"
|
||||
"URL: http://www.somesite.com/\n"
|
||||
"Notes: Notes\n"));
|
||||
|
||||
qint64 pos = m_stdoutFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
showCmd.execute({"show", m_dbFile->fileName(), "-q", "/Sample Entry"});
|
||||
showCmd.execute({"show", "-s", m_dbFile->fileName(), "/Sample Entry"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
QCOMPARE(m_stdoutFile->readAll(),
|
||||
QByteArray("Title: Sample Entry\n"
|
||||
"UserName: User Name\n"
|
||||
@ -1697,6 +1698,17 @@ void TestCli::testShow()
|
||||
"URL: http://www.somesite.com/\n"
|
||||
"Notes: Notes\n"));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
showCmd.execute({"show", m_dbFile->fileName(), "-q", "/Sample Entry"});
|
||||
m_stdoutFile->seek(pos);
|
||||
QCOMPARE(m_stdoutFile->readAll(),
|
||||
QByteArray("Title: Sample Entry\n"
|
||||
"UserName: User Name\n"
|
||||
"Password: PROTECTED\n"
|
||||
"URL: http://www.somesite.com/\n"
|
||||
"Notes: Notes\n"));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
showCmd.execute({"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"});
|
||||
@ -1704,6 +1716,13 @@ void TestCli::testShow()
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Sample Entry\n"));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
showCmd.execute({"show", "-a", "Password", m_dbFile->fileName(), "/Sample Entry"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Password\n"));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
showCmd.execute({"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"});
|
||||
|
@ -111,7 +111,7 @@ void TestCsvParser::testEmptySimple()
|
||||
out << "";
|
||||
QVERIFY(parser->parse(file.data()));
|
||||
t = parser->getCsvTable();
|
||||
QVERIFY(t.size() == 0);
|
||||
QVERIFY(t.isEmpty());
|
||||
}
|
||||
|
||||
void TestCsvParser::testEmptyQuoted()
|
||||
@ -120,7 +120,7 @@ void TestCsvParser::testEmptyQuoted()
|
||||
out << "\"\"";
|
||||
QVERIFY(parser->parse(file.data()));
|
||||
t = parser->getCsvTable();
|
||||
QVERIFY(t.size() == 0);
|
||||
QVERIFY(t.isEmpty());
|
||||
}
|
||||
|
||||
void TestCsvParser::testEmptyNewline()
|
||||
@ -129,14 +129,14 @@ void TestCsvParser::testEmptyNewline()
|
||||
out << "\"\n\"";
|
||||
QVERIFY(parser->parse(file.data()));
|
||||
t = parser->getCsvTable();
|
||||
QVERIFY(t.size() == 0);
|
||||
QVERIFY(t.isEmpty());
|
||||
}
|
||||
|
||||
void TestCsvParser::testEmptyFile()
|
||||
{
|
||||
QVERIFY(parser->parse(file.data()));
|
||||
t = parser->getCsvTable();
|
||||
QVERIFY(t.size() == 0);
|
||||
QVERIFY(t.isEmpty());
|
||||
}
|
||||
|
||||
void TestCsvParser::testNewline()
|
||||
@ -281,7 +281,7 @@ void TestCsvParser::testEmptyReparsing()
|
||||
parser->parse(nullptr);
|
||||
QVERIFY(parser->reparse());
|
||||
t = parser->getCsvTable();
|
||||
QVERIFY(t.size() == 0);
|
||||
QVERIFY(t.isEmpty());
|
||||
}
|
||||
|
||||
void TestCsvParser::testReparsing()
|
||||
|
@ -296,7 +296,7 @@ void TestEntryModel::testProxyModel()
|
||||
QSignalSpy spyColumnRemove(modelProxy, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)));
|
||||
modelProxy->hideColumn(0, true);
|
||||
QCOMPARE(modelProxy->columnCount(), 12);
|
||||
QVERIFY(spyColumnRemove.size() >= 1);
|
||||
QVERIFY(!spyColumnRemove.isEmpty());
|
||||
|
||||
int oldSpyColumnRemoveSize = spyColumnRemove.size();
|
||||
modelProxy->hideColumn(0, true);
|
||||
@ -318,7 +318,7 @@ void TestEntryModel::testProxyModel()
|
||||
QSignalSpy spyColumnInsert(modelProxy, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)));
|
||||
modelProxy->hideColumn(0, false);
|
||||
QCOMPARE(modelProxy->columnCount(), 13);
|
||||
QVERIFY(spyColumnInsert.size() >= 1);
|
||||
QVERIFY(!spyColumnInsert.isEmpty());
|
||||
|
||||
int oldSpyColumnInsertSize = spyColumnInsert.size();
|
||||
modelProxy->hideColumn(0, false);
|
||||
|
@ -1070,7 +1070,7 @@ void TestGroup::testHierarchy()
|
||||
QVERIFY(hierarchy.contains("group3"));
|
||||
|
||||
hierarchy = group3->hierarchy(0);
|
||||
QVERIFY(hierarchy.size() == 0);
|
||||
QVERIFY(hierarchy.isEmpty());
|
||||
|
||||
hierarchy = group3->hierarchy(1);
|
||||
QVERIFY(hierarchy.size() == 1);
|
||||
|
@ -16,5 +16,8 @@
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
|
||||
add_unit_test(NAME testgui SOURCES TestGui.cpp ../util/TemporaryFile.cpp LIBS ${TEST_LIBRARIES})
|
||||
add_unit_test(NAME testguibrowser SOURCES TestGuiBrowser.cpp ../util/TemporaryFile.cpp LIBS ${TEST_LIBRARIES})
|
||||
add_unit_test(NAME testguipixmaps SOURCES TestGuiPixmaps.cpp LIBS ${TEST_LIBRARIES})
|
||||
|
||||
if(WITH_XC_BROWSER)
|
||||
add_unit_test(NAME testguibrowser SOURCES TestGuiBrowser.cpp ../util/TemporaryFile.cpp LIBS ${TEST_LIBRARIES})
|
||||
endif()
|
||||
|
Loading…
Reference in New Issue
Block a user