Branch data Line data Source code
1 : : #include "DavCredentials.h"
2 : :
3 : : #include "CredentialStore.h"
4 : :
5 : : #include <QCryptographicHash>
6 : : #include <QEventLoop>
7 : : #include <QTimer>
8 : :
9 : : namespace {
10 : :
11 : : constexpr int kKeyringTimeoutMs = 30000;
12 : : constexpr auto kDavService = "carddav";
13 : :
14 : 65 : QString endpointDigest(const QString &accountId, const QString &serverUrl,
15 : : const QString &username) {
16 [ + - + - ]: 130 : const QString material = accountId.trimmed() + QLatin1Char('\n') +
17 [ + - + - : 260 : serverUrl.trimmed() + QLatin1Char('\n') +
+ - ]
18 [ + - + - ]: 195 : username.trimmed();
19 : : return QString::fromLatin1(
20 [ + - + - ]: 130 : QCryptographicHash::hash(material.toUtf8(),
21 : : QCryptographicHash::Sha256)
22 [ + - + - ]: 130 : .toHex());
23 : 65 : }
24 : :
25 : 65 : QString legacyAccountName(const QString &accountId, const QString &serverUrl,
26 : : const QString &username) {
27 [ + - ]: 130 : const QString id = accountId.trimmed().isEmpty()
28 [ + + - - ]: 65 : ? QStringLiteral("unknown")
29 [ + + + - ]: 75 : : accountId.trimmed();
30 : 130 : return QStringLiteral("%1/%2").arg(
31 [ + - + - ]: 130 : id, endpointDigest(accountId, serverUrl, username));
32 : 65 : }
33 : :
34 : 123 : QByteArray readPasswordKeyBlocking(const QString &accountName, bool &success,
35 : : QString *error) {
36 : 123 : success = false;
37 : :
38 [ + - ]: 123 : CredentialStore store;
39 [ + - ]: 123 : QEventLoop loop;
40 [ + - ]: 123 : QTimer timeout;
41 [ + - ]: 123 : timeout.setSingleShot(true);
42 : :
43 : 123 : bool completed = false;
44 : 123 : QByteArray password;
45 : 123 : QString readError;
46 : :
47 [ + - ]: 123 : QObject::connect(&timeout, &QTimer::timeout, &loop, [&]() {
48 [ # # ]: 0 : if (completed)
49 : 0 : return;
50 : 0 : completed = true;
51 : 0 : readError = QStringLiteral("Timed out reading DAV password from keyring");
52 : 0 : loop.quit();
53 : : });
54 : :
55 [ + - + - ]: 123 : store.readPassword(QString::fromLatin1(kDavService), accountName,
56 [ + - ]: 123 : [&](bool ok, const QByteArray &pw) {
57 [ - + ]: 123 : if (completed)
58 : 0 : return;
59 : 123 : completed = true;
60 : 123 : success = ok;
61 : 123 : password = pw;
62 : 123 : loop.quit();
63 : : });
64 : :
65 [ + - ]: 123 : timeout.start(kKeyringTimeoutMs);
66 [ + - ]: 123 : loop.exec();
67 : :
68 [ + + + + ]: 123 : if (!success && error) {
69 : 44 : *error = readError.isEmpty()
70 [ + - + - ]: 132 : ? QStringLiteral("Could not read DAV password from keyring")
71 : 44 : : readError;
72 : : }
73 [ + + ]: 246 : return success ? password : QByteArray();
74 : 123 : }
75 : :
76 : 26 : bool writePasswordKeyBlocking(const QString &accountName,
77 : : const QByteArray &password, QString *error) {
78 [ + - ]: 26 : CredentialStore store;
79 [ + - ]: 26 : QEventLoop loop;
80 [ + - ]: 26 : QTimer timeout;
81 [ + - ]: 26 : timeout.setSingleShot(true);
82 : :
83 : 26 : bool completed = false;
84 : 26 : bool success = false;
85 : 26 : QString writeError;
86 : :
87 [ + - ]: 26 : QObject::connect(&timeout, &QTimer::timeout, &loop, [&]() {
88 [ # # ]: 0 : if (completed)
89 : 0 : return;
90 : 0 : completed = true;
91 : 0 : writeError = QStringLiteral("Timed out writing DAV password to keyring");
92 : 0 : loop.quit();
93 : : });
94 : :
95 [ + - + - ]: 26 : store.writePassword(QString::fromLatin1(kDavService), accountName, password,
96 [ + - ]: 26 : [&](bool ok, const QString &err) {
97 [ - + ]: 26 : if (completed)
98 : 0 : return;
99 : 26 : completed = true;
100 : 26 : success = ok;
101 : 26 : writeError = err;
102 : 26 : loop.quit();
103 : : });
104 : :
105 [ + - ]: 26 : timeout.start(kKeyringTimeoutMs);
106 [ + - ]: 26 : loop.exec();
107 : :
108 [ + + + + ]: 26 : if (!success && error) {
109 : 4 : *error = writeError.isEmpty()
110 [ - + - + ]: 8 : ? QStringLiteral("Could not write DAV password to keyring")
111 : 4 : : writeError;
112 : : }
113 : 26 : return success;
114 : 26 : }
115 : :
116 : 13 : bool deletePasswordKeyBlocking(const QString &accountName) {
117 [ + - ]: 13 : CredentialStore store;
118 [ + - ]: 13 : QEventLoop loop;
119 [ + - ]: 13 : QTimer timeout;
120 [ + - ]: 13 : timeout.setSingleShot(true);
121 : :
122 : 13 : bool completed = false;
123 : 13 : bool success = false;
124 : :
125 [ + - ]: 13 : QObject::connect(&timeout, &QTimer::timeout, &loop, [&]() {
126 [ # # ]: 0 : if (completed)
127 : 0 : return;
128 : 0 : completed = true;
129 : 0 : loop.quit();
130 : : });
131 : :
132 [ + - + - ]: 13 : store.deletePassword(QString::fromLatin1(kDavService), accountName,
133 [ + - ]: 13 : [&](bool ok) {
134 [ - + ]: 13 : if (completed)
135 : 0 : return;
136 : 13 : completed = true;
137 : 13 : success = ok;
138 : 13 : loop.quit();
139 : : });
140 : :
141 [ + - ]: 13 : timeout.start(kKeyringTimeoutMs);
142 [ + - ]: 13 : loop.exec();
143 : 13 : return success;
144 : 13 : }
145 : :
146 : : } // namespace
147 : :
148 : : namespace DavCredentials {
149 : :
150 : 110 : QString accountName(const QString &accountId, const QString &serverUrl,
151 : : const QString &username) {
152 [ + - ]: 110 : const QString id = accountId.trimmed();
153 [ + + ]: 110 : if (id.isEmpty())
154 [ + - ]: 7 : return legacyAccountName(accountId, serverUrl, username);
155 : 103 : return id;
156 : 110 : }
157 : :
158 : 68 : QByteArray readPasswordBlocking(const QString &accountId,
159 : : const QString &serverUrl,
160 : : const QString &username,
161 : : QString *error) {
162 : 68 : bool success = false;
163 [ + - ]: 68 : const QString stableName = accountName(accountId, serverUrl, username);
164 [ + - ]: 68 : QByteArray password = readPasswordKeyBlocking(stableName, success, error);
165 [ + + ]: 68 : if (success)
166 : 19 : return password;
167 : :
168 : : const QString legacyName =
169 [ + - ]: 49 : legacyAccountName(accountId, serverUrl, username);
170 [ + + ]: 49 : if (legacyName == stableName)
171 : 2 : return {};
172 : :
173 [ + - ]: 47 : password = readPasswordKeyBlocking(legacyName, success, nullptr);
174 [ + + ]: 47 : if (!success)
175 : 44 : return {};
176 : :
177 : 3 : QString migrationError;
178 [ + - + + ]: 3 : if (writePasswordKeyBlocking(stableName, password, &migrationError))
179 [ + - ]: 2 : deletePasswordKeyBlocking(legacyName);
180 [ + + ]: 3 : if (error)
181 : 2 : error->clear();
182 : 3 : return password;
183 : 68 : }
184 : :
185 : 24 : bool writePasswordBlocking(const QString &accountId, const QString &serverUrl,
186 : : const QString &username,
187 : : const QByteArray &password,
188 : : QString *error) {
189 [ + + ]: 24 : if (password.isEmpty())
190 : 1 : return true;
191 [ + - ]: 23 : return writePasswordKeyBlocking(
192 [ + - ]: 46 : accountName(accountId, serverUrl, username), password, error);
193 : : }
194 : :
195 : 9 : bool deletePasswordBlocking(const QString &accountId, const QString &serverUrl,
196 : : const QString &username,
197 : : QString *error) {
198 [ + - ]: 9 : const QString stableName = accountName(accountId, serverUrl, username);
199 [ + - ]: 9 : bool success = deletePasswordKeyBlocking(stableName);
200 : :
201 : : const QString legacyName =
202 [ + - ]: 9 : legacyAccountName(accountId, serverUrl, username);
203 [ + + ]: 9 : if (legacyName != stableName) {
204 : 8 : bool legacyExists = false;
205 [ + - ]: 8 : readPasswordKeyBlocking(legacyName, legacyExists, nullptr);
206 [ + + ]: 8 : if (legacyExists)
207 [ + - - + : 2 : success = deletePasswordKeyBlocking(legacyName) || success;
- - ]
208 : : }
209 : :
210 [ + + + + ]: 9 : if (!success && error)
211 : 1 : *error = QStringLiteral("Could not delete DAV password from keyring");
212 : 9 : return success;
213 : 9 : }
214 : :
215 : : } // namespace DavCredentials
|