Repository navigation
v3.0.0
⛓️💥 Breaking changes
- STARTTLS is now enforced. With
SocketType.starttlsorplain, IMAP and SMTP used to send credentials in clear text if the server did not advertise STARTTLS, so a man-in-the-middle only had to strip that capability. The client now refuses to authenticate unlessplainNoStartTlswas explicitly chosen. Capabilities from before the TLS upgrade are discarded, and the certificate is checked against the configured host name. - Streams replace
event_bus. Events are now delivered through Dart streams instead of theevent_buspackage.
🔒 Security fixes
A security review in September 2026 (#296) fixed several serious problems. Upgrading is strongly recommended.
- STARTTLS is now enforced. With
SocketType.starttlsorplain, IMAP and SMTP used to send credentials in clear text if the server did not advertise STARTTLS, so a man-in-the-middle only had to strip that capability. The client now refuses to authenticate unlessplainNoStartTlswas explicitly chosen. Capabilities from before the TLS upgrade are discarded, and the certificate is checked against the configured host name. - SMTP: DATA payload could be cut short. Two consecutive lines consisting of a single period ended the DATA phase early, and the rest of the message was run as SMTP commands on the authenticated session. Every line starting with a period is now dot-stuffed correctly. Lines such as
.NETalso no longer lose their leading period. - Header and SMTP envelope injection is blocked. CR/LF is stripped from header values, so a subject, display name, file name or custom header can no longer start another header. Email addresses with control characters, whitespace, angle brackets or list separators are refused. The address parser handles escaped quotes correctly and no longer crashes on emoji.
- IMAP command injection is blocked. User names, passwords, mailbox names and search terms are now quoted and escaped according to RFC 3501. Credentials containing
",\, non-ASCII characters or line breaks previously produced malformed or injectable commands. - Bcc recipients could leak. A long Bcc header is folded over several lines, and only its first line was removed, so every To/Cc recipient received the rest of the Bcc list.
- The DATA command was fixed by David Hobley (#294).
- The BDAT command, which is preferred whenever a server announces CHUNKING, had the same problem and is fixed as well. The Bcc header is now removed only from the header section, so a body line starting with
Bcc:is left alone.
- Credentials no longer appear in logs.
toString()ofMailAccount,MailServerConfigandOauthTokennow hides passwords and tokens.toJson()is unchanged. - Safer MIME parsing. Multipart delimiters are only accepted at the start of a line, so a text line cannot smuggle in a part that other clients and scanners do not see. Nesting is limited to 50 levels.
- Safer auto-discovery. Fetching configurations has a timeout and a 1 MiB size limit and no longer follows https-to-http redirects. Results obtained over plain http are flagged through
ClientConfig.isFromInsecureSource. - Secure random IDs. Message-IDs and multipart boundaries use
Random.secure()where available. - Exact plus-alias matching.
ad+min@example.comis no longer treated asadmin@example.com.
✨ New features
- Streams replace
event_bus. Events are now delivered through Dart streams instead of theevent_buspackage. - IMAP NAMESPACE extension (RFC 2342). New
ImapClient.namespace()returns the personal, other users' and shared namespaces. Contributed by Dmitry Protasov (#295). - APPEND with an explicit INTERNALDATE and from raw bytes. Copied messages keep their original date, and messages fetched with
BODY[]can be appended without corruption through the newappendMessageBytes. Contributed by David Hobley (#292). - Mutual TLS (mTLS).
ImapClient,SmtpClientandClientBaseaccept an optionalSecurityContextfor client certificate authentication. This change also fixes a missingonBadCertificatewhen upgrading to SSL. Contributed by icd360sevofficial (#273). idleStart(waitForContinuation: true). The returned future waits until the server confirms IDLE (RFC 2177), which is useful when handing the connection off to another process. It fails if the connection is lost first. Contributed by BrLumen (#274).- Gmail extensions in FETCH.
FetchParsernow understandsX-GM-THRID,X-GM-MSGIDandX-GM-LABELS. Contributed by dev10m-adv (#275). - POP3 with OAuth.
PopClient.authenticateWithOAuth2()usesAUTH XOAUTH2.PopClientalso accepts aSecurityContext.
🐛 Bug fixes
Messages could be lost, deleted or delivered incorrectly
- Wrong message deleted.
deleteMessages()and the move/trash fallbacks used a plainSTOREfor UID sequences, so a different message could be flagged and permanently removed. They now useUID STOREand, where available,UID EXPUNGElimited to the affected messages. Where supported,UNSELECTreplacesCLOSE, so messages deleted withcanUndoare no longer expunged as a side effect. - SMTP ignored rejections. Negative replies to MAIL FROM, RCPT TO, DATA and BDAT were ignored. A message could be partially delivered while the caller saw success. The transaction is now aborted with
RSET, the connection stays usable, and the call fails with the rejecting reply. - Invalid envelope addresses are reported as
SmtpException, soMailClient.sendMessagefails with aMailExceptioninstead of an uncaughtArgumentError. - POP3. Polling compared message counts the wrong way round and never found new messages. Retrieved messages had no sequence id and could not be deleted or re-fetched, and undelete and
store()were broken. A failed retrieve no longer makes the next poll skip the remaining new messages. - Quoted-printable attachments were returned undecoded (
=25PDF=2D1=2E7instead of%PDF-1.7). They are now decoded to their bytes. Contributed by David Hobley (#287). - Empty body for servers using bare LF line endings. A fetched
BODY[]literal is now normalised on the raw bytes, without corrupting non-UTF-8 charsets. Contributed by David Hobley (#293).
Connections hanging or breaking
- Reconnect deadlock.
MailClient.reconnect()andresume()could hang forever anddisconnect()could not abort them. The retry interval now grows exponentially, and an aborted reconnect no longer drops events permanently. - Operations hanging on connection problems. Connecting, and pending IMAP, SMTP and POP commands, now fail instead of waiting forever when the connection is lost, the server stays silent or a write fails.
- David Hobley fixed the IMAP case (#288).
- Failed pending tasks report the plain error message instead of nested "connection lost: …" text.
- IMAP survives malformed server responses instead of leaving every later command queued forever. Literal sizes are validated and capped at 128 MiB (configurable), and parsing is bounds-checked.
- SMTP: bare reply codes such as
250<CRLF>no longer throw and hang the pending command. Contributed by BrLumen (#285). - IMAP IDLE no longer leaks an unhandled error when it is still queued during a disconnect, which could terminate an isolate with
errorsAreFatal. Contributed by BrLumen (#302). - Reconnecting clients reset their response parsers, so a half-received reply from the old connection no longer corrupts the first replies of the next one. Manually connected sockets no longer create an unawaited greeting completer.
- SMTP and POP multi-line replies are framed correctly. A multi-line EHLO reply split across chunks is no longer truncated, and
LIST/UIDLon an empty POP3 mailbox now completes. - OAuth. Rotated refresh tokens are kept, concurrent refreshes are serialised, and the token is checked for expiry before the SMTP connection is authenticated.
Mailbox names
- Parentheses. Mailbox names such as
INBOX.Financial.Audit(s)work in extended LIST responses and in commands. Contributed by David Hobley (#289). - Unquoted mailbox names in STATUS responses, as sent by Dovecot, are parsed. Previously MESSAGES, UNSEEN and UIDNEXT stayed at 0. Contributed by David Hobley (#290).
- HIGHESTMODSEQ from a STATUS response is recorded. Contributed by David Hobley (#291).
- No double encoding of the target mailbox path for APPEND, COPY and MOVE, which servers rejected with e.g.
NO [TRYCREATE]. Contributed by BrLumen (#298). - Encoding unless
UTF8=ACCEPTis enabled.selectMailboxByPath()encodes names in modified UTF-7, and UTF-8 is only sent onceUTF8=ACCEPThas been enabled (RFC 6855). Gmail otherwise answersBAD Could not parse command. Contributed by BrLumen (#300).
Encoding and parsing
- Date headers. Half-hour zones west of Greenwich were rendered as
-03-30, and UTC as-0000. Two- and three-digit years now follow RFC 5322. - Header encoding.
- Non-ASCII display names are rendered as bare RFC 2047 encoded-words, not inside quoted strings.
- Encoded words are labelled
UTF-8. - The Q and B encoders no longer break on astral characters such as emoji.
- Header folding never splits an encoded word.
- Folding no longer happens inside long email addresses or directly after an opening angle bracket. Contributed by nogringo (#272).
- Lenient MIME decoding. Quoted-printable and base64 decoders no longer throw on truncated or invalid input, and undecodable encoded words are kept verbatim. Binary bodies are no longer corrupted by a UTF-8 round trip. Header parameters are split correctly according to RFC 2231.
- Bare LF line endings are tolerated when parsing MIME text. Contributed by nogringo.
- Auto-discovery.
UsernameType.emailLocalPartnow returns the local part instead of the domain, andforceSslConnectionalso updates the generic preferred servers. - DKIM signing. The signer now hashes the whole canonicalized body and signs From, To, Cc, Subject, Date, Message-ID, MIME-Version and Content-Type. Building a message twice no longer duplicates the text part.
- SMTP AUTH PLAIN/LOGIN credentials are encoded as UTF-8 (RFC 4616), so umlauts and non-Latin-1 characters work.
⚡ Performance
- Fixed an O(n²) slowdown in
MailCodec.wrapText(). Contributed by nogringo (#276). - POP3 multi-line reply detection scans only new bytes instead of the whole buffer on every chunk.
🧹 Maintenance
- Shared the STARTTLS enforcement and the write-failure handling between the IMAP, POP and SMTP clients.
- Simplified how the IMAP parser finds the next literal marker.
- Corrected the package name and upgraded dependencies.
- Resolved eight
dart analyzeissues (nogringo). - Added tests for quoted-printable edge cases, the SMTP and POP write-failure path, and the new IMAP features.
- Updated the analysis options, example, readme, docs and changelog.
Contributors
| Contributor | Contributions |
|---|---|
| Robert Virkus | Security review, reliability and codec fixes, event streams, maintenance |
| David Hobley | APPEND with INTERNALDATE and raw bytes, Bcc leak fix, six IMAP, codec and parsing fixes |
| BrLumen | idleStart continuation flag, mailbox path encoding fixes, SMTP bare reply codes, IDLE error leak |
| nogringo | Header folding fixes, bare LF tolerance, wrapText() performance, analyzer cleanup |
| Dmitry Protasov | IMAP NAMESPACE extension |
| icd360sevofficial | mTLS SecurityContext support |
| dev10m-adv | Gmail extension fields in FetchParser |