Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warn user if deleting entries that are referenced. #1744

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 79 additions & 6 deletions src/core/Entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/core/Entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,16 @@ class Entry : public QObject
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();
Expand Down Expand Up @@ -139,6 +143,7 @@ class Entry : public QObject
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);
Expand Down Expand Up @@ -237,6 +242,7 @@ private slots:
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);
Expand Down
11 changes: 10 additions & 1 deletion src/core/Group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()) {
Expand Down
1 change: 1 addition & 0 deletions src/core/Group.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class Group : public QObject
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);
Expand Down
46 changes: 25 additions & 21 deletions src/core/Tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <QLocale>
#include <QRegularExpression>
#include <QStringList>
#include <QUuid>
#include <cctype>

#ifdef Q_OS_WIN
Expand Down Expand Up @@ -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 + "$";
}
// Exact modifier
if (exactMatch) {
pattern = "^" + pattern + "$";
}

auto regex = QRegularExpression(pattern);
if (!caseSensitive) {
regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
}
auto regex = QRegularExpression(pattern);
if (!caseSensitive) {
regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
}

return regex;
}
return regex;
}

QString uuidToHex(const QUuid& uuid) {
return QString::fromLatin1(uuid.toRfc4122().toHex());
}
} // namespace Tools
4 changes: 3 additions & 1 deletion src/core/Tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include <QObject>
#include <QString>
#include <QUuid>

#include <algorithm>

Expand All @@ -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>
Expand Down
Loading