Warn user if deleting entries that are referenced. (#1744)

On warning, references can be replaced with original values or ignored.
Removal process can be also skipped for each conflicting entry. Resolves #852.
This commit is contained in:
Wojtek Gumuła
2018-12-25 00:15:46 +01:00
committed by Jonathan White
parent 4d4c839afa
commit c630214915
9 changed files with 205 additions and 61 deletions

View File

@@ -24,6 +24,7 @@
#include "core/DatabaseIcons.h"
#include "core/Group.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#include "totp/totp.h"
#include <QDir>
@@ -100,6 +101,32 @@ void Entry::setUpdateTimeinfo(bool value)
m_updateTimeinfo = value;
}
QString Entry::buildReference(const QUuid& uuid, const QString& field)
{
Q_ASSERT(EntryAttributes::DefaultAttributes.count(field) > 0);
QString uuidStr = Tools::uuidToHex(uuid).toUpper();
QString shortField;
if (field == EntryAttributes::TitleKey) {
shortField = "T";
} else if (field == EntryAttributes::UserNameKey) {
shortField = "U";
} else if (field == EntryAttributes::PasswordKey) {
shortField = "P";
} else if (field == EntryAttributes::URLKey) {
shortField = "A";
} else if (field == EntryAttributes::NotesKey) {
shortField = "N";
}
if (shortField.isEmpty()) {
return {};
}
return QString("{REF:%1@I:%2}").arg(shortField, uuidStr);
}
EntryReferenceType Entry::referenceType(const QString& referenceStr)
{
const QString referenceLowerStr = referenceStr.toLower();
@@ -130,7 +157,7 @@ const QUuid& Entry::uuid() const
const QString Entry::uuidToHex() const
{
return QString::fromLatin1(m_uuid.toRfc4122().toHex());
return Tools::uuidToHex(m_uuid);
}
QImage Entry::icon() const
@@ -304,11 +331,25 @@ QString Entry::notes() const
return m_attributes->value(EntryAttributes::NotesKey);
}
QString Entry::attribute(const QString& key) const
{
return m_attributes->value(key);
}
bool Entry::isExpired() const
{
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTimeUtc();
}
bool Entry::isAttributeReferenceOf(const QString& key, const QUuid& uuid) const
{
if (!m_attributes->isReference(key)) {
return false;
}
return m_attributes->value(key).contains(Tools::uuidToHex(uuid), Qt::CaseInsensitive);
}
bool Entry::hasReferences() const
{
const QList<QString> keyList = EntryAttributes::DefaultAttributes;
@@ -320,6 +361,26 @@ bool Entry::hasReferences() const
return false;
}
bool Entry::hasReferencesTo(const QUuid& uuid) const
{
const QList<QString> keyList = EntryAttributes::DefaultAttributes;
for (const QString& key : keyList) {
if (isAttributeReferenceOf(key, uuid)) {
return true;
}
}
return false;
}
void Entry::replaceReferencesWithValues(const Entry* other)
{
for (const QString& key : EntryAttributes::DefaultAttributes) {
if (isAttributeReferenceOf(key, other->uuid())) {
setDefaultAttribute(key, other->attribute(key));
}
}
}
EntryAttributes* Entry::attributes()
{
return m_attributes;
@@ -496,6 +557,17 @@ void Entry::setNotes(const QString& notes)
m_attributes->set(EntryAttributes::NotesKey, notes, m_attributes->isProtected(EntryAttributes::NotesKey));
}
void Entry::setDefaultAttribute(const QString& attribute, const QString& value)
{
Q_ASSERT(EntryAttributes::isDefaultAttribute(attribute));
if (!EntryAttributes::isDefaultAttribute(attribute)) {
return;
}
m_attributes->set(attribute, value, m_attributes->isProtected(attribute));
}
void Entry::setExpires(const bool& value)
{
if (m_data.timeInfo.expires() != value) {
@@ -654,16 +726,17 @@ Entry* Entry::clone(CloneFlags flags) const
entry->m_attachments->copyDataFrom(m_attachments);
if (flags & CloneUserAsRef) {
// Build the username reference
QString username = "{REF:U@I:" + uuidToHex() + "}";
entry->m_attributes->set(
EntryAttributes::UserNameKey, username.toUpper(), m_attributes->isProtected(EntryAttributes::UserNameKey));
EntryAttributes::UserNameKey,
buildReference(uuid(), EntryAttributes::UserNameKey),
m_attributes->isProtected(EntryAttributes::UserNameKey));
}
if (flags & ClonePassAsRef) {
QString password = "{REF:P@I:" + uuidToHex() + "}";
entry->m_attributes->set(
EntryAttributes::PasswordKey, password.toUpper(), m_attributes->isProtected(EntryAttributes::PasswordKey));
EntryAttributes::PasswordKey,
buildReference(uuid(), EntryAttributes::PasswordKey),
m_attributes->isProtected(EntryAttributes::PasswordKey));
}
entry->m_autoTypeAssociations->copyDataFrom(m_autoTypeAssociations);

View File

