mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-14 08:50:08 -05:00
Merge pull request #1178 from frostasm/implement-resolving-for-references-placeholders
Implement search for reference placeholder based on fields other than ID
This commit is contained in:
commit
18c4df98ff
@ -95,10 +95,15 @@ const Metadata* Database::metadata() const
|
|||||||
|
|
||||||
Entry* Database::resolveEntry(const Uuid& uuid)
|
Entry* Database::resolveEntry(const Uuid& uuid)
|
||||||
{
|
{
|
||||||
return recFindEntry(uuid, m_rootGroup);
|
return findEntryRecursive(uuid, m_rootGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
|
Entry* Database::resolveEntry(const QString& text, EntryReferenceType referenceType)
|
||||||
|
{
|
||||||
|
return findEntryRecursive(text, referenceType, m_rootGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* Database::findEntryRecursive(const Uuid& uuid, Group* group)
|
||||||
{
|
{
|
||||||
const QList<Entry*> entryList = group->entries();
|
const QList<Entry*> entryList = group->entries();
|
||||||
for (Entry* entry : entryList) {
|
for (Entry* entry : entryList) {
|
||||||
@ -109,7 +114,57 @@ Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
|
|||||||
|
|
||||||
const QList<Group*> children = group->children();
|
const QList<Group*> children = group->children();
|
||||||
for (Group* child : children) {
|
for (Group* child : children) {
|
||||||
Entry* result = recFindEntry(uuid, child);
|
Entry* result = findEntryRecursive(uuid, child);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* Database::findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group)
|
||||||
|
{
|
||||||
|
Q_ASSERT_X(referenceType != EntryReferenceType::Unknown, "Database::findEntryRecursive",
|
||||||
|
"Can't search entry with \"referenceType\" parameter equal to \"Unknown\"");
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
const QList<Entry*> entryList = group->entries();
|
||||||
|
for (Entry* entry : entryList) {
|
||||||
|
switch (referenceType) {
|
||||||
|
case EntryReferenceType::Unknown:
|
||||||
|
return nullptr;
|
||||||
|
case EntryReferenceType::Title:
|
||||||
|
found = entry->title() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::UserName:
|
||||||
|
found = entry->username() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Password:
|
||||||
|
found = entry->password() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Url:
|
||||||
|
found = entry->url() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Notes:
|
||||||
|
found = entry->notes() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::Uuid:
|
||||||
|
found = entry->uuid().toHex() == text;
|
||||||
|
break;
|
||||||
|
case EntryReferenceType::CustomAttributes:
|
||||||
|
found = entry->attributes()->containsValue(text);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<Group*> children = group->children();
|
||||||
|
for (Group* child : children) {
|
||||||
|
Entry* result = findEntryRecursive(text, referenceType, child);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -120,10 +175,10 @@ Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
|
|||||||
|
|
||||||
Group* Database::resolveGroup(const Uuid& uuid)
|
Group* Database::resolveGroup(const Uuid& uuid)
|
||||||
{
|
{
|
||||||
return recFindGroup(uuid, m_rootGroup);
|
return findGroupRecursive(uuid, m_rootGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
Group* Database::recFindGroup(const Uuid& uuid, Group* group)
|
Group* Database::findGroupRecursive(const Uuid& uuid, Group* group)
|
||||||
{
|
{
|
||||||
if (group->uuid() == uuid) {
|
if (group->uuid() == uuid) {
|
||||||
return group;
|
return group;
|
||||||
@ -131,7 +186,7 @@ Group* Database::recFindGroup(const Uuid& uuid, Group* group)
|
|||||||
|
|
||||||
const QList<Group*> children = group->children();
|
const QList<Group*> children = group->children();
|
||||||
for (Group* child : children) {
|
for (Group* child : children) {
|
||||||
Group* result = recFindGroup(uuid, child);
|
Group* result = findGroupRecursive(uuid, child);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "keys/CompositeKey.h"
|
#include "keys/CompositeKey.h"
|
||||||
|
|
||||||
class Entry;
|
class Entry;
|
||||||
|
enum class EntryReferenceType;
|
||||||
class Group;
|
class Group;
|
||||||
class Metadata;
|
class Metadata;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
@ -81,6 +82,7 @@ public:
|
|||||||
Metadata* metadata();
|
Metadata* metadata();
|
||||||
const Metadata* metadata() const;
|
const Metadata* metadata() const;
|
||||||
Entry* resolveEntry(const Uuid& uuid);
|
Entry* resolveEntry(const Uuid& uuid);
|
||||||
|
Entry* resolveEntry(const QString& text, EntryReferenceType referenceType);
|
||||||
Group* resolveGroup(const Uuid& uuid);
|
Group* resolveGroup(const Uuid& uuid);
|
||||||
QList<DeletedObject> deletedObjects();
|
QList<DeletedObject> deletedObjects();
|
||||||
void addDeletedObject(const DeletedObject& delObj);
|
void addDeletedObject(const DeletedObject& delObj);
|
||||||
@ -141,8 +143,9 @@ private slots:
|
|||||||
void startModifiedTimer();
|
void startModifiedTimer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Entry* recFindEntry(const Uuid& uuid, Group* group);
|
Entry* findEntryRecursive(const Uuid& uuid, Group* group);
|
||||||
Group* recFindGroup(const Uuid& uuid, Group* group);
|
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group);
|
||||||
|
Group* findGroupRecursive(const Uuid& uuid, Group* group);
|
||||||
|
|
||||||
void createRecycleBin();
|
void createRecycleBin();
|
||||||
|
|
||||||
|
@ -92,6 +92,29 @@ void Entry::setUpdateTimeinfo(bool value)
|
|||||||
m_updateTimeinfo = value;
|
m_updateTimeinfo = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EntryReferenceType Entry::referenceType(const QString& referenceStr)
|
||||||
|
{
|
||||||
|
const QString referenceLowerStr = referenceStr.toLower();
|
||||||
|
EntryReferenceType result = EntryReferenceType::Unknown;
|
||||||
|
if (referenceLowerStr == QLatin1String("t")) {
|
||||||
|
result = EntryReferenceType::Title;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("u")) {
|
||||||
|
result = EntryReferenceType::UserName;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("p")) {
|
||||||
|
result = EntryReferenceType::Password;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("a")) {
|
||||||
|
result = EntryReferenceType::Url;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("n")) {
|
||||||
|
result = EntryReferenceType::Notes;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("i")) {
|
||||||
|
result = EntryReferenceType::Uuid;
|
||||||
|
} else if (referenceLowerStr == QLatin1String("o")) {
|
||||||
|
result = EntryReferenceType::CustomAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Uuid Entry::uuid() const
|
Uuid Entry::uuid() const
|
||||||
{
|
{
|
||||||
return m_uuid;
|
return m_uuid;
|
||||||
@ -699,7 +722,7 @@ void Entry::updateModifiedSinceBegin()
|
|||||||
m_modifiedSinceBegin = true;
|
m_modifiedSinceBegin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Entry::resolveMultiplePlaceholdersRecursive(const QString &str, int maxDepth) const
|
QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const
|
||||||
{
|
{
|
||||||
if (maxDepth <= 0) {
|
if (maxDepth <= 0) {
|
||||||
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", qPrintable(uuid().toHex()));
|
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", qPrintable(uuid().toHex()));
|
||||||
@ -723,15 +746,12 @@ QString Entry::resolveMultiplePlaceholdersRecursive(const QString &str, int maxD
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Entry::resolvePlaceholderRecursive(const QString &placeholder, int maxDepth) const
|
QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const
|
||||||
{
|
{
|
||||||
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
|
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
|
||||||
switch (typeOfPlaceholder) {
|
switch (typeOfPlaceholder) {
|
||||||
case PlaceholderType::NotPlaceholder:
|
case PlaceholderType::NotPlaceholder:
|
||||||
return placeholder;
|
|
||||||
case PlaceholderType::Unknown:
|
case PlaceholderType::Unknown:
|
||||||
qWarning("Can't resolve placeholder %s for entry with uuid %s", qPrintable(placeholder),
|
|
||||||
qPrintable(uuid().toHex()));
|
|
||||||
return placeholder;
|
return placeholder;
|
||||||
case PlaceholderType::Title:
|
case PlaceholderType::Title:
|
||||||
return title();
|
return title();
|
||||||
@ -762,45 +782,64 @@ QString Entry::resolvePlaceholderRecursive(const QString &placeholder, int maxDe
|
|||||||
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
|
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
|
||||||
return attributes()->hasKey(key) ? attributes()->value(key) : QString();
|
return attributes()->hasKey(key) ? attributes()->value(key) : QString();
|
||||||
}
|
}
|
||||||
case PlaceholderType::Reference: {
|
case PlaceholderType::Reference:
|
||||||
// resolving references in format: {REF:<WantedField>@I:<uuid of referenced entry>}
|
return resolveReferencePlaceholderRecursive(placeholder, maxDepth);
|
||||||
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing,
|
|
||||||
// but supporting lookups of standard fields and references by UUID only
|
|
||||||
|
|
||||||
QString result;
|
|
||||||
QRegExp* referenceRegExp = m_attributes->referenceRegExp();
|
|
||||||
if (referenceRegExp->indexIn(placeholder) != -1) {
|
|
||||||
constexpr int wantedFieldIndex = 1;
|
|
||||||
constexpr int referencedUuidIndex = 2;
|
|
||||||
const Uuid referencedUuid(QByteArray::fromHex(referenceRegExp->cap(referencedUuidIndex).toLatin1()));
|
|
||||||
const Entry* refEntry = m_group->database()->resolveEntry(referencedUuid);
|
|
||||||
if (refEntry) {
|
|
||||||
const QString wantedField = referenceRegExp->cap(wantedFieldIndex).toLower();
|
|
||||||
if (wantedField == "t") {
|
|
||||||
result = refEntry->title();
|
|
||||||
} else if (wantedField == "u") {
|
|
||||||
result = refEntry->username();
|
|
||||||
} else if (wantedField == "p") {
|
|
||||||
result = refEntry->password();
|
|
||||||
} else if (wantedField == "a") {
|
|
||||||
result = refEntry->url();
|
|
||||||
} else if (wantedField == "n") {
|
|
||||||
result = refEntry->notes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Referencing fields of other entries only works with standard fields, not with custom user strings.
|
|
||||||
// If you want to reference a custom user string, you need to place a redirection in a standard field
|
|
||||||
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
|
|
||||||
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return placeholder;
|
return placeholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const
|
||||||
|
{
|
||||||
|
// resolving references in format: {REF:<WantedField>@<SearchIn>:<SearchText>}
|
||||||
|
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing
|
||||||
|
|
||||||
|
QRegularExpressionMatch match = EntryAttributes::matchReference(placeholder);
|
||||||
|
if (!match.hasMatch()) {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString result;
|
||||||
|
const QString searchIn = match.captured(EntryAttributes::SearchInGroupName);
|
||||||
|
const QString searchText = match.captured(EntryAttributes::SearchTextGroupName);
|
||||||
|
|
||||||
|
const EntryReferenceType searchInType = Entry::referenceType(searchIn);
|
||||||
|
const Entry* refEntry = m_group->database()->resolveEntry(searchText, searchInType);
|
||||||
|
|
||||||
|
if (refEntry) {
|
||||||
|
const QString wantedField = match.captured(EntryAttributes::WantedFieldGroupName);
|
||||||
|
result = refEntry->referenceFieldValue(Entry::referenceType(wantedField));
|
||||||
|
|
||||||
|
// Referencing fields of other entries only works with standard fields, not with custom user strings.
|
||||||
|
// If you want to reference a custom user string, you need to place a redirection in a standard field
|
||||||
|
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
|
||||||
|
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Entry::referenceFieldValue(EntryReferenceType referenceType) const
|
||||||
|
{
|
||||||
|
switch (referenceType) {
|
||||||
|
case EntryReferenceType::Title:
|
||||||
|
return title();
|
||||||
|
case EntryReferenceType::UserName:
|
||||||
|
return username();
|
||||||
|
case EntryReferenceType::Password:
|
||||||
|
return password();
|
||||||
|
case EntryReferenceType::Url:
|
||||||
|
return url();
|
||||||
|
case EntryReferenceType::Notes:
|
||||||
|
return notes();
|
||||||
|
case EntryReferenceType::Uuid:
|
||||||
|
return uuid().toHex();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
Group* Entry::group()
|
Group* Entry::group()
|
||||||
{
|
{
|
||||||
return m_group;
|
return m_group;
|
||||||
@ -858,7 +897,7 @@ const Database* Entry::database() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Entry::maskPasswordPlaceholders(const QString &str) const
|
QString Entry::maskPasswordPlaceholders(const QString& str) const
|
||||||
{
|
{
|
||||||
QString result = str;
|
QString result = str;
|
||||||
result.replace(QRegExp("(\\{PASSWORD\\})", Qt::CaseInsensitive, QRegExp::RegExp2), "******");
|
result.replace(QRegExp("(\\{PASSWORD\\})", Qt::CaseInsensitive, QRegExp::RegExp2), "******");
|
||||||
@ -875,7 +914,7 @@ QString Entry::resolvePlaceholder(const QString& placeholder) const
|
|||||||
return resolvePlaceholderRecursive(placeholder, ResolveMaximumDepth);
|
return resolvePlaceholderRecursive(placeholder, ResolveMaximumDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Entry::resolveUrlPlaceholder(const QString &str, Entry::PlaceholderType placeholderType) const
|
QString Entry::resolveUrlPlaceholder(const QString& str, Entry::PlaceholderType placeholderType) const
|
||||||
{
|
{
|
||||||
if (str.isEmpty())
|
if (str.isEmpty())
|
||||||
return QString();
|
return QString();
|
||||||
@ -911,7 +950,7 @@ QString Entry::resolveUrlPlaceholder(const QString &str, Entry::PlaceholderType
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry::PlaceholderType Entry::placeholderType(const QString &placeholder) const
|
Entry::PlaceholderType Entry::placeholderType(const QString& placeholder) const
|
||||||
{
|
{
|
||||||
if (!placeholder.startsWith(QLatin1Char('{')) || !placeholder.endsWith(QLatin1Char('}'))) {
|
if (!placeholder.startsWith(QLatin1Char('{')) || !placeholder.endsWith(QLatin1Char('}'))) {
|
||||||
return PlaceholderType::NotPlaceholder;
|
return PlaceholderType::NotPlaceholder;
|
||||||
|
@ -36,6 +36,17 @@
|
|||||||
class Database;
|
class Database;
|
||||||
class Group;
|
class Group;
|
||||||
|
|
||||||
|
enum class EntryReferenceType {
|
||||||
|
Unknown,
|
||||||
|
Title,
|
||||||
|
UserName,
|
||||||
|
Password,
|
||||||
|
Url,
|
||||||
|
Notes,
|
||||||
|
Uuid,
|
||||||
|
CustomAttributes
|
||||||
|
};
|
||||||
|
|
||||||
struct EntryData
|
struct EntryData
|
||||||
{
|
{
|
||||||
int iconNumber;
|
int iconNumber;
|
||||||
@ -170,7 +181,7 @@ public:
|
|||||||
QString maskPasswordPlaceholders(const QString& str) const;
|
QString maskPasswordPlaceholders(const QString& str) const;
|
||||||
QString resolveMultiplePlaceholders(const QString& str) const;
|
QString resolveMultiplePlaceholders(const QString& str) const;
|
||||||
QString resolvePlaceholder(const QString& str) const;
|
QString resolvePlaceholder(const QString& str) const;
|
||||||
QString resolveUrlPlaceholder(const QString &str, PlaceholderType placeholderType) const;
|
QString resolveUrlPlaceholder(const QString& str, PlaceholderType placeholderType) const;
|
||||||
PlaceholderType placeholderType(const QString& placeholder) const;
|
PlaceholderType placeholderType(const QString& placeholder) const;
|
||||||
QString resolveUrl(const QString& url) const;
|
QString resolveUrl(const QString& url) const;
|
||||||
|
|
||||||
@ -203,6 +214,10 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
QString resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const;
|
QString resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const;
|
||||||
QString resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
QString resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
||||||
|
QString resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
||||||
|
QString referenceFieldValue(EntryReferenceType referenceType) const;
|
||||||
|
|
||||||
|
static EntryReferenceType referenceType(const QString& referenceStr);
|
||||||
|
|
||||||
const Database* database() const;
|
const Database* database() const;
|
||||||
template <class T> bool set(T& property, const T& value);
|
template <class T> bool set(T& property, const T& value);
|
||||||
|
@ -86,7 +86,7 @@ void EntryAttachments::remove(const QString& key)
|
|||||||
emit modified();
|
emit modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntryAttachments::remove(const QStringList &keys)
|
void EntryAttachments::remove(const QStringList& keys)
|
||||||
{
|
{
|
||||||
if (keys.isEmpty()) {
|
if (keys.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -25,11 +25,15 @@ const QString EntryAttributes::URLKey = "URL";
|
|||||||
const QString EntryAttributes::NotesKey = "Notes";
|
const QString EntryAttributes::NotesKey = "Notes";
|
||||||
const QStringList EntryAttributes::DefaultAttributes(QStringList() << TitleKey << UserNameKey
|
const QStringList EntryAttributes::DefaultAttributes(QStringList() << TitleKey << UserNameKey
|
||||||
<< PasswordKey << URLKey << NotesKey);
|
<< PasswordKey << URLKey << NotesKey);
|
||||||
|
|
||||||
|
const QString EntryAttributes::WantedFieldGroupName = "WantedField";
|
||||||
|
const QString EntryAttributes::SearchInGroupName = "SearchIn";
|
||||||
|
const QString EntryAttributes::SearchTextGroupName = "SearchText";
|
||||||
|
|
||||||
const QString EntryAttributes::RememberCmdExecAttr = "_EXEC_CMD";
|
const QString EntryAttributes::RememberCmdExecAttr = "_EXEC_CMD";
|
||||||
|
|
||||||
EntryAttributes::EntryAttributes(QObject* parent)
|
EntryAttributes::EntryAttributes(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_referenceRegExp("\\{REF:([TUPAN])@I:([^}]+)\\}", Qt::CaseInsensitive, QRegExp::RegExp2)
|
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
@ -61,11 +65,16 @@ QString EntryAttributes::value(const QString& key) const
|
|||||||
return m_attributes.value(key);
|
return m_attributes.value(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntryAttributes::contains(const QString &key) const
|
bool EntryAttributes::contains(const QString& key) const
|
||||||
{
|
{
|
||||||
return m_attributes.contains(key);
|
return m_attributes.contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntryAttributes::containsValue(const QString& value) const
|
||||||
|
{
|
||||||
|
return m_attributes.values().contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
bool EntryAttributes::isProtected(const QString& key) const
|
bool EntryAttributes::isProtected(const QString& key) const
|
||||||
{
|
{
|
||||||
return m_protectedAttributes.contains(key);
|
return m_protectedAttributes.contains(key);
|
||||||
@ -78,16 +87,8 @@ bool EntryAttributes::isReference(const QString& key) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString data = value(key);
|
const QString data = value(key);
|
||||||
if (m_referenceRegExp.indexIn(data) != -1) {
|
return matchReference(data).hasMatch();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRegExp* EntryAttributes::referenceRegExp()
|
|
||||||
{
|
|
||||||
return &m_referenceRegExp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntryAttributes::set(const QString& key, const QString& value, bool protect)
|
void EntryAttributes::set(const QString& key, const QString& value, bool protect)
|
||||||
@ -258,6 +259,15 @@ bool EntryAttributes::operator!=(const EntryAttributes& other) const
|
|||||||
|| m_protectedAttributes != other.m_protectedAttributes);
|
|| m_protectedAttributes != other.m_protectedAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRegularExpressionMatch EntryAttributes::matchReference(const QString& text)
|
||||||
|
{
|
||||||
|
static QRegularExpression referenceRegExp(
|
||||||
|
"\\{REF:(?<WantedField>[TUPANI])@(?<SearchIn>[TUPANIO]):(?<SearchText>[^}]+)\\}",
|
||||||
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
|
return referenceRegExp.match(text);
|
||||||
|
}
|
||||||
|
|
||||||
void EntryAttributes::clear()
|
void EntryAttributes::clear()
|
||||||
{
|
{
|
||||||
emit aboutToBeReset();
|
emit aboutToBeReset();
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
@ -35,9 +36,9 @@ public:
|
|||||||
QList<QString> customKeys();
|
QList<QString> customKeys();
|
||||||
QString value(const QString& key) const;
|
QString value(const QString& key) const;
|
||||||
bool contains(const QString& key) const;
|
bool contains(const QString& key) const;
|
||||||
|
bool containsValue(const QString& value) const;
|
||||||
bool isProtected(const QString& key) const;
|
bool isProtected(const QString& key) const;
|
||||||
bool isReference(const QString& key) const;
|
bool isReference(const QString& key) const;
|
||||||
QRegExp* referenceRegExp();
|
|
||||||
void set(const QString& key, const QString& value, bool protect = false);
|
void set(const QString& key, const QString& value, bool protect = false);
|
||||||
void remove(const QString& key);
|
void remove(const QString& key);
|
||||||
void rename(const QString& oldKey, const QString& newKey);
|
void rename(const QString& oldKey, const QString& newKey);
|
||||||
@ -49,6 +50,8 @@ public:
|
|||||||
bool operator==(const EntryAttributes& other) const;
|
bool operator==(const EntryAttributes& other) const;
|
||||||
bool operator!=(const EntryAttributes& other) const;
|
bool operator!=(const EntryAttributes& other) const;
|
||||||
|
|
||||||
|
static QRegularExpressionMatch matchReference(const QString& text);
|
||||||
|
|
||||||
static const QString TitleKey;
|
static const QString TitleKey;
|
||||||
static const QString UserNameKey;
|
static const QString UserNameKey;
|
||||||
static const QString PasswordKey;
|
static const QString PasswordKey;
|
||||||
@ -58,6 +61,10 @@ public:
|
|||||||
static const QString RememberCmdExecAttr;
|
static const QString RememberCmdExecAttr;
|
||||||
static bool isDefaultAttribute(const QString& key);
|
static bool isDefaultAttribute(const QString& key);
|
||||||
|
|
||||||
|
static const QString WantedFieldGroupName;
|
||||||
|
static const QString SearchInGroupName;
|
||||||
|
static const QString SearchTextGroupName;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void modified();
|
void modified();
|
||||||
void defaultKeyModified();
|
void defaultKeyModified();
|
||||||
@ -74,7 +81,6 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QMap<QString, QString> m_attributes;
|
QMap<QString, QString> m_attributes;
|
||||||
QSet<QString> m_protectedAttributes;
|
QSet<QString> m_protectedAttributes;
|
||||||
QRegExp m_referenceRegExp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_ENTRYATTRIBUTES_H
|
#endif // KEEPASSX_ENTRYATTRIBUTES_H
|
||||||
|
@ -262,3 +262,155 @@ void TestEntry::testResolveRecursivePlaceholders()
|
|||||||
QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->url()), url);
|
QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->url()), url);
|
||||||
QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->title()), QString("title+/some/path+fragment+title"));
|
QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->title()), QString("title+/some/path+fragment+title"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestEntry::testResolveReferencePlaceholders()
|
||||||
|
{
|
||||||
|
Database db;
|
||||||
|
Group* root = db.rootGroup();
|
||||||
|
|
||||||
|
Entry* entry1 = new Entry();
|
||||||
|
entry1->setGroup(root);
|
||||||
|
entry1->setUuid(Uuid::random());
|
||||||
|
entry1->setTitle("Title1");
|
||||||
|
entry1->setUsername("Username1");
|
||||||
|
entry1->setPassword("Password1");
|
||||||
|
entry1->setUrl("Url1");
|
||||||
|
entry1->setNotes("Notes1");
|
||||||
|
entry1->attributes()->set("CustomAttribute1", "CustomAttributeValue1");
|
||||||
|
|
||||||
|
Group* group = new Group();
|
||||||
|
group->setParent(root);
|
||||||
|
Entry* entry2 = new Entry();
|
||||||
|
entry2->setGroup(group);
|
||||||
|
entry2->setUuid(Uuid::random());
|
||||||
|
entry2->setTitle("Title2");
|
||||||
|
entry2->setUsername("Username2");
|
||||||
|
entry2->setPassword("Password2");
|
||||||
|
entry2->setUrl("Url2");
|
||||||
|
entry2->setNotes("Notes2");
|
||||||
|
entry2->attributes()->set("CustomAttribute2", "CustomAttributeValue2");
|
||||||
|
|
||||||
|
Entry* entry3 = new Entry();
|
||||||
|
entry3->setGroup(group);
|
||||||
|
entry3->setUuid(Uuid::random());
|
||||||
|
entry3->setTitle("{S:AttributeTitle}");
|
||||||
|
entry3->setUsername("{S:AttributeUsername}");
|
||||||
|
entry3->setPassword("{S:AttributePassword}");
|
||||||
|
entry3->setUrl("{S:AttributeUrl}");
|
||||||
|
entry3->setNotes("{S:AttributeNotes}");
|
||||||
|
entry3->attributes()->set("AttributeTitle", "TitleValue");
|
||||||
|
entry3->attributes()->set("AttributeUsername", "UsernameValue");
|
||||||
|
entry3->attributes()->set("AttributePassword", "PasswordValue");
|
||||||
|
entry3->attributes()->set("AttributeUrl", "UrlValue");
|
||||||
|
entry3->attributes()->set("AttributeNotes", "NotesValue");
|
||||||
|
|
||||||
|
Entry* tstEntry = new Entry();
|
||||||
|
tstEntry->setGroup(root);
|
||||||
|
tstEntry->setUuid(Uuid::random());
|
||||||
|
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuid().toHex())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry1->username())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry1->password())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@A:%1}").arg(entry1->url())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@N:%1}").arg(entry1->notes())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@O:%1}").arg(entry1->attributes()->value("CustomAttribute1"))), entry1->title());
|
||||||
|
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuid().toHex())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@U:%1}").arg(entry1->username())), entry1->username());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@P:%1}").arg(entry1->password())), entry1->password());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry1->url())), entry1->url());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry1->notes())), entry1->notes());
|
||||||
|
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry2->uuid().toHex())), entry2->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry2->title())), entry2->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry2->username())), entry2->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry2->password())), entry2->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@A:%1}").arg(entry2->url())), entry2->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@N:%1}").arg(entry2->notes())), entry2->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@O:%1}").arg(entry2->attributes()->value("CustomAttribute2"))), entry2->title());
|
||||||
|
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry2->title())), entry2->title());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@U:%1}").arg(entry2->username())), entry2->username());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@P:%1}").arg(entry2->password())), entry2->password());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry2->url())), entry2->url());
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry2->notes())), entry2->notes());
|
||||||
|
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuid().toHex())), entry3->attributes()->value("AttributeTitle"));
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuid().toHex())), entry3->attributes()->value("AttributeTitle"));
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuid().toHex())), entry3->attributes()->value("AttributeUsername"));
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuid().toHex())), entry3->attributes()->value("AttributePassword"));
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuid().toHex())), entry3->attributes()->value("AttributeUrl"));
|
||||||
|
QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuid().toHex())), entry3->attributes()->value("AttributeNotes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEntry::testResolveNonIdPlaceholdersToUuid()
|
||||||
|
{
|
||||||
|
Database db;
|
||||||
|
auto* root = db.rootGroup();
|
||||||
|
|
||||||
|
Entry referencedEntryTitle;
|
||||||
|
referencedEntryTitle.setGroup(root);
|
||||||
|
referencedEntryTitle.setTitle("myTitle");
|
||||||
|
referencedEntryTitle.setUuid(Uuid::random());
|
||||||
|
|
||||||
|
Entry referencedEntryUsername;
|
||||||
|
referencedEntryUsername.setGroup(root);
|
||||||
|
referencedEntryUsername.setUsername("myUser");
|
||||||
|
referencedEntryUsername.setUuid(Uuid::random());
|
||||||
|
|
||||||
|
Entry referencedEntryPassword;
|
||||||
|
referencedEntryPassword.setGroup(root);
|
||||||
|
referencedEntryPassword.setPassword("myPassword");
|
||||||
|
referencedEntryPassword.setUuid(Uuid::random());
|
||||||
|
|
||||||
|
Entry referencedEntryUrl;
|
||||||
|
referencedEntryUrl.setGroup(root);
|
||||||
|
referencedEntryUrl.setUrl("myUrl");
|
||||||
|
referencedEntryUrl.setUuid(Uuid::random());
|
||||||
|
|
||||||
|
Entry referencedEntryNotes;
|
||||||
|
referencedEntryNotes.setGroup(root);
|
||||||
|
referencedEntryNotes.setNotes("myNotes");
|
||||||
|
referencedEntryNotes.setUuid(Uuid::random());
|
||||||
|
|
||||||
|
const QList<QChar> placeholders{'T', 'U', 'P', 'A', 'N'};
|
||||||
|
for (const QChar searchIn : placeholders) {
|
||||||
|
const Entry* referencedEntry = nullptr;
|
||||||
|
QString newEntryNotesRaw("{REF:I@%1:%2}");
|
||||||
|
|
||||||
|
switch(searchIn.toLatin1()) {
|
||||||
|
case 'T':
|
||||||
|
referencedEntry = &referencedEntryTitle;
|
||||||
|
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->title());
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
referencedEntry = &referencedEntryUsername;
|
||||||
|
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->username());
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
referencedEntry = &referencedEntryPassword;
|
||||||
|
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->password());
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
referencedEntry = &referencedEntryUrl;
|
||||||
|
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->url());
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
referencedEntry = &referencedEntryNotes;
|
||||||
|
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->notes());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry newEntry;
|
||||||
|
newEntry.setGroup(root);
|
||||||
|
newEntry.setNotes(newEntryNotesRaw);
|
||||||
|
|
||||||
|
const auto newEntryNotesResolved =
|
||||||
|
newEntry.resolveMultiplePlaceholders(newEntry.notes());
|
||||||
|
QCOMPARE(newEntryNotesResolved, QString(referencedEntry->uuid().toHex()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -34,6 +34,8 @@ private slots:
|
|||||||
void testResolveUrl();
|
void testResolveUrl();
|
||||||
void testResolveUrlPlaceholders();
|
void testResolveUrlPlaceholders();
|
||||||
void testResolveRecursivePlaceholders();
|
void testResolveRecursivePlaceholders();
|
||||||
|
void testResolveReferencePlaceholders();
|
||||||
|
void testResolveNonIdPlaceholdersToUuid();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTENTRY_H
|
#endif // KEEPASSX_TESTENTRY_H
|
||||||
|
Loading…
Reference in New Issue
Block a user