Skip to content

Commit

Permalink
Introduce synchronize merge method
Browse files Browse the repository at this point in the history
* Create history-based merging that keeps older data in history instead of discarding or deleting it
* Extract merge logic into the Merger class
* Allows special merge behavior
* Improve handling of deletion and changes on groups
* Enable basic change tracking while merging
* Prevent unintended timestamp changes while merging
* Handle differences in timestamp precision
* Introduce comparison operators to allow for more sophisticated comparisons (ignore special properties, ...)
* Introduce Clock class to handle datetime across the app

Merge Strategies:
* Default (use inherited/fallback method)
* Duplicate (duplicate conflicting nodes, apply all deletions)
* KeepLocal (use local values, but apply all deletions)
* KeepRemote (use remote values, but apply all deletions)
* KeepNewer (merge history only)
* Synchronize (merge history, newest value stays on top, apply all deletions)
  • Loading branch information
droidmonkey committed Sep 30, 2018
1 parent b40e568 commit c1e9f45
Show file tree
Hide file tree
Showing 43 changed files with 2,779 additions and 587 deletions.
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ set(keepassx_SOURCES
core/AutoTypeAssociations.cpp
core/AsyncTask.h
core/AutoTypeMatch.cpp
core/Compare.cpp
core/Config.cpp
core/CsvParser.cpp
core/CustomData.cpp
Expand All @@ -54,6 +55,7 @@ set(keepassx_SOURCES
core/Group.cpp
core/InactivityTimer.cpp
core/ListDeleter.h
core/Merger.cpp
core/Metadata.cpp
core/PasswordGenerator.cpp
core/PassphraseGenerator.cpp
Expand All @@ -64,6 +66,7 @@ set(keepassx_SOURCES
core/ScreenLockListenerPrivate.cpp
core/TimeDelta.cpp
core/TimeInfo.cpp
core/Clock.cpp
core/Tools.cpp
core/Translator.cpp
core/Base32.h
Expand Down
7 changes: 3 additions & 4 deletions src/autotype/AutoType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -756,10 +756,9 @@ bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
}
} else if (AutoType::checkHighRepetition(sequence)) {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(nullptr,
tr("Auto-Type"),
tr("This Auto-Type command contains arguments which are "
"repeated very often. Do you really want to proceed?"));
reply =
QMessageBox::question(nullptr, tr("Auto-Type"), tr("This Auto-Type command contains arguments which are "
"repeated very often. Do you really want to proceed?"));

if (reply == QMessageBox::No) {
return false;
Expand Down
6 changes: 3 additions & 3 deletions src/browser/BrowserService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ QString BrowserService::getDatabaseRootUuid()
return QString();
}

return QString::fromLatin1(rootGroup->uuid().toRfc4122().toHex());
return rootGroup->uuidToHex();
}

QString BrowserService::getDatabaseRecycleBinUuid()
Expand All @@ -128,7 +128,7 @@ QString BrowserService::getDatabaseRecycleBinUuid()
if (!recycleBin) {
return QString();
}
return QString::fromLatin1(recycleBin->uuid().toRfc4122().toHex());
return recycleBin->uuidToHex();
}

Entry* BrowserService::getConfigEntry(bool create)
Expand Down Expand Up @@ -636,7 +636,7 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
res["login"] = entry->resolveMultiplePlaceholders(entry->username());
res["password"] = entry->resolveMultiplePlaceholders(entry->password());
res["name"] = entry->resolveMultiplePlaceholders(entry->title());
res["uuid"] = entry->resolveMultiplePlaceholders(QString::fromLatin1(entry->uuid().toRfc4122().toHex()));
res["uuid"] = entry->resolveMultiplePlaceholders(entry->uuidToHex());