@@ -105,12 +105,16 @@ public:
QString username() const;
QString password() const;
QString notes() const;
QString attribute(const QString& key) const;
QString totp() const;
QSharedPointer<Totp::Settings> totpSettings() const;
bool hasTotp() const;
bool isExpired() const;
bool isAttributeReferenceOf(const QString& key, const QUuid& uuid) const;
void replaceReferencesWithValues(const Entry* other);
bool hasReferences() const;
bool hasReferencesTo(const QUuid& uuid) const;
EntryAttributes* attributes();
const EntryAttributes* attributes() const;
EntryAttachments* attachments();
@@ -139,6 +143,7 @@ public:
void setUsername(const QString& username);
void setPassword(const QString& password);
void setNotes(const QString& notes);
void setDefaultAttribute(const QString& attribute, const QString& value);
void setExpires(const bool& value);
void setExpiryTime(const QDateTime& dateTime);
void setTotp(QSharedPointer<Totp::Settings> settings);
@@ -237,6 +242,7 @@ private:
QString resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
QString referenceFieldValue(EntryReferenceType referenceType) const;
static QString buildReference(const QUuid& uuid, const QString& field);
static EntryReferenceType referenceType(const QString& referenceStr);
template <class T> bool set(T& property, const T& value);

View File

@@ -23,6 +23,9 @@
#include "core/DatabaseIcons.h"
#include "core/Global.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#include <QtConcurrent>
const int Group::DefaultIconNumber = 48;
const int Group::RecycleBinIconNumber = 43;
@@ -119,7 +122,7 @@ const QUuid& Group::uuid() const
const QString Group::uuidToHex() const
{
return QString::fromLatin1(m_uuid.toRfc4122().toHex());
return Tools::uuidToHex(m_uuid);
}
QString Group::name() const
@@ -548,6 +551,12 @@ QList<Entry*> Group::entriesRecursive(bool includeHistoryItems) const
return entryList;
}
QList<Entry*> Group::referencesRecursive(const Entry* entry) const
{
auto entries = entriesRecursive();
return QtConcurrent::blockingFiltered(entries, [entry](const Entry* e) { return e->hasReferencesTo(entry->uuid()); });
}
Entry* Group::findEntryByUuid(const QUuid& uuid) const
{
if (uuid.isNull()) {

View File

@@ -151,6 +151,7 @@ public:
QList<Entry*> entries();
const QList<Entry*>& entries() const;
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group = nullptr);
QList<Entry*> referencesRecursive(const Entry* entry) const;
QList<Entry*> entriesRecursive(bool includeHistoryItems = false) const;
QList<const Group*> groupsRecursive(bool includeSelf) const;
QList<Group*> groupsRecursive(bool includeSelf);

View File

@@ -28,6 +28,7 @@
#include <QLocale>
#include <QRegularExpression>
#include <QStringList>
#include <QUuid>
#include <cctype>
#ifdef Q_OS_WIN
@@ -197,31 +198,34 @@ namespace Tools
}
}
// Escape common regex symbols except for *, ?, and |
auto regexEscape = QRegularExpression(R"re(([-[\]{}()+.,\\\/^$#]))re");
// Escape common regex symbols except for *, ?, and |
auto regexEscape = QRegularExpression(R"re(([-[\]{}()+.,\\\/^$#]))re");
QRegularExpression convertToRegex(const QString& string, bool useWildcards, bool exactMatch, bool caseSensitive)
{
QString pattern = string;
QRegularExpression convertToRegex(const QString& string, bool useWildcards, bool exactMatch, bool caseSensitive)
{
QString pattern = string;
// Wildcard support (*, ?, |)
if (useWildcards) {
pattern.replace(regexEscape, "\\\\1");
pattern.replace("*", ".*");
pattern.replace("?", ".");
// Wildcard support (*, ?, |)
if (useWildcards) {
pattern.replace(regexEscape, "\\\\1");
pattern.replace("*", ".*");
pattern.replace("?", ".");
}
// Exact modifier
if (exactMatch) {
pattern = "^" + pattern + "$";
}
auto regex = QRegularExpression(pattern);
if (!caseSensitive) {
regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
}
return regex;
}
// Exact modifier
if (exactMatch) {
pattern = "^" + pattern + "$";
QString uuidToHex(const QUuid& uuid) {
return QString::fromLatin1(uuid.toRfc4122().toHex());
}
auto regex = QRegularExpression(pattern);
if (!caseSensitive) {
regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
}
return regex;
}
} // namespace Tools

View File

@@ -23,6 +23,7 @@
#include <QObject>
#include <QString>
#include <QUuid>
#include <algorithm>
@@ -39,7 +40,8 @@ namespace Tools
bool isBase64(const QByteArray& ba);
void sleep(int ms);
void wait(int ms);
QRegularExpression convertToRegex(const QString& string, bool useWildcards = false,
QString uuidToHex(const QUuid& uuid);
QRegularExpression convertToRegex(const QString& string, bool useWildcards = false,
bool exactMatch = false, bool caseSensitive = false);
template <typename RandomAccessIterator, typename T>