MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 4 / 0 / 4
Functions: 100.0% 4 / 0 / 4
Branches: -% 0 / 0 / 0

service/SieveDiscovery.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QDateTime>
4 #include <QList>
5 #include <QObject>
6 #include <QSslCertificate>
7 #include <QStringList>
8
9 #include <functional>
10 #include <memory>
11 #include <optional>
12
13 #include "data/AccountConfig.h"
14
15 // Finds the ManageSieve server of an account (Sprint 95 S95.1.4, D-10).
16 //
17 // Candidate order: (1) the IMAP capability "IMAPSIEVE=<sieve URL>"
18 // (RFC 6785), (2) DNS SRV "_sieve._tcp.<mail domain>" (RFC 5804 §1.8),
19 // (3) the IMAP host on port 4190. Manual mode tries only the configured
20 // server. Each candidate is probed without authentication (connect,
21 // STARTTLS, capabilities) with a bounded timeout; the first one that answers
22 // wins. TLS verification is never relaxed, so a candidate whose certificate
23 // is not valid for its host fails like an unreachable one. A winner in
24 // host outside the configured IMAP host and mail domain is reported with
25 // `needsConfirmation` — credentials are only sent after the user
26 // confirmed it once (the caller stores that decision).
27 //
28 // Seams: `srvLookup` replaces QDnsLookup, `caCertificates` lets tests trust
29 // the fixture CA.
30 class SieveDiscovery : public QObject {
31 Q_OBJECT
32
33 public:
34 enum class State { Available, NotAvailable, Disabled, Error };
35
36 struct Candidate {
37 QString host;
38 quint16 port = 4190;
39 QString security = QStringLiteral("starttls");
40 QString source; // "imapsieve", "srv", "imap-host", "manual"
41 };
42
43 struct Result {
44 State state = State::NotAvailable;
45 Candidate server;
46 QString implementation;
47 int extensionCount = 0;
48 QStringList extensions;
49 bool needsConfirmation = false;
50 bool confirmedByUser = false;
51 QDateTime checkedAt;
52 QStringList tried; // "host:port (source): outcome" for Details/logs
53 };
54
55 struct SrvRecord {
56 QString target;
57 quint16 port = 0;
58 int priority = 0;
59 int weight = 0;
60 };
61 using SrvCallback = std::function<void(const QList<SrvRecord> &)>;
62 using SrvLookup = std::function<void(const QString &name, SrvCallback)>;
63 using Callback = std::function<void(const Result &)>;
64
65 explicit SieveDiscovery(QObject *parent = nullptr);
66
67 5 void setSrvLookup(SrvLookup lookup) { m_srvLookup = std::move(lookup); }
68 891 void setCaCertificates(const QList<QSslCertificate> &certificates) { m_ca = certificates; }
69 1 void setProbeTimeoutMs(int ms) { m_probeTimeoutMs = ms; }
70 4 void setFallbackPort(quint16 port) { m_fallbackPort = port; } // tests
71
72 // `imapCapabilities` are the post-login IMAP capabilities (may be empty).
73 void discover(const AccountConfig &account, const QStringList &imapCapabilities,
74 Callback callback);
75
76 // Parses "IMAPSIEVE=sieve://host[:port]" from an IMAP capability list.
77 static std::optional<Candidate> imapSieveCandidate(const QStringList &imapCapabilities);
78 // No public-suffix heuristic: exact IMAP host or a child of the mail domain.
79 static bool candidateNeedsConfirmation(const AccountConfig &account, const Candidate &candidate);
80 static QString mailDomain(const AccountConfig &account);
81
82 private:
83 struct Run;
84 void probeNext(const std::shared_ptr<Run> &run);
85
86 SrvLookup m_srvLookup;
87 QList<QSslCertificate> m_ca;
88 int m_probeTimeoutMs = 5000;
89 quint16 m_fallbackPort = 4190;
90 };
91