Merge branch 'master' into develop

This commit is contained in:
Jonathan White
2019-03-19 19:01:31 -04:00
99 changed files with 4428 additions and 3872 deletions

View File

@@ -275,6 +275,7 @@ bool Database::writeDatabase(QIODevice* device, QString* error)
return false;
}
QByteArray oldTransformedKey = m_data.transformedMasterKey;
KeePass2Writer writer;
setEmitModified(false);
writer.writeDatabase(device, this);
@@ -287,6 +288,15 @@ bool Database::writeDatabase(QIODevice* device, QString* error)
return false;
}
Q_ASSERT(!m_data.transformedMasterKey.isEmpty());
Q_ASSERT(m_data.transformedMasterKey != oldTransformedKey);
if (m_data.transformedMasterKey.isEmpty() || m_data.transformedMasterKey == oldTransformedKey) {
if (error) {
*error = tr("Key not transformed. This is a bug, please report it to the developers!");
}
return false;
}
markAsClean();
return true;
}
@@ -307,16 +317,18 @@ bool Database::extract(QByteArray& xmlOutput, QString* error)
/**
* Remove the old backup and replace it with a new one
* backups are named <filename>.old.kdbx
* backups are named <filename>.old.<extension>
*
* @param filePath Path to the file to backup
* @return true on success
*/
bool Database::backupDatabase(const QString& filePath)
{
QString backupFilePath = filePath;
auto re = QRegularExpression("\\.kdbx$|(?<!\\.kdbx)$", QRegularExpression::CaseInsensitiveOption);
backupFilePath.replace(re, ".old.kdbx");
static auto re = QRegularExpression("(\\.[^.]+)$");
auto match = re.match(filePath);
auto backupFilePath = filePath;
backupFilePath = backupFilePath.replace(re, "") + ".old" + match.captured(1);
QFile::remove(backupFilePath);
return QFile::copy(filePath, backupFilePath);
}
@@ -512,9 +524,13 @@ void Database::setCompressionAlgorithm(Database::CompressionAlgorithm algo)
* @param key key to set and transform or nullptr to reset the key
* @param updateChangedTime true to update database change time
* @param updateTransformSalt true to update the transform salt
* @param transformKey trigger the KDF after setting the key
* @return true on success
*/
bool Database::setKey(const QSharedPointer<const CompositeKey>& key, bool updateChangedTime, bool updateTransformSalt)
bool Database::setKey(const QSharedPointer<const CompositeKey>& key,
bool updateChangedTime,
bool updateTransformSalt,
bool transformKey)
{
Q_ASSERT(!m_data.isReadOnly);
@@ -532,7 +548,9 @@ bool Database::setKey(const QSharedPointer<const CompositeKey>& key, bool update
QByteArray oldTransformedMasterKey = m_data.transformedMasterKey;
QByteArray transformedMasterKey;
if (!key->transform(*m_data.kdf, transformedMasterKey)) {
if (!transformKey) {
transformedMasterKey = oldTransformedMasterKey;
} else if (!key->transform(*m_data.kdf, transformedMasterKey)) {
return false;
}

View File

@@ -109,7 +109,8 @@ public:
QSharedPointer<const CompositeKey> key() const;
bool setKey(const QSharedPointer<const CompositeKey>& key,
bool updateChangedTime = true,
bool updateTransformSalt = false);
bool updateTransformSalt = false,
bool transformKey = true);
QByteArray challengeResponseKey() const;
bool challengeMasterSeed(const QByteArray& masterSeed);
bool verifyKey(const QSharedPointer<CompositeKey>& key) const;

View File

@@ -926,7 +926,7 @@ QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder,
Q_ASSERT(m_group);
Q_ASSERT(m_group->database());
const Entry* refEntry = m_group->findEntryBySearchTerm(searchText, searchInType);
const Entry* refEntry = m_group->database()->rootGroup()->findEntryBySearchTerm(searchText, searchInType);
if (refEntry) {
const QString wantedField = match.captured(EntryAttributes::WantedFieldGroupName);

View File

@@ -67,6 +67,15 @@ QString EntryAttributes::value(const QString& key) const
return m_attributes.value(key);
}
QList<QString> EntryAttributes::values(const QList<QString>& keys) const
{
QList<QString> values;
for (const QString& key : keys) {
values.append(m_attributes.value(key));
}
return values;
}
bool EntryAttributes::contains(const QString& key) const
{
return m_attributes.contains(key);

View File

@@ -36,6 +36,7 @@ public:
bool hasKey(const QString& key) const;
QList<QString> customKeys() const;
QString value(const QString& key) const;
QList<QString> values(const QList<QString>& keys) const;
bool contains(const QString& key) const;
bool containsValue(const QString& value) const;
bool isProtected(const QString& key) const;

View File

@@ -28,31 +28,83 @@ EntrySearcher::EntrySearcher(bool caseSensitive)
{
}
/**
* Search group, and its children, by parsing the provided search
* string for search terms.
*
* @param searchString search terms
* @param baseGroup group to start search from, cannot be null
* @param forceSearch ignore group search settings
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::search(const QString& searchString, const Group* baseGroup, bool forceSearch)
{
Q_ASSERT(baseGroup);
parseSearchTerms(searchString);
return repeat(baseGroup, forceSearch);
}
/**
* Repeat the last search starting from the given group
*
* @param baseGroup group to start search from, cannot be null
* @param forceSearch ignore group search settings
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::repeat(const Group* baseGroup, bool forceSearch)
{
Q_ASSERT(baseGroup);
QList<Entry*> results;
for (const auto group : baseGroup->groupsRecursive(true)) {
if (forceSearch || group->resolveSearchingEnabled()) {
results.append(searchEntries(searchString, group->entries()));
for (auto* entry : group->entries()) {
if (searchEntryImpl(entry)) {
results.append(entry);
}
}
}
}
return results;
}
/**
* Search provided entries by parsing the search string
* for search terms.
*
* @param searchString search terms
* @param entries list of entries to include in the search
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::searchEntries(const QString& searchString, const QList<Entry*>& entries)
{
parseSearchTerms(searchString);
return repeatEntries(entries);
}
/**
* Repeat the last search on the given entries
*
* @param entries list of entries to include in the search
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::repeatEntries(const QList<Entry*>& entries)
{
QList<Entry*> results;
for (Entry* entry : entries) {
if (searchEntryImpl(searchString, entry)) {
for (auto* entry : entries) {
if (searchEntryImpl(entry)) {
results.append(entry);
}
}
return results;
}
/**
* Set the next search to be case sensitive or not
*
* @param state
*/
void EntrySearcher::setCaseSensitive(bool state)
{
m_caseSensitive = state;
@@ -63,16 +115,15 @@ bool EntrySearcher::isCaseSensitive()
return m_caseSensitive;
}
bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry)
bool EntrySearcher::searchEntryImpl(Entry* entry)
{
// Pre-load in case they are needed
auto attributes = QStringList(entry->attributes()->keys());
auto attributes_keys = entry->attributes()->customKeys();
auto attributes = QStringList(attributes_keys + entry->attributes()->values(attributes_keys));
auto attachments = QStringList(entry->attachments()->keys());
bool found;
auto searchTerms = parseSearchTerms(searchString);
for (const auto& term : searchTerms) {
for (const auto& term : m_searchTerms) {
switch (term->field) {
case Field::Title:
found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch();
@@ -112,10 +163,9 @@ bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry)
return true;
}
QList<QSharedPointer<EntrySearcher::SearchTerm>> EntrySearcher::parseSearchTerms(const QString& searchString)
void EntrySearcher::parseSearchTerms(const QString& searchString)
{
auto terms = QList<QSharedPointer<SearchTerm>>();
m_searchTerms.clear();
auto results = m_termParser.globalMatch(searchString);
while (results.hasNext()) {
auto result = results.next();
@@ -165,8 +215,6 @@ QList<QSharedPointer<EntrySearcher::SearchTerm>> EntrySearcher::parseSearchTerms
}
}
terms.append(term);
m_searchTerms.append(term);
}
return terms;
}

View File

@@ -31,14 +31,15 @@ public:
explicit EntrySearcher(bool caseSensitive = false);
QList<Entry*> search(const QString& searchString, const Group* baseGroup, bool forceSearch = false);
QList<Entry*> repeat(const Group* baseGroup, bool forceSearch = false);
QList<Entry*> searchEntries(const QString& searchString, const QList<Entry*>& entries);
QList<Entry*> repeatEntries(const QList<Entry*>& entries);
void setCaseSensitive(bool state);
bool isCaseSensitive();
private:
bool searchEntryImpl(const QString& searchString, Entry* entry);
enum class Field
{
Undefined,
@@ -59,10 +60,12 @@ private:
bool exclude;
};
QList<QSharedPointer<SearchTerm>> parseSearchTerms(const QString& searchString);
bool searchEntryImpl(Entry* entry);
void parseSearchTerms(const QString& searchString);
bool m_caseSensitive;
QRegularExpression m_termParser;
QList<QSharedPointer<SearchTerm>> m_searchTerms;
friend class TestEntrySearcher;
};

View File

@@ -123,6 +123,7 @@ BulkFileWatcher::BulkFileWatcher(QObject* parent)
connect(&m_fileWatchUnblockTimer, SIGNAL(timeout()), this, SLOT(observeFileChanges()));
connect(&m_pendingSignalsTimer, SIGNAL(timeout()), this, SLOT(emitSignals()));
m_fileWatchUnblockTimer.setSingleShot(true);
m_pendingSignalsTimer.setSingleShot(true);
}
void BulkFileWatcher::clear()

View File

@@ -1,8 +1,30 @@
/*
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "OSEventFilter.h"
#include <QByteArray>
#include "autotype/AutoType.h"
#include "gui/MainWindow.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
OSEventFilter::OSEventFilter()
{
@@ -15,12 +37,18 @@ bool OSEventFilter::nativeEventFilter(const QByteArray& eventType, void* message
#if defined(Q_OS_UNIX)
if (eventType == QByteArrayLiteral("xcb_generic_event_t")) {
#elif defined(Q_OS_WIN)
if (eventType == QByteArrayLiteral("windows_generic_MSG")
|| eventType == QByteArrayLiteral("windows_dispatcher_MSG")) {
auto winmsg = static_cast<MSG*>(message);
if (winmsg->message == WM_QUERYENDSESSION) {
*result = 1;
return true;
} else if (winmsg->message == WM_ENDSESSION) {
getMainWindow()->appExit();
*result = 0;
return true;
} else if (eventType == QByteArrayLiteral("windows_generic_MSG")
|| eventType == QByteArrayLiteral("windows_dispatcher_MSG")) {
#endif
int retCode = autoType()->callEventFilter(message);
return retCode == 1;
return autoType()->callEventFilter(message) == 1;
}
return false;

View File

@@ -1,3 +1,21 @@
/*
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OSEVENTFILTER_H
#define OSEVENTFILTER_H
#include <QAbstractNativeEventFilter>