if (entry->hasTotp()) {
res["totp"] = entry->totp();
Expand Down
4 changes: 3 additions & 1 deletion src/cli/Merge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <QTextStream>

#include "core/Database.h"
#include "core/Merger.h"

Merge::Merge()
{
Expand Down Expand Up @@ -82,7 +83,8 @@ int Merge::execute(const QStringList& arguments)
return EXIT_FAILURE;
}

db1->merge(db2);
Merger merger(db2, db1);
merger.merge();

QString errorMessage = db1->saveToFile(args.at(0));
if (!errorMessage.isEmpty()) {
Expand Down
18 changes: 18 additions & 0 deletions src/core/AutoTypeAssociations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,21 @@ void AutoTypeAssociations::clear()
{
m_associations.clear();
}

bool AutoTypeAssociations::operator==(const AutoTypeAssociations& other) const
{
if (m_associations.count() != other.m_associations.count()) {
return false;
}
for (int i = 0; i < m_associations.count(); ++i) {
if (m_associations[i] != other.m_associations[i]) {
return false;
}
}
return true;
}

bool AutoTypeAssociations::operator!=(const AutoTypeAssociations& other) const
{
return !(*this == other);
}
3 changes: 3 additions & 0 deletions src/core/AutoTypeAssociations.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class AutoTypeAssociations : public QObject
int associationsSize() const;
void clear();

bool operator==(const AutoTypeAssociations& other) const;
bool operator!=(const AutoTypeAssociations& other) const;

private:
QList<AutoTypeAssociations::Association> m_associations;

Expand Down
109 changes: 109 additions & 0 deletions src/core/Clock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2018 KeePassXC Team <[email protected]>
*
* 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 "Clock.h"

QSharedPointer<Clock> Clock::m_instance = QSharedPointer<Clock>();

QDateTime Clock::currentDateTimeUtc()
{
return instance().currentDateTimeUtcImpl();
}

QDateTime Clock::currentDateTime()
{
return instance().currentDateTimeImpl();
}

uint Clock::currentSecondsSinceEpoch()
{
return instance().currentDateTimeImpl().toTime_t();
}

QDateTime Clock::serialized(const QDateTime& dateTime)
{
auto time = dateTime.time();
if (time.isValid() && time.msec() != 0) {
return dateTime.addMSecs(-time.msec());
}
return dateTime;
}

QDateTime Clock::datetimeUtc(int year, int month, int day, int hour, int min, int second)
{
return QDateTime(QDate(year, month, day), QTime(hour, min, second), Qt::UTC);
}

QDateTime Clock::datetime(int year, int month, int day, int hour, int min, int second)
{
return QDateTime(QDate(year, month, day), QTime(hour, min, second), Qt::LocalTime);
}

QDateTime Clock::datetimeUtc(qint64 msecSinceEpoch)
{
return QDateTime::fromMSecsSinceEpoch(msecSinceEpoch, Qt::UTC);
}

QDateTime Clock::datetime(qint64 msecSinceEpoch)
{
return QDateTime::fromMSecsSinceEpoch(msecSinceEpoch, Qt::LocalTime);
}

QDateTime Clock::parse(const QString& text, Qt::DateFormat format)
{
return QDateTime::fromString(text, format);
}

QDateTime Clock::parse(const QString& text, const QString& format)
{
return QDateTime::fromString(text, format);
}

Clock::~Clock()
{
}

Clock::Clock()
{
}

QDateTime Clock::currentDateTimeUtcImpl() const
{
return QDateTime::currentDateTimeUtc();
}

QDateTime Clock::currentDateTimeImpl() const
{
return QDateTime::currentDateTime();
}

void Clock::resetInstance()
{
m_instance.clear();
}

void Clock::setInstance(Clock* clock)
{
m_instance = QSharedPointer<Clock>(clock);
}

const Clock& Clock::instance()
{
if (!m_instance) {
m_instance = QSharedPointer<Clock>(new Clock());
}
return *m_instance;
}
58 changes: 58 additions & 0 deletions src/core/Clock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2018 KeePassXC Team <[email protected]>
*
* 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_CLOCK_H
#define KEEPASSXC_CLOCK_H

#include <QDateTime>
#include <QSharedPointer>

class Clock
{
public:
static QDateTime currentDateTimeUtc();
static QDateTime currentDateTime();

static uint currentSecondsSinceEpoch();

static QDateTime serialized(const QDateTime& dateTime);

static QDateTime datetimeUtc(int year, int month, int day, int hour, int min, int second);
static QDateTime datetime(int year, int month, int day, int hour, int min, int second);

static QDateTime datetimeUtc(qint64 msecSinceEpoch);
static QDateTime datetime(qint64 msecSinceEpoch);

static QDateTime parse(const QString& text, Qt::DateFormat format = Qt::TextDate);
static QDateTime parse(const QString& text, const QString& format);

virtual ~Clock();

protected:
Clock();
virtual QDateTime currentDateTimeUtcImpl() const;
virtual QDateTime currentDateTimeImpl() const;

static void resetInstance();
static void setInstance(Clock* clock);
static const Clock& instance();

private:
static QSharedPointer<Clock> m_instance;
};

#endif // KEEPASSX_ENTRY_H
38 changes: 38 additions & 0 deletions src/core/Compare.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2018 KeePassXC Team <[email protected]>
*
* 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 "Compare.h"

#include <QColor>

bool operator<(const QColor& lhs, const QColor& rhs)
{
const QColor adaptedLhs = lhs.toCmyk();
const QColor adaptedRhs = rhs.toCmyk();
const int iCyan = compare(adaptedLhs.cyanF(), adaptedRhs.cyanF());
if (iCyan != 0) {
return iCyan;
}
const int iMagenta = compare(adaptedLhs.magentaF(), adaptedRhs.magentaF());
if (iMagenta != 0) {
return iMagenta;
}
const int iYellow = compare(adaptedLhs.yellowF(), adaptedRhs.yellowF());
if (iYellow != 0) {
return iYellow;
}
return compare(adaptedLhs.blackF(), adaptedRhs.blackF()) < 0;
}
Loading

0 comments on commit c1e9f45

Please sign in to comment.