KWallet

kwalletfreedesktopattributes.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7#include "kwalletfreedesktopattributes.h"
8
9#include "ksecretd.h"
10#include "ksecretd_debug.h"
11#include "kwalletfreedesktopcollection.h"
12#include <QDir>
13#include <QFile>
14#include <QJsonDocument>
15#include <QSaveFile>
16
17KWalletFreedesktopAttributes::KWalletFreedesktopAttributes(const QString &walletName)
18{
19 const QString writeLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kwalletd");
20 _path = writeLocation + QChar::fromLatin1('/') + KSecretD::encodeWalletName(walletName) + QStringLiteral("_attributes.json");
21
22 read();
23
24 if (!_params.contains(FDO_KEY_CREATED)) {
25 const auto currentTime = QString::number(QDateTime::currentSecsSinceEpoch());
26 _params[FDO_KEY_CREATED] = currentTime;
27 _params[FDO_KEY_MODIFIED] = currentTime;
28 }
29}
30
31void KWalletFreedesktopAttributes::read()
32{
33 QByteArray content;
34 {
35 QFile file(_path);
37 if (!file.isOpen()) {
38 qCDebug(KSECRETD_LOG) << "Can't read attributes file " << _path;
39 return;
40 }
41 content = file.readAll();
42 }
43
44 const auto jsonDoc = QJsonDocument::fromJson(content);
45 if (jsonDoc.isObject()) {
46 _params = jsonDoc.object();
47 } else {
48 qCWarning(KSECRETD_LOG) << "Can't read attributes: the root element must be an JSON-object: " << _path;
49 _params = QJsonObject();
50 }
51}
52
53void KWalletFreedesktopAttributes::write()
54{
55 if (_params.empty()) {
56 QFile::remove(_path);
57 return;
58 }
59
60 updateLastModified();
61
62 QSaveFile sf(_path);
64 qCWarning(KSECRETD_LOG) << "Can't write attributes file: " << _path;
65 return;
66 }
67 sf.setPermissions(QSaveFile::ReadUser | QSaveFile::WriteUser);
68
69 const QJsonDocument saveDoc(_params);
70
71 const QByteArray jsonBytes = saveDoc.toJson();
72 if (sf.write(jsonBytes) != jsonBytes.size()) {
73 sf.cancelWriting();
74 qCWarning(KSECRETD_LOG) << "Cannot write attributes file " << _path;
75 return;
76 }
77 if (!sf.commit()) {
78 qCWarning(KSECRETD_LOG) << "Cannot commit attributes file " << _path;
79 }
80}
81
82static QString entryLocationToStr(const EntryLocation &entryLocation)
83{
84 return entryLocation.folder + QChar::fromLatin1('/') + entryLocation.key;
85}
86
87static EntryLocation splitToEntryLocation(const QString &entryLocation)
88{
89 const int slashPos = entryLocation.indexOf(QChar::fromLatin1('/'));
90 if (slashPos == -1) {
91 qCWarning(KSECRETD_LOG) << "Entry location '" << entryLocation << "' has no slash '/'";
92 return {};
93 } else {
94 return {entryLocation.left(slashPos), entryLocation.right((entryLocation.size() - slashPos) - 1)};
95 }
96}
97
98void KWalletFreedesktopAttributes::remove(const EntryLocation &entryLocation)
99{
100 _params.remove(entryLocationToStr(entryLocation));
101 if (_params.empty()) {
102 QFile::remove(_path);
103 } else {
104 write();
105 }
106}
107
108void KWalletFreedesktopAttributes::deleteFile()
109{
110 QFile::remove(_path);
111}
112
113void KWalletFreedesktopAttributes::renameLabel(const EntryLocation &oldLocation, const EntryLocation &newLocation)
114{
115 const QString oldLoc = entryLocationToStr(oldLocation);
116
117 const auto found = _params.find(oldLoc);
118 if (found == _params.end() || !found->isObject()) {
119 qCWarning(KSECRETD_LOG) << "Can't rename label (!?)";
120 return;
121 }
122 const auto obj = found->toObject();
123 _params.erase(found);
124 _params.insert(entryLocationToStr(newLocation), obj);
125
126 write();
127}
128
129void KWalletFreedesktopAttributes::renameWallet(const QString &newName)
130{
131 const QString writeLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kwalletd");
132 const QString newPath = writeLocation + QChar::fromLatin1('/') + newName + QStringLiteral("_attributes.json");
133
134 QFile::rename(_path, newPath);
135 _path = newPath;
136}
137
138void KWalletFreedesktopAttributes::newItem(const EntryLocation &entryLocation)
139{
140 _params[entryLocationToStr(entryLocation)] = QJsonObject();
141}
142
143QList<EntryLocation> KWalletFreedesktopAttributes::matchAttributes(const StrStrMap &attributes) const
144{
145 QList<EntryLocation> items;
146
147 for (auto i = _params.constBegin(); i != _params.constEnd(); ++i) {
148 if (!i->isObject()) {
149 continue;
150 }
151
152 bool match = true;
153 const auto itemParams = i->toObject();
154 const auto foundItemAttribs = itemParams.find(QStringLiteral("attributes"));
155 if (foundItemAttribs == itemParams.end() || !foundItemAttribs->isObject()) {
156 continue;
157 }
158 const auto itemAttribs = foundItemAttribs->toObject();
159
160 for (auto i = attributes.constBegin(); i != attributes.constEnd(); ++i) {
161 const auto foundKey = itemAttribs.find(i.key());
162 if (foundKey == itemAttribs.end() || !foundKey->isString() || foundKey->toString() != i.value()) {
163 match = false;
164 break;
165 }
166 }
167
168 if (match) {
169 items += splitToEntryLocation(i.key());
170 }
171 }
172
173 return items;
174}
175
176void KWalletFreedesktopAttributes::setAttributes(const EntryLocation &entryLocation, const StrStrMap &attributes)
177{
178 QJsonObject jsonAttrs;
179 for (auto i = attributes.constBegin(); i != attributes.constEnd(); ++i) {
180 jsonAttrs.insert(i.key(), i.value());
181 }
182
183 const QString strLocation = entryLocationToStr(entryLocation);
184 const auto foundParams = _params.find(strLocation);
185 QJsonObject params;
186 if (foundParams != _params.end() && foundParams->isObject()) {
187 params = foundParams->toObject();
188 } else {
189 return;
190 }
191
192 if (jsonAttrs.empty()) {
193 params.remove(QStringLiteral("attributes"));
194 } else {
195 params[QStringLiteral("attributes")] = jsonAttrs;
196 }
197
198 _params[strLocation] = params;
199
200 write();
201}
202
203StrStrMap KWalletFreedesktopAttributes::getAttributes(const EntryLocation &entryLocation) const
204{
205 const auto foundObj = _params.find(entryLocationToStr(entryLocation));
206 if (foundObj == _params.end() || !foundObj->isObject()) {
207 return StrStrMap();
208 }
209 const auto jsonParams = foundObj->toObject();
210
211 const auto foundAttrs = jsonParams.find(QStringLiteral("attributes"));
212 if (foundAttrs == jsonParams.end() || !foundAttrs->isObject()) {
213 return StrStrMap();
214 }
215 const auto jsonAttrs = foundAttrs->toObject();
216
217 StrStrMap itemAttrs;
218
219 for (auto i = jsonAttrs.constBegin(); i != jsonAttrs.constEnd(); ++i) {
220 if (i.value().isString()) {
221 itemAttrs.insert(i.key(), i.value().toString());
222 }
223 }
224
225 return itemAttrs;
226}
227
228QString KWalletFreedesktopAttributes::getStringParam(const EntryLocation &entryLocation, const QString &paramName, const QString &defaultParam) const
229{
230 const auto foundParams = _params.find(entryLocationToStr(entryLocation));
231 if (foundParams == _params.end() || !foundParams->isObject()) {
232 return defaultParam;
233 }
234 const auto params = foundParams->toObject();
235
236 const auto foundParam = params.find(paramName);
237 if (foundParam == params.end() || !foundParam->isString()) {
238 return defaultParam;
239 }
240
241 return foundParam->toString();
242}
243
244qulonglong KWalletFreedesktopAttributes::getULongLongParam(const EntryLocation &entryLocation, const QString &paramName, qulonglong defaultParam) const
245{
246 const auto str = getStringParam(entryLocation, paramName, QString::number(defaultParam));
247 bool ok = false;
248 const auto result = str.toULongLong(&ok);
249 return ok ? result : defaultParam;
250}
251
252void KWalletFreedesktopAttributes::setParam(const EntryLocation &entryLocation, const QString &paramName, const QString &param)
253{
254 const auto entryLoc = entryLocationToStr(entryLocation);
255 const auto foundParams = _params.find(entryLoc);
256 if (foundParams == _params.end() || !foundParams->isObject()) {
257 return;
258 }
259
260 auto params = foundParams->toObject();
261
262 params[paramName] = param;
263 _params[entryLoc] = params;
264
265 write();
266}
267
268void KWalletFreedesktopAttributes::setParam(const EntryLocation &entryLocation, const QString &paramName, qulonglong param)
269{
270 setParam(entryLocation, paramName, QString::number(param));
271}
272
273void KWalletFreedesktopAttributes::remove(const FdoUniqueLabel &itemUniqLabel)
274{
275 remove(itemUniqLabel.toEntryLocation());
276}
277
278void KWalletFreedesktopAttributes::setAttributes(const FdoUniqueLabel &itemUniqLabel, const StrStrMap &attributes)
279{
280 setAttributes(itemUniqLabel.toEntryLocation(), attributes);
281}
282
283StrStrMap KWalletFreedesktopAttributes::getAttributes(const FdoUniqueLabel &itemUniqLabel) const
284{
285 return getAttributes(itemUniqLabel.toEntryLocation());
286}
287
288QString KWalletFreedesktopAttributes::getStringParam(const FdoUniqueLabel &itemUniqLabel, const QString &paramName, const QString &defaultParam) const
289{
290 return getStringParam(itemUniqLabel.toEntryLocation(), paramName, defaultParam);
291}
292
293qulonglong KWalletFreedesktopAttributes::getULongLongParam(const FdoUniqueLabel &itemUniqLabel, const QString &paramName, qulonglong defaultParam) const
294{
295 return getULongLongParam(itemUniqLabel.toEntryLocation(), paramName, defaultParam);
296}
297
298void KWalletFreedesktopAttributes::setParam(const FdoUniqueLabel &itemUniqLabel, const QString &paramName, const QString &param)
299{
300 setParam(itemUniqLabel.toEntryLocation(), paramName, param);
301}
302
303void KWalletFreedesktopAttributes::setParam(const FdoUniqueLabel &itemUniqLabel, const QString &paramName, qulonglong param)
304{
305 setParam(itemUniqLabel.toEntryLocation(), paramName, param);
306}
307
308QList<EntryLocation> KWalletFreedesktopAttributes::listItems() const
309{
310 QList<EntryLocation> items;
311 for (auto i = _params.constBegin(); i != _params.constEnd(); ++i) {
312 if (i->isObject()) {
313 items.push_back(splitToEntryLocation(i.key()));
314 }
315 }
316 return items;
317}
318
319qulonglong KWalletFreedesktopAttributes::lastModified() const
320{
321 auto found = _params.constFind(FDO_KEY_MODIFIED);
322 if (found == _params.constEnd()) {
323 return 0;
324 }
325 return found->toString().toULongLong();
326}
327
328qulonglong KWalletFreedesktopAttributes::birthTime() const
329{
330 auto found = _params.constFind(FDO_KEY_CREATED);
331 if (found == _params.constEnd()) {
332 return 0;
333 }
334 return found->toString().toULongLong();
335}
336
337void KWalletFreedesktopAttributes::updateLastModified()
338{
339 _params[FDO_KEY_MODIFIED] = QString::number(QDateTime::currentSecsSinceEpoch());
340}
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
qsizetype size() const const
QChar fromLatin1(char c)
qint64 currentSecsSinceEpoch()
bool remove()
bool rename(const QString &newName)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
const_iterator constBegin() const const
const_iterator constEnd() const const
bool empty() const const
iterator end()
iterator find(QLatin1StringView key)
iterator insert(QLatin1StringView key, const QJsonValue &value)
void remove(QLatin1StringView key)
void push_back(parameter_type value)
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator insert(const Key &key, const T &value)
QString writableLocation(StandardLocation type)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
QString number(double n, char format, int precision)
QString right(qsizetype n) const const
qsizetype size() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:53:00 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.