Write down the known gaps, and implement RFC 3461 DSN
The README grows a "Known gaps" section, because the list was being
rediscovered each time somebody asked what zsmtp does not do. It opens
with what is deliberately absent -- message composition, and everything an
MTA does around a session -- so that the rest reads as a list of things to
do rather than a list of complaints.
The first of them is now done. DSN is the SMTP extension of RFC 3461 and
nothing else: `RET` and `ENVID` on MAIL, `NOTIFY` and `ORCPT` on RCPT.
Generating the `multipart/report` that carries a delivery status back to
the sender is RFC 3464, which is message composition wearing a protocol
hat, and it stays out.
The server advertises DSN, validates all four parameters and answers a bad
one with 501 as RFC 3461 §6 asks. `RET` and `ENVID` land on the
`Envelope`; `NOTIFY` and `ORCPT` belong to a recipient rather than a
message, so `Envelope.recipients` is now a slice of `Recipient` and the
`rcptTo` callback receives one instead of a bare address -- which is the
breaking part of this change, along with RCPT parameters no longer being
refused wholesale with 555.
On the client, `mail` and `rcpt` are the parameterized forms of `mailFrom`
and `rcptTo`, and `mailFromUtf8` becomes a wrapper over `mail`. The demo
CLI exposes the four as --ret, --envid, --notify and --orcpt.
Both `ENVID` and the `ORCPT` address are xtext (RFC 3461 §4), so that
codec is in `protocol`: `writeXtext` escapes everything that is not an
xchar, which means the encoded form can never end the command line and a
value from untrusted input is safe by construction rather than by
checking. Decoding is strict in the other direction -- a byte the encoder
was obliged to escape is rejected rather than passed through -- so one
sequence of bytes has one spelling. The length limits are on the encoded
form, which is why they are checked there: 100 characters for ENVID, 500
for the whole ORCPT parameter.
Verified against real implementations in the interop test, which now sends
the DSN parameters to postfix and to exim (told to advertise DSN, which it
does not do by default) and round-trips them through zsmtp's own server,
where the ENVID comes back with its space intact and the ORCPT with the
'+' that had to be encoded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDrB41sGu5k1ubD1ufbxqC