Merge branch 'release/2.5.1' into develop

This commit is contained in:
Janek Bevendorff 2019-11-06 10:09:28 +01:00
commit eed935c923
23 changed files with 108 additions and 47 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,6 +29,7 @@ public:
static const QCommandLineOption TotpOption;
static const QCommandLineOption AttributesOption;
static const QCommandLineOption ProtectedAttributesOption;
};
#endif // KEEPASSXC_SHOW_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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