data/DavCredentials.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "DavCredentials.h" | ||
| 2 | |||
| 3 | #include "CredentialStore.h" | ||
| 4 | |||
| 5 | #include <QCryptographicHash> | ||
| 6 | #include <QEventLoop> | ||
| 7 | #include <QSettings> | ||
| 8 | #include <QTimer> | ||
| 9 | #include <QPointer> | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | constexpr int kKeyringTimeoutMs = 30000; | ||
| 14 | constexpr auto kDavService = "carddav"; | ||
| 15 | |||
| 16 | 76 | QString digestMaterial(const QString &accountId, const QString &origin, | |
| 17 | const QString &username) { | ||
| 18 |
2/2✓ Branch 7 → 8 taken 76 times.
✓ Branch 8 → 9 taken 76 times.
|
152 | const QString material = accountId.trimmed() + QLatin1Char('\n') + |
| 19 |
2/2✓ Branch 9 → 10 taken 76 times.
✓ Branch 10 → 11 taken 76 times.
|
228 | origin + QLatin1Char('\n') + |
| 20 |
2/2✓ Branch 2 → 3 taken 76 times.
✓ Branch 11 → 12 taken 76 times.
|
228 | username.trimmed(); |
| 21 | return QString::fromLatin1( | ||
| 22 |
2/2✓ Branch 17 → 18 taken 76 times.
✓ Branch 19 → 20 taken 76 times.
|
152 | QCryptographicHash::hash(material.toUtf8(), |
| 23 | QCryptographicHash::Sha256) | ||
| 24 |
2/2✓ Branch 20 → 21 taken 76 times.
✓ Branch 21 → 22 taken 76 times.
|
152 | .toHex()); |
| 25 | 76 | } | |
| 26 | |||
| 27 | 29 | QString legacyAccountName(const QString &accountId, const QString &serverUrl, | |
| 28 | const QString &username) { | ||
| 29 |
1/1✓ Branch 2 → 3 taken 29 times.
|
58 | const QString id = accountId.trimmed().isEmpty() |
| 30 |
1/4✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 29 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
|
29 | ? QStringLiteral("unknown") |
| 31 |
2/3✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 29 times.
✓ Branch 8 → 9 taken 29 times.
|
29 | : accountId.trimmed(); |
| 32 | // Pre-2026-07 endpoint-bound format. Keep this exact raw-URL digest solely | ||
| 33 | // for a safe one-way migration; unlike the later account-ID-only format it | ||
| 34 | // cannot move a secret to another endpoint. | ||
| 35 | 58 | return QStringLiteral("%1/%2").arg( | |
| 36 |
3/3✓ Branch 15 → 16 taken 29 times.
✓ Branch 16 → 17 taken 29 times.
✓ Branch 17 → 18 taken 29 times.
|
58 | id, digestMaterial(accountId, serverUrl.trimmed(), username)); |
| 37 | 29 | } | |
| 38 | |||
| 39 | 40 | QByteArray readPasswordKeyBlocking(const QString &accountName, bool &success, | |
| 40 | QString *error) { | ||
| 41 | 40 | success = false; | |
| 42 | |||
| 43 |
1/1✓ Branch 2 → 3 taken 40 times.
|
40 | CredentialStore store; |
| 44 |
1/1✓ Branch 3 → 4 taken 40 times.
|
40 | QEventLoop loop; |
| 45 |
1/1✓ Branch 4 → 5 taken 40 times.
|
40 | QTimer timeout; |
| 46 |
1/1✓ Branch 5 → 6 taken 40 times.
|
40 | timeout.setSingleShot(true); |
| 47 | |||
| 48 | 40 | bool completed = false; | |
| 49 | 40 | QByteArray password; | |
| 50 | 40 | QString readError; | |
| 51 | |||
| 52 |
1/1✓ Branch 6 → 7 taken 40 times.
|
40 | QObject::connect(&timeout, &QTimer::timeout, &loop, [&]() { |
| 53 | ✗ | if (completed) | |
| 54 | ✗ | return; | |
| 55 | ✗ | completed = true; | |
| 56 | ✗ | readError = QStringLiteral("Timed out reading DAV password from keyring"); | |
| 57 | ✗ | loop.quit(); | |
| 58 | }); | ||
| 59 | |||
| 60 |
2/2✓ Branch 10 → 11 taken 40 times.
✓ Branch 11 → 12 taken 40 times.
|
40 | store.readPassword(QString::fromLatin1(kDavService), accountName, |
| 61 |
1/1✓ Branch 8 → 9 taken 40 times.
|
40 | [&](bool ok, const QByteArray &pw) { |
| 62 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 40 times.
|
40 | if (completed) |
| 63 | ✗ | return; | |
| 64 | 40 | completed = true; | |
| 65 | 40 | success = ok; | |
| 66 | 40 | password = pw; | |
| 67 | 40 | loop.quit(); | |
| 68 | }); | ||
| 69 | |||
| 70 | // S106-201: a synchronous answer (a refusal off the GUI thread) quits | ||
| 71 | // the loop before exec() — the quit is lost and the loop never ends. | ||
| 72 |
2/2✓ Branch 14 → 15 taken 38 times.
✓ Branch 14 → 19 taken 2 times.
|
40 | if (!completed) { |
| 73 |
1/1✓ Branch 15 → 16 taken 38 times.
|
38 | timeout.start(kKeyringTimeoutMs); |
| 74 |
1/1✓ Branch 17 → 18 taken 38 times.
|
38 | loop.exec(); |
| 75 | } | ||
| 76 | |||
| 77 |
4/4✓ Branch 19 → 20 taken 32 times.
✓ Branch 19 → 32 taken 8 times.
✓ Branch 20 → 21 taken 6 times.
✓ Branch 20 → 32 taken 26 times.
|
40 | if (!success && error) { |
| 78 | 6 | *error = readError.isEmpty() | |
| 79 |
2/4✓ Branch 22 → 23 taken 6 times.
✗ Branch 22 → 26 not taken.
✓ Branch 29 → 30 taken 6 times.
✗ Branch 29 → 31 not taken.
|
18 | ? QStringLiteral("Could not read DAV password from keyring") |
| 80 | 6 | : readError; | |
| 81 | } | ||
| 82 |
2/2✓ Branch 32 → 33 taken 8 times.
✓ Branch 32 → 34 taken 32 times.
|
80 | return success ? password : QByteArray(); |
| 83 | 40 | } | |
| 84 | |||
| 85 | 7 | bool writePasswordKeyBlocking(const QString &accountName, | |
| 86 | const QByteArray &password, QString *error) { | ||
| 87 |
1/1✓ Branch 2 → 3 taken 7 times.
|
7 | CredentialStore store; |
| 88 |
1/1✓ Branch 3 → 4 taken 7 times.
|
7 | QEventLoop loop; |
| 89 |
1/1✓ Branch 4 → 5 taken 7 times.
|
7 | QTimer timeout; |
| 90 |
1/1✓ Branch 5 → 6 taken 7 times.
|
7 | timeout.setSingleShot(true); |
| 91 | |||
| 92 | 7 | bool completed = false; | |
| 93 | 7 | bool success = false; | |
| 94 | 7 | QString writeError; | |
| 95 | |||
| 96 |
1/1✓ Branch 6 → 7 taken 7 times.
|
7 | QObject::connect(&timeout, &QTimer::timeout, &loop, [&]() { |
| 97 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1 time.
|
1 | if (completed) |
| 98 | ✗ | return; | |
| 99 | 1 | completed = true; | |
| 100 | 1 | writeError = QStringLiteral("Timed out writing DAV password to keyring"); | |
| 101 | 1 | loop.quit(); | |
| 102 | }); | ||
| 103 | |||
| 104 |
2/2✓ Branch 10 → 11 taken 7 times.
✓ Branch 11 → 12 taken 7 times.
|
7 | store.writePassword(QString::fromLatin1(kDavService), accountName, password, |
| 105 |
1/1✓ Branch 8 → 9 taken 7 times.
|
7 | [&](bool ok, const QString &err) { |
| 106 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 6 times.
|
6 | if (completed) |
| 107 | ✗ | return; | |
| 108 | 6 | completed = true; | |
| 109 | 6 | success = ok; | |
| 110 | 6 | writeError = err; | |
| 111 | 6 | loop.quit(); | |
| 112 | }); | ||
| 113 | |||
| 114 | // S106-201: a synchronous answer (a refusal off the GUI thread) quits | ||
| 115 | // the loop before exec() — the quit is lost and the loop never ends. | ||
| 116 |
1/2✓ Branch 14 → 15 taken 7 times.
✗ Branch 14 → 19 not taken.
|
7 | if (!completed) { |
| 117 |
1/1✓ Branch 15 → 16 taken 7 times.
|
7 | timeout.start(kKeyringTimeoutMs); |
| 118 |
1/1✓ Branch 17 → 18 taken 7 times.
|
7 | loop.exec(); |
| 119 | } | ||
| 120 | |||
| 121 |
3/4✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 32 taken 6 times.
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 32 not taken.
|
7 | if (!success && error) { |
| 122 | 1 | *error = writeError.isEmpty() | |
| 123 |
2/4✗ Branch 22 → 23 not taken.
✓ Branch 22 → 26 taken 1 time.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 1 time.
|
2 | ? QStringLiteral("Could not write DAV password to keyring") |
| 124 | 1 | : writeError; | |
| 125 | } | ||
| 126 | 7 | return success; | |
| 127 | 7 | } | |
| 128 | |||
| 129 | 4 | bool deletePasswordKeyBlocking(const QString &accountName) { | |
| 130 |
1/1✓ Branch 2 → 3 taken 4 times.
|
4 | CredentialStore store; |
| 131 |
1/1✓ Branch 3 → 4 taken 4 times.
|
4 | QEventLoop loop; |
| 132 |
1/1✓ Branch 4 → 5 taken 4 times.
|
4 | QTimer timeout; |
| 133 |
1/1✓ Branch 5 → 6 taken 4 times.
|
4 | timeout.setSingleShot(true); |
| 134 | |||
| 135 | 4 | bool completed = false; | |
| 136 | 4 | bool success = false; | |
| 137 | |||
| 138 |
1/1✓ Branch 6 → 7 taken 4 times.
|
4 | QObject::connect(&timeout, &QTimer::timeout, &loop, [&]() { |
| 139 | ✗ | if (completed) | |
| 140 | ✗ | return; | |
| 141 | ✗ | completed = true; | |
| 142 | ✗ | loop.quit(); | |
| 143 | }); | ||
| 144 | |||
| 145 |
2/2✓ Branch 10 → 11 taken 4 times.
✓ Branch 11 → 12 taken 4 times.
|
4 | store.deletePassword(QString::fromLatin1(kDavService), accountName, |
| 146 |
1/1✓ Branch 8 → 9 taken 4 times.
|
4 | [&](bool ok) { |
| 147 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 4 times.
|
4 | if (completed) |
| 148 | ✗ | return; | |
| 149 | 4 | completed = true; | |
| 150 | 4 | success = ok; | |
| 151 | 4 | loop.quit(); | |
| 152 | }); | ||
| 153 | |||
| 154 | // S106-201: a synchronous answer (a refusal off the GUI thread) quits | ||
| 155 | // the loop before exec() — the quit is lost and the loop never ends. | ||
| 156 |
1/2✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 19 not taken.
|
4 | if (!completed) { |
| 157 |
1/1✓ Branch 15 → 16 taken 4 times.
|
4 | timeout.start(kKeyringTimeoutMs); |
| 158 |
1/1✓ Branch 17 → 18 taken 4 times.
|
4 | loop.exec(); |
| 159 | } | ||
| 160 | 4 | return success; | |
| 161 | 4 | } | |
| 162 | |||
| 163 | 32 | bool requiresLocalAuthorization(const QString &accountId, | |
| 164 | const QString &serverUrl, | ||
| 165 | const QString &username) { | ||
| 166 |
1/1✓ Branch 2 → 3 taken 32 times.
|
32 | QSettings settings; |
| 167 |
1/1✓ Branch 7 → 8 taken 32 times.
|
32 | const int count = settings.beginReadArray(QStringLiteral("carddav/accounts")); |
| 168 | 32 | bool required = false; | |
| 169 |
2/2✓ Branch 96 → 11 taken 7 times.
✓ Branch 96 → 97 taken 25 times.
|
32 | for (int i = 0; i < count; ++i) { |
| 170 |
1/1✓ Branch 11 → 12 taken 7 times.
|
7 | settings.setArrayIndex(i); |
| 171 |
3/3✓ Branch 17 → 18 taken 7 times.
✓ Branch 18 → 19 taken 7 times.
✓ Branch 19 → 20 taken 7 times.
|
14 | if (settings.value(QStringLiteral("id")).toString().trimmed() != |
| 172 |
2/3✓ Branch 12 → 13 taken 7 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 7 times.
|
21 | accountId.trimmed()) { |
| 173 | ✗ | continue; | |
| 174 | } | ||
| 175 | const bool sameIdentity = | ||
| 176 |
1/3✓ Branch 36 → 37 taken 7 times.
✗ Branch 150 → 151 not taken.
✗ Branch 150 → 152 not taken.
|
14 | DavCredentials::canonicalOrigin( |
| 177 |
6/18✓ Branch 34 → 35 taken 7 times.
✓ Branch 35 → 36 taken 7 times.
✓ Branch 65 → 66 taken 7 times.
✗ Branch 65 → 67 not taken.
✓ Branch 67 → 68 taken 7 times.
✗ Branch 67 → 69 not taken.
✓ Branch 69 → 70 taken 7 times.
✗ Branch 69 → 71 not taken.
✓ Branch 71 → 72 taken 7 times.
✗ Branch 71 → 73 not taken.
✗ Branch 154 → 155 not taken.
✗ Branch 154 → 156 not taken.
✗ Branch 158 → 159 not taken.
✗ Branch 158 → 160 not taken.
✗ Branch 163 → 164 not taken.
✗ Branch 163 → 165 not taken.
✗ Branch 167 → 168 not taken.
✗ Branch 167 → 169 not taken.
|
14 | settings.value(QStringLiteral("serverUrl")).toString()) == |
| 178 |
4/9✓ Branch 29 → 30 taken 7 times.
✓ Branch 38 → 39 taken 7 times.
✗ Branch 38 → 50 not taken.
✓ Branch 48 → 49 taken 7 times.
✗ Branch 48 → 50 not taken.
✓ Branch 73 → 74 taken 7 times.
✗ Branch 73 → 75 not taken.
✗ Branch 171 → 172 not taken.
✗ Branch 171 → 173 not taken.
|
35 | DavCredentials::canonicalOrigin(serverUrl) && |
| 179 |
7/21✓ Branch 44 → 45 taken 7 times.
✓ Branch 45 → 46 taken 7 times.
✓ Branch 46 → 47 taken 7 times.
✓ Branch 53 → 54 taken 7 times.
✗ Branch 53 → 55 not taken.
✓ Branch 55 → 56 taken 7 times.
✗ Branch 55 → 57 not taken.
✓ Branch 57 → 58 taken 7 times.
✗ Branch 57 → 59 not taken.
✓ Branch 59 → 60 taken 7 times.
✗ Branch 59 → 61 not taken.
✗ Branch 125 → 126 not taken.
✗ Branch 125 → 127 not taken.
✗ Branch 129 → 130 not taken.
✗ Branch 129 → 131 not taken.
✗ Branch 133 → 134 not taken.
✗ Branch 133 → 135 not taken.
✗ Branch 138 → 139 not taken.
✗ Branch 138 → 140 not taken.
✗ Branch 142 → 143 not taken.
✗ Branch 142 → 144 not taken.
|
14 | settings.value(QStringLiteral("username")).toString().trimmed() == |
| 180 |
4/9✓ Branch 39 → 40 taken 7 times.
✓ Branch 51 → 52 taken 7 times.
✗ Branch 51 → 53 not taken.
✓ Branch 61 → 62 taken 7 times.
✗ Branch 61 → 63 not taken.
✓ Branch 63 → 64 taken 7 times.
✗ Branch 63 → 65 not taken.
✗ Branch 146 → 147 not taken.
✗ Branch 146 → 148 not taken.
|
21 | username.trimmed(); |
| 181 |
1/2✓ Branch 75 → 76 taken 7 times.
✗ Branch 75 → 85 not taken.
|
14 | required = sameIdentity && |
| 182 |
5/17✓ Branch 81 → 82 taken 7 times.
✓ Branch 86 → 87 taken 7 times.
✗ Branch 86 → 88 not taken.
✓ Branch 88 → 89 taken 7 times.
✗ Branch 88 → 90 not taken.
✓ Branch 90 → 91 taken 7 times.
✗ Branch 90 → 92 not taken.
✓ Branch 92 → 93 taken 7 times.
✗ Branch 92 → 94 not taken.
✗ Branch 175 → 176 not taken.
✗ Branch 175 → 177 not taken.
✗ Branch 180 → 181 not taken.
✗ Branch 180 → 182 not taken.
✗ Branch 184 → 185 not taken.
✗ Branch 184 → 186 not taken.
✗ Branch 188 → 189 not taken.
✗ Branch 188 → 190 not taken.
|
21 | settings.value(QStringLiteral("requiresLocalAuthorization"), |
| 183 | false) | ||
| 184 |
3/3✓ Branch 82 → 83 taken 7 times.
✓ Branch 83 → 84 taken 2 times.
✓ Branch 83 → 85 taken 5 times.
|
7 | .toBool(); |
| 185 | 7 | break; | |
| 186 | } | ||
| 187 |
1/1✓ Branch 97 → 98 taken 32 times.
|
32 | settings.endArray(); |
| 188 | 32 | return required; | |
| 189 | 32 | } | |
| 190 | |||
| 191 | 28 | QString persistedCredentialId(const QString &accountId, | |
| 192 | const QString &serverUrl, | ||
| 193 | const QString &username, | ||
| 194 | const QString &explicitId) { | ||
| 195 |
4/4✓ Branch 2 → 3 taken 28 times.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 8 taken 26 times.
✓ Branch 6 → 7 taken 2 times.
|
28 | if (!explicitId.trimmed().isEmpty()) return explicitId.trimmed(); |
| 196 |
1/1✓ Branch 8 → 9 taken 26 times.
|
26 | QSettings settings; |
| 197 |
1/1✓ Branch 13 → 14 taken 26 times.
|
26 | const int count = settings.beginReadArray(QStringLiteral("carddav/accounts")); |
| 198 | 26 | QString result; | |
| 199 | 26 | int matches = 0; | |
| 200 |
2/2✓ Branch 98 → 17 taken 7 times.
✓ Branch 98 → 99 taken 26 times.
|
33 | for (int i = 0; i < count; ++i) { |
| 201 |
1/1✓ Branch 17 → 18 taken 7 times.
|
7 | settings.setArrayIndex(i); |
| 202 |
5/6✓ Branch 18 → 19 taken 7 times.
✓ Branch 23 → 24 taken 7 times.
✓ Branch 24 → 25 taken 7 times.
✓ Branch 25 → 26 taken 7 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 7 times.
|
14 | if (settings.value(QStringLiteral("id")).toString().trimmed() != accountId.trimmed()) continue; |
| 203 |
7/21✓ Branch 40 → 41 taken 7 times.
✓ Branch 41 → 42 taken 7 times.
✓ Branch 42 → 43 taken 7 times.
✓ Branch 71 → 72 taken 7 times.
✗ Branch 71 → 73 not taken.
✓ Branch 73 → 74 taken 7 times.
✗ Branch 73 → 75 not taken.
✓ Branch 75 → 76 taken 7 times.
✗ Branch 75 → 77 not taken.
✓ Branch 77 → 78 taken 7 times.
✗ Branch 77 → 79 not taken.
✗ Branch 158 → 159 not taken.
✗ Branch 158 → 160 not taken.
✗ Branch 162 → 163 not taken.
✗ Branch 162 → 164 not taken.
✗ Branch 166 → 167 not taken.
✗ Branch 166 → 168 not taken.
✗ Branch 171 → 172 not taken.
✗ Branch 171 → 173 not taken.
✗ Branch 175 → 176 not taken.
✗ Branch 175 → 177 not taken.
|
14 | if (DavCredentials::canonicalOrigin(settings.value(QStringLiteral("serverUrl")).toString()) != |
| 204 |
4/9✓ Branch 35 → 36 taken 7 times.
✓ Branch 44 → 45 taken 7 times.
✗ Branch 44 → 55 not taken.
✓ Branch 79 → 80 taken 7 times.
✗ Branch 79 → 81 not taken.
✗ Branch 81 → 82 not taken.
✓ Branch 81 → 83 taken 7 times.
✗ Branch 179 → 180 not taken.
✗ Branch 179 → 181 not taken.
|
35 | DavCredentials::canonicalOrigin(serverUrl) || |
| 205 |
12/32✓ Branch 45 → 46 taken 7 times.
✓ Branch 50 → 51 taken 7 times.
✓ Branch 51 → 52 taken 7 times.
✓ Branch 52 → 53 taken 7 times.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 56 taken 7 times.
✓ Branch 57 → 58 taken 7 times.
✗ Branch 57 → 59 not taken.
✓ Branch 59 → 60 taken 7 times.
✗ Branch 59 → 61 not taken.
✓ Branch 61 → 62 taken 7 times.
✗ Branch 61 → 63 not taken.
✓ Branch 63 → 64 taken 7 times.
✗ Branch 63 → 65 not taken.
✓ Branch 65 → 66 taken 7 times.
✗ Branch 65 → 67 not taken.
✓ Branch 67 → 68 taken 7 times.
✗ Branch 67 → 69 not taken.
✓ Branch 69 → 70 taken 7 times.
✗ Branch 69 → 71 not taken.
✗ Branch 133 → 134 not taken.
✗ Branch 133 → 135 not taken.
✗ Branch 137 → 138 not taken.
✗ Branch 137 → 139 not taken.
✗ Branch 141 → 142 not taken.
✗ Branch 141 → 143 not taken.
✗ Branch 146 → 147 not taken.
✗ Branch 146 → 148 not taken.
✗ Branch 150 → 151 not taken.
✗ Branch 150 → 152 not taken.
✗ Branch 154 → 155 not taken.
✗ Branch 154 → 156 not taken.
|
21 | settings.value(QStringLiteral("username")).toString().trimmed() != username.trimmed()) continue; |
| 206 |
3/3✓ Branch 87 → 88 taken 7 times.
✓ Branch 88 → 89 taken 7 times.
✓ Branch 89 → 90 taken 7 times.
|
7 | result = settings.value(QStringLiteral("credentialId")).toString().trimmed(); |
| 207 | 7 | ++matches; | |
| 208 | } | ||
| 209 |
1/1✓ Branch 99 → 100 taken 26 times.
|
26 | settings.endArray(); |
| 210 |
2/2✓ Branch 100 → 101 taken 7 times.
✓ Branch 100 → 102 taken 19 times.
|
26 | return matches == 1 ? result : QString{}; |
| 211 | 26 | } | |
| 212 | |||
| 213 | } // namespace | ||
| 214 | |||
| 215 | namespace DavCredentials { | ||
| 216 | |||
| 217 | 53 | QString accountName(const QString &accountId, const QString &serverUrl, | |
| 218 | const QString &username, const QString &credentialId) { | ||
| 219 |
3/3✓ Branch 2 → 3 taken 53 times.
✓ Branch 5 → 6 taken 6 times.
✓ Branch 5 → 17 taken 47 times.
|
53 | if (!credentialId.trimmed().isEmpty()) |
| 220 |
2/2✓ Branch 10 → 11 taken 6 times.
✓ Branch 11 → 12 taken 6 times.
|
12 | return QStringLiteral("credential/%1").arg(credentialId.trimmed()); |
| 221 |
1/1✓ Branch 17 → 18 taken 47 times.
|
47 | const QString id = accountId.trimmed().isEmpty() |
| 222 |
2/4✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 26 taken 46 times.
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 71 not taken.
|
47 | ? QStringLiteral("unknown") |
| 223 |
3/3✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 23 taken 46 times.
✓ Branch 23 → 24 taken 46 times.
|
48 | : accountId.trimmed(); |
| 224 |
1/1✓ Branch 27 → 28 taken 47 times.
|
47 | const QString canonical = canonicalOrigin(serverUrl); |
| 225 | 47 | const QString origin = canonical.isEmpty() | |
| 226 |
3/13✗ Branch 30 → 31 not taken.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 47 times.
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 47 times.
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 47 times.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 86 not taken.
|
47 | ? QStringLiteral("invalid:") + serverUrl.trimmed() |
| 227 |
1/3✗ Branch 29 → 30 not taken.
✓ Branch 29 → 35 taken 47 times.
✗ Branch 34 → 36 not taken.
|
47 | : canonical; |
| 228 | const QString digest = | ||
| 229 |
2/2✓ Branch 42 → 43 taken 47 times.
✓ Branch 43 → 44 taken 47 times.
|
47 | digestMaterial(accountId, origin, username.trimmed()); |
| 230 |
1/1✓ Branch 48 → 49 taken 47 times.
|
47 | return QStringLiteral("%1/v2/%2").arg(id, digest); |
| 231 | 47 | } | |
| 232 | |||
| 233 | 12 | QObject *readPasswordAsync(QObject *owner, const QString &accountId, | |
| 234 | const QString &serverUrl, const QString &username, | ||
| 235 | PasswordCallback callback, | ||
| 236 | const QString &credentialId) { | ||
| 237 |
3/6✓ Branch 2 → 3 taken 12 times.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 12 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 12 times.
|
12 | if (!owner || !callback) return nullptr; |
| 238 |
3/6✓ Branch 9 → 10 taken 12 times.
✓ Branch 10 → 11 taken 12 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 12 times.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
|
12 | auto *store = new CredentialStore(owner); |
| 239 |
1/1✓ Branch 13 → 14 taken 12 times.
|
12 | const QPointer<CredentialStore> guard(store); |
| 240 | 18 | const auto finish = [guard, accountId, serverUrl, username, callback = std::move(callback)] | |
| 241 | (QByteArray password, QString error) { | ||
| 242 |
6/12✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 8 not taken.
✓ Branch 5 → 6 taken 6 times.
✓ Branch 6 → 7 taken 6 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 6 times.
✓ Branch 10 → 11 taken 6 times.
✗ Branch 10 → 12 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 6 times.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
|
6 | if (!guard || guard->property("completed").toBool()) return; |
| 243 |
1/1✓ Branch 16 → 17 taken 6 times.
|
6 | guard->setProperty("completed", true); |
| 244 |
2/2✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 28 taken 5 times.
|
6 | if (requiresLocalAuthorization(accountId, serverUrl, username)) { |
| 245 | 1 | password.clear(); | |
| 246 | 1 | error = QStringLiteral("DAV account requires local authorization"); | |
| 247 | } | ||
| 248 |
1/1✓ Branch 32 → 33 taken 6 times.
|
6 | callback(std::move(password), std::move(error)); |
| 249 |
2/2✓ Branch 36 → 37 taken 5 times.
✓ Branch 36 → 39 taken 1 time.
|
6 | if (guard) guard->deleteLater(); |
| 250 | 12 | }; | |
| 251 |
2/2✓ Branch 20 → 21 taken 12 times.
✓ Branch 21 → 22 taken 12 times.
|
12 | QTimer::singleShot(kKeyringTimeoutMs, store, [finish] { |
| 252 | ✗ | finish({}, QStringLiteral("DAV password lookup timed out")); | |
| 253 | ✗ | }); | |
| 254 | const QString effectiveCredential = persistedCredentialId( | ||
| 255 |
1/1✓ Branch 23 → 24 taken 12 times.
|
12 | accountId, serverUrl, username, credentialId); |
| 256 |
2/12✓ Branch 29 → 30 taken 12 times.
✓ Branch 30 → 31 taken 12 times.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 47 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 50 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
✗ Branch 57 → 58 not taken.
✗ Branch 57 → 59 not taken.
|
12 | QTimer::singleShot(0, store, [guard, accountId, serverUrl, username, |
| 257 | effectiveCredential, finish] { | ||
| 258 |
10/23✓ Branch 2 → 3 taken 9 times.
✓ Branch 4 → 5 taken 9 times.
✗ Branch 4 → 11 not taken.
✓ Branch 5 → 6 taken 9 times.
✓ Branch 7 → 8 taken 9 times.
✗ Branch 7 → 11 not taken.
✓ Branch 8 → 9 taken 9 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 9 times.
✓ Branch 13 → 14 taken 9 times.
✗ Branch 13 → 15 not taken.
✓ Branch 15 → 16 taken 9 times.
✗ Branch 15 → 17 not taken.
✓ Branch 17 → 18 taken 9 times.
✗ Branch 17 → 19 not taken.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 29 taken 9 times.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 62 not taken.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
|
9 | if (accountId.trimmed().isEmpty() || username.trimmed().isEmpty() || canonicalOrigin(serverUrl).isEmpty()) { |
| 259 | ✗ | finish({}, QStringLiteral("Invalid DAV credential identity")); | |
| 260 | ✗ | return; | |
| 261 | } | ||
| 262 |
2/3✓ Branch 29 → 30 taken 9 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 40 taken 9 times.
|
9 | if (requiresLocalAuthorization(accountId, serverUrl, username)) { |
| 263 | ✗ | finish({}, QStringLiteral("DAV account requires local authorization")); | |
| 264 | ✗ | return; | |
| 265 | } | ||
| 266 |
1/1✓ Branch 40 → 41 taken 9 times.
|
9 | const QString stable = accountName(accountId, serverUrl, username, effectiveCredential); |
| 267 |
1/1✓ Branch 41 → 42 taken 9 times.
|
9 | const QString legacy = legacyAccountName(accountId, serverUrl, username); |
| 268 |
2/2✓ Branch 50 → 51 taken 9 times.
✓ Branch 51 → 52 taken 9 times.
|
18 | guard->readPassword(QString::fromLatin1(kDavService), stable, |
| 269 |
2/8✓ Branch 47 → 48 taken 9 times.
✓ Branch 48 → 49 taken 9 times.
✗ Branch 98 → 99 not taken.
✗ Branch 98 → 100 not taken.
✗ Branch 101 → 102 not taken.
✗ Branch 101 → 103 not taken.
✗ Branch 104 → 105 not taken.
✗ Branch 104 → 106 not taken.
|
18 | [guard, stable, legacy, versioned = !effectiveCredential.isEmpty(), finish](bool found, const QByteArray &password) { |
| 270 |
6/12✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 8 not taken.
✓ Branch 5 → 6 taken 6 times.
✓ Branch 6 → 7 taken 6 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 6 times.
✓ Branch 10 → 11 taken 6 times.
✗ Branch 10 → 12 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 6 times.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
|
6 | if (!guard || guard->property("completed").toBool()) return; |
| 271 |
3/3✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 21 taken 4 times.
✓ Branch 17 → 18 taken 2 times.
|
6 | if (found) { finish(password, {}); return; } |
| 272 |
1/3✗ Branch 21 → 22 not taken.
✓ Branch 21 → 31 taken 4 times.
✗ Branch 26 → 27 not taken.
|
4 | if (versioned) { finish({}, QStringLiteral("DAV password is unavailable")); return; } |
| 273 |
1/3✗ Branch 32 → 33 not taken.
✓ Branch 32 → 42 taken 4 times.
✗ Branch 37 → 38 not taken.
|
4 | if (legacy == stable) { finish({}, QStringLiteral("DAV password is unavailable")); return; } |
| 274 | // Only the endpoint-bound legacy identity is eligible. Never consult | ||
| 275 | // an unbound account-ID key that could belong to another server. | ||
| 276 |
2/2✓ Branch 49 → 50 taken 4 times.
✓ Branch 50 → 51 taken 4 times.
|
8 | guard->readPassword(QString::fromLatin1(kDavService), legacy, |
| 277 |
2/8✓ Branch 46 → 47 taken 4 times.
✓ Branch 47 → 48 taken 4 times.
✗ Branch 92 → 93 not taken.
✗ Branch 92 → 94 not taken.
✗ Branch 95 → 96 not taken.
✗ Branch 95 → 97 not taken.
✗ Branch 98 → 99 not taken.
✗ Branch 98 → 100 not taken.
|
8 | [guard, stable, legacy, finish](bool legacyFound, const QByteArray &legacyPassword) { |
| 278 |
6/12✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 8 not taken.
✓ Branch 5 → 6 taken 4 times.
✓ Branch 6 → 7 taken 4 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
✗ Branch 10 → 12 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 4 times.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
|
4 | if (!guard || guard->property("completed").toBool()) return; |
| 279 |
3/3✓ Branch 14 → 15 taken 3 times.
✓ Branch 14 → 24 taken 1 time.
✓ Branch 19 → 20 taken 3 times.
|
7 | if (!legacyFound) { finish({}, QStringLiteral("DAV password is unavailable")); return; } |
| 280 |
2/2✓ Branch 31 → 32 taken 1 time.
✓ Branch 32 → 33 taken 1 time.
|
2 | guard->writePassword(QString::fromLatin1(kDavService), stable, legacyPassword, |
| 281 |
2/8✓ Branch 28 → 29 taken 1 time.
✓ Branch 29 → 30 taken 1 time.
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 61 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
|
2 | [guard, legacy, password = legacyPassword, finish](bool migrated, const QString &) { |
| 282 |
6/12✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 8 not taken.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 12 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 1 time.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
|
1 | if (!guard || guard->property("completed").toBool()) return; |
| 283 |
1/3✗ Branch 14 → 15 not taken.
✓ Branch 14 → 21 taken 1 time.
✗ Branch 17 → 18 not taken.
|
1 | if (!migrated) { finish(password, {}); return; } |
| 284 |
2/2✓ Branch 26 → 27 taken 1 time.
✓ Branch 27 → 28 taken 1 time.
|
2 | guard->deletePassword(QString::fromLatin1(kDavService), legacy, |
| 285 |
3/5✓ Branch 4 → 5 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
|
3 | [finish, password](bool) { finish(password, {}); }); |
| 286 | }); | ||
| 287 | }); | ||
| 288 | }); | ||
| 289 | 9 | }); | |
| 290 | 12 | return store; | |
| 291 | 12 | } | |
| 292 | |||
| 293 | 17 | QByteArray readPasswordBlocking(const QString &accountId, | |
| 294 | const QString &serverUrl, | ||
| 295 | const QString &username, | ||
| 296 | QString *error, | ||
| 297 | const QString &credentialId) { | ||
| 298 |
3/3✓ Branch 2 → 3 taken 17 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 13 taken 16 times.
|
17 | if (requiresLocalAuthorization(accountId, serverUrl, username)) { |
| 299 |
1/2✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 12 not taken.
|
1 | if (error) |
| 300 | 1 | *error = QStringLiteral("DAV account requires local authorization"); | |
| 301 | 1 | return {}; | |
| 302 | } | ||
| 303 | |||
| 304 | 16 | bool success = false; | |
| 305 | const QString effectiveCredential = persistedCredentialId( | ||
| 306 |
1/1✓ Branch 13 → 14 taken 16 times.
|
16 | accountId, serverUrl, username, credentialId); |
| 307 |
1/1✓ Branch 14 → 15 taken 16 times.
|
16 | const QString stableName = accountName(accountId, serverUrl, username, effectiveCredential); |
| 308 |
1/1✓ Branch 15 → 16 taken 16 times.
|
16 | QByteArray password = readPasswordKeyBlocking(stableName, success, error); |
| 309 |
2/2✓ Branch 16 → 17 taken 7 times.
✓ Branch 16 → 18 taken 9 times.
|
16 | if (success) |
| 310 | 7 | return password; | |
| 311 |
2/2✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 21 taken 8 times.
|
9 | if (!effectiveCredential.isEmpty()) return {}; |
| 312 | |||
| 313 |
1/1✓ Branch 21 → 22 taken 8 times.
|
8 | const QString legacyName = legacyAccountName(accountId, serverUrl, username); |
| 314 |
1/2✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 8 times.
|
8 | if (legacyName == stableName) |
| 315 | ✗ | return {}; | |
| 316 | |||
| 317 |
1/1✓ Branch 25 → 26 taken 8 times.
|
8 | password = readPasswordKeyBlocking(legacyName, success, nullptr); |
| 318 |
2/2✓ Branch 28 → 29 taken 7 times.
✓ Branch 28 → 30 taken 1 time.
|
8 | if (!success) |
| 319 | 7 | return {}; | |
| 320 | |||
| 321 | 1 | QString migrationError; | |
| 322 |
2/3✓ Branch 30 → 31 taken 1 time.
✓ Branch 31 → 32 taken 1 time.
✗ Branch 31 → 33 not taken.
|
1 | if (writePasswordKeyBlocking(stableName, password, &migrationError)) |
| 323 |
1/1✓ Branch 32 → 33 taken 1 time.
|
1 | deletePasswordKeyBlocking(legacyName); |
| 324 |
1/2✓ Branch 33 → 34 taken 1 time.
✗ Branch 33 → 35 not taken.
|
1 | if (error) |
| 325 | 1 | error->clear(); | |
| 326 | 1 | return password; | |
| 327 | 16 | } | |
| 328 | |||
| 329 | 7 | bool writePasswordBlocking(const QString &accountId, const QString &serverUrl, | |
| 330 | const QString &username, | ||
| 331 | const QByteArray &password, | ||
| 332 | QString *error, | ||
| 333 | const QString &credentialId) { | ||
| 334 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 6 times.
|
7 | if (password.isEmpty()) |
| 335 | 1 | return true; | |
| 336 |
1/1✓ Branch 5 → 6 taken 6 times.
|
6 | const QString boundName = accountName(accountId, serverUrl, username, credentialId); |
| 337 |
3/3✓ Branch 6 → 7 taken 6 times.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 5 times.
|
6 | if (!writePasswordKeyBlocking(boundName, password, error)) |
| 338 | 1 | return false; | |
| 339 | |||
| 340 | // SEC-2026-07-21-24: Delete ALL legacy key forms after an explicit write, | ||
| 341 | // not just the ID-only key. The endpoint-bound legacy key may still hold | ||
| 342 | // an OLD password (e.g. after re-authorization against a changed endpoint); | ||
| 343 | // leaving it orphaned creates a valid stale credential in the keyring. | ||
| 344 | const QString legacyName = | ||
| 345 |
1/1✓ Branch 9 → 10 taken 5 times.
|
5 | legacyAccountName(accountId, serverUrl, username); |
| 346 |
1/2✓ Branch 11 → 12 taken 5 times.
✗ Branch 11 → 17 not taken.
|
5 | if (legacyName != boundName) { |
| 347 | 5 | bool legacyExists = false; | |
| 348 |
1/1✓ Branch 12 → 13 taken 5 times.
|
5 | readPasswordKeyBlocking(legacyName, legacyExists, nullptr); |
| 349 |
1/2✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 5 times.
|
5 | if (legacyExists) |
| 350 | ✗ | deletePasswordKeyBlocking(legacyName); | |
| 351 | } | ||
| 352 | |||
| 353 |
1/1✓ Branch 17 → 18 taken 5 times.
|
5 | const QString legacyStableName = accountId.trimmed(); |
| 354 |
3/6✓ Branch 19 → 20 taken 5 times.
✗ Branch 19 → 25 not taken.
✓ Branch 21 → 22 taken 5 times.
✗ Branch 21 → 25 not taken.
✓ Branch 26 → 27 taken 5 times.
✗ Branch 26 → 32 not taken.
|
10 | if (!legacyStableName.isEmpty() && legacyStableName != boundName && |
| 355 |
1/2✓ Branch 23 → 24 taken 5 times.
✗ Branch 23 → 25 not taken.
|
5 | legacyStableName != legacyName) { |
| 356 | 5 | bool legacyStableExists = false; | |
| 357 |
1/1✓ Branch 27 → 28 taken 5 times.
|
5 | readPasswordKeyBlocking(legacyStableName, legacyStableExists, nullptr); |
| 358 |
1/2✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 5 times.
|
5 | if (legacyStableExists) |
| 359 | ✗ | deletePasswordKeyBlocking(legacyStableName); | |
| 360 | } | ||
| 361 | 5 | return true; | |
| 362 | 6 | } | |
| 363 | |||
| 364 | 3 | bool deletePasswordBlocking(const QString &accountId, const QString &serverUrl, | |
| 365 | const QString &username, | ||
| 366 | QString *error, | ||
| 367 | const QString &credentialId) { | ||
| 368 |
1/1✓ Branch 2 → 3 taken 3 times.
|
3 | const QString stableName = accountName(accountId, serverUrl, username, credentialId); |
| 369 |
1/1✓ Branch 3 → 4 taken 3 times.
|
3 | bool success = deletePasswordKeyBlocking(stableName); |
| 370 | |||
| 371 | const QString legacyName = | ||
| 372 |
1/1✓ Branch 4 → 5 taken 3 times.
|
3 | legacyAccountName(accountId, serverUrl, username); |
| 373 |
1/2✓ Branch 6 → 7 taken 3 times.
✗ Branch 6 → 17 not taken.
|
3 | if (legacyName != stableName) { |
| 374 | 3 | bool legacyExists = false; | |
| 375 |
1/1✓ Branch 7 → 8 taken 3 times.
|
3 | readPasswordKeyBlocking(legacyName, legacyExists, nullptr); |
| 376 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 16 taken 3 times.
|
3 | if (legacyExists) |
| 377 | ✗ | success = deletePasswordKeyBlocking(legacyName) || success; | |
| 378 | } | ||
| 379 | |||
| 380 |
1/1✓ Branch 17 → 18 taken 3 times.
|
3 | const QString legacyStableName = accountId.trimmed(); |
| 381 |
3/6✓ Branch 19 → 20 taken 3 times.
✗ Branch 19 → 25 not taken.
✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 25 not taken.
✓ Branch 26 → 27 taken 3 times.
✗ Branch 26 → 37 not taken.
|
6 | if (!legacyStableName.isEmpty() && legacyStableName != stableName && |
| 382 |
1/2✓ Branch 23 → 24 taken 3 times.
✗ Branch 23 → 25 not taken.
|
3 | legacyStableName != legacyName) { |
| 383 | 3 | bool legacyStableExists = false; | |
| 384 |
1/1✓ Branch 27 → 28 taken 3 times.
|
3 | readPasswordKeyBlocking(legacyStableName, legacyStableExists, nullptr); |
| 385 |
1/2✗ Branch 29 → 30 not taken.
✓ Branch 29 → 36 taken 3 times.
|
3 | if (legacyStableExists) |
| 386 | ✗ | success = deletePasswordKeyBlocking(legacyStableName) || success; | |
| 387 | } | ||
| 388 | |||
| 389 |
3/4✓ Branch 37 → 38 taken 1 time.
✓ Branch 37 → 46 taken 2 times.
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 46 not taken.
|
3 | if (!success && error) |
| 390 | 1 | *error = QStringLiteral("Could not delete DAV password from keyring"); | |
| 391 | 3 | return success; | |
| 392 | 3 | } | |
| 393 | |||
| 394 | 4 | QObject *deletePasswordAsync(QObject *owner, const QString &accountId, | |
| 395 | const QString &serverUrl, const QString &username, | ||
| 396 | std::function<void(bool)> callback, | ||
| 397 | const QString &credentialId) { | ||
| 398 | QStringList names{accountName(accountId, serverUrl, username, credentialId), | ||
| 399 |
2/4✓ Branch 6 → 7 taken 8 times.
✓ Branch 6 → 8 taken 4 times.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
|
12 | legacyAccountName(accountId, serverUrl, username)}; |
| 400 |
4/5✓ Branch 8 → 9 taken 4 times.
✓ Branch 11 → 12 taken 4 times.
✗ Branch 11 → 16 not taken.
✓ Branch 12 → 13 taken 4 times.
✓ Branch 13 → 14 taken 4 times.
|
4 | if (!accountId.trimmed().isEmpty()) names.append(accountId.trimmed()); |
| 401 |
1/1✓ Branch 16 → 17 taken 4 times.
|
4 | names.removeDuplicates(); |
| 402 | 4 | QList<CredentialStore::SecretKey> keys; | |
| 403 |
4/4✓ Branch 17 → 18 taken 4 times.
✓ Branch 18 → 19 taken 4 times.
✓ Branch 28 → 20 taken 12 times.
✓ Branch 28 → 29 taken 4 times.
|
16 | for (const auto &name : names) |
| 404 | 24 | keys.append({QString::fromLatin1(kDavService), name}); | |
| 405 |
1/1✓ Branch 31 → 32 taken 4 times.
|
8 | return CredentialStore::deletePasswordsAsync(owner, keys, std::move(callback)); |
| 406 | 32 | } | |
| 407 | |||
| 408 | } // namespace DavCredentials | ||
| 409 |