Current note — PR #37: TabMail's app encoder now owns the complete raw Subject boundary for both Gmail builders and
IMAPProvider.buildEmail; it is deliberately no longer byte-identical to SwiftMail's encoder. This supersedes the historical "keep the two byte-for-byte identical" instruction retained below. It protects complete substrings consumed by the shipped unanchored Gmail/SwiftMail decoders, plus complete SP/HTAB-delimited composer words (including malformed=?=), while bare/unterminated shapes remain literal. It splits between Unicode scalars and uses 39-byte first / 45-byte continuation budgets so encoded-words stay within 75 octets and encoded physical Subject lines within 76. SwiftMail's equivalent library defects and MIME parameter interpolation remain open upstream work underIOS-IMAP-016/ issue #10; the fork remains deviation-free.
- Header fields MUST be 7-bit ASCII. Non-ASCII Subject/display-name text must be wrapped in an RFC 2047 encoded-word (
=?UTF-8?B?…?=). Injecting raw 8-bit UTF-8 into a header (the old behavior) lets downstream agents charset-guess and mojibake it (Korean가→갅= double UTF-8-as-Latin-1). Bodies were never affected — they carrycharset=UTF-8+ Content-Transfer-Encoding; headers have no mechanism except RFC 2047. - Encoder lives in TWO places (cannot share — SwiftMail is a remote SPM pin, not a local path): iOS app
TabMail/Helpers/RFC2047.swift(RFC2047.encodeHeaderValue, used byGmailProvider.buildRFC822/buildMIMEMessage); SwiftMail forkString.rfc2047EncodedHeader()(used byEmail+Content.writeHeadersSubject +EmailAddress.headerString()for From/To/Cc names, +EMLSerializer). Keep the two byte-for-byte identical. ≤45 UTF-8 bytes/word (≤75-octet word), split on char boundaries, folded withCRLF SPACE. - Gmail API decode is UNDOCUMENTED — the REST reference does not say whether
payload.headers[].valueis RFC 2047-decoded. SoGmailParse.parseMessagedecodes the Subject defensively viaRFC5322Parse.decodeRFC2047(idempotent no-op when there's no=?…?=). This guarantees our own encoded sent subjects (and any encoded incoming subject) render clean instead of literal=?UTF-8?B?…?=. Note:GmailParsedeliberately does NOT decode From/To/Cc names (theparseFromHeadertest asserts the raw encoded displayName) — out of scope; subject-only. RFC5322Parse.decodeRFC2047merges adjacent encoded-words (drops fold whitespace between two=?…?=, preserves whitespace around plain text) — required so folded long subjects decode without a stray\r\n. SwiftMail'sdecodeMIMEHeader()already did this.- Exchange/Outlook is unaffected —
ExchangeProvidersendsmessage["subject"]as a structured JSON field to Graph/sendMail; Graph builds the MIME and encodes the header server-side. Never hand-builds a header. - Fork changes need push + Package.resolved re-pin to reach IMAP/iCloud builds. The iOS GmailProvider fix is app code → live on next build.