Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorChristian Kieschnick <christian.kieschnick@hicknhack-software.com>2018-10-01 17:26:24 +0300
committerJonathan White <support@dmapps.us>2018-10-01 17:39:37 +0300
commiteca9c658f4d0a8e956d49ce2e9eea81704e1de9b (patch)
treef49da9147abee9a96a0acce17548233a988b1f34 /src/core
parentc1e9f45df9f21b7697241037643770a2862bb7ef (diff)
Add sharing of groups between databases
* Add source folder keeshare for sharing with corresponding define WITH_XC_KEESHARE * Move common crypto parts to src/crypto/ssh * Extended OpenSSHKey * Move filewatching to own file (currently in two related classes DelayedFileWatcher and BulkFileWatcher) * Small improvements for style and code in several classes * Sharing is secured using RSA-Keys which are generated on demand * Publisher signs the container using their private key * Client can verify the signed container and choose to decline an import, import only once or trust the publisher and automatically import all data of this source henceforth * Integration of settings into Group-Settings, Database-Settings and Application-Settings * Introduced dependency QuaZip as dependency to allow combined export of key container and the (custom format) certificate
Diffstat (limited to 'src/core')
-rw-r--r--src/core/Clock.cpp4
-rw-r--r--src/core/Config.cpp9
-rw-r--r--src/core/Config.h3
-rw-r--r--src/core/Database.cpp11
-rw-r--r--src/core/Database.h5
-rw-r--r--src/core/DatabaseIcons.cpp2
-rw-r--r--src/core/DatabaseIcons.h2
-rw-r--r--src/core/Entry.cpp14
-rw-r--r--src/core/Entry.h1
-rw-r--r--src/core/FileWatcher.cpp238
-rw-r--r--src/core/FileWatcher.h92
-rw-r--r--src/core/TimeInfo.cpp6
-rw-r--r--src/core/Tools.cpp27
-rw-r--r--src/core/Tools.h27
14 files changed, 429 insertions, 12 deletions
diff --git a/src/core/Clock.cpp b/src/core/Clock.cpp
index 02c2ae1bc..88ac4fb77 100644
--- a/src/core/Clock.cpp
+++ b/src/core/Clock.cpp
@@ -16,7 +16,7 @@
*/
#include "Clock.h"
-QSharedPointer<Clock> Clock::m_instance = QSharedPointer<Clock>();
+QSharedPointer<Clock> Clock::m_instance;
QDateTime Clock::currentDateTimeUtc()
{
@@ -92,7 +92,7 @@ QDateTime Clock::currentDateTimeImpl() const
void Clock::resetInstance()
{
- m_instance.clear();
+ m_instance.reset();
}
void Clock::setInstance(Clock* clock)
diff --git a/src/core/Config.cpp b/src/core/Config.cpp
index 8f1dc55fd..8fd2faad9 100644
--- a/src/core/Config.cpp
+++ b/src/core/Config.cpp
@@ -48,7 +48,16 @@ QString Config::getFileName()
void Config::set(const QString& key, const QVariant& value)
{
+ if (m_settings->contains(key) && m_settings->value(key) == value) {
+ return;
+ }
+ const bool surpressSignal = !m_settings->contains(key) && m_defaults.value(key) == value;
+
m_settings->setValue(key, value);
+
+ if (!surpressSignal) {
+ emit changed(key);
+ }
}
/**
diff --git a/src/core/Config.h b/src/core/Config.h
index fcb27e2ca..347350754 100644
--- a/src/core/Config.h
+++ b/src/core/Config.h
@@ -43,6 +43,9 @@ public:
static void createConfigFromFile(const QString& file);
static void createTempFileInstance();
+signals:
+ void changed(const QString& key);
+
private:
Config(const QString& fileName, QObject* parent);
explicit Config(QObject* parent);
diff --git a/src/core/Database.cpp b/src/core/Database.cpp
index 5b7a3c07d..607ecc93f 100644
--- a/src/core/Database.cpp
+++ b/src/core/Database.cpp
@@ -111,7 +111,7 @@ void Database::setFilePath(const QString& filePath)
m_filePath = filePath;
}
-Entry* Database::resolveEntry(const QUuid& uuid)
+Entry* Database::resolveEntry(const QUuid& uuid) const
{
return findEntryRecursive(uuid, m_rootGroup);
}
@@ -121,7 +121,7 @@ Entry* Database::resolveEntry(const QString& text, EntryReferenceType referenceT
return findEntryRecursive(text, referenceType, m_rootGroup);
}
-Entry* Database::findEntryRecursive(const QUuid& uuid, Group* group)
+Entry* Database::findEntryRecursive(const QUuid& uuid, Group* group) const
{
const QList<Entry*> entryList = group->entries();
for (Entry* entry : entryList) {
@@ -289,8 +289,11 @@ QByteArray Database::challengeResponseKey() const
bool Database::challengeMasterSeed(const QByteArray& masterSeed)
{
- m_data.masterSeed = masterSeed;
- return m_data.key->challenge(masterSeed, m_data.challengeResponseKey);
+ if (m_data.key) {
+ m_data.masterSeed = masterSeed;
+ return m_data.key->challenge(masterSeed, m_data.challengeResponseKey);
+ }
+ return true;
}
void Database::setCipher(const QUuid& cipher)
diff --git a/src/core/Database.h b/src/core/Database.h
index a5ae3effa..9253cb9ea 100644
--- a/src/core/Database.h
+++ b/src/core/Database.h
@@ -23,6 +23,7 @@
#include <QHash>
#include <QObject>
+#include "config-keepassx.h"
#include "crypto/kdf/Kdf.h"
#include "keys/CompositeKey.h"
@@ -88,7 +89,7 @@ public:
const Metadata* metadata() const;
QString filePath() const;
void setFilePath(const QString& filePath);
- Entry* resolveEntry(const QUuid& uuid);
+ Entry* resolveEntry(const QUuid& uuid) const;
Entry* resolveEntry(const QString& text, EntryReferenceType referenceType);
Group* resolveGroup(const QUuid& uuid);
QList<DeletedObject> deletedObjects();
@@ -149,7 +150,7 @@ private slots:
void startModifiedTimer();
private:
- Entry* findEntryRecursive(const QUuid& uuid, Group* group);
+ Entry* findEntryRecursive(const QUuid& uuid, Group* group) const;
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group);
Group* findGroupRecursive(const QUuid& uuid, Group* group);
diff --git a/src/core/DatabaseIcons.cpp b/src/core/DatabaseIcons.cpp
index ddb4e9106..6219d41f5 100644
--- a/src/core/DatabaseIcons.cpp
+++ b/src/core/DatabaseIcons.cpp
@@ -22,6 +22,8 @@
DatabaseIcons* DatabaseIcons::m_instance(nullptr);
const int DatabaseIcons::IconCount(69);
const int DatabaseIcons::ExpiredIconIndex(45);
+const int DatabaseIcons::SharedIconIndex(1);
+const int DatabaseIcons::UnsharedIconIndex(45);
// clang-format off
const char* const DatabaseIcons::m_indexToName[] = {
diff --git a/src/core/DatabaseIcons.h b/src/core/DatabaseIcons.h
index 43a6df216..ecd38fd8a 100644
--- a/src/core/DatabaseIcons.h
+++ b/src/core/DatabaseIcons.h
@@ -33,6 +33,8 @@ public:
static const int IconCount;
static const int ExpiredIconIndex;
+ static const int SharedIconIndex;
+ static const int UnsharedIconIndex;
private:
DatabaseIcons();
diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp
index 929447f9c..f3391189c 100644
--- a/src/core/Entry.cpp
+++ b/src/core/Entry.cpp
@@ -955,6 +955,20 @@ QString Entry::maskPasswordPlaceholders(const QString& str) const
return result;
}
+Entry* Entry::resolveReference(const QString& str) const
+{
+ QRegularExpressionMatch match = EntryAttributes::matchReference(str);
+ if (!match.hasMatch()) {
+ return nullptr;
+ }
+
+ const QString searchIn = match.captured(EntryAttributes::SearchInGroupName);
+ const QString searchText = match.captured(EntryAttributes::SearchTextGroupName);
+
+ const EntryReferenceType searchInType = Entry::referenceType(searchIn);
+ return m_group->database()->resolveEntry(searchText, searchInType);
+}
+
QString Entry::resolveMultiplePlaceholders(const QString& str) const
{
return resolveMultiplePlaceholdersRecursive(str, ResolveMaximumDepth);
diff --git a/src/core/Entry.h b/src/core/Entry.h
index 05ed30bc0..94649444b 100644
--- a/src/core/Entry.h
+++ b/src/core/Entry.h
@@ -195,6 +195,7 @@ public:
Entry* clone(CloneFlags flags) const;
void copyDataFrom(const Entry* other);
QString maskPasswordPlaceholders(const QString& str) const;
+ Entry* resolveReference(const QString& str) const;
QString resolveMultiplePlaceholders(const QString& str) const;
QString resolvePlaceholder(const QString& str) const;
QString resolveUrlPlaceholder(const QString& str, PlaceholderType placeholderType) const;
diff --git a/src/core/FileWatcher.cpp b/src/core/FileWatcher.cpp
new file mode 100644
index 000000000..ac44174bd
--- /dev/null
+++ b/src/core/FileWatcher.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
+ * Copyright (C) 2017 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 2 or (at your option)
+ * version 3 of the License.
+ *
+ * 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 "FileWatcher.h"
+
+#include "core/Clock.h"
+#include <QFileInfo>
+
+#ifdef Q_OS_LINUX
+#include <sys/vfs.h>
+#endif
+
+namespace
+{
+ const int FileChangeDelay = 500;
+ const int TimerResolution = 100;
+}
+
+DelayingFileWatcher::DelayingFileWatcher(QObject* parent)
+ : QObject(parent)
+ , m_ignoreFileChange(false)
+{
+ connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onWatchedFileChanged()));
+ connect(&m_fileUnblockTimer, SIGNAL(timeout()), this, SLOT(observeFileChanges()));
+ connect(&m_fileChangeDelayTimer, SIGNAL(timeout()), SIGNAL(fileChanged()));
+
+ m_fileChangeDelayTimer.setSingleShot(true);
+ m_fileUnblockTimer.setSingleShot(true);
+}
+
+void DelayingFileWatcher::restart()
+{
+ m_fileWatcher.addPath(m_filePath);
+}
+
+void DelayingFileWatcher::stop()
+{
+ m_fileWatcher.removePath(m_filePath);
+}
+
+void DelayingFileWatcher::start(const QString& filePath)
+{
+ if (!m_filePath.isEmpty()) {
+ m_fileWatcher.removePath(m_filePath);
+ }
+
+#if defined(Q_OS_LINUX)
+ struct statfs statfsBuf;
+ bool forcePolling = false;
+ const auto NFS_SUPER_MAGIC = 0x6969;
+
+ if (!statfs(filePath.toLocal8Bit().constData(), &statfsBuf)) {
+ forcePolling = (statfsBuf.f_type == NFS_SUPER_MAGIC);
+ } else {
+ // if we can't get the fs type let's fall back to polling
+ forcePolling = true;
+ }
+ auto objectName = forcePolling ? QLatin1String("_qt_autotest_force_engine_poller") : QLatin1String("");
+ m_fileWatcher.setObjectName(objectName);
+#endif
+
+ m_fileWatcher.addPath(filePath);
+
+ if (!filePath.isEmpty()) {
+ m_filePath = filePath;
+ }
+}
+
+void DelayingFileWatcher::ignoreFileChanges()
+{
+ m_ignoreFileChange = true;
+ m_fileChangeDelayTimer.stop();
+}
+
+void DelayingFileWatcher::observeFileChanges(bool delayed)
+{
+ int timeout = 0;
+ if (delayed) {
+ timeout = FileChangeDelay;
+ } else {
+ m_ignoreFileChange = false;
+ start(m_filePath);
+ }
+ if (timeout > 0 && !m_fileUnblockTimer.isActive()) {
+ m_fileUnblockTimer.start(timeout);
+ }
+}
+
+void DelayingFileWatcher::onWatchedFileChanged()
+{
+ if (m_ignoreFileChange) {
+ // the client forcefully silenced us
+ return;
+ }
+ if (m_fileChangeDelayTimer.isActive()) {
+ // we are waiting to fire the delayed fileChanged event, so nothing
+ // to do here
+ return;
+ }
+
+ m_fileChangeDelayTimer.start(FileChangeDelay);
+}
+
+BulkFileWatcher::BulkFileWatcher(QObject* parent)
+ : QObject(parent)
+{
+ connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(handleFileChanged(QString)));
+ connect(&m_fileWatcher, SIGNAL(directoryChanged(QString)), SLOT(handleDirectoryChanged(QString)));
+ connect(&m_fileWatchUnblockTimer, SIGNAL(timeout()), this, SLOT(observeFileChanges()));
+ m_fileWatchUnblockTimer.setSingleShot(true);
+}
+
+void BulkFileWatcher::clear()
+{
+ for (const QString& path : m_fileWatcher.files() + m_fileWatcher.directories()) {
+ const QFileInfo info(path);
+ m_fileWatcher.removePath(info.absoluteFilePath());
+ m_fileWatcher.removePath(info.absolutePath());
+ }
+ m_filePaths.clear();
+ m_watchedFilesInDirectory.clear();
+ m_ignoreFilesChangess.clear();
+}
+
+void BulkFileWatcher::removePath(const QString& path)
+{
+ const QFileInfo info(path);
+ m_fileWatcher.removePath(info.absoluteFilePath());
+ m_fileWatcher.removePath(info.absolutePath());
+ m_filePaths.remove(info.absoluteFilePath());
+ m_filePaths.remove(info.absolutePath());
+ m_watchedFilesInDirectory[info.absolutePath()].remove(info.absoluteFilePath());
+}
+
+void BulkFileWatcher::addPath(const QString& path)
+{
+ const QFileInfo info(path);
+ m_fileWatcher.addPath(info.absoluteFilePath());
+ m_fileWatcher.addPath(info.absolutePath());
+ m_filePaths.insert(info.absoluteFilePath());
+ m_filePaths.insert(info.absolutePath());
+ m_watchedFilesInDirectory[info.absolutePath()][info.absoluteFilePath()] = info.exists();
+}
+
+void BulkFileWatcher::restart(const QString& path)
+{
+ const QFileInfo info(path);
+ Q_ASSERT(m_filePaths.contains(info.absoluteFilePath()));
+ Q_ASSERT(m_filePaths.contains(info.absolutePath()));
+ m_fileWatcher.addPath(info.absoluteFilePath());
+ m_fileWatcher.addPath(info.absolutePath());
+}
+
+void BulkFileWatcher::handleFileChanged(const QString& path)
+{
+ addPath(path);
+
+ const QFileInfo info(path);
+ if (m_ignoreFilesChangess[info.canonicalFilePath()] > Clock::currentDateTimeUtc()) {
+ // changes are blocked
+ return;
+ }
+
+ emit fileChanged(path);
+}
+
+void BulkFileWatcher::handleDirectoryChanged(const QString& path)
+{
+ qDebug("Directory changed %s", qPrintable(path));
+ const QFileInfo directory(path);
+ const QMap<QString, bool>& watchedFiles = m_watchedFilesInDirectory[directory.absolutePath()];
+ for (const QString& file : watchedFiles.keys()) {
+ const QFileInfo info(file);
+ const bool existed = watchedFiles[info.absoluteFilePath()];
+ if (!info.exists() && existed) {
+ qDebug("Remove watch file %s", qPrintable(info.absoluteFilePath()));
+ m_fileWatcher.removePath(info.absolutePath());
+ emit fileRemoved(info.absoluteFilePath());
+ }
+ if (!existed && info.exists()) {
+ qDebug("Add watch file %s", qPrintable(info.absoluteFilePath()));
+ m_fileWatcher.addPath(info.absolutePath());
+ emit fileCreated(info.absoluteFilePath());
+ }
+ if (existed && info.exists()) {
+ qDebug("Refresh watch file %s", qPrintable(info.absoluteFilePath()));
+ m_fileWatcher.removePath(info.absolutePath());
+ m_fileWatcher.addPath(info.absolutePath());
+ emit fileChanged(info.absoluteFilePath());
+ }
+ m_watchedFilesInDirectory[info.absolutePath()][info.absoluteFilePath()] = info.exists();
+ }
+}
+
+void BulkFileWatcher::ignoreFileChanges(const QString& path)
+{
+ const QFileInfo info(path);
+ m_ignoreFilesChangess[info.canonicalFilePath()] = Clock::currentDateTimeUtc().addMSecs(FileChangeDelay);
+}
+
+void BulkFileWatcher::observeFileChanges(bool delayed)
+{
+ int timeout = 0;
+ if (delayed) {
+ timeout = TimerResolution;
+ } else {
+ const QDateTime current = Clock::currentDateTimeUtc();
+ for (const QString& key : m_ignoreFilesChangess.keys()) {
+ if (m_ignoreFilesChangess[key] < current) {
+ // We assume that there was no concurrent change of the database
+ // during our block - so no need to reimport
+ qDebug("Remove block from %s", qPrintable(key));
+ m_ignoreFilesChangess.remove(key);
+ continue;
+ }
+ qDebug("Keep block from %s", qPrintable(key));
+ timeout = static_cast<int>(current.msecsTo(m_ignoreFilesChangess[key]));
+ }
+ }
+ if (timeout > 0 && !m_fileWatchUnblockTimer.isActive()) {
+ m_fileWatchUnblockTimer.start(timeout);
+ }
+}
diff --git a/src/core/FileWatcher.h b/src/core/FileWatcher.h
new file mode 100644
index 000000000..de7dbb1c2
--- /dev/null
+++ b/src/core/FileWatcher.h
@@ -0,0 +1,92 @@
+/*
+ * 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 2 or (at your option)
+ * version 3 of the License.
+ *
+ * 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 KEEPASSXC_FILEWATCHER_H
+#define KEEPASSXC_FILEWATCHER_H
+
+#include <QFileSystemWatcher>
+#include <QSet>
+#include <QTimer>
+#include <QVariant>
+
+class DelayingFileWatcher : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit DelayingFileWatcher(QObject* parent = nullptr);
+
+ void blockAutoReload(bool block);
+ void start(const QString& path);
+
+ void restart();
+ void stop();
+ void ignoreFileChanges();
+
+signals:
+ void fileChanged();
+
+public slots:
+ void observeFileChanges(bool delayed = false);
+
+private slots:
+ void onWatchedFileChanged();
+
+private:
+ QString m_filePath;
+ QFileSystemWatcher m_fileWatcher;
+ QTimer m_fileChangeDelayTimer;
+ QTimer m_fileUnblockTimer;
+ bool m_ignoreFileChange;
+};
+
+class BulkFileWatcher : public QObject
+{
+ Q_OBJECT
+public:
+ explicit BulkFileWatcher(QObject* parent = nullptr);
+
+ void clear();
+
+ void removePath(const QString& path);
+ void addPath(const QString& path);
+
+ void restart(const QString& path);
+
+ void ignoreFileChanges(const QString& path);
+
+signals:
+ void fileCreated(QString);
+ void fileChanged(QString);
+ void fileRemoved(QString);
+
+public slots:
+ void observeFileChanges(bool delayed = false);
+
+private slots:
+ void handleFileChanged(const QString& path);
+ void handleDirectoryChanged(const QString& path);
+
+private:
+ QSet<QString> m_filePaths;
+ QMap<QString, QDateTime> m_ignoreFilesChangess;
+ QFileSystemWatcher m_fileWatcher;
+ QMap<QString, QMap<QString, bool>> m_watchedFilesInDirectory;
+ QTimer m_fileWatchUnblockTimer; // needed for Import/Export-References
+};
+
+#endif // KEEPASSXC_FILEWATCHER_H
diff --git a/src/core/TimeInfo.cpp b/src/core/TimeInfo.cpp
index c774a7c81..b48ad42ea 100644
--- a/src/core/TimeInfo.cpp
+++ b/src/core/TimeInfo.cpp
@@ -124,8 +124,7 @@ bool TimeInfo::equals(const TimeInfo& other, CompareItemOptions options) const
if (::compare(m_creationTime, other.m_creationTime, options) != 0) {
return false;
}
- if (::compare(!options.testFlag(CompareItemIgnoreStatistics), m_lastAccessTime, other.m_lastAccessTime, options)
- != 0) {
+ if (::compare(!options.testFlag(CompareItemIgnoreStatistics), m_lastAccessTime, other.m_lastAccessTime, options) != 0) {
return false;
}
if (::compare(m_expires, m_expiryTime, other.m_expires, other.expiryTime(), options) != 0) {
@@ -134,8 +133,7 @@ bool TimeInfo::equals(const TimeInfo& other, CompareItemOptions options) const
if (::compare(!options.testFlag(CompareItemIgnoreStatistics), m_usageCount, other.m_usageCount, options) != 0) {
return false;
}
- if (::compare(!options.testFlag(CompareItemIgnoreLocation), m_locationChanged, other.m_locationChanged, options)
- != 0) {
+ if (::compare(!options.testFlag(CompareItemIgnoreLocation), m_locationChanged, other.m_locationChanged, options) != 0) {
return false;
}
return true;
diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp
index 458d42988..2dc75873b 100644
--- a/src/core/Tools.cpp
+++ b/src/core/Tools.cpp
@@ -346,4 +346,31 @@ namespace Tools
return bSuccess;
}
+
+ Buffer::Buffer()
+ : raw(nullptr)
+ , size(0)
+ {
+
+ }
+
+ Buffer::~Buffer()
+ {
+ clear();
+ }
+
+ void Buffer::clear()
+ {
+ if(size > 0){
+ free(raw);
+ }
+ raw = nullptr; size = 0;
+ }
+
+ QByteArray Buffer::content() const
+ {
+ return QByteArray(reinterpret_cast<char*>(raw), size );
+ }
+
+
} // namespace Tools
diff --git a/src/core/Tools.h b/src/core/Tools.h
index 4f75b750b..c1814f756 100644
--- a/src/core/Tools.h
+++ b/src/core/Tools.h
@@ -56,6 +56,33 @@ namespace Tools
}
}
+ template <typename Key, typename Value, void deleter(Value)> struct Map
+ {
+ QMap<Key, Value> values;
+ Value& operator[](const Key index)
+ {
+ return values[index];
+ }
+
+ ~Map()
+ {
+ for (Value m : values) {
+ deleter(m);
+ }
+ }
+ };
+
+ struct Buffer
+ {
+ unsigned char* raw;
+ size_t size;
+
+ Buffer();
+ ~Buffer();
+
+ void clear();
+ QByteArray content() const;
+ };
} // namespace Tools
#endif // KEEPASSX_TOOLS_H