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 &paramName, 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 &paramName, 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 &paramName, const QString &param)
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 &paramName, 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 &paramName, const QString &defaultParam) const
0289 {
0290     return getStringParam(itemUniqLabel.toEntryLocation(), paramName, defaultParam);
0291 }
0292 
0293 qulonglong KWalletFreedesktopAttributes::getULongLongParam(const FdoUniqueLabel &itemUniqLabel, const QString &paramName, qulonglong defaultParam) const
0294 {
0295     return getULongLongParam(itemUniqLabel.toEntryLocation(), paramName, defaultParam);
0296 }
0297 
0298 void KWalletFreedesktopAttributes::setParam(const FdoUniqueLabel &itemUniqLabel, const QString &paramName, const QString &param)
0299 {
0300     setParam(itemUniqLabel.toEntryLocation(), paramName, param);
0301 }
0302 
0303 void KWalletFreedesktopAttributes::setParam(const FdoUniqueLabel &itemUniqLabel, const QString &paramName, 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 }