1 /*
2    SPDX-FileCopyrightText: 2020 Sandro Kanuß <sknauss@kde.org>
3 
4    SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "autocryptstorage.h"
8 #include "autocryptrecipient_p.h"
9 #include "autocryptstorage_p.h"
10 
11 #include <QDir>
12 #include <QFile>
13 #include <QStandardPaths>
14 #include <QTemporaryFile>
15 #include <QUrl>
16 #include <autocrypt_debug.h>
17 
18 using namespace MessageCore;
19 
AutocryptStoragePrivate()20 AutocryptStoragePrivate::AutocryptStoragePrivate()
21     : basePath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/autocrypt"))
22 {
23 }
24 
25 AutocryptStorage::Ptr AutocryptStorage::mSelf = nullptr;
26 
self()27 AutocryptStorage::Ptr AutocryptStorage::self()
28 {
29     if (!mSelf) {
30         mSelf = AutocryptStorage::Ptr(new AutocryptStorage());
31     }
32 
33     return mSelf;
34 }
35 
address2Filename(const QByteArray & addr)36 QString address2Filename(const QByteArray &addr)
37 {
38     const auto url = QUrl::toPercentEncoding(QString::fromUtf8(addr));
39     return QString::fromLatin1(url + ".json");
40 }
41 
AutocryptStorage()42 AutocryptStorage::AutocryptStorage()
43     : d_ptr(new AutocryptStoragePrivate())
44 {
45 }
46 
getRecipient(const QByteArray & addr)47 AutocryptRecipient::Ptr AutocryptStorage::getRecipient(const QByteArray &addr)
48 {
49     Q_D(AutocryptStorage);
50     if (d->recipients.contains(addr)) {
51         return d->recipients.value(addr);
52     }
53 
54     const QString fileName(address2Filename(addr));
55     if (d->basePath.exists(fileName)) {
56         QFile file(d->basePath.filePath(fileName));
57         auto recipient = AutocryptRecipient::Ptr(new AutocryptRecipient);
58         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
59             return nullptr;
60         }
61         recipient->fromJson(file.readAll());
62         d->recipients[addr] = recipient;
63         return recipient;
64     }
65     return nullptr;
66 }
67 
addRecipient(const QByteArray & addr)68 AutocryptRecipient::Ptr AutocryptStorage::addRecipient(const QByteArray &addr)
69 {
70     Q_D(AutocryptStorage);
71 
72     auto recipient = getRecipient(addr);
73     if (recipient) {
74         return recipient;
75     }
76 
77     recipient = AutocryptRecipient::Ptr(new AutocryptRecipient);
78     recipient->d_func()->addr = addr;
79     d->recipients[addr] = recipient;
80     return recipient;
81 }
82 
deleteRecipient(const QByteArray & addr)83 void AutocryptStorage::deleteRecipient(const QByteArray &addr)
84 {
85     Q_D(AutocryptStorage);
86     const QString fileName(address2Filename(addr));
87     d->basePath.remove(fileName);
88     d->recipients.remove(addr);
89 }
90 
save()91 void AutocryptStorage::save()
92 {
93     Q_D(AutocryptStorage);
94     if (!d->basePath.exists()) {
95         QDir parent = d->basePath;
96         if (!parent.cdUp()) {
97             qCWarning(AUTOCRYPT_LOG) << parent.absolutePath() << "does not exist. Cancel saving Autocrypt storage.";
98             return;
99         }
100 
101         if (!parent.mkdir(d->basePath.dirName())) {
102             qCWarning(AUTOCRYPT_LOG) << "Cancel saving Autocrypt storage, because failed to create" << d->basePath.absolutePath();
103             return;
104         }
105     }
106     const auto keys = d->recipients.keys();
107     for (const auto &addr : keys) {
108         const auto recipient = d->recipients.value(addr);
109         const QString fileName(address2Filename(addr));
110         if (recipient->hasChanged() || !d->basePath.exists(fileName)) {
111             QTemporaryFile file(d->basePath.path() + QLatin1Char('/'));
112             if (!file.open()) {
113                 continue;
114             }
115             file.write(recipient->toJson(QJsonDocument::Compact));
116             file.close();
117             d->basePath.remove(fileName);
118             file.rename(d->basePath.filePath(fileName));
119             file.setAutoRemove(false);
120             recipient->setChangedFlag(false);
121         }
122     }
123 }
124