smtp

An SMTP client (RFC 5321) written in Ecko, over std.net raw sockets + STARTTLS.

ecko get github.com/ecko-lang/smtp-client
import smtp

ecko get records this under smtp-client, which is not a valid Ecko identifier. Alias it to smtp in the dependencies block of your ecko.json.

Declares net. A capability is only advisory in the manifest: what the package actually gets is the grant you give it when you import it.

Version 0.9.5 - source - MIT.


addr_of(s)

addr_of("Ada <ada@x>") -> "ada@x"; a bare address passes through trimmed.

parse_reply_line(line)

One reply line -> { code, more, text }. "250-x" continues a multiline reply; "250 x" (or a bare "250") ends it.

dot_stuff(text)

Normalize line endings to CRLF and escape leading dots (RFC 5321 §4.5.2), so a body line of "." can't terminate the DATA phase early.

auth_plain(user, pass)

The AUTH PLAIN initial response: base64("\0user\0pass").

build_message(msg)

build_message(msg) -> RFC 5322 text (CRLF line endings): From / To / Subject [/ Date] / extra headers (sorted by name) / MIME headers, a blank line, then the body. msg: { from, to (string|list), subject, body, date?, headers? }.

read_reply(sock)

read_reply(sock) -> { code, lines }, collecting a full (possibly multiline) server reply.

expect_code(sock, want, label)

Require the next reply to carry want, or throw { kind: "smtp", code }.

command(sock, line, want)

Send one command line and require a reply code. Errors are labelled with the command VERB only, so AUTH credentials never appear in error text.

send(server, msg)

send(server, msg) -> { accepted, reply } server: { host, port?, tls?, user?, pass?, verify?, helo?, mock? } msg: { from, to (string|list), subject, body, date?, headers? }

The password may be a secret - it is revealed only for the AUTH exchange.