File indexing completed on 2024-11-24 03:41:06
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 #include "kwalletfreedesktopattributes.h" 0008 0009 #include "kwalletd.h" 0010 #include "kwalletd_debug.h" 0011 #include "kwalletfreedesktopcollection.h" 0012 #include <QDir> 0013 #include <QFile> 0014 #include <QSaveFile> 0015 0016 KWalletFreedesktopAttributes::KWalletFreedesktopAttributes(const QString &walletName) 0017 { 0018 const QString writeLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kwalletd"); 0019 _path = writeLocation + QChar::fromLatin1('/') + KWalletD::encodeWalletName(walletName) + QStringLiteral("_attributes.json"); 0020 0021 read(); 0022 0023 if (!_params.contains(FDO_KEY_CREATED)) { 0024 const auto currentTime = QString::number(QDateTime::currentSecsSinceEpoch()); 0025 _params[FDO_KEY_CREATED] = currentTime; 0026 _params[FDO_KEY_MODIFIED] = currentTime; 0027 } 0028 } 0029 0030 void KWalletFreedesktopAttributes::read() 0031 { 0032 QByteArray content; 0033 { 0034 QFile file(_path); 0035 file.open(QIODevice::ReadOnly | QIODevice::Text); 0036 if (!file.isOpen()) { 0037 qCDebug(KWALLETD_LOG) << "Can't read attributes file " << _path; 0038 return; 0039 } 0040 content = file.readAll(); 0041 } 0042 0043 const auto jsonDoc = QJsonDocument::fromJson(content); 0044 if (jsonDoc.isObject()) { 0045 _params = jsonDoc.object(); 0046 } else { 0047 qCWarning(KWALLETD_LOG) << "Can't read attributes: the root element must be an JSON-object: " << _path; 0048 _params = QJsonObject(); 0049 } 0050 } 0051 0052 void KWalletFreedesktopAttributes::write() 0053 { 0054 if (_params.empty()) { 0055 QFile::remove(_path); 0056 return; 0057 } 0058 0059 updateLastModified(); 0060 0061 QSaveFile sf(_path); 0062 if (!sf.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) { 0063 qCWarning(KWALLETD_LOG) << "Can't write attributes file: " << _path; 0064 return; 0065 } 0066 sf.setPermissions(QSaveFile::ReadUser | QSaveFile::WriteUser); 0067 0068 const QJsonDocument saveDoc(_params); 0069 0070 const QByteArray jsonBytes = saveDoc.toJson(); 0071 if (sf.write(jsonBytes) != jsonBytes.size()) { 0072 sf.cancelWriting(); 0073 qCWarning(KWALLETD_LOG) << "Cannot write attributes file " << _path; 0074 return; 0075 } 0076 if (!sf.commit()) { 0077 qCWarning(KWALLETD_LOG) << "Cannot commit attributes file " << _path; 0078 } 0079 } 0080 0081 static QString entryLocationToStr(const EntryLocation &entryLocation) 0082 { 0083 return entryLocation.folder + QChar::fromLatin1('/') + entryLocation.key; 0084 } 0085 0086 static EntryLocation splitToEntryLocation(const QString &entryLocation) 0087 { 0088 const int slashPos = entryLocation.indexOf(QChar::fromLatin1('/')); 0089 if (slashPos == -1) { 0090 qCWarning(KWALLETD_LOG) << "Entry location '" << entryLocation << "' has no slash '/'"; 0091 return {}; 0092 } else { 0093 return {entryLocation.left(slashPos), entryLocation.right((entryLocation.size() - slashPos) - 1)}; 0094 } 0095 } 0096 0097 void KWalletFreedesktopAttributes::remove(const EntryLocation &entryLocation) 0098 { 0099 _params.remove(entryLocationToStr(entryLocation)); 0100 if (_params.empty()) { 0101 QFile::remove(_path); 0102 } else { 0103 write(); 0104 } 0105 } 0106 0107 void KWalletFreedesktopAttributes::deleteFile() 0108 { 0109 QFile::remove(_path); 0110 } 0111 0112 void KWalletFreedesktopAttributes::renameLabel(const EntryLocation &oldLocation, const EntryLocation &newLocation) 0113 { 0114 const QString oldLoc = entryLocationToStr(oldLocation); 0115 0116 const auto found = _params.find(oldLoc); 0117 if (found == _params.end() || !found->isObject()) { 0118 qCWarning(KWALLETD_LOG) << "Can't rename label (!?)"; 0119 return; 0120 } 0121 const auto obj = found->toObject(); 0122 _params.erase(found); 0123 _params.insert(entryLocationToStr(newLocation), obj); 0124 0125 write(); 0126 } 0127 0128 void KWalletFreedesktopAttributes::renameWallet(const QString &newName) 0129 { 0130 const QString writeLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kwalletd"); 0131 const QString newPath = writeLocation + QChar::fromLatin1('/') + newName + QStringLiteral("_attributes.json"); 0132 0133 QFile::rename(_path, newPath); 0134 _path = newPath; 0135 } 0136 0137 void KWalletFreedesktopAttributes::newItem(const EntryLocation &entryLocation) 0138 { 0139 _params[entryLocationToStr(entryLocation)] = QJsonObject(); 0140 } 0141 0142 QList<EntryLocation> KWalletFreedesktopAttributes::matchAttributes(const StrStrMap &attributes) const 0143 { 0144 QList<EntryLocation> items; 0145 0146 for (auto i = _params.constBegin(); i != _params.constEnd(); ++i) { 0147 if (!i->isObject()) { 0148 continue; 0149 } 0150 0151 bool match = true; 0152 const auto itemParams = i->toObject(); 0153 const auto foundItemAttribs = itemParams.find(QStringLiteral("attributes")); 0154 if (foundItemAttribs == itemParams.end() || !foundItemAttribs->isObject()) { 0155 continue; 0156 } 0157 const auto itemAttribs = foundItemAttribs->toObject(); 0158 0159 for (auto i = attributes.constBegin(); i != attributes.constEnd(); ++i) { 0160 const auto foundKey = itemAttribs.find(i.key()); 0161 if (foundKey == itemAttribs.end() || !foundKey->isString() || foundKey->toString() != i.value()) { 0162 match = false; 0163 break; 0164 } 0165 } 0166 0167 if (match) { 0168 items += splitToEntryLocation(i.key()); 0169 } 0170 } 0171 0172 return items; 0173 } 0174 0175 void KWalletFreedesktopAttributes::setAttributes(const EntryLocation &entryLocation, const StrStrMap &attributes) 0176 { 0177 QJsonObject jsonAttrs; 0178 for (auto i = attributes.constBegin(); i != attributes.constEnd(); ++i) { 0179 jsonAttrs.insert(i.key(), i.value()); 0180 } 0181 0182 const QString strLocation = entryLocationToStr(entryLocation); 0183 0184 const auto foundParams = _params.find(strLocation); 0185 QJsonObject params; 0186 if (foundParams != _params.end() && foundParams->isObject()) { 0187 params = foundParams->toObject(); 0188 } else { 0189 return; 0190 } 0191 0192 if (jsonAttrs.empty()) { 0193 params.remove(QStringLiteral("attributes")); 0194 } else { 0195 params[QStringLiteral("attributes")] = jsonAttrs; 0196 } 0197 0198 _params[strLocation] = params; 0199 0200 write(); 0201 } 0202 0203 StrStrMap KWalletFreedesktopAttributes::getAttributes(const EntryLocation &entryLocation) const 0204 { 0205 const auto foundObj = _params.find(entryLocationToStr(entryLocation)); 0206 if (foundObj == _params.end() || !foundObj->isObject()) { 0207 return StrStrMap(); 0208 } 0209 const auto jsonParams = foundObj->toObject(); 0210 0211 const auto foundAttrs = jsonParams.find(QStringLiteral("attributes")); 0212 if (foundAttrs == jsonParams.end() || !foundAttrs->isObject()) { 0213 return StrStrMap(); 0214 } 0215 const auto jsonAttrs = foundAttrs->toObject(); 0216 0217 StrStrMap itemAttrs; 0218 0219 for (auto i = jsonAttrs.constBegin(); i != jsonAttrs.constEnd(); ++i) { 0220 if (i.value().isString()) { 0221 itemAttrs.insert(i.key(), i.value().toString()); 0222 } 0223 } 0224 0225 return itemAttrs; 0226 } 0227 0228 QString KWalletFreedesktopAttributes::getStringParam(const EntryLocation &entryLocation, const QString ¶mName, const QString &defaultParam) const 0229 { 0230 const auto foundParams = _params.find(entryLocationToStr(entryLocation)); 0231 if (foundParams == _params.end() || !foundParams->isObject()) { 0232 return defaultParam; 0233 } 0234 const auto params = foundParams->toObject(); 0235 0236 const auto foundParam = params.find(paramName); 0237 if (foundParam == params.end() || !foundParam->isString()) { 0238 return defaultParam; 0239 } 0240 0241 return foundParam->toString(); 0242 } 0243 0244 qulonglong KWalletFreedesktopAttributes::getULongLongParam(const EntryLocation &entryLocation, const QString ¶mName, qulonglong defaultParam) const 0245 { 0246 const auto str = getStringParam(entryLocation, paramName, QString::number(defaultParam)); 0247 bool ok = false; 0248 const auto result = str.toULongLong(&ok); 0249 return ok ? result : defaultParam; 0250 } 0251 0252 void KWalletFreedesktopAttributes::setParam(const EntryLocation &entryLocation, const QString ¶mName, const QString ¶m) 0253 { 0254 const auto entryLoc = entryLocationToStr(entryLocation); 0255 const auto foundParams = _params.find(entryLoc); 0256 if (foundParams == _params.end() || !foundParams->isObject()) { 0257 return; 0258 } 0259 0260 auto params = foundParams->toObject(); 0261 0262 params[paramName] = param; 0263 _params[entryLoc] = params; 0264 0265 write(); 0266 } 0267 0268 void KWalletFreedesktopAttributes::setParam(const EntryLocation &entryLocation, const QString ¶mName, qulonglong param) 0269 { 0270 setParam(entryLocation, paramName, QString::number(param)); 0271 } 0272 0273 void KWalletFreedesktopAttributes::remove(const FdoUniqueLabel &itemUniqLabel) 0274 { 0275 remove(itemUniqLabel.toEntryLocation()); 0276 } 0277 0278 void KWalletFreedesktopAttributes::setAttributes(const FdoUniqueLabel &itemUniqLabel, const StrStrMap &attributes) 0279 { 0280 setAttributes(itemUniqLabel.toEntryLocation(), attributes); 0281 } 0282 0283 StrStrMap KWalletFreedesktopAttributes::getAttributes(const FdoUniqueLabel &itemUniqLabel) const 0284 { 0285 return getAttributes(itemUniqLabel.toEntryLocation()); 0286 } 0287 0288 QString KWalletFreedesktopAttributes::getStringParam(const FdoUniqueLabel &itemUniqLabel, const QString ¶mName, const QString &defaultParam) const 0289 { 0290 return getStringParam(itemUniqLabel.toEntryLocation(), paramName, defaultParam); 0291 } 0292 0293 qulonglong KWalletFreedesktopAttributes::getULongLongParam(const FdoUniqueLabel &itemUniqLabel, const QString ¶mName, qulonglong defaultParam) const 0294 { 0295 return getULongLongParam(itemUniqLabel.toEntryLocation(), paramName, defaultParam); 0296 } 0297 0298 void KWalletFreedesktopAttributes::setParam(const FdoUniqueLabel &itemUniqLabel, const QString ¶mName, const QString ¶m) 0299 { 0300 setParam(itemUniqLabel.toEntryLocation(), paramName, param); 0301 } 0302 0303 void KWalletFreedesktopAttributes::setParam(const FdoUniqueLabel &itemUniqLabel, const QString ¶mName, qulonglong param) 0304 { 0305 setParam(itemUniqLabel.toEntryLocation(), paramName, param); 0306 } 0307 0308 QList<EntryLocation> KWalletFreedesktopAttributes::listItems() const 0309 { 0310 QList<EntryLocation> items; 0311 for (auto i = _params.constBegin(); i != _params.constEnd(); ++i) { 0312 if (i->isObject()) { 0313 items.push_back(splitToEntryLocation(i.key())); 0314 } 0315 } 0316 return items; 0317 } 0318 0319 qulonglong KWalletFreedesktopAttributes::lastModified() const 0320 { 0321 auto found = _params.constFind(FDO_KEY_MODIFIED); 0322 if (found == _params.constEnd()) { 0323 return 0; 0324 } 0325 return found->toString().toULongLong(); 0326 } 0327 0328 qulonglong KWalletFreedesktopAttributes::birthTime() const 0329 { 0330 auto found = _params.constFind(FDO_KEY_CREATED); 0331 if (found == _params.constEnd()) { 0332 return 0; 0333 } 0334 return found->toString().toULongLong(); 0335 } 0336 0337 void KWalletFreedesktopAttributes::updateLastModified() 0338 { 0339 _params[FDO_KEY_MODIFIED] = QString::number(QDateTime::currentSecsSinceEpoch()); 0340 }