# zsmtp An SMTP client and server library for Zig (RFC 5321). Both the client and the server run over plain `std.Io.Reader`/`std.Io.Writer` pairs, so they are transport-agnostic: wrap a TCP stream for real use, or fixed in-memory buffers in tests. Requires Zig 0.16. ## Client ```zig const zsmtp = @import("zsmtp"); var reply_buf: [1024]u8 = undefined; var client: zsmtp.Client = .init(&stream_reader.interface, &stream_writer.interface, &reply_buf); _ = try client.greet(); // read the 220 greeting _ = try client.hello("my-host.example.com"); // EHLO (HELO fallback), returns extensions try client.sendMail("me@example.com", &.{"you@example.net"}, message); try client.quit(); ``` Line endings in the message are normalized to CRLF and leading dots are stuffed automatically. On `error.UnexpectedReply`, `client.last_reply` holds the server's actual code and text. `mailFrom`/`rcptTo`/`sendMessage` are also available individually, as is `authPlain`. ## Server ```zig var session: zsmtp.Server = .init(&stream_reader.interface, &stream_writer.interface, .{ .context = &my_state, .vtable = &.{ .rcptTo = onRcptTo, // optional; accept/reject each recipient .message = onMessage, // required; receives envelope + message data }, }, .{ .hostname = "mx.example.com" }); try session.run(gpa); ``` `run` serves one connection until QUIT or disconnect, enforcing command sequencing, recipient and message-size limits, and un-stuffing message data. Listening, accepting, and concurrency are up to the caller. ## Demo CLI ```sh zig build # Debug server that prints received messages to stdout: ./zig-out/bin/zsmtp serve 2525 # Send a message read from stdin: printf 'Subject: hi\r\n\r\nhello\r\n' | \ ./zig-out/bin/zsmtp send 127.0.0.1 2525 me@example.com you@example.net ``` ## Status Plaintext SMTP only for now — STARTTLS/implicit TLS is the next planned step (the transport-agnostic design is meant to make that a drop-in layer). Not yet implemented: TLS, streaming (non-slice) message bodies, AUTH beyond PLAIN, and ESMTP parameter handling (SIZE=, BODY=) on the server side. ## Tests ```sh zig build test ```