presentation/QuickAccountSettings.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "QuickAccountSettings.h" | ||
| 2 | #include "QuickCalendarSync.h" | ||
| 3 | |||
| 4 | #include "ComposeDraftStore.h" | ||
| 5 | #include "DavAccountSettings.h" | ||
| 6 | #include "QuickPimBackend.h" | ||
| 7 | #include "QuickPreferences.h" | ||
| 8 | #include "WorkspaceState.h" | ||
| 9 | #include "app/core/AccountSession.h" | ||
| 10 | #include "app/core/CalendarSyncCoordinator.h" | ||
| 11 | #include "data/CalendarStore.h" | ||
| 12 | #include "app/core/ContactSyncCoordinator.h" | ||
| 13 | #include "app/core/MailSessionCore.h" | ||
| 14 | #include "controller/UndoManager.h" | ||
| 15 | #include "data/AccountConfig.h" | ||
| 16 | #include "data/CredentialStore.h" | ||
| 17 | #include "data/DavCredentials.h" | ||
| 18 | #include "data/MailCache.h" | ||
| 19 | #include "quick/ExternalWhitelistModel.h" | ||
| 20 | #include "ui/ExternalContentPolicy.h" | ||
| 21 | #include <QDir> | ||
| 22 | #include <QLoggingCategory> | ||
| 23 | #include <QPointer> | ||
| 24 | #include <QSettings> | ||
| 25 | #include <QTimer> | ||
| 26 | #include <QUuid> | ||
| 27 | |||
| 28 | ✗ | Q_LOGGING_CATEGORY(lcAccountSettings, "mailjd.quick.accountsettings") | |
| 29 | |||
| 30 | 795 | QuickAccountSettings::QuickAccountSettings(MailSessionCore &session, WorkspaceState &workspace, | |
| 31 | QuickPreferences &preferences, QuickPimBackend &pim, | ||
| 32 | const QString &profileDirectory, Deps deps, | ||
| 33 | 795 | QObject *parent) | |
| 34 | 795 | : QObject(parent), m_session(session), m_workspace(workspace), m_preferences(preferences), | |
| 35 | 795 | m_pim(pim), m_profileDirectory(profileDirectory), m_deps(std::move(deps)), | |
| 36 |
4/7✓ Branch 5 → 6 taken 795 times.
✓ Branch 7 → 8 taken 795 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 795 times.
✓ Branch 16 → 17 taken 795 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
|
1590 | m_whitelist(new ExternalWhitelistModel(*session.cache(), this)), m_serviceCheck(session.accounts(), this), |
| 37 |
5/5✓ Branch 10 → 11 taken 795 times.
✓ Branch 11 → 12 taken 795 times.
✓ Branch 12 → 13 taken 795 times.
✓ Branch 13 → 14 taken 795 times.
✓ Branch 18 → 19 taken 795 times.
|
2385 | m_setup(this, session.accounts(), this) { |
| 38 |
1/1✓ Branch 19 → 20 taken 795 times.
|
795 | connect(this, &QuickAccountSettings::davSettingsChanged, this, &QuickAccountSettings::accountOverviewChanged); |
| 39 |
1/1✓ Branch 22 → 23 taken 795 times.
|
795 | connect(session.accounts(), &AccountSession::availableAccountsChanged, this, |
| 40 | 795 | &QuickAccountSettings::accountOverviewChanged); | |
| 41 |
1/1✓ Branch 25 → 26 taken 795 times.
|
795 | connect(session.accounts(), &AccountSession::configurationChanged, this, |
| 42 | 795 | &QuickAccountSettings::accountOverviewChanged); | |
| 43 |
1/2✗ Branch 28 → 29 not taken.
✓ Branch 28 → 32 taken 795 times.
|
795 | if (auto *sync = m_pim.sync()) |
| 44 | ✗ | connect(sync, &QuickCalendarSync::changed, this, &QuickAccountSettings::accountOverviewChanged); | |
| 45 | // D-21: a calendar with appointments not uploaded yet is not removed. | ||
| 46 | 795 | m_davDiscovery.setPendingCounter([&session](const QString &account, const QString &path, bool calendar) { | |
| 47 | ✗ | if (!calendar || !session.calendarStore()) return 0; | |
| 48 | ✗ | return int(session.calendarStore()->outboxUids(account, path).size()); | |
| 49 | }); | ||
| 50 | 795 | } | |
| 51 | |||
| 52 | 2474 | QVariantList QuickAccountSettings::accountOverview() const { | |
| 53 | 2474 | QList<AccountOverview::Mail> mails; | |
| 54 |
4/4✓ Branch 3 → 4 taken 2474 times.
✓ Branch 4 → 5 taken 2474 times.
✓ Branch 73 → 6 taken 2503 times.
✓ Branch 73 → 74 taken 2474 times.
|
4977 | for (const auto &value : configuredAccounts()) { |
| 55 |
1/1✓ Branch 7 → 8 taken 2503 times.
|
2503 | const auto row = value.toMap(); |
| 56 |
2/2✓ Branch 12 → 13 taken 2503 times.
✓ Branch 18 → 19 taken 2503 times.
|
12515 | mails.append({row.value(QStringLiteral("id")).toString(), row.value(QStringLiteral("name")).toString(), |
| 57 |
2/2✓ Branch 24 → 25 taken 2503 times.
✓ Branch 30 → 31 taken 2503 times.
|
7509 | row.value(QStringLiteral("email")).toString(), row.value(QStringLiteral("imapHost")).toString(), |
| 58 |
2/2✓ Branch 36 → 37 taken 2503 times.
✓ Branch 42 → 43 taken 2503 times.
|
7509 | row.value(QStringLiteral("imapUsername")).toString(), row.value(QStringLiteral("sieveMode")).toString()}); |
| 59 | 4977 | } | |
| 60 | 2474 | QHash<QString, QVariantMap> syncStates; | |
| 61 |
1/2✓ Branch 76 → 77 taken 2474 times.
✗ Branch 76 → 104 not taken.
|
2474 | if (m_pim.sync()) |
| 62 |
5/5✓ Branch 78 → 79 taken 2474 times.
✓ Branch 79 → 80 taken 2474 times.
✓ Branch 80 → 81 taken 2474 times.
✓ Branch 101 → 82 taken 624 times.
✓ Branch 101 → 102 taken 2474 times.
|
3098 | for (const auto &value : m_pim.sync()->accounts()) |
| 63 |
5/5✓ Branch 83 → 84 taken 624 times.
✓ Branch 84 → 85 taken 624 times.
✓ Branch 89 → 90 taken 624 times.
✓ Branch 90 → 91 taken 624 times.
✓ Branch 91 → 92 taken 624 times.
|
3722 | syncStates.insert(value.toMap().value(QStringLiteral("accountId")).toString(), value.toMap()); |
| 64 |
1/1✓ Branch 104 → 105 taken 2474 times.
|
2474 | const auto configs = DavAccountSettings::calendarConfigs(); |
| 65 | 2474 | QList<AccountOverview::Dav> davs; | |
| 66 |
5/5✓ Branch 105 → 106 taken 2474 times.
✓ Branch 106 → 107 taken 2474 times.
✓ Branch 107 → 108 taken 2474 times.
✓ Branch 166 → 109 taken 624 times.
✓ Branch 166 → 167 taken 2474 times.
|
3098 | for (const auto &account : DavAccountSettings::accounts()) { |
| 67 | 624 | AccountOverview::Dav dav; | |
| 68 | 624 | dav.id = account.id; | |
| 69 | 624 | dav.serverUrl = account.serverUrl; | |
| 70 | 624 | dav.username = account.username; | |
| 71 | 624 | dav.mailAccountId = account.mailAccountId; | |
| 72 | 624 | dav.books = int(account.selectedBooks.size()); | |
| 73 |
2/2✓ Branch 124 → 117 taken 72 times.
✓ Branch 124 → 125 taken 624 times.
|
696 | for (const auto &config : configs) |
| 74 |
1/2✓ Branch 119 → 120 taken 72 times.
✗ Branch 119 → 122 not taken.
|
72 | if (config.accountId == account.id) dav.calendars = int(config.selectedCalendars.size()); |
| 75 | 624 | const auto state = syncStates.value(account.id); | |
| 76 |
2/2✓ Branch 130 → 131 taken 624 times.
✓ Branch 131 → 132 taken 624 times.
|
1248 | dav.state = state.value(QStringLiteral("state")).toString(); |
| 77 |
2/2✓ Branch 142 → 143 taken 624 times.
✓ Branch 143 → 144 taken 624 times.
|
1248 | dav.error = state.value(QStringLiteral("error")).toString(); |
| 78 |
2/2✓ Branch 154 → 155 taken 624 times.
✓ Branch 155 → 156 taken 624 times.
|
1248 | dav.needsCredentials = state.value(QStringLiteral("needsCredentials")).toBool(); |
| 79 | 624 | dav.requiresAuthorization = account.requiresLocalAuthorization; | |
| 80 |
1/1✓ Branch 160 → 161 taken 624 times.
|
624 | dav.fresh = m_serverHints.freshCount(account.id); |
| 81 |
1/1✓ Branch 161 → 162 taken 624 times.
|
624 | davs.append(dav); |
| 82 | 3098 | } | |
| 83 |
3/4✓ Branch 169 → 170 taken 2474 times.
✗ Branch 169 → 171 not taken.
✓ Branch 170 → 172 taken 2474 times.
✓ Branch 172 → 173 taken 2474 times.
|
4948 | return AccountOverview::build(mails, davs, m_deps.activeState ? m_deps.activeState() : AccountOverview::Active{}); |
| 84 | 17492 | } | |
| 85 | |||
| 86 | ✗ | bool QuickAccountSettings::linkDavAccount(const QString &davAccountId, const QString &mailAccountId) { | |
| 87 | ✗ | if (!DavAccountSettings::setMailAccountLink(davAccountId, mailAccountId)) return false; | |
| 88 | ✗ | emit davSettingsChanged(); | |
| 89 | ✗ | return true; | |
| 90 | } | ||
| 91 | |||
| 92 | // ─── D3 DAV group: carddav/accounts + caldav/syncIntervalMin ─── | ||
| 93 | // The CalendarSyncCoordinator reads the same QSettings keys, so these are | ||
| 94 | // live settings, not sync-payload metadata. | ||
| 95 | |||
| 96 | namespace { | ||
| 97 | using DavAccountRow = DavAccountSettings::Account; | ||
| 98 | 21 | QList<DavAccountRow> readDavAccounts() { return DavAccountSettings::accounts(); } | |
| 99 | } | ||
| 100 | |||
| 101 | // Account connection-test secrets, settings sync and reading-mode/DAV settings fan-out. | ||
| 102 | // Formerly QuickMailBackend::wireSettingsSync(); called by the backend at the | ||
| 103 | // same constructor position (Sprint 91 rule 4). | ||
| 104 | 795 | void QuickAccountSettings::connectSettingsSync() { | |
| 105 | 795 | MailSessionCore &session = m_session; | |
| 106 | 795 | WorkspaceState &workspace = m_workspace; | |
| 107 |
1/1✓ Branch 7 → 8 taken 795 times.
|
795 | m_connectionTest.setSecretReader( |
| 108 |
2/2✓ Branch 2 → 3 taken 795 times.
✓ Branch 6 → 7 taken 795 times.
|
2385 | [directory = QDir(m_profileDirectory).filePath(QStringLiteral("accounts"))]( |
| 109 | QObject *owner, const QString &accountId, | ||
| 110 | std::function<void(bool, const QByteArray &)> callback) { | ||
| 111 | ✗ | QString identity; | |
| 112 | ✗ | for (const auto &account : AccountConfigLoader::loadAll( | |
| 113 | ✗ | directory, AccountConfigLoader::LoadMode::MetadataOnly)) { | |
| 114 | ✗ | if (account.id == accountId) { | |
| 115 | ✗ | identity = AccountConfigLoader::imapCredentialName(account); | |
| 116 | ✗ | break; | |
| 117 | } | ||
| 118 | ✗ | } | |
| 119 | ✗ | if (identity.isEmpty()) { | |
| 120 | ✗ | QTimer::singleShot(0, owner, [callback = std::move(callback)] { | |
| 121 | ✗ | callback(false, {}); | |
| 122 | ✗ | }); | |
| 123 | ✗ | return; | |
| 124 | } | ||
| 125 | ✗ | auto *store = new CredentialStore(owner); | |
| 126 | ✗ | store->readPassword(QStringLiteral("imap"), identity, | |
| 127 | ✗ | [store, callback = std::move(callback)](bool success, const QByteArray &password) { | |
| 128 | ✗ | store->deleteLater(); | |
| 129 | ✗ | callback(success, password); | |
| 130 | ✗ | }); | |
| 131 | ✗ | }); | |
| 132 |
2/2✓ Branch 15 → 16 taken 794 times.
✓ Branch 15 → 27 taken 1 time.
|
795 | if (auto *sync = session.settingsSync()) { |
| 133 |
1/1✓ Branch 16 → 17 taken 794 times.
|
794 | connect(this, &QuickAccountSettings::davSettingsChanged, sync, &SettingsSyncSession::localSettingsChanged); |
| 134 |
1/1✓ Branch 18 → 19 taken 794 times.
|
794 | connect(this, &QuickAccountSettings::defaultReadingModeChanged, sync, &SettingsSyncSession::localSettingsChanged); |
| 135 |
1/1✓ Branch 20 → 21 taken 794 times.
|
794 | connect(&m_pim, &QuickPimBackend::calendarChoicesChanged, sync, &SettingsSyncSession::localSettingsChanged); |
| 136 |
1/1✓ Branch 22 → 23 taken 794 times.
|
794 | connect(&m_preferences, &QuickPreferences::changed, sync, &SettingsSyncSession::localSettingsChanged); |
| 137 |
1/1✓ Branch 24 → 25 taken 794 times.
|
794 | connect(sync, &SettingsSyncSession::settingsApplied, this, [this](const QStringList &) { |
| 138 | ✗ | emit m_preferences.changed(); | |
| 139 | ✗ | emit defaultReadingModeChanged(); | |
| 140 | ✗ | emit davSettingsChanged(); | |
| 141 | ✗ | m_pim.calendarModel().reload(); | |
| 142 | ✗ | m_pim.taskModel().reload(); | |
| 143 | ✗ | m_deps.loadLocalFolders(); | |
| 144 | ✗ | }); | |
| 145 | } | ||
| 146 |
1/1✓ Branch 27 → 28 taken 795 times.
|
795 | connect(this, &QuickAccountSettings::defaultReadingModeChanged, &workspace, [&workspace] { |
| 147 |
2/2✓ Branch 2 → 3 taken 2 times.
✓ Branch 3 → 4 taken 2 times.
|
2 | workspace.setConfiguredReadingMode(WorkspaceState::configuredReadingMode()); |
| 148 | 2 | }); | |
| 149 |
1/1✓ Branch 29 → 30 taken 795 times.
|
795 | connect(this, &QuickAccountSettings::davSettingsChanged, &m_davDiscovery, &DavCollectionDiscovery::invalidateConfiguration); |
| 150 |
1/1✓ Branch 31 → 32 taken 795 times.
|
795 | connect(this, &QuickAccountSettings::davSettingsChanged, &m_preferences, &QuickPreferences::contactIntervalChanged); |
| 151 | 795 | } | |
| 152 | |||
| 153 | // DAV discovery results re-arm the calendar sync. | ||
| 154 | // Formerly QuickMailBackend::wireDavDiscovery(); same constructor position. | ||
| 155 | 795 | void QuickAccountSettings::connectDavDiscovery() { | |
| 156 | 2 | const auto applied = [this](const QString &id) { | |
| 157 | 2 | emit davSettingsChanged(); | |
| 158 |
4/5✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 22 → 7 taken 2 times.
✗ Branch 22 → 23 not taken.
|
2 | for (const auto &config : DavAccountSettings::calendarConfigs()) |
| 159 |
4/8✓ Branch 9 → 10 taken 2 times.
✗ Branch 9 → 15 not taken.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 15 not taken.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 15 not taken.
✓ Branch 16 → 17 taken 2 times.
✗ Branch 16 → 20 not taken.
|
2 | if (config.accountId == id && !config.selectedCalendars.isEmpty() && m_session.calendarSync()) { |
| 160 |
1/1✓ Branch 18 → 19 taken 2 times.
|
2 | QTimer::singleShot(0, m_session.calendarSync(), &CalendarSyncCoordinator::triggerSync); |
| 161 | 2 | break; | |
| 162 | 2 | } | |
| 163 | 2 | }; | |
| 164 |
1/1✓ Branch 2 → 3 taken 795 times.
|
795 | connect(&m_davDiscovery, &DavCollectionDiscovery::applied, this, applied); |
| 165 | // S100.4.8: a collection added from a sidebar hint is an applied selection. | ||
| 166 |
1/1✓ Branch 4 → 5 taken 795 times.
|
795 | connect(&m_serverHints, &DavServerHints::added, this, applied); |
| 167 |
1/1✓ Branch 6 → 7 taken 795 times.
|
795 | connect(&m_davDiscovery, &DavCollectionDiscovery::stateChanged, &m_serverHints, &DavServerHints::refresh); |
| 168 |
1/1✓ Branch 8 → 9 taken 795 times.
|
795 | connect(this, &QuickAccountSettings::davSettingsChanged, &m_serverHints, &DavServerHints::refresh); |
| 169 |
1/1✓ Branch 10 → 11 taken 795 times.
|
795 | connect(&m_serverHints, &DavServerHints::changed, this, &QuickAccountSettings::accountOverviewChanged); |
| 170 | // D-22: every calendar sync already lists the account's calendars. | ||
| 171 |
2/2✓ Branch 13 → 14 taken 794 times.
✓ Branch 13 → 17 taken 1 time.
|
795 | if (auto *calendars = m_session.calendarSync()) |
| 172 | 794 | connect(calendars, &CalendarSyncCoordinator::collectionsDiscovered, this, | |
| 173 |
1/1✓ Branch 14 → 15 taken 794 times.
|
1588 | [this](const QString &accountId, const QList<CalendarInfo> &found) { |
| 174 | ✗ | QList<DavCollectionDiscovery::Item> items; | |
| 175 | ✗ | for (const auto &calendar : found) | |
| 176 | ✗ | items.append({calendar.path, calendar.displayName, calendar.color, calendar.supportedComponents, | |
| 177 | ✗ | calendar.readOnly, calendar.push.capability == DavPushCapability::Supported}); | |
| 178 | ✗ | m_serverHints.recordCalendars(accountId, items); | |
| 179 | ✗ | }); | |
| 180 | 795 | } | |
| 181 | |||
| 182 | namespace { | ||
| 183 | // Sieve fields of the account form (S95.1.3). A form without them (older | ||
| 184 | // callers) keeps the stored rule settings instead of resetting them. | ||
| 185 | 11 | SieveConfig sieveConfigFrom(const QVariantMap &fields, const std::optional<AccountConfig> &stored) { | |
| 186 |
3/3✓ Branch 5 → 6 taken 11 times.
✓ Branch 8 → 9 taken 9 times.
✓ Branch 8 → 30 taken 2 times.
|
11 | if (!fields.contains(QStringLiteral("sieveMode"))) |
| 187 |
8/8✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 13 taken 7 times.
✓ Branch 23 → 24 taken 7 times.
✓ Branch 23 → 25 taken 2 times.
✓ Branch 25 → 26 taken 7 times.
✓ Branch 25 → 27 taken 2 times.
✓ Branch 27 → 28 taken 7 times.
✓ Branch 27 → 29 taken 2 times.
|
30 | return stored ? stored->sieve : SieveConfig{}; |
| 188 |
1/1✓ Branch 30 → 31 taken 2 times.
|
2 | SieveConfig sieve; |
| 189 |
2/2✓ Branch 35 → 36 taken 2 times.
✓ Branch 36 → 37 taken 2 times.
|
4 | sieve.mode = fields.value(QStringLiteral("sieveMode")).toString(); |
| 190 |
3/3✓ Branch 47 → 48 taken 2 times.
✓ Branch 48 → 49 taken 2 times.
✓ Branch 49 → 50 taken 2 times.
|
4 | sieve.host = fields.value(QStringLiteral("sieveHost")).toString().trimmed(); |
| 191 | // S106-250: the same whole-number range check as the mail ports; out of | ||
| 192 | // range reads as 0, which validate() refuses for a manual rule server | ||
| 193 | // (65600 used to wrap to 64). | ||
| 194 | 2 | bool portOk = false; | |
| 195 |
2/2✓ Branch 61 → 62 taken 2 times.
✓ Branch 62 → 63 taken 2 times.
|
4 | const double port = fields.value(QStringLiteral("sievePort"), 4190).toDouble(&portOk); |
| 196 |
5/10✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 74 not taken.
✓ Branch 69 → 70 taken 2 times.
✗ Branch 69 → 74 not taken.
✓ Branch 70 → 71 taken 2 times.
✗ Branch 70 → 74 not taken.
✓ Branch 71 → 72 taken 2 times.
✗ Branch 71 → 74 not taken.
✓ Branch 72 → 73 taken 2 times.
✗ Branch 72 → 74 not taken.
|
2 | sieve.port = portOk && std::isfinite(port) && port >= 1 && port <= 65535 && std::floor(port) == port |
| 197 | ? static_cast<quint16>(port) : 0; | ||
| 198 |
2/2✓ Branch 82 → 83 taken 2 times.
✓ Branch 83 → 84 taken 2 times.
|
4 | sieve.security = fields.value(QStringLiteral("sieveSecurity"), QStringLiteral("starttls")).toString(); |
| 199 |
2/2✓ Branch 99 → 100 taken 2 times.
✓ Branch 100 → 101 taken 2 times.
|
4 | sieve.login = fields.value(QStringLiteral("sieveLogin"), QStringLiteral("imap")).toString(); |
| 200 |
3/3✓ Branch 113 → 114 taken 2 times.
✓ Branch 114 → 115 taken 2 times.
✓ Branch 115 → 116 taken 2 times.
|
4 | sieve.username = fields.value(QStringLiteral("sieveUsername")).toString().trimmed(); |
| 201 |
3/3✓ Branch 127 → 128 taken 2 times.
✓ Branch 128 → 129 taken 2 times.
✓ Branch 129 → 130 taken 2 times.
|
4 | sieve.password = fields.value(QStringLiteral("sievePassword")).toString().toUtf8(); |
| 202 |
3/6✓ Branch 138 → 139 taken 2 times.
✗ Branch 138 → 142 not taken.
✗ Branch 140 → 141 not taken.
✓ Branch 140 → 142 taken 2 times.
✗ Branch 143 → 144 not taken.
✓ Branch 143 → 146 taken 2 times.
|
2 | if (stored && sieve.usesSeparateLogin()) |
| 203 | ✗ | sieve.credentialId = stored->sieve.credentialId; | |
| 204 | 2 | return sieve; | |
| 205 | 2 | } | |
| 206 | } // namespace | ||
| 207 | |||
| 208 | 2527 | QVariantList QuickAccountSettings::configuredAccounts() const { | |
| 209 | 2527 | return m_session.accounts()->configurationMetadata(); | |
| 210 | } | ||
| 211 | |||
| 212 | 23 | QVariantMap QuickAccountSettings::saveAccount(const QVariantMap &fields) { | |
| 213 |
2/2✓ Branch 6 → 7 taken 23 times.
✓ Branch 7 → 8 taken 23 times.
|
46 | const QString requestedId = fields.value(QStringLiteral("id")).toString(); |
| 214 |
2/2✓ Branch 13 → 14 taken 3 times.
✓ Branch 13 → 44 taken 20 times.
|
23 | if (!requestedId.isEmpty()) { |
| 215 | 3 | const auto accounts = configuredAccounts(); | |
| 216 |
1/1✓ Branch 17 → 18 taken 3 times.
|
3 | const auto matches = std::count_if(accounts.cbegin(), accounts.cend(), |
| 217 | 3 | [&requestedId](const QVariant &row) { | |
| 218 |
3/3✓ Branch 2 → 3 taken 3 times.
✓ Branch 7 → 8 taken 3 times.
✓ Branch 8 → 9 taken 3 times.
|
6 | return row.toMap().value(QStringLiteral("id")).toString() == requestedId; |
| 219 | }); | ||
| 220 | // A stale editor must not recreate a removed credential identity while | ||
| 221 | // its asynchronous keyring cleanup is still in flight. | ||
| 222 |
2/2✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 38 taken 2 times.
|
3 | if (matches != 1) |
| 223 | 2 | return {{QStringLiteral("ok"), false}, | |
| 224 |
3/5✓ Branch 26 → 27 taken 1 time.
✓ Branch 30 → 31 taken 2 times.
✓ Branch 30 → 32 taken 1 time.
✗ Branch 398 → 399 not taken.
✗ Branch 398 → 400 not taken.
|
5 | {QStringLiteral("error"), tr("The account no longer exists.")}}; |
| 225 | 3 | } | |
| 226 |
5/7✓ Branch 44 → 45 taken 22 times.
✓ Branch 46 → 47 taken 5 times.
✓ Branch 46 → 50 taken 17 times.
✓ Branch 49 → 50 taken 1 time.
✓ Branch 49 → 51 taken 4 times.
✗ Branch 430 → 431 not taken.
✗ Branch 430 → 432 not taken.
|
49 | const bool changesActive = m_deps.activeAccountId().isEmpty() || |
| 227 |
4/7✓ Branch 47 → 48 taken 5 times.
✓ Branch 52 → 53 taken 5 times.
✓ Branch 52 → 54 taken 17 times.
✓ Branch 54 → 55 taken 22 times.
✗ Branch 54 → 56 not taken.
✗ Branch 426 → 427 not taken.
✗ Branch 426 → 428 not taken.
|
27 | requestedId == m_deps.activeAccountId(); |
| 228 |
6/9✓ Branch 56 → 57 taken 18 times.
✓ Branch 56 → 63 taken 4 times.
✓ Branch 57 → 58 taken 18 times.
✓ Branch 58 → 59 taken 18 times.
✗ Branch 58 → 62 not taken.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 63 taken 18 times.
✗ Branch 64 → 65 not taken.
✓ Branch 64 → 84 taken 22 times.
|
22 | if (changesActive && (m_deps.sendOrActionPending() || m_session.undo()->busy())) |
| 229 | ✗ | return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), | |
| 230 | ✗ | tr("Wait for the current send or action to finish before changing accounts.")}}; | |
| 231 |
4/6✓ Branch 84 → 85 taken 18 times.
✓ Branch 84 → 89 taken 4 times.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 18 times.
✗ Branch 90 → 91 not taken.
✓ Branch 90 → 110 taken 22 times.
|
22 | if (changesActive && m_session.accounts()->resolvingCredentials()) |
| 232 | ✗ | return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), | |
| 233 | ✗ | tr("Wait for credential loading to finish before saving this account.")}}; | |
| 234 | 44 | const auto port = [&fields](const QString &key, int fallback) -> std::optional<quint16> { | |
| 235 | 44 | bool ok = false; | |
| 236 |
2/2✓ Branch 3 → 4 taken 44 times.
✓ Branch 4 → 5 taken 44 times.
|
44 | const double value = fields.value(key, fallback).toDouble(&ok); |
| 237 |
12/12✓ Branch 7 → 8 taken 42 times.
✓ Branch 7 → 13 taken 2 times.
✓ Branch 9 → 10 taken 40 times.
✓ Branch 9 → 13 taken 2 times.
✓ Branch 10 → 11 taken 36 times.
✓ Branch 10 → 13 taken 4 times.
✓ Branch 11 → 12 taken 32 times.
✓ Branch 11 → 13 taken 4 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 30 times.
✓ Branch 15 → 16 taken 14 times.
✓ Branch 15 → 19 taken 30 times.
|
44 | if (!ok || !std::isfinite(value) || value < 1 || value > 65535 || std::floor(value) != value) |
| 238 | 14 | return std::nullopt; | |
| 239 | 30 | return static_cast<quint16>(value); | |
| 240 | 22 | }; | |
| 241 |
1/1✓ Branch 113 → 114 taken 22 times.
|
22 | const auto imapPort = port(QStringLiteral("imapPort"), 993); |
| 242 |
1/1✓ Branch 119 → 120 taken 22 times.
|
22 | const auto smtpPort = port(QStringLiteral("smtpPort"), 587); |
| 243 |
6/6✓ Branch 123 → 124 taken 15 times.
✓ Branch 123 → 126 taken 7 times.
✓ Branch 125 → 126 taken 7 times.
✓ Branch 125 → 127 taken 8 times.
✓ Branch 128 → 129 taken 14 times.
✓ Branch 128 → 148 taken 8 times.
|
22 | if (!imapPort || !smtpPort) |
| 244 | 56 | return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), | |
| 245 |
3/5✓ Branch 136 → 137 taken 14 times.
✓ Branch 140 → 141 taken 28 times.
✓ Branch 140 → 142 taken 14 times.
✗ Branch 499 → 500 not taken.
✗ Branch 499 → 501 not taken.
|
70 | tr("Enter whole port numbers between 1 and 65535.")}}; |
| 246 |
1/1✓ Branch 148 → 149 taken 8 times.
|
8 | AccountConfig config; |
| 247 | 8 | config.id = requestedId; | |
| 248 |
3/3✓ Branch 154 → 155 taken 8 times.
✓ Branch 155 → 156 taken 8 times.
✓ Branch 156 → 157 taken 8 times.
|
16 | config.name = fields.value(QStringLiteral("name")).toString().trimmed(); |
| 249 |
3/3✓ Branch 168 → 169 taken 8 times.
✓ Branch 169 → 170 taken 8 times.
✓ Branch 170 → 171 taken 8 times.
|
16 | config.email = fields.value(QStringLiteral("email")).toString().trimmed(); |
| 250 |
3/3✓ Branch 182 → 183 taken 8 times.
✓ Branch 183 → 184 taken 8 times.
✓ Branch 184 → 185 taken 8 times.
|
16 | config.imap.host = fields.value(QStringLiteral("imapHost")).toString().trimmed(); |
| 251 | 8 | config.imap.port = *imapPort; | |
| 252 |
2/2✓ Branch 200 → 201 taken 8 times.
✓ Branch 201 → 202 taken 8 times.
|
16 | config.imap.security = fields.value(QStringLiteral("imapSecurity"), QStringLiteral("ssl")).toString(); |
| 253 |
3/3✓ Branch 214 → 215 taken 8 times.
✓ Branch 215 → 216 taken 8 times.
✓ Branch 216 → 217 taken 8 times.
|
16 | config.imap.username = fields.value(QStringLiteral("imapUsername")).toString().trimmed(); |
| 254 |
3/3✓ Branch 228 → 229 taken 8 times.
✓ Branch 229 → 230 taken 8 times.
✓ Branch 230 → 231 taken 8 times.
|
16 | config.imap.password = fields.value(QStringLiteral("imapPassword")).toString().toUtf8(); |
| 255 |
3/3✓ Branch 242 → 243 taken 8 times.
✓ Branch 243 → 244 taken 8 times.
✓ Branch 244 → 245 taken 8 times.
|
16 | config.smtp.host = fields.value(QStringLiteral("smtpHost")).toString().trimmed(); |
| 256 | 8 | config.smtp.port = *smtpPort; | |
| 257 |
2/2✓ Branch 260 → 261 taken 8 times.
✓ Branch 261 → 262 taken 8 times.
|
16 | config.smtp.security = fields.value(QStringLiteral("smtpSecurity"), QStringLiteral("starttls")).toString(); |
| 258 |
3/3✓ Branch 274 → 275 taken 8 times.
✓ Branch 275 → 276 taken 8 times.
✓ Branch 276 → 277 taken 8 times.
|
16 | config.smtp.username = fields.value(QStringLiteral("smtpUsername")).toString().trimmed(); |
| 259 |
3/3✓ Branch 288 → 289 taken 8 times.
✓ Branch 289 → 290 taken 8 times.
✓ Branch 290 → 291 taken 8 times.
|
16 | config.smtp.password = fields.value(QStringLiteral("smtpPassword")).toString().toUtf8(); |
| 260 |
2/2✓ Branch 299 → 300 taken 8 times.
✓ Branch 300 → 301 taken 8 times.
|
8 | config.sieve = sieveConfigFrom(fields, m_session.accounts()->configuration(config.id)); |
| 261 |
1/1✓ Branch 304 → 305 taken 8 times.
|
8 | const QStringList errors = AccountConfigLoader::validate(config); |
| 262 |
2/2✓ Branch 306 → 307 taken 1 time.
✓ Branch 306 → 331 taken 7 times.
|
8 | if (!errors.isEmpty()) |
| 263 | 2 | return {{QStringLiteral("ok"), false}, | |
| 264 |
3/5✓ Branch 317 → 318 taken 1 time.
✓ Branch 321 → 322 taken 2 times.
✓ Branch 321 → 323 taken 1 time.
✗ Branch 695 → 696 not taken.
✗ Branch 695 → 697 not taken.
|
6 | {QStringLiteral("error"), errors.join(QStringLiteral("; "))}}; |
| 265 |
2/2✓ Branch 331 → 332 taken 7 times.
✓ Branch 335 → 336 taken 7 times.
|
14 | const QString directory = QDir(m_profileDirectory).filePath(QStringLiteral("accounts")); |
| 266 |
2/3✓ Branch 339 → 340 taken 7 times.
✗ Branch 340 → 341 not taken.
✓ Branch 340 → 360 taken 7 times.
|
7 | if (!AccountConfigLoader::save(config, directory)) |
| 267 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 268 | ✗ | {QStringLiteral("error"), tr("The account could not be saved.")}}; | |
| 269 |
1/1✓ Branch 361 → 362 taken 7 times.
|
7 | m_session.accounts()->configurationSaved(config.id); |
| 270 |
2/4✓ Branch 372 → 373 taken 14 times.
✓ Branch 372 → 374 taken 7 times.
✗ Branch 762 → 763 not taken.
✗ Branch 762 → 764 not taken.
|
42 | return {{QStringLiteral("ok"), true}, {QStringLiteral("id"), config.id}}; |
| 271 | 115 | } | |
| 272 | |||
| 273 | 5 | QVariantMap QuickAccountSettings::saveAccountAsync(const QVariantMap &fields) { | |
| 274 | 5 | QVariantMap fieldErrors; | |
| 275 | 34 | const auto required = [&fields, &fieldErrors](const QString &key, | |
| 276 | const QString &message) { | ||
| 277 |
5/5✓ Branch 3 → 4 taken 34 times.
✓ Branch 4 → 5 taken 34 times.
✓ Branch 5 → 6 taken 34 times.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 16 taken 32 times.
|
34 | if (fields.value(key).toString().trimmed().isEmpty()) |
| 278 |
1/1✓ Branch 13 → 14 taken 2 times.
|
2 | fieldErrors.insert(key, message); |
| 279 | 34 | }; | |
| 280 |
2/2✓ Branch 2 → 3 taken 5 times.
✓ Branch 6 → 7 taken 5 times.
|
10 | required(QStringLiteral("name"), tr("Enter an account name.")); |
| 281 |
2/2✓ Branch 10 → 11 taken 5 times.
✓ Branch 14 → 15 taken 5 times.
|
10 | required(QStringLiteral("email"), tr("Enter an email address.")); |
| 282 |
2/2✓ Branch 18 → 19 taken 5 times.
✓ Branch 22 → 23 taken 5 times.
|
10 | required(QStringLiteral("imapHost"), tr("Enter the IMAP server.")); |
| 283 |
2/2✓ Branch 26 → 27 taken 5 times.
✓ Branch 30 → 31 taken 5 times.
|
10 | required(QStringLiteral("imapUsername"), tr("Enter the IMAP username.")); |
| 284 |
2/2✓ Branch 34 → 35 taken 5 times.
✓ Branch 38 → 39 taken 5 times.
|
10 | required(QStringLiteral("smtpHost"), tr("Enter the SMTP server.")); |
| 285 |
2/2✓ Branch 42 → 43 taken 5 times.
✓ Branch 46 → 47 taken 5 times.
|
10 | required(QStringLiteral("smtpUsername"), tr("Enter the SMTP username.")); |
| 286 |
2/2✓ Branch 54 → 55 taken 5 times.
✓ Branch 55 → 56 taken 5 times.
|
10 | const QString requestedId = fields.value(QStringLiteral("id")).toString(); |
| 287 |
2/2✓ Branch 61 → 62 taken 2 times.
✓ Branch 61 → 79 taken 3 times.
|
5 | if (requestedId.isEmpty()) { |
| 288 |
2/2✓ Branch 62 → 63 taken 2 times.
✓ Branch 66 → 67 taken 2 times.
|
4 | required(QStringLiteral("imapPassword"), tr("Enter the IMAP password.")); |
| 289 |
2/2✓ Branch 70 → 71 taken 2 times.
✓ Branch 74 → 75 taken 2 times.
|
4 | required(QStringLiteral("smtpPassword"), tr("Enter the SMTP password.")); |
| 290 | } | ||
| 291 | 10 | const auto port = [&fields, &fieldErrors, this](const QString &key, int fallback) -> std::optional<quint16> { | |
| 292 | 10 | bool ok = false; | |
| 293 |
2/2✓ Branch 3 → 4 taken 10 times.
✓ Branch 4 → 5 taken 10 times.
|
10 | const double value = fields.value(key, fallback).toDouble(&ok); |
| 294 |
6/12✓ Branch 7 → 8 taken 10 times.
✗ Branch 7 → 13 not taken.
✓ Branch 9 → 10 taken 10 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 10 times.
✗ Branch 10 → 13 not taken.
✓ Branch 11 → 12 taken 10 times.
✗ Branch 11 → 13 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 10 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 24 taken 10 times.
|
10 | if (!ok || !std::isfinite(value) || value < 1 || value > 65535 || std::floor(value) != value) { |
| 295 | ✗ | fieldErrors.insert(key, tr("Enter a whole port number between 1 and 65535.")); | |
| 296 | ✗ | return std::nullopt; | |
| 297 | } | ||
| 298 | 10 | return static_cast<quint16>(value); | |
| 299 | 5 | }; | |
| 300 |
1/1✓ Branch 82 → 83 taken 5 times.
|
5 | const auto imapPort = port(QStringLiteral("imapPort"), 993); |
| 301 |
1/1✓ Branch 88 → 89 taken 5 times.
|
5 | const auto smtpPort = port(QStringLiteral("smtpPort"), 587); |
| 302 |
3/3✓ Branch 91 → 92 taken 5 times.
✓ Branch 92 → 93 taken 1 time.
✓ Branch 92 → 118 taken 4 times.
|
5 | if (!fieldErrors.isEmpty()) |
| 303 | 4 | return {{QStringLiteral("ok"), false}, {QStringLiteral("fieldErrors"), fieldErrors}, | |
| 304 |
3/5✓ Branch 104 → 105 taken 1 time.
✓ Branch 108 → 109 taken 3 times.
✓ Branch 108 → 110 taken 1 time.
✗ Branch 618 → 619 not taken.
✗ Branch 618 → 620 not taken.
|
6 | {QStringLiteral("error"), tr("Correct the highlighted account fields.")}}; |
| 305 | |||
| 306 |
2/2✓ Branch 119 → 120 taken 3 times.
✓ Branch 119 → 150 taken 1 time.
|
4 | if (!requestedId.isEmpty()) { |
| 307 | 3 | const auto accounts = configuredAccounts(); | |
| 308 |
1/1✓ Branch 123 → 124 taken 3 times.
|
3 | const auto matches = std::count_if(accounts.cbegin(), accounts.cend(), |
| 309 | 3 | [&requestedId](const QVariant &row) { | |
| 310 |
3/3✓ Branch 2 → 3 taken 3 times.
✓ Branch 7 → 8 taken 3 times.
✓ Branch 8 → 9 taken 3 times.
|
6 | return row.toMap().value(QStringLiteral("id")).toString() == requestedId; |
| 311 | }); | ||
| 312 |
1/2✗ Branch 124 → 125 not taken.
✓ Branch 124 → 144 taken 3 times.
|
3 | if (matches != 1) |
| 313 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 314 | ✗ | {QStringLiteral("error"), tr("The account no longer exists.")}}; | |
| 315 | 3 | } | |
| 316 |
7/14✓ Branch 150 → 151 taken 4 times.
✓ Branch 152 → 153 taken 4 times.
✗ Branch 152 → 156 not taken.
✓ Branch 153 → 154 taken 4 times.
✓ Branch 155 → 156 taken 3 times.
✓ Branch 155 → 157 taken 1 time.
✓ Branch 158 → 159 taken 4 times.
✗ Branch 158 → 160 not taken.
✓ Branch 160 → 161 taken 4 times.
✗ Branch 160 → 162 not taken.
✗ Branch 678 → 679 not taken.
✗ Branch 678 → 680 not taken.
✗ Branch 682 → 683 not taken.
✗ Branch 682 → 684 not taken.
|
4 | const bool changesActive = m_deps.activeAccountId().isEmpty() || requestedId == m_deps.activeAccountId(); |
| 317 |
6/9✓ Branch 162 → 163 taken 3 times.
✓ Branch 162 → 169 taken 1 time.
✓ Branch 163 → 164 taken 3 times.
✓ Branch 164 → 165 taken 3 times.
✗ Branch 164 → 168 not taken.
✗ Branch 167 → 168 not taken.
✓ Branch 167 → 169 taken 3 times.
✗ Branch 170 → 171 not taken.
✓ Branch 170 → 190 taken 4 times.
|
4 | if (changesActive && (m_deps.sendOrActionPending() || m_session.undo()->busy())) |
| 318 | ✗ | return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), | |
| 319 | ✗ | tr("Wait for the current send or action to finish before changing accounts.")}}; | |
| 320 |
6/6✓ Branch 190 → 191 taken 3 times.
✓ Branch 190 → 195 taken 1 time.
✓ Branch 193 → 194 taken 1 time.
✓ Branch 193 → 195 taken 2 times.
✓ Branch 196 → 197 taken 1 time.
✓ Branch 196 → 216 taken 3 times.
|
4 | if (changesActive && m_session.accounts()->resolvingCredentials()) |
| 321 | 4 | return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), | |
| 322 |
3/5✓ Branch 204 → 205 taken 1 time.
✓ Branch 208 → 209 taken 2 times.
✓ Branch 208 → 210 taken 1 time.
✗ Branch 713 → 714 not taken.
✗ Branch 713 → 715 not taken.
|
5 | tr("Wait for credential loading to finish before saving this account.")}}; |
| 323 | |||
| 324 | // F-48 (SPRINT-104): a rename re-keys the account's cached rows in the | ||
| 325 | // same step, so the cache, the queued actions and the session stay one | ||
| 326 | // identity instead of re-downloading the mailbox under the new name. | ||
| 327 | 3 | QString previousName; | |
| 328 |
2/2✓ Branch 217 → 218 taken 2 times.
✓ Branch 217 → 260 taken 1 time.
|
3 | if (!requestedId.isEmpty()) { |
| 329 |
3/4✓ Branch 219 → 220 taken 2 times.
✓ Branch 220 → 221 taken 2 times.
✓ Branch 257 → 222 taken 2 times.
✗ Branch 257 → 258 not taken.
|
2 | for (const auto &entry : configuredAccounts()) { |
| 330 |
1/1✓ Branch 223 → 224 taken 2 times.
|
2 | const auto account = entry.toMap(); |
| 331 |
3/4✓ Branch 228 → 229 taken 2 times.
✓ Branch 229 → 230 taken 2 times.
✓ Branch 236 → 237 taken 2 times.
✗ Branch 236 → 250 not taken.
|
4 | if (account.value(QStringLiteral("id")).toString() == requestedId) { |
| 332 |
2/2✓ Branch 241 → 242 taken 2 times.
✓ Branch 242 → 243 taken 2 times.
|
4 | previousName = account.value(QStringLiteral("name")).toString(); |
| 333 | 2 | break; | |
| 334 | } | ||
| 335 | 4 | } | |
| 336 | } | ||
| 337 | |||
| 338 |
1/1✓ Branch 260 → 261 taken 3 times.
|
3 | AccountConfig config; |
| 339 | 3 | config.id = requestedId; | |
| 340 |
3/3✓ Branch 266 → 267 taken 3 times.
✓ Branch 267 → 268 taken 3 times.
✓ Branch 268 → 269 taken 3 times.
|
6 | config.name = fields.value(QStringLiteral("name")).toString().trimmed(); |
| 341 |
3/3✓ Branch 280 → 281 taken 3 times.
✓ Branch 281 → 282 taken 3 times.
✓ Branch 282 → 283 taken 3 times.
|
6 | config.email = fields.value(QStringLiteral("email")).toString().trimmed(); |
| 342 |
3/3✓ Branch 294 → 295 taken 3 times.
✓ Branch 295 → 296 taken 3 times.
✓ Branch 296 → 297 taken 3 times.
|
6 | config.imap.host = fields.value(QStringLiteral("imapHost")).toString().trimmed(); |
| 343 | 3 | config.imap.port = *imapPort; | |
| 344 |
2/2✓ Branch 312 → 313 taken 3 times.
✓ Branch 313 → 314 taken 3 times.
|
6 | config.imap.security = fields.value(QStringLiteral("imapSecurity"), QStringLiteral("ssl")).toString(); |
| 345 |
3/3✓ Branch 326 → 327 taken 3 times.
✓ Branch 327 → 328 taken 3 times.
✓ Branch 328 → 329 taken 3 times.
|
6 | config.imap.username = fields.value(QStringLiteral("imapUsername")).toString().trimmed(); |
| 346 |
3/3✓ Branch 340 → 341 taken 3 times.
✓ Branch 341 → 342 taken 3 times.
✓ Branch 342 → 343 taken 3 times.
|
6 | config.imap.password = fields.value(QStringLiteral("imapPassword")).toString().toUtf8(); |
| 347 |
3/3✓ Branch 354 → 355 taken 3 times.
✓ Branch 355 → 356 taken 3 times.
✓ Branch 356 → 357 taken 3 times.
|
6 | config.smtp.host = fields.value(QStringLiteral("smtpHost")).toString().trimmed(); |
| 348 | 3 | config.smtp.port = *smtpPort; | |
| 349 |
2/2✓ Branch 372 → 373 taken 3 times.
✓ Branch 373 → 374 taken 3 times.
|
6 | config.smtp.security = fields.value(QStringLiteral("smtpSecurity"), QStringLiteral("starttls")).toString(); |
| 350 |
3/3✓ Branch 386 → 387 taken 3 times.
✓ Branch 387 → 388 taken 3 times.
✓ Branch 388 → 389 taken 3 times.
|
6 | config.smtp.username = fields.value(QStringLiteral("smtpUsername")).toString().trimmed(); |
| 351 |
3/3✓ Branch 400 → 401 taken 3 times.
✓ Branch 401 → 402 taken 3 times.
✓ Branch 402 → 403 taken 3 times.
|
6 | config.smtp.password = fields.value(QStringLiteral("smtpPassword")).toString().toUtf8(); |
| 352 |
2/2✓ Branch 411 → 412 taken 3 times.
✓ Branch 412 → 413 taken 3 times.
|
3 | config.sieve = sieveConfigFrom(fields, m_session.accounts()->configuration(config.id)); |
| 353 |
1/1✓ Branch 416 → 417 taken 3 times.
|
3 | const auto errors = AccountConfigLoader::validate(config); |
| 354 |
1/2✗ Branch 418 → 419 not taken.
✓ Branch 418 → 443 taken 3 times.
|
3 | if (!errors.isEmpty()) |
| 355 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 356 | ✗ | {QStringLiteral("error"), errors.join(QStringLiteral("; "))}}; | |
| 357 | |||
| 358 |
2/2✓ Branch 443 → 444 taken 3 times.
✓ Branch 444 → 445 taken 3 times.
|
3 | const QString requestId = QUuid::createUuid().toString(QUuid::WithoutBraces); |
| 359 |
2/2✓ Branch 445 → 446 taken 3 times.
✓ Branch 449 → 450 taken 3 times.
|
6 | const QString directory = QDir(m_profileDirectory).filePath(QStringLiteral("accounts")); |
| 360 |
1/1✓ Branch 453 → 454 taken 3 times.
|
3 | QPointer<QuickAccountSettings> guard(this); |
| 361 |
1/1✓ Branch 461 → 462 taken 3 times.
|
3 | QObject *request = AccountConfigLoader::saveAsync(m_session.accounts(), std::move(config), directory, |
| 362 |
1/5✓ Branch 457 → 458 taken 3 times.
✗ Branch 989 → 990 not taken.
✗ Branch 989 → 991 not taken.
✗ Branch 992 → 993 not taken.
✗ Branch 992 → 994 not taken.
|
6 | [guard, requestId, previousName](bool success, const AccountConfig &stored, const QString &error) { |
| 363 |
3/6✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 2 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 2 times.
|
2 | if (!guard || !guard->m_settingsRequests.contains(requestId)) return; |
| 364 |
1/1✓ Branch 12 → 13 taken 2 times.
|
2 | guard->m_settingsRequests.remove(requestId); |
| 365 |
2/4✓ Branch 19 → 20 taken 2 times.
✓ Branch 19 → 21 taken 2 times.
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 71 not taken.
|
6 | QVariantMap result{{QStringLiteral("ok"), success}}; |
| 366 |
2/2✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 48 taken 1 time.
|
2 | if (success) { |
| 367 |
1/1✓ Branch 28 → 29 taken 1 time.
|
2 | result.insert(QStringLiteral("id"), stored.id); |
| 368 |
3/8✓ Branch 33 → 34 taken 1 time.
✗ Branch 33 → 40 not taken.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 40 taken 1 time.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 45 taken 1 time.
|
1 | if (!previousName.isEmpty() && previousName != stored.name && |
| 369 | ✗ | guard->m_session.cache()) | |
| 370 | ✗ | guard->m_session.cache()->renameAccount(previousName, stored.name); | |
| 371 |
1/1✓ Branch 47 → 62 taken 1 time.
|
1 | guard->m_session.accounts()->configurationSaved(stored.id); |
| 372 | } else { | ||
| 373 |
1/1✓ Branch 56 → 57 taken 1 time.
|
3 | result.insert(QStringLiteral("error"), error.isEmpty() |
| 374 |
1/3✗ Branch 49 → 50 not taken.
✓ Branch 49 → 51 taken 1 time.
✗ Branch 50 → 52 not taken.
|
2 | ? QuickAccountSettings::tr("The account could not be saved.") : error); |
| 375 | } | ||
| 376 |
1/1✓ Branch 63 → 64 taken 2 times.
|
2 | emit guard->accountSaveFinished(requestId, result); |
| 377 |
1/5✓ Branch 17 → 18 taken 2 times.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 82 not taken.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
|
6 | }); |
| 378 |
1/2✗ Branch 465 → 466 not taken.
✓ Branch 465 → 485 taken 3 times.
|
3 | if (!request) |
| 379 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 380 | ✗ | {QStringLiteral("error"), tr("The account save could not be started.")}}; | |
| 381 |
2/2✓ Branch 485 → 486 taken 3 times.
✓ Branch 486 → 487 taken 3 times.
|
3 | m_settingsRequests.insert(requestId, request); |
| 382 | 12 | return {{QStringLiteral("ok"), true}, {QStringLiteral("pending"), true}, | |
| 383 |
2/4✓ Branch 502 → 503 taken 9 times.
✓ Branch 502 → 504 taken 3 times.
✗ Branch 1027 → 1028 not taken.
✗ Branch 1027 → 1029 not taken.
|
18 | {QStringLiteral("requestId"), requestId}}; |
| 384 | 32 | } | |
| 385 | |||
| 386 | 1 | void QuickAccountSettings::cancelSettingsRequest(const QString &requestId) { | |
| 387 |
1/1✓ Branch 2 → 3 taken 1 time.
|
1 | const QPointer<QObject> request = m_settingsRequests.take(requestId); |
| 388 |
1/2✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
|
1 | delete request.data(); |
| 389 | 1 | } | |
| 390 | |||
| 391 | 4 | QVariantMap QuickAccountSettings::removeAccount(const QString &id) { | |
| 392 |
2/2✓ Branch 2 → 3 taken 4 times.
✓ Branch 6 → 7 taken 4 times.
|
8 | const QString directory = QDir(m_profileDirectory).filePath(QStringLiteral("accounts")); |
| 393 | 4 | QString victimName; | |
| 394 | 4 | int matches = 0; | |
| 395 |
4/4✓ Branch 11 → 12 taken 4 times.
✓ Branch 12 → 13 taken 4 times.
✓ Branch 50 → 14 taken 6 times.
✓ Branch 50 → 51 taken 4 times.
|
10 | for (const auto &entry : configuredAccounts()) { |
| 396 |
1/1✓ Branch 15 → 16 taken 6 times.
|
6 | const auto account = entry.toMap(); |
| 397 |
4/4✓ Branch 20 → 21 taken 6 times.
✓ Branch 21 → 22 taken 6 times.
✓ Branch 28 → 29 taken 2 times.
✓ Branch 28 → 30 taken 4 times.
|
12 | if (account.value(QStringLiteral("id")).toString() != id) continue; |
| 398 |
2/2✓ Branch 34 → 35 taken 4 times.
✓ Branch 35 → 36 taken 4 times.
|
8 | victimName = account.value(QStringLiteral("name")).toString(); |
| 399 | 4 | ++matches; | |
| 400 | 10 | } | |
| 401 |
3/6✓ Branch 52 → 53 taken 4 times.
✗ Branch 52 → 55 not taken.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 56 taken 4 times.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 77 taken 4 times.
|
4 | if (matches != 1 || victimName.isEmpty()) |
| 402 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 403 | ✗ | {QStringLiteral("error"), tr("The account no longer exists.")}}; | |
| 404 |
5/9✓ Branch 77 → 78 taken 4 times.
✓ Branch 79 → 80 taken 3 times.
✓ Branch 79 → 86 taken 1 time.
✓ Branch 87 → 88 taken 4 times.
✗ Branch 87 → 89 not taken.
✗ Branch 89 → 90 not taken.
✓ Branch 89 → 109 taken 4 times.
✗ Branch 238 → 239 not taken.
✗ Branch 238 → 240 not taken.
|
7 | if (id == m_deps.activeAccountId() && |
| 405 |
3/5✓ Branch 80 → 81 taken 3 times.
✓ Branch 81 → 82 taken 3 times.
✗ Branch 81 → 85 not taken.
✗ Branch 84 → 85 not taken.
✓ Branch 84 → 86 taken 3 times.
|
3 | (m_deps.sendOrActionPending() || m_session.undo()->busy())) |
| 406 | ✗ | return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), | |
| 407 | ✗ | tr("Wait for the current send or action to finish before deleting the account.")}}; | |
| 408 |
2/3✓ Branch 109 → 110 taken 4 times.
✗ Branch 110 → 111 not taken.
✓ Branch 110 → 130 taken 4 times.
|
4 | if (!AccountConfigLoader::remove(victimName, directory, id)) |
| 409 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 410 | ✗ | {QStringLiteral("error"), tr("The account could not be deleted.")}}; | |
| 411 | // F-43 (SPRINT-104): the downloaded mail leaves with the account — | ||
| 412 | // folders, headers, bodies, attachments, search documents and queued | ||
| 413 | // actions — instead of staying on disk under a name no account owns. | ||
| 414 |
4/7✓ Branch 131 → 132 taken 4 times.
✗ Branch 131 → 136 not taken.
✓ Branch 133 → 134 taken 4 times.
✗ Branch 134 → 135 not taken.
✓ Branch 134 → 136 taken 4 times.
✗ Branch 137 → 138 not taken.
✓ Branch 137 → 149 taken 4 times.
|
4 | if (m_session.cache() && !m_session.cache()->removeAccountData(victimName)) |
| 415 | ✗ | qCWarning(lcAccountSettings) << "Account removed, but its cached mail could not be deleted"; | |
| 416 | // The deleted account's recovery draft must not resurface later. | ||
| 417 |
2/2✓ Branch 150 → 151 taken 4 times.
✓ Branch 151 → 152 taken 4 times.
|
4 | ComposeDraftStore(m_profileDirectory).clear(id); |
| 418 |
1/1✓ Branch 155 → 156 taken 4 times.
|
4 | m_session.accounts()->configurationRemoved(id); |
| 419 |
2/4✓ Branch 162 → 163 taken 4 times.
✓ Branch 162 → 164 taken 4 times.
✗ Branch 306 → 307 not taken.
✗ Branch 306 → 308 not taken.
|
16 | return {{QStringLiteral("ok"), true}}; |
| 420 | 16 | } | |
| 421 | |||
| 422 | 49 | int QuickAccountSettings::caldavSyncIntervalMinutes() const { | |
| 423 |
1/1✓ Branch 2 → 3 taken 49 times.
|
49 | QSettings settings; |
| 424 |
2/2✓ Branch 8 → 9 taken 49 times.
✓ Branch 9 → 10 taken 49 times.
|
147 | return settings.value(QStringLiteral("caldav/syncIntervalMin"), 15).toInt(); |
| 425 | 49 | } | |
| 426 | |||
| 427 | 1398 | SettingsSyncSession *QuickAccountSettings::settingsSync() const { return m_session.settingsSync(); } | |
| 428 | |||
| 429 | 10 | void QuickAccountSettings::setCaldavSyncIntervalMinutes(int minutes) { | |
| 430 | 10 | minutes = qBound(0, minutes, 1440); | |
| 431 |
1/1✓ Branch 3 → 4 taken 10 times.
|
10 | QSettings settings; |
| 432 |
2/2✓ Branch 9 → 10 taken 10 times.
✓ Branch 10 → 11 taken 10 times.
|
20 | const int current = settings.value(QStringLiteral("caldav/syncIntervalMin"), 15).toInt(); |
| 433 |
1/2✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 10 times.
|
10 | if (current == minutes) return; |
| 434 |
1/1✓ Branch 22 → 23 taken 10 times.
|
20 | settings.setValue(QStringLiteral("caldav/syncIntervalMin"), minutes); |
| 435 | // Re-arm the live scheduler: 0 stops it, anything else restarts the timer | ||
| 436 | // (the restart schedules one refresh after three seconds, like startup). | ||
| 437 | // The shared DAV interval drives the calendar and contact coordinators. | ||
| 438 |
1/2✓ Branch 27 → 28 taken 10 times.
✗ Branch 27 → 30 not taken.
|
10 | if (const auto sync = m_session.calendarSync()) { |
| 439 |
1/1✓ Branch 28 → 29 taken 10 times.
|
10 | sync->stopBackgroundSync(); |
| 440 |
1/1✓ Branch 29 → 30 taken 10 times.
|
10 | sync->startBackgroundSync(); |
| 441 | } | ||
| 442 |
1/2✓ Branch 31 → 32 taken 10 times.
✗ Branch 31 → 34 not taken.
|
10 | if (const auto contacts = m_session.contactSync()) { |
| 443 |
1/1✓ Branch 32 → 33 taken 10 times.
|
10 | contacts->stopBackgroundSync(); |
| 444 |
1/1✓ Branch 33 → 34 taken 10 times.
|
10 | contacts->startBackgroundSync(); |
| 445 | } | ||
| 446 |
1/1✓ Branch 34 → 35 taken 10 times.
|
10 | emit davSettingsChanged(); |
| 447 | 10 | } | |
| 448 | |||
| 449 | 21 | QVariantList QuickAccountSettings::davAccounts() const { | |
| 450 | 21 | QVariantList result; | |
| 451 |
5/5✓ Branch 2 → 3 taken 21 times.
✓ Branch 3 → 4 taken 21 times.
✓ Branch 4 → 5 taken 21 times.
✓ Branch 35 → 6 taken 15 times.
✓ Branch 35 → 36 taken 21 times.
|
36 | for (const auto &row : readDavAccounts()) |
| 452 |
1/1✓ Branch 21 → 22 taken 15 times.
|
45 | result.append(QVariantMap{{QStringLiteral("id"), row.id}, |
| 453 | 30 | {QStringLiteral("serverUrl"), row.serverUrl}, | |
| 454 |
2/4✓ Branch 25 → 26 taken 45 times.
✓ Branch 25 → 27 taken 15 times.
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
|
111 | {QStringLiteral("username"), row.username}}); |
| 455 | 21 | return result; | |
| 456 | 60 | } | |
| 457 | |||
| 458 | 3 | QVariantMap QuickAccountSettings::saveDavAccount(const QVariantMap &fields) { | |
| 459 |
1/1✓ Branch 3 → 4 taken 3 times.
|
3 | const auto result = DavAccountSettings::save(fields); |
| 460 |
3/5✓ Branch 9 → 10 taken 3 times.
✓ Branch 10 → 11 taken 3 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 3 times.
✗ Branch 16 → 17 not taken.
|
6 | if (result.value(QStringLiteral("ok")).toBool()) emit davSettingsChanged(); |
| 461 | 3 | return result; | |
| 462 | ✗ | } | |
| 463 | |||
| 464 | ✗ | QVariantMap QuickAccountSettings::saveDavAccountAsync(const QVariantMap &fields) { | |
| 465 | ✗ | QVariantMap fieldErrors; | |
| 466 | ✗ | const QString requestedId = fields.value(QStringLiteral("id")).toString(); | |
| 467 | ✗ | const QString username = fields.value(QStringLiteral("username")).toString().trimmed(); | |
| 468 | ✗ | const QString serverUrl = fields.value(QStringLiteral("serverUrl")).toString().trimmed(); | |
| 469 | ✗ | const QString password = fields.value(QStringLiteral("password")).toString(); | |
| 470 | ✗ | if (username.isEmpty()) | |
| 471 | ✗ | fieldErrors.insert(QStringLiteral("username"), tr("Enter a user name.")); | |
| 472 | ✗ | const QString canonical = DavCredentials::canonicalOrigin(serverUrl); | |
| 473 | ✗ | if (canonical.isEmpty()) | |
| 474 | ✗ | fieldErrors.insert(QStringLiteral("serverUrl"), | |
| 475 | ✗ | tr("Enter a server address starting with https://")); | |
| 476 | |||
| 477 | ✗ | std::optional<DavAccountSettings::Account> previous; | |
| 478 | ✗ | if (!requestedId.isEmpty()) { | |
| 479 | ✗ | int matches = 0; | |
| 480 | ✗ | for (const auto &account : DavAccountSettings::accounts()) { | |
| 481 | ✗ | if (account.id != requestedId) continue; | |
| 482 | ✗ | previous = account; | |
| 483 | ✗ | ++matches; | |
| 484 | ✗ | } | |
| 485 | ✗ | if (matches != 1) | |
| 486 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 487 | ✗ | {QStringLiteral("error"), tr("The DAV account no longer exists.")}}; | |
| 488 | } | ||
| 489 | ✗ | if (password.isEmpty() && !previous) | |
| 490 | ✗ | fieldErrors.insert(QStringLiteral("password"), tr("Enter a password.")); | |
| 491 | ✗ | if (password.isEmpty() && previous && | |
| 492 | ✗ | (DavCredentials::canonicalOrigin(previous->serverUrl) != canonical || | |
| 493 | ✗ | previous->username != username)) | |
| 494 | ✗ | fieldErrors.insert(QStringLiteral("password"), | |
| 495 | ✗ | tr("Enter a password when changing the server or user name.")); | |
| 496 | ✗ | if (!fieldErrors.isEmpty()) | |
| 497 | ✗ | return {{QStringLiteral("ok"), false}, {QStringLiteral("fieldErrors"), fieldErrors}, | |
| 498 | ✗ | {QStringLiteral("error"), tr("Correct the highlighted DAV fields.")}}; | |
| 499 | |||
| 500 | ✗ | QVariantMap admitted = fields; | |
| 501 | ✗ | admitted.insert(QStringLiteral("serverUrl"), canonical); | |
| 502 | ✗ | const QString requestId = QUuid::createUuid().toString(QUuid::WithoutBraces); | |
| 503 | ✗ | QPointer<QuickAccountSettings> guard(this); | |
| 504 | ✗ | QObject *request = DavAccountSettings::saveAsync(m_session.accounts(), admitted, | |
| 505 | ✗ | [guard, requestId](const QVariantMap &result) { | |
| 506 | ✗ | if (!guard || !guard->m_settingsRequests.contains(requestId)) return; | |
| 507 | ✗ | guard->m_settingsRequests.remove(requestId); | |
| 508 | ✗ | if (result.value(QStringLiteral("ok")).toBool()) emit guard->davSettingsChanged(); | |
| 509 | ✗ | emit guard->davAccountSaveFinished(requestId, result); | |
| 510 | }); | ||
| 511 | ✗ | if (!request) | |
| 512 | ✗ | return {{QStringLiteral("ok"), false}, | |
| 513 | ✗ | {QStringLiteral("error"), tr("The DAV account save could not be started.")}}; | |
| 514 | ✗ | m_settingsRequests.insert(requestId, request); | |
| 515 | ✗ | return {{QStringLiteral("ok"), true}, {QStringLiteral("pending"), true}, | |
| 516 | ✗ | {QStringLiteral("requestId"), requestId}}; | |
| 517 | ✗ | } | |
| 518 | |||
| 519 | 3 | bool QuickAccountSettings::removeDavAccount(const QString &id) { | |
| 520 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3 times.
|
3 | if (id.isEmpty()) return false; |
| 521 |
1/1✓ Branch 5 → 6 taken 3 times.
|
3 | const auto removed = DavAccountSettings::removeMetadata(id); |
| 522 |
1/2✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 3 times.
|
3 | if (!removed) return false; |
| 523 | // The removed UUID cannot be reused by a newly created DAV account. Keep | ||
| 524 | // cleanup alive across backend reloads, without processing nested UI events. | ||
| 525 |
2/2✓ Branch 11 → 12 taken 3 times.
✓ Branch 16 → 17 taken 3 times.
|
6 | DavCredentials::deletePasswordAsync(QCoreApplication::instance(), removed->id, |
| 526 | 6 | removed->serverUrl, removed->username, [id](bool success) { | |
| 527 | ✗ | if (!success) qWarning() << "DAV account removed, but keyring cleanup failed for" << id; | |
| 528 | 3 | }, removed->credentialId); | |
| 529 | // S106-055: the account's calendars and tasks leave with it. | ||
| 530 |
2/3✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 24 not taken.
✓ Branch 23 → 24 taken 3 times.
|
3 | if (m_session.calendarSync()) m_session.calendarSync()->forgetAccount(removed->id); |
| 531 |
1/1✓ Branch 24 → 25 taken 3 times.
|
3 | emit davSettingsChanged(); |
| 532 | 3 | return true; | |
| 533 | 3 | } | |
| 534 | |||
| 535 | 19 | QAbstractItemModel *QuickAccountSettings::imageWhitelist() const { return m_whitelist; } | |
| 536 | |||
| 537 | 39 | QString QuickAccountSettings::imagePolicyMode() const { | |
| 538 | 39 | return ExternalContentPolicy::instance()->mode(); | |
| 539 | } | ||
| 540 | |||
| 541 | 4 | void QuickAccountSettings::setImagePolicyMode(const QString &mode) { | |
| 542 |
1/2✓ Branch 4 → 5 taken 4 times.
✗ Branch 4 → 6 not taken.
|
4 | if (ExternalContentPolicy::instance()->setMode(mode)) |
| 543 | 4 | emit imagePolicyModeChanged(); | |
| 544 | 4 | } | |
| 545 | |||
| 546 | 36 | QString QuickAccountSettings::defaultReadingMode() const { | |
| 547 | 36 | return WorkspaceState::configuredReadingMode(); | |
| 548 | } | ||
| 549 | |||
| 550 | 2 | void QuickAccountSettings::setDefaultReadingMode(const QString &mode) { | |
| 551 |
2/4✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 2 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 2 times.
|
2 | const QString canonical = mode == QLatin1String("html-light") ? QStringLiteral("light") : mode; |
| 552 |
3/4✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 21 taken 1 time.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 21 taken 1 time.
|
2 | if (canonical != QLatin1String("text") && canonical != QLatin1String("light") && |
| 553 |
1/4✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 2 times.
|
2 | canonical != QLatin1String("html")) |
| 554 | ✗ | return; | |
| 555 |
2/3✓ Branch 24 → 25 taken 2 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 2 times.
|
2 | if (canonical == WorkspaceState::configuredReadingMode()) return; |
| 556 |
2/2✓ Branch 29 → 30 taken 2 times.
✓ Branch 35 → 36 taken 2 times.
|
4 | QSettings().setValue(QStringLiteral("view/defaultMode"), canonical); |
| 557 | // The notification refreshes the settings surface and the WorkspaceState | ||
| 558 | // default used by later selections, without reading QSettings on each click. | ||
| 559 |
1/1✓ Branch 40 → 41 taken 2 times.
|
2 | emit defaultReadingModeChanged(); |
| 560 | 2 | } | |
| 561 | |||
| 562 | 7 | bool QuickAccountSettings::addImageWhitelistDomain(const QString &domain) { | |
| 563 |
1/1✓ Branch 2 → 3 taken 7 times.
|
7 | const QString normalized = normalizeWhitelistDomain(domain); |
| 564 |
2/2✓ Branch 4 → 5 taken 3 times.
✓ Branch 4 → 6 taken 4 times.
|
7 | if (normalized.isEmpty()) return false; |
| 565 |
1/1✓ Branch 11 → 12 taken 4 times.
|
8 | return m_session.cache()->allowlist().addWhitelistEntry(QStringLiteral("domain"), normalized); |
| 566 | 7 | } | |
| 567 | |||
| 568 | 3 | bool QuickAccountSettings::removeImageWhitelistEntry(qint64 entryId) { | |
| 569 | 3 | return m_session.cache()->allowlist().removeWhitelistEntry(entryId); | |
| 570 | } | ||
| 571 | |||
| 572 | // Formerly inline in ~QuickMailBackend(); the backend destructor calls it at | ||
| 573 | // the same point (after the autosave writer stopped). | ||
| 574 | 795 | void QuickAccountSettings::destroyPendingRequests() { | |
| 575 | 795 | const auto settingsRequests = m_settingsRequests; | |
| 576 | 795 | m_settingsRequests.clear(); | |
| 577 |
1/2✗ Branch 12 → 6 not taken.
✓ Branch 12 → 13 taken 795 times.
|
795 | for (const auto &request : settingsRequests) |
| 578 | ✗ | delete request.data(); | |
| 579 | 795 | } | |
| 580 | |||
| 581 | ✗ | void QuickAccountSettings::notifyDavChanged(const QString &davAccountId) { | |
| 582 | ✗ | emit davSettingsChanged(); | |
| 583 | ✗ | for (const auto &config : DavAccountSettings::calendarConfigs()) | |
| 584 | ✗ | if (config.accountId == davAccountId && !config.selectedCalendars.isEmpty() && m_session.calendarSync()) { | |
| 585 | ✗ | QTimer::singleShot(0, m_session.calendarSync(), &CalendarSyncCoordinator::triggerSync); | |
| 586 | ✗ | break; | |
| 587 | ✗ | } | |
| 588 | ✗ | if (m_session.contactSync()) QTimer::singleShot(0, m_session.contactSync(), &ContactSyncCoordinator::triggerSync); | |
| 589 | ✗ | } | |
| 590 | |||
| 591 | ✗ | QVariantMap QuickAccountSettings::saveDavWithMailLogin(const QVariantMap &input) { | |
| 592 | ✗ | const QString mailId = input.value(QStringLiteral("mailAccountId")).toString(); | |
| 593 | ✗ | const auto config = m_session.accounts()->configuration(mailId); | |
| 594 | ✗ | if (!config) return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), tr("The mail account no longer exists.")}}; | |
| 595 | // S106-252: the editor sends no id; the DAV account already linked to | ||
| 596 | // this mail account is updated instead of a second one being created. | ||
| 597 | ✗ | QVariantMap fields = input; | |
| 598 | ✗ | if (fields.value(QStringLiteral("id")).toString().isEmpty()) | |
| 599 | ✗ | if (const QString linked = DavAccountSettings::linkedTo(mailId); !linked.isEmpty()) | |
| 600 | ✗ | fields.insert(QStringLiteral("id"), linked); | |
| 601 | ✗ | const QString requestId = QUuid::createUuid().toString(QUuid::WithoutBraces); | |
| 602 | ✗ | auto *store = new CredentialStore(this); | |
| 603 | ✗ | m_settingsRequests.insert(requestId, store); | |
| 604 | ✗ | QPointer<QuickAccountSettings> guard(this); | |
| 605 | ✗ | store->readPassword(QStringLiteral("imap"), AccountConfigLoader::imapCredentialName(*config), | |
| 606 | ✗ | [guard, store, requestId, fields](bool success, const QByteArray &password) { | |
| 607 | ✗ | store->deleteLater(); | |
| 608 | ✗ | if (!guard || !guard->m_settingsRequests.contains(requestId)) return; | |
| 609 | ✗ | if (!success || password.isEmpty()) { | |
| 610 | ✗ | guard->m_settingsRequests.remove(requestId); | |
| 611 | ✗ | emit guard->davAccountSaveFinished(requestId, {{QStringLiteral("ok"), false}, | |
| 612 | ✗ | {QStringLiteral("error"), tr("The password of the mail account could not be read. Enter it once.")}}); | |
| 613 | ✗ | return; | |
| 614 | } | ||
| 615 | ✗ | QVariantMap davFields = fields; | |
| 616 | ✗ | davFields.insert(QStringLiteral("password"), QString::fromUtf8(password)); | |
| 617 | ✗ | QObject *request = DavAccountSettings::saveAsync(guard, davFields, [guard, requestId](const QVariantMap &result) { | |
| 618 | ✗ | if (!guard || !guard->m_settingsRequests.contains(requestId)) return; | |
| 619 | ✗ | guard->m_settingsRequests.remove(requestId); | |
| 620 | ✗ | if (result.value(QStringLiteral("ok")).toBool()) guard->notifyDavChanged(result.value(QStringLiteral("id")).toString()); | |
| 621 | ✗ | emit guard->davAccountSaveFinished(requestId, result); | |
| 622 | }); | ||
| 623 | ✗ | guard->m_settingsRequests.insert(requestId, request); | |
| 624 | ✗ | }); | |
| 625 | ✗ | return {{QStringLiteral("ok"), true}, {QStringLiteral("requestId"), requestId}}; | |
| 626 | ✗ | } | |
| 627 |