MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - data - DavCredentials.cpp (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 87.9 % 141 124
Test Date: 2026-06-21 21:10:19 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 57.1 % 182 104

             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
        

Generated by: LCOV version 2.0-1