File indexing completed on 2025-03-09 04:54:13

0001 /*
0002    SPDX-FileCopyrightText: 2020 Sandro Kanuß <sknauss@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "autocryptstorage.h"
0008 #include "autocryptrecipient_p.h"
0009 #include "autocryptstorage_p.h"
0010 
0011 #include <QDir>
0012 #include <QFile>
0013 #include <QStandardPaths>
0014 #include <QTemporaryFile>
0015 #include <QUrl>
0016 #include <autocrypt_debug.h>
0017 
0018 using namespace MessageCore;
0019 
0020 AutocryptStoragePrivate::AutocryptStoragePrivate()
0021     : basePath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/autocrypt"))
0022 {
0023 }
0024 
0025 AutocryptStorage::Ptr AutocryptStorage::mSelf = nullptr;
0026 
0027 AutocryptStorage::Ptr AutocryptStorage::self()
0028 {
0029     if (!mSelf) {
0030         mSelf = AutocryptStorage::Ptr(new AutocryptStorage());
0031     }
0032 
0033     return mSelf;
0034 }
0035 
0036 QString address2Filename(const QByteArray &addr)
0037 {
0038     const auto url = QUrl::toPercentEncoding(QString::fromUtf8(addr));
0039     return QString::fromLatin1(url + ".json");
0040 }
0041 
0042 AutocryptStorage::AutocryptStorage()
0043     : d_ptr(new AutocryptStoragePrivate())
0044 {
0045 }
0046 
0047 AutocryptRecipient::Ptr AutocryptStorage::getRecipient(const QByteArray &addr)
0048 {
0049     Q_D(AutocryptStorage);
0050     if (d->recipients.contains(addr)) {
0051         return d->recipients.value(addr);
0052     }
0053 
0054     const QString fileName(address2Filename(addr));
0055     if (d->basePath.exists(fileName)) {
0056         QFile file(d->basePath.filePath(fileName));
0057         auto recipient = AutocryptRecipient::Ptr(new AutocryptRecipient);
0058         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0059             return nullptr;
0060         }
0061         recipient->fromJson(file.readAll());
0062         d->recipients[addr] = recipient;
0063         return recipient;
0064     }
0065     return nullptr;
0066 }
0067 
0068 AutocryptRecipient::Ptr AutocryptStorage::addRecipient(const QByteArray &addr)
0069 {
0070     Q_D(AutocryptStorage);
0071 
0072     auto recipient = getRecipient(addr);
0073     if (recipient) {
0074         return recipient;
0075     }
0076 
0077     recipient = AutocryptRecipient::Ptr(new AutocryptRecipient);
0078     recipient->d_func()->addr = addr;
0079     d->recipients[addr] = recipient;
0080     return recipient;
0081 }
0082 
0083 void AutocryptStorage::deleteRecipient(const QByteArray &addr)
0084 {
0085     Q_D(AutocryptStorage);
0086     const QString fileName(address2Filename(addr));
0087     d->basePath.remove(fileName);
0088     d->recipients.remove(addr);
0089 }
0090 
0091 void AutocryptStorage::save()
0092 {
0093     Q_D(AutocryptStorage);
0094     if (!d->basePath.exists()) {
0095         QDir parent = d->basePath;
0096         if (!parent.cdUp()) {
0097             qCWarning(AUTOCRYPT_LOG) << parent.absolutePath() << "does not exist. Cancel saving Autocrypt storage.";
0098             return;
0099         }
0100 
0101         if (!parent.mkdir(d->basePath.dirName())) {
0102             qCWarning(AUTOCRYPT_LOG) << "Cancel saving Autocrypt storage, because failed to create" << d->basePath.absolutePath();
0103             return;
0104         }
0105     }
0106     const auto keys = d->recipients.keys();
0107     for (const auto &addr : keys) {
0108         const auto recipient = d->recipients.value(addr);
0109         const QString fileName(address2Filename(addr));
0110         if (recipient->hasChanged() || !d->basePath.exists(fileName)) {
0111             QTemporaryFile file(d->basePath.path() + QLatin1Char('/'));
0112             if (!file.open()) {
0113                 continue;
0114             }
0115             file.write(recipient->toJson(QJsonDocument::Compact));
0116             file.close();
0117             d->basePath.remove(fileName);
0118             file.rename(d->basePath.filePath(fileName));
0119             file.setAutoRemove(false);
0120             recipient->setChangedFlag(false);
0121         }
0122     }
0123 }