Skip to content

Commit 18c4df9

Browse files
authored
Merge pull request #1178 from frostasm/implement-resolving-for-references-placeholders
Implement search for reference placeholder based on fields other than ID
2 parents fb6636d + c4bbb76 commit 18c4df9

File tree

9 files changed

+347
-65
lines changed

9 files changed

+347
-65
lines changed

src/core/Database.cpp

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,15 @@ const Metadata* Database::metadata() const
9595

9696
Entry* Database::resolveEntry(const Uuid& uuid)
9797
{
98-
return recFindEntry(uuid, m_rootGroup);
98+
return findEntryRecursive(uuid, m_rootGroup);
9999
}
100100

101-
Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
101+
Entry* Database::resolveEntry(const QString& text, EntryReferenceType referenceType)
102+
{
103+
return findEntryRecursive(text, referenceType, m_rootGroup);
104+
}
105+
106+
Entry* Database::findEntryRecursive(const Uuid& uuid, Group* group)
102107
{
103108
const QList<Entry*> entryList = group->entries();
104109
for (Entry* entry : entryList) {
@@ -109,7 +114,57 @@ Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
109114

110115
const QList<Group*> children = group->children();
111116
for (Group* child : children) {
112-
Entry* result = recFindEntry(uuid, child);
117+
Entry* result = findEntryRecursive(uuid, child);
118+
if (result) {
119+
return result;
120+
}
121+
}
122+
123+
return nullptr;
124+
}
125+
126+
Entry* Database::findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group)
127+
{
128+
Q_ASSERT_X(referenceType != EntryReferenceType::Unknown, "Database::findEntryRecursive",
129+
"Can't search entry with \"referenceType\" parameter equal to \"Unknown\"");
130+
131+
bool found = false;
132+
const QList<Entry*> entryList = group->entries();
133+
for (Entry* entry : entryList) {
134+
switch (referenceType) {
135+
case EntryReferenceType::Unknown:
136+
return nullptr;
137+
case EntryReferenceType::Title:
138+
found = entry->title() == text;
139+
break;
140+
case EntryReferenceType::UserName:
141+
found = entry->username() == text;
142+
break;
143+
case EntryReferenceType::Password:
144+
found = entry->password() == text;
145+
break;
146+
case EntryReferenceType::Url:
147+
found = entry->url() == text;
148+
break;
149+
case EntryReferenceType::Notes:
150+
found = entry->notes() == text;
151+
break;
152+
case EntryReferenceType::Uuid:
153+
found = entry->uuid().toHex() == text;
154+
break;
155+
case EntryReferenceType::CustomAttributes:
156+
found = entry->attributes()->containsValue(text);
157+
break;
158+
}
159+
160+
if (found) {
161+
return entry;
162+
}
163+
}
164+
165+
const QList<Group*> children = group->children();
166+
for (Group* child : children) {
167+
Entry* result = findEntryRecursive(text, referenceType, child);
113168
if (result) {
114169
return result;
115170
}
@@ -120,18 +175,18 @@ Entry* Database::recFindEntry(const Uuid& uuid, Group* group)
120175

121176
Group* Database::resolveGroup(const Uuid& uuid)
122177
{
123-
return recFindGroup(uuid, m_rootGroup);
178+
return findGroupRecursive(uuid, m_rootGroup);
124179
}
125180

126-
Group* Database::recFindGroup(const Uuid& uuid, Group* group)
181+
Group* Database::findGroupRecursive(const Uuid& uuid, Group* group)
127182
{
128183
if (group->uuid() == uuid) {
129184
return group;
130185
}
131186

132187
const QList<Group*> children = group->children();
133188
for (Group* child : children) {
134-
Group* result = recFindGroup(uuid, child);
189+
Group* result = findGroupRecursive(uuid, child);
135190
if (result) {
136191
return result;
137192
}

src/core/Database.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "keys/CompositeKey.h"
2828

2929
class Entry;
30+
enum class EntryReferenceType;
3031
class Group;
3132
class Metadata;
3233
class QTimer;
@@ -81,6 +82,7 @@ class Database : public QObject
8182
Metadata* metadata();
8283
const Metadata* metadata() const;
8384
Entry* resolveEntry(const Uuid& uuid);
85+
Entry* resolveEntry(const QString& text, EntryReferenceType referenceType);
8486
Group* resolveGroup(const Uuid& uuid);
8587
QList<DeletedObject> deletedObjects();
8688
void addDeletedObject(const DeletedObject& delObj);
@@ -141,8 +143,9 @@ private slots:
141143
void startModifiedTimer();
142144

143145
private:
144-
Entry* recFindEntry(const Uuid& uuid, Group* group);
145-
Group* recFindGroup(const Uuid& uuid, Group* group);
146+
Entry* findEntryRecursive(const Uuid& uuid, Group* group);
147+
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group);
148+
Group* findGroupRecursive(const Uuid& uuid, Group* group);
146149

147150
void createRecycleBin();
148151

src/core/Entry.cpp

Lines changed: 80 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,29 @@ void Entry::setUpdateTimeinfo(bool value)
9292
m_updateTimeinfo = value;
9393
}
9494

95+
EntryReferenceType Entry::referenceType(const QString& referenceStr)
96+
{
97+
const QString referenceLowerStr = referenceStr.toLower();
98+
EntryReferenceType result = EntryReferenceType::Unknown;
99+
if (referenceLowerStr == QLatin1String("t")) {
100+
result = EntryReferenceType::Title;
101+
} else if (referenceLowerStr == QLatin1String("u")) {
102+
result = EntryReferenceType::UserName;
103+
} else if (referenceLowerStr == QLatin1String("p")) {
104+
result = EntryReferenceType::Password;
105+
} else if (referenceLowerStr == QLatin1String("a")) {
106+
result = EntryReferenceType::Url;
107+
} else if (referenceLowerStr == QLatin1String("n")) {
108+
result = EntryReferenceType::Notes;
109+
} else if (referenceLowerStr == QLatin1String("i")) {
110+
result = EntryReferenceType::Uuid;
111+
} else if (referenceLowerStr == QLatin1String("o")) {
112+
result = EntryReferenceType::CustomAttributes;
113+
}
114+
115+
return result;
116+
}
117+
95118
Uuid Entry::uuid() const
96119
{
97120
return m_uuid;
@@ -699,7 +722,7 @@ void Entry::updateModifiedSinceBegin()
699722
m_modifiedSinceBegin = true;
700723
}
701724

702-
QString Entry::resolveMultiplePlaceholdersRecursive(const QString &str, int maxDepth) const
725+
QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const
703726
{
704727
if (maxDepth <= 0) {
705728
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
723746
return result;
724747
}
725748

726-
QString Entry::resolvePlaceholderRecursive(const QString &placeholder, int maxDepth) const
749+
QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const
727750
{
728751
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
729752
switch (typeOfPlaceholder) {
730753
case PlaceholderType::NotPlaceholder:
731-
return placeholder;
732754
case PlaceholderType::Unknown:
733-
qWarning("Can't resolve placeholder %s for entry with uuid %s", qPrintable(placeholder),
734-
qPrintable(uuid().toHex()));
735755
return placeholder;
736756
case PlaceholderType::Title:
737757
return title();
@@ -762,43 +782,62 @@ QString Entry::resolvePlaceholderRecursive(const QString &placeholder, int maxDe
762782
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
763783
return attributes()->hasKey(key) ? attributes()->value(key) : QString();
764784
}
765-
case PlaceholderType::Reference: {
766-
// resolving references in format: {REF:<WantedField>@I:<uuid of referenced entry>}
767-
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing,
768-
// but supporting lookups of standard fields and references by UUID only
769-
770-
QString result;
771-
QRegExp* referenceRegExp = m_attributes->referenceRegExp();
772-
if (referenceRegExp->indexIn(placeholder) != -1) {
773-
constexpr int wantedFieldIndex = 1;
774-
constexpr int referencedUuidIndex = 2;
775-
const Uuid referencedUuid(QByteArray::fromHex(referenceRegExp->cap(referencedUuidIndex).toLatin1()));
776-
const Entry* refEntry = m_group->database()->resolveEntry(referencedUuid);
777-
if (refEntry) {
778-
const QString wantedField = referenceRegExp->cap(wantedFieldIndex).toLower();
779-
if (wantedField == "t") {
780-
result = refEntry->title();
781-
} else if (wantedField == "u") {
782-
result = refEntry->username();
783-
} else if (wantedField == "p") {
784-
result = refEntry->password();
785-
} else if (wantedField == "a") {
786-
result = refEntry->url();
787-
} else if (wantedField == "n") {
788-
result = refEntry->notes();
789-
}
785+
case PlaceholderType::Reference:
786+
return resolveReferencePlaceholderRecursive(placeholder, maxDepth);
787+
}
790788

791-
// Referencing fields of other entries only works with standard fields, not with custom user strings.
792-
// If you want to reference a custom user string, you need to place a redirection in a standard field
793-
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
794-
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
795-
}
796-
}
797-
return result;
789+
return placeholder;
790+
}
791+
792+
QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const
793+
{
794+
// resolving references in format: {REF:<WantedField>@<SearchIn>:<SearchText>}
795+
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing
796+
797+
QRegularExpressionMatch match = EntryAttributes::matchReference(placeholder);
798+
if (!match.hasMatch()) {
799+
return placeholder;
798800
}
801+
802+
QString result;
803+
const QString searchIn = match.captured(EntryAttributes::SearchInGroupName);
804+
const QString searchText = match.captured(EntryAttributes::SearchTextGroupName);
805+
806+
const EntryReferenceType searchInType = Entry::referenceType(searchIn);
807+
const Entry* refEntry = m_group->database()->resolveEntry(searchText, searchInType);
808+
809+
if (refEntry) {
810+
const QString wantedField = match.captured(EntryAttributes::WantedFieldGroupName);
811+
result = refEntry->referenceFieldValue(Entry::referenceType(wantedField));
812+
813+
// Referencing fields of other entries only works with standard fields, not with custom user strings.
814+
// If you want to reference a custom user string, you need to place a redirection in a standard field
815+
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
816+
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
799817
}
800818

801-
return placeholder;
819+
return result;
820+
}
821+
822+
QString Entry::referenceFieldValue(EntryReferenceType referenceType) const
823+
{
824+
switch (referenceType) {
825+
case EntryReferenceType::Title:
826+
return title();
827+
case EntryReferenceType::UserName:
828+
return username();
829+
case EntryReferenceType::Password:
830+
return password();
831+
case EntryReferenceType::Url:
832+
return url();
833+
case EntryReferenceType::Notes:
834+
return notes();
835+
case EntryReferenceType::Uuid:
836+
return uuid().toHex();
837+
default:
838+
break;
839+
}
840+
return QString();
802841
}
803842

804843
Group* Entry::group()
@@ -858,7 +897,7 @@ const Database* Entry::database() const
858897
}
859898
}
860899

861-
QString Entry::maskPasswordPlaceholders(const QString &str) const
900+
QString Entry::maskPasswordPlaceholders(const QString& str) const
862901
{
863902
QString result = str;
864903
result.replace(QRegExp("(\\{PASSWORD\\})", Qt::CaseInsensitive, QRegExp::RegExp2), "******");
@@ -875,7 +914,7 @@ QString Entry::resolvePlaceholder(const QString& placeholder) const
875914
return resolvePlaceholderRecursive(placeholder, ResolveMaximumDepth);
876915
}
877916

878-
QString Entry::resolveUrlPlaceholder(const QString &str, Entry::PlaceholderType placeholderType) const
917+
QString Entry::resolveUrlPlaceholder(const QString& str, Entry::PlaceholderType placeholderType) const
879918
{
880919
if (str.isEmpty())
881920
return QString();
@@ -911,7 +950,7 @@ QString Entry::resolveUrlPlaceholder(const QString &str, Entry::PlaceholderType
911950
return QString();
912951
}
913952

914-
Entry::PlaceholderType Entry::placeholderType(const QString &placeholder) const
953+
Entry::PlaceholderType Entry::placeholderType(const QString& placeholder) const
915954
{
916955
if (!placeholder.startsWith(QLatin1Char('{')) || !placeholder.endsWith(QLatin1Char('}'))) {
917956
return PlaceholderType::NotPlaceholder;

src/core/Entry.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@
3636
class Database;
3737
class Group;
3838

39+
enum class EntryReferenceType {
40+
Unknown,
41+
Title,
42+
UserName,
43+
Password,
44+
Url,
45+
Notes,
46+
Uuid,
47+
CustomAttributes
48+
};
49+
3950
struct EntryData
4051
{
4152
int iconNumber;
@@ -170,7 +181,7 @@ class Entry : public QObject
170181
QString maskPasswordPlaceholders(const QString& str) const;
171182
QString resolveMultiplePlaceholders(const QString& str) const;
172183
QString resolvePlaceholder(const QString& str) const;
173-
QString resolveUrlPlaceholder(const QString &str, PlaceholderType placeholderType) const;
184+
QString resolveUrlPlaceholder(const QString& str, PlaceholderType placeholderType) const;
174185
PlaceholderType placeholderType(const QString& placeholder) const;
175186
QString resolveUrl(const QString& url) const;
176187

@@ -203,6 +214,10 @@ private slots:
203214
private:
204215
QString resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const;
205216
QString resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
217+
QString resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
218+
QString referenceFieldValue(EntryReferenceType referenceType) const;
219+
220+
static EntryReferenceType referenceType(const QString& referenceStr);
206221

207222
const Database* database() const;
208223
template <class T> bool set(T& property, const T& value);

src/core/EntryAttachments.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void EntryAttachments::remove(const QString& key)
8686
emit modified();
8787
}
8888

89-
void EntryAttachments::remove(const QStringList &keys)
89+
void EntryAttachments::remove(const QStringList& keys)
9090
{
9191
if (keys.isEmpty()) {
9292
return;

0 commit comments

Comments
 (0)