2
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
3
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 2 or (at your option)
8
* version 3 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#include "EntryAttributes.h"
20
#include "core/Global.h"
22
#include <QRegularExpression>
25
const QString EntryAttributes::TitleKey = "Title";
26
const QString EntryAttributes::UserNameKey = "UserName";
27
const QString EntryAttributes::PasswordKey = "Password";
28
const QString EntryAttributes::URLKey = "URL";
29
const QString EntryAttributes::NotesKey = "Notes";
30
const QStringList EntryAttributes::DefaultAttributes(QStringList()
31
<< TitleKey << UserNameKey << PasswordKey << URLKey << NotesKey);
32
const QString EntryAttributes::WantedFieldGroupName = "WantedField";
33
const QString EntryAttributes::SearchInGroupName = "SearchIn";
34
const QString EntryAttributes::SearchTextGroupName = "SearchText";
36
const QString EntryAttributes::RememberCmdExecAttr = "_EXEC_CMD";
37
const QString EntryAttributes::AdditionalUrlAttribute = "KP2A_URL";
38
const QString EntryAttributes::PasskeyAttribute = "KPEX_PASSKEY";
40
EntryAttributes::EntryAttributes(QObject* parent)
41
: ModifiableObject(parent)
46
QList<QString> EntryAttributes::keys() const
48
return m_attributes.keys();
51
bool EntryAttributes::hasKey(const QString& key) const
53
return m_attributes.contains(key);
56
bool EntryAttributes::hasPasskey() const
58
const auto keyList = keys();
59
for (const auto& key : keyList) {
60
if (isPasskeyAttribute(key)) {
68
QList<QString> EntryAttributes::customKeys() const
70
QList<QString> customKeys;
71
const QList<QString> keyList = keys();
72
for (const QString& key : keyList) {
73
if (!isDefaultAttribute(key) && !isPasskeyAttribute(key)) {
74
customKeys.append(key);
80
QString EntryAttributes::value(const QString& key) const
82
return m_attributes.value(key);
85
QList<QString> EntryAttributes::values(const QList<QString>& keys) const
87
QList<QString> values;
88
for (const QString& key : keys) {
89
values.append(m_attributes.value(key));
94
bool EntryAttributes::contains(const QString& key) const
96
return m_attributes.contains(key);
99
bool EntryAttributes::containsValue(const QString& value) const
101
return asConst(m_attributes).values().contains(value);
104
bool EntryAttributes::isProtected(const QString& key) const
106
return m_protectedAttributes.contains(key);
109
bool EntryAttributes::isReference(const QString& key) const
111
if (!m_attributes.contains(key)) {
116
const QString data = value(key);
117
return matchReference(data).hasMatch();
120
void EntryAttributes::set(const QString& key, const QString& value, bool protect)
122
bool shouldEmitModified = false;
124
bool addAttribute = !m_attributes.contains(key);
125
bool changeValue = !addAttribute && (m_attributes.value(key) != value);
126
bool defaultAttribute = isDefaultAttribute(key);
128
if (addAttribute && !defaultAttribute) {
129
emit aboutToBeAdded(key);
132
if (addAttribute || changeValue) {
133
m_attributes.insert(key, value);
134
shouldEmitModified = true;
138
if (!m_protectedAttributes.contains(key)) {
139
shouldEmitModified = true;
141
m_protectedAttributes.insert(key);
142
} else if (m_protectedAttributes.remove(key)) {
143
shouldEmitModified = true;
146
if (shouldEmitModified) {
150
if (defaultAttribute && changeValue) {
151
emit defaultKeyModified();
152
} else if (addAttribute) {
154
} else if (shouldEmitModified) {
155
emit customKeyModified(key);
159
void EntryAttributes::remove(const QString& key)
161
Q_ASSERT(!isDefaultAttribute(key));
163
if (!m_attributes.contains(key)) {
167
emit aboutToBeRemoved(key);
169
m_attributes.remove(key);
170
m_protectedAttributes.remove(key);
176
void EntryAttributes::rename(const QString& oldKey, const QString& newKey)
178
Q_ASSERT(!isDefaultAttribute(oldKey));
179
Q_ASSERT(!isDefaultAttribute(newKey));
181
if (!m_attributes.contains(oldKey)) {
186
if (m_attributes.contains(newKey)) {
191
QString data = value(oldKey);
192
bool protect = isProtected(oldKey);
194
emit aboutToRename(oldKey, newKey);
196
m_attributes.remove(oldKey);
197
m_attributes.insert(newKey, data);
199
m_protectedAttributes.remove(oldKey);
200
m_protectedAttributes.insert(newKey);
204
emit renamed(oldKey, newKey);
207
void EntryAttributes::copyCustomKeysFrom(const EntryAttributes* other)
209
if (!areCustomKeysDifferent(other)) {
213
emit aboutToBeReset();
215
// remove all non-default keys
216
const QList<QString> keyList = keys();
217
for (const QString& key : keyList) {
218
if (!isDefaultAttribute(key)) {
219
m_attributes.remove(key);
220
m_protectedAttributes.remove(key);
224
const QList<QString> otherKeyList = other->keys();
225
for (const QString& key : otherKeyList) {
226
if (!isDefaultAttribute(key)) {
227
m_attributes.insert(key, other->value(key));
228
if (other->isProtected(key)) {
229
m_protectedAttributes.insert(key);
238
bool EntryAttributes::areCustomKeysDifferent(const EntryAttributes* other)
240
// check if they are equal ignoring the order of the keys
241
if (keys().toSet() != other->keys().toSet()) {
245
const QList<QString> keyList = keys();
246
for (const QString& key : keyList) {
247
if (isDefaultAttribute(key)) {
251
if (isProtected(key) != other->isProtected(key) || value(key) != other->value(key)) {
259
void EntryAttributes::copyDataFrom(const EntryAttributes* other)
261
if (*this != *other) {
262
emit aboutToBeReset();
264
m_attributes = other->m_attributes;
265
m_protectedAttributes = other->m_protectedAttributes;
272
QUuid EntryAttributes::referenceUuid(const QString& key) const
274
if (!m_attributes.contains(key)) {
279
auto match = matchReference(value(key));
280
if (match.hasMatch()) {
281
const QString uuid = match.captured("SearchText");
282
if (!uuid.isEmpty()) {
283
return QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1()));
290
bool EntryAttributes::operator==(const EntryAttributes& other) const
292
return (m_attributes == other.m_attributes && m_protectedAttributes == other.m_protectedAttributes);
295
bool EntryAttributes::operator!=(const EntryAttributes& other) const
297
return (m_attributes != other.m_attributes || m_protectedAttributes != other.m_protectedAttributes);
300
QRegularExpressionMatch EntryAttributes::matchReference(const QString& text)
302
static QRegularExpression referenceRegExp(
303
"\\{REF:(?<WantedField>[TUPANI])@(?<SearchIn>[TUPANIO]):(?<SearchText>[^}]+)\\}",
304
QRegularExpression::CaseInsensitiveOption);
306
return referenceRegExp.match(text);
309
void EntryAttributes::clear()
311
emit aboutToBeReset();
313
m_attributes.clear();
314
m_protectedAttributes.clear();
316
for (const QString& key : DefaultAttributes) {
317
m_attributes.insert(key, "");
324
int EntryAttributes::attributesSize() const
327
for (auto it = m_attributes.constBegin(); it != m_attributes.constEnd(); ++it) {
328
size += it.key().toUtf8().size() + it.value().toUtf8().size();
333
bool EntryAttributes::isDefaultAttribute(const QString& key)
335
return DefaultAttributes.contains(key);
338
bool EntryAttributes::isPasskeyAttribute(const QString& key)
340
return key.startsWith(PasskeyAttribute);