Implement BINARYMIME, the other half of RFC 3030
CHUNKING was here and BINARYMIME was refused with 555, which left the
framing without the thing it exists to frame. BDAT carries a length, so
it can carry content that holds what would otherwise be a terminator;
that is the whole reason RFC 3030 defines the two together and says
BINARYMIME can only be used with CHUNKING.
The server advertises it beside CHUNKING, takes `BODY=BINARYMIME`, and
answers DATA for such a message with 503 as §3 requires -- binary has no
line structure, so a line holding a single dot cannot mean the end of it.
Nothing had to change in the receive path: BDAT already copied octets
without touching line endings, which is what "preserve all bits in each
octet" asks for, and there is now a test that walks all 256 byte values
through it to keep that true.
The client gained `MailOptions.body`, so `BODY=` is sayable at all --
7BIT and 8BITMIME as well, which the client could parse off EHLO and
never send. Declaring `.binary_mime` commits the transaction to BDAT, and
`data` refuses it with `error.BinaryRequiresChunking` rather than making
the round trip to be told 503. The demo CLI exposes it as --binarymime,
which implies --chunking because there is no other way to send it.
`Envelope.Body` moved to `protocol.Body` and lost its `unspecified`
variant in favour of `?protocol.Body`, since the client needs the same
enum and the envelope's other optional parameters are already spelled
that way. That is the breaking part.
The torture dialogue asserted 555 for BODY=BINARYMIME, which was exim's
answer and is no longer ours; those cases now use BODY=BINARY, which is
still not a body-value, so they go on testing what they were written to
test.
Verified against postfix and exim, neither of which offers BINARYMIME:
the client refuses to send binary to them at all, which is what RFC 3030
demands without qualification. Bit-exactness is checked end to end
through the CLI, all 256 octets plus a bare CR and a lone dot line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDrB41sGu5k1ubD1ufbxqC
Implement BINARYMIME, the other half of RFC 3030
CHUNKING was here and BINARYMIME was refused with 555, which left the
framing without the thing it exists to frame. BDAT carries a length, so
it can carry content that holds what would otherwise be a terminator;
that is the whole reason RFC 3030 defines the two together and says
BINARYMIME can only be used with CHUNKING.
The server advertises it beside CHUNKING, takes `BODY=BINARYMIME`, and
answers DATA for such a message with 503 as §3 requires -- binary has no
line structure, so a line holding a single dot cannot mean the end of it.
Nothing had to change in the receive path: BDAT already copied octets
without touching line endings, which is what "preserve all bits in each
octet" asks for, and there is now a test that walks all 256 byte values
through it to keep that true.
The client gained `MailOptions.body`, so `BODY=` is sayable at all --
7BIT and 8BITMIME as well, which the client could parse off EHLO and
never send. Declaring `.binary_mime` commits the transaction to BDAT, and
`data` refuses it with `error.BinaryRequiresChunking` rather than making
the round trip to be told 503. The demo CLI exposes it as --binarymime,
which implies --chunking because there is no other way to send it.
`Envelope.Body` moved to `protocol.Body` and lost its `unspecified`
variant in favour of `?protocol.Body`, since the client needs the same
enum and the envelope's other optional parameters are already spelled
that way. That is the breaking part.
The torture dialogue asserted 555 for BODY=BINARYMIME, which was exim's
answer and is no longer ours; those cases now use BODY=BINARY, which is
still not a body-value, so they go on testing what they were written to
test.
Verified against postfix and exim, neither of which offers BINARYMIME:
the client refuses to send binary to them at all, which is what RFC 3030
demands without qualification. Bit-exactness is checked end to end
through the CLI, all 256 octets plus a bare CR and a lone dot line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDrB41sGu5k1ubD1ufbxqC
Implement BINARYMIME, the other half of RFC 3030
CHUNKING was here and BINARYMIME was refused with 555, which left the
framing without the thing it exists to frame. BDAT carries a length, so
it can carry content that holds what would otherwise be a terminator;
that is the whole reason RFC 3030 defines the two together and says
BINARYMIME can only be used with CHUNKING.
The server advertises it beside CHUNKING, takes `BODY=BINARYMIME`, and
answers DATA for such a message with 503 as §3 requires -- binary has no
line structure, so a line holding a single dot cannot mean the end of it.
Nothing had to change in the receive path: BDAT already copied octets
without touching line endings, which is what "preserve all bits in each
octet" asks for, and there is now a test that walks all 256 byte values
through it to keep that true.
The client gained `MailOptions.body`, so `BODY=` is sayable at all --
7BIT and 8BITMIME as well, which the client could parse off EHLO and
never send. Declaring `.binary_mime` commits the transaction to BDAT, and
`data` refuses it with `error.BinaryRequiresChunking` rather than making
the round trip to be told 503. The demo CLI exposes it as --binarymime,
which implies --chunking because there is no other way to send it.
`Envelope.Body` moved to `protocol.Body` and lost its `unspecified`
variant in favour of `?protocol.Body`, since the client needs the same
enum and the envelope's other optional parameters are already spelled
that way. That is the breaking part.
The torture dialogue asserted 555 for BODY=BINARYMIME, which was exim's
answer and is no longer ours; those cases now use BODY=BINARY, which is
still not a body-value, so they go on testing what they were written to
test.
Verified against postfix and exim, neither of which offers BINARYMIME:
the client refuses to send binary to them at all, which is what RFC 3030
demands without qualification. Bit-exactness is checked end to end
through the CLI, all 256 octets plus a bare CR and a lone dot line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDrB41sGu5k1ubD1ufbxqC
Implement BINARYMIME, the other half of RFC 3030
CHUNKING was here and BINARYMIME was refused with 555, which left the
framing without the thing it exists to frame. BDAT carries a length, so
it can carry content that holds what would otherwise be a terminator;
that is the whole reason RFC 3030 defines the two together and says
BINARYMIME can only be used with CHUNKING.
The server advertises it beside CHUNKING, takes `BODY=BINARYMIME`, and
answers DATA for such a message with 503 as §3 requires -- binary has no
line structure, so a line holding a single dot cannot mean the end of it.
Nothing had to change in the receive path: BDAT already copied octets
without touching line endings, which is what "preserve all bits in each
octet" asks for, and there is now a test that walks all 256 byte values
through it to keep that true.
The client gained `MailOptions.body`, so `BODY=` is sayable at all --
7BIT and 8BITMIME as well, which the client could parse off EHLO and
never send. Declaring `.binary_mime` commits the transaction to BDAT, and
`data` refuses it with `error.BinaryRequiresChunking` rather than making
the round trip to be told 503. The demo CLI exposes it as --binarymime,
which implies --chunking because there is no other way to send it.
`Envelope.Body` moved to `protocol.Body` and lost its `unspecified`
variant in favour of `?protocol.Body`, since the client needs the same
enum and the envelope's other optional parameters are already spelled
that way. That is the breaking part.
The torture dialogue asserted 555 for BODY=BINARYMIME, which was exim's
answer and is no longer ours; those cases now use BODY=BINARY, which is
still not a body-value, so they go on testing what they were written to
test.
Verified against postfix and exim, neither of which offers BINARYMIME:
the client refuses to send binary to them at all, which is what RFC 3030
demands without qualification. Bit-exactness is checked end to end
through the CLI, all 256 octets plus a bare CR and a lone dot line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDrB41sGu5k1ubD1ufbxqC
RFC review: standards list, doc links, exim-derived protocol gauntlet
README gains a Standards section listing every implemented RFC with
its per-side coverage (5321, 1870, 6152, 2920, 3207, 8314, 4954, 4616,
2195, draft-murchison-sasl-login, 3463/2034, 6531, and 8446 via
tls.zig). Doc comments now link each RFC mention to the datatracker,
with section fragments where a section is cited; authLogin's doc notes
it has no RFC.
The review surfaced two fixes: the server always emitted RFC 3463
enhanced status codes but never advertised ENHANCEDSTATUSCODES
(RFC 2034) - now it does; and root.zig's module doc still called TLS
an eventual feature.
Also adds a protocol gauntlet unit test distilled from exim's test
suite (test/scripts/0000-Basic, notably 0019's syntax-error dialogue
and the 0008/0100 dotted message lines), asserting the exact 28-reply
transcript and resulting envelope. The dialogue was first validated by
running exim's own scriptable test client (test/src/client.c, built
with zig cc) against zsmtp serve.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012HBHFhoTYa8TU9GLwobfbx