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% 8 / 0 / 8
Functions: 100.0% 8 / 0 / 8
Branches: -% 0 / 0 / 0

service/Rfc2822Builder.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QByteArray>
4 #include <QDateTime>
5 #include <QString>
6 #include <QStringList>
7
8 // T-183: Builds RFC-2822 compliant email messages.
9 // Used by the composer backend (QuickComposeBackend) for sending (SMTP),
10 // draft saving (IMAP APPEND) and Sent-folder copies (IMAP APPEND), and by
11 // the invitation reply (iMIP).
12 class Rfc2822Builder {
13 public:
14 403 Rfc2822Builder() = default;
15
16 // Header setters
17 void setFrom(const QString &name, const QString &email);
18 void setTo(const QStringList &recipients);
19 void setCc(const QStringList &cc);
20 void setBcc(const QStringList &bcc);
21 void setSubject(const QString &subject);
22 void setDate(const QDateTime &date = QDateTime::currentDateTimeUtc());
23 void setMessageId(const QString &messageId = {});
24 void setInReplyTo(const QString &messageId);
25 void setReferences(const QStringList &messageIds);
26
27 enum class BccHeader { Omit, Include };
28
29 // Sprint 108 S108.2.4 (D-27, D-15, D-21): optional headers. Priority 1
30 // (high) and 5 (low) write Importance and X-Priority; 3 writes nothing.
31 5 void setReplyTo(const QStringList &addresses) { m_replyTo = addresses; }
32 67 void setPriority(int priority) { m_priority = priority; }
33 5 void setDispositionNotificationTo(const QString &address) { m_dispositionNotificationTo = address; }
34 // A private X-MailJD-* header (the resumable draft's identity, format and
35 // reference, D-21). Other names are ignored; values are header-safe.
36 void setMailJdHeader(const QString &name, const QString &value);
37
38 // Body
39 void setBodyText(const QString &plainText);
40 // Sprint 108 S108.2.4 (D-4, D-8): a formatted mail — the body becomes
41 // multipart/alternative of the text and this HTML (MailHtmlWriter output);
42 // inline images make the HTML a multipart/related with their cid: parts.
43 // Without HTML every build is byte-identical to the plain-text builder (G2).
44 34 void setBodyHtml(const QString &html) { m_bodyHtml = html; }
45 struct InlineImage {
46 QString contentId; // without <>
47 QString filename;
48 QByteArray data;
49 QByteArray mimeType; // e.g. "image/png"
50 };
51 6 void addInlineImage(const InlineImage &image) { m_inlineImages.append(image); }
52 // Sprint 93 S93.5.2: an iMIP message (RFC 6047 §2.4): the body becomes
53 // multipart/alternative of the text and
54 // "text/calendar; method=<method>; charset=UTF-8". Not combined with
55 // attachments (attachments win).
56 void setCalendarPart(const QByteArray &calendar, const QString &method);
57
58 // Attachments
59 struct Attachment {
60 QString filename;
61 QByteArray data;
62 QByteArray mimeType; // e.g. "application/octet-stream"
63 };
64 void addAttachment(const Attachment &attachment);
65 // Shared per-file size budget (F1): matches ubiquitous MTA limits and keeps
66 // a single attachment from exhausting RAM. The size is checked BEFORE any
67 // read, so oversize files never enter memory; the post-read check covers
68 // files that grew concurrently. Callers may pass a tighter budget.
69 static constexpr qint64 MaxAttachmentBytes = 25LL * 1024 * 1024;
70 // T-622/FUNC-07: Returns false if file cannot be opened
71 // F1: Returns false without reading when the file exceeds maxBytes.
72 bool addAttachmentFromFile(const QString &filePath,
73 qint64 maxBytes = MaxAttachmentBytes,
74 qint64 *loadedBytes = nullptr);
75
76 // D-17: the MIME type of a file from its content and name (QMimeDatabase);
77 // "application/octet-stream" when unknown.
78 static QByteArray mimeTypeForFile(const QString &filePath, const QByteArray &head = {});
79
80 // Build the final RFC-2822 message (CRLF line-endings, proper encoding)
81 QByteArray build(BccHeader bccHeader) const;
82
83 // S108.0.3: MIME boundaries become "----=_Part_<seed>_<n>" (n counts up
84 // per build) instead of random UUIDs, so golden outputs are byte-stable.
85 // Only the MIME oracles and fuzz targets use it.
86 14 void setDeterministicBoundaries(const QByteArray &seed) { m_boundarySeed = seed; }
87
88 // Access generated Message-ID (valid after build())
89 84 QString messageId() const { return m_messageId; }
90 // The Message-ID header value of a built message ("" when it has none).
91 static QString messageIdOf(const QByteArray &message);
92
93 // Utility: RFC 2047 encode a header value if it contains non-ASCII
94 // Longest whitespace-free run sent as plain text; a longer one is sent as
95 // encoded words, so a header line never exceeds RFC 5322's 998 bytes.
96 static constexpr qsizetype kMaxUnfoldableToken = 900;
97 static QByteArray encodeHeader(const QString &value);
98
99 // Utility: Encode address "Name <email>" with RFC-2047 for the name part
100 static QByteArray encodeAddress(const QString &name, const QString &email);
101
102 // Utility: Encode a list of addresses for To/Cc/Bcc headers
103 static QByteArray encodeAddressList(const QStringList &addresses);
104
105 // Utility: Quoted-Printable encode a UTF-8 body
106 static QByteArray encodeQuotedPrintable(const QByteArray &data);
107
108 private:
109 QByteArray generateBoundary() const;
110 void appendAttachmentParts(QByteArray &msg, const QByteArray &boundary) const;
111 void appendFormattedBody(QByteArray &msg) const;
112 static QByteArray generateMessageId(const QString &senderAddress);
113
114 QString m_fromName;
115 QString m_fromEmail;
116 QStringList m_to;
117 QStringList m_cc;
118 QStringList m_bcc;
119 QString m_subject;
120 QDateTime m_date;
121 mutable QString m_messageId;
122 QString m_inReplyTo;
123 QStringList m_references;
124 QString m_bodyText;
125 QString m_bodyHtml;
126 QList<InlineImage> m_inlineImages;
127 QStringList m_replyTo;
128 int m_priority = 3;
129 QString m_dispositionNotificationTo;
130 QList<QPair<QByteArray, QString>> m_mailJdHeaders;
131 QByteArray m_calendar;
132 QByteArray m_calendarMethod;
133 QList<Attachment> m_attachments;
134 QByteArray m_boundarySeed;
135 mutable int m_boundaryCounter = 0;
136 };
137