Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

mail.smtp.client

import mail

Everything here is re-exported by mail, so import mail is enough and the names are called as mail.*. Importing mail.smtp.client on its own works too and reaches the same definitions.

The sending end of SMTP.

SmtpClient opens a connection, negotiates what the server can do, authenticates, and hands messages over. Most programs want mail.send(), which does all of that for one message and closes the connection again; the client itself is for sending many, or for needing the steps apart.

import mail.smtp { SmtpClient }

var server = SmtpClient.connect('smtp://mail.example.com', {
  username: 'reports',
  password: secret,
})

for note in queue {
  server.send(note, nil)
}

server.quit()

Constants

SMTP_SCHEMES

mail.SMTP_SCHEMES = {...}

MAX_REPLY_LINE

mail.MAX_REPLY_LINE = 1002

Classes

Reply

class mail.Reply

One reply from the server: its code, and whatever it said with it.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

mail.Reply(code: number, lines: list)

Parameters

  • code (number) — The three digit reply code.
  • lines (list) — Each line of the reply, without its code.

Reply.is_positive()

mail.Reply.is_positive() -> bool

Whether the server accepted: a code in the 200s or 300s.

Returns bool

Reply.is_intermediate()

mail.Reply.is_intermediate() -> bool

Whether the server wants more from the client before it will say, which is the 300s.

Returns bool

Reply.is_transient()

mail.Reply.is_transient() -> bool

Whether this is a refusal that may not be one tomorrow.

Returns bool

Reply.is_permanent()

mail.Reply.is_permanent() -> bool

Whether this is a refusal that trying again will not change.

Returns bool

Reply.ok()

mail.Reply.ok(doing: ?string) -> Reply

Raises the error this reply stands for, or returns it when the server accepted.

Parameters

  • doing (?string) — What was being attempted, for the message.

Returns Reply — itself.

Raises SmtpTransientError|SmtpPermanentError

Reply.to_string()

mail.Reply.to_string()

SmtpClient

class mail.SmtpClient

A connection to a server that sends mail.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

mail.SmtpClient(connection, options: ?dict)

Builds a client around a connection that is already open.

connect() is what opens one; this is for a connection made some other way, and for tests that hand it a transcript instead of a socket.

Parameters

  • connection (LineStream)
  • options (?dict) — As connect().

SmtpClient.connect()

mail.SmtpClient.connect(url: string, options: ?dict) -> SmtpClient

Opens a connection and gets as far as being ready to send.

The greeting is read, capabilities are asked for, TLS is negotiated if the connection did not start with it, and the client authenticates when given something to authenticate with.

optiondefaultwhat it does
username, passwordnoneauthenticate when both are given
tokennoneauthenticate with a bearer token instead
tlsrequirerequire, prefer or disable, when not already encrypted
tls_configa default onethe trust settings for TLS
mechanismsall of themrestricts which are acceptable
client_namethe local hostnamethe name to greet the server with
timeout30000milliseconds to wait on the socket

Parameters

  • url (string) — smtp://host, smtps://host, or a bare host. A username and password in the string are used when the options do not carry them.
  • options (?dict)

Returns SmtpClient

Raises SmtpError if the server refuses the greeting.

Raises AuthenticationError if it refuses the credentials.

Raises ProtocolError if TLS is required and not offered.

SmtpClient.begin()

mail.SmtpClient.begin() -> SmtpClient

Reads the greeting, introduces the client, negotiates TLS and authenticates. connect() calls this; a client built around an existing connection has to.

Returns SmtpClient — itself.

SmtpClient.greeting()

mail.SmtpClient.greeting() -> Reply|nil

What the server said when the connection opened.

Returns Reply|nil

SmtpClient.capabilities()

mail.SmtpClient.capabilities() -> dict

What the server said it can do, by name. A capability with no value of its own maps to true.

Returns dict

SmtpClient.supports()

mail.SmtpClient.supports(name: string) -> bool

Whether the server offers a capability.

Parameters

  • name (string) — Matched without regard to case.

Returns bool

SmtpClient.is_secure()

mail.SmtpClient.is_secure() -> bool

Whether the connection is encrypted.

Returns bool

SmtpClient.is_authenticated()

mail.SmtpClient.is_authenticated() -> bool

Whether the client has authenticated.

Returns bool

SmtpClient.max_size()

mail.SmtpClient.max_size() -> number|nil

The largest message the server said it will take, or nil when it did not say.

Returns number|nil

SmtpClient.read_reply()

mail.SmtpClient.read_reply() -> Reply

Reads one reply, however many lines it runs to.

Returns Reply

Raises ProtocolError if what arrives is not a reply.

SmtpClient.command()

mail.SmtpClient.command(line: string) -> Reply

Sends a command and reads the reply it draws.

Parameters

  • line (string)

Returns Reply

SmtpClient.ehlo()

mail.SmtpClient.ehlo(name: ?string) -> Reply

Introduces the client and reads back what the server can do.

Falls back to the older HELO when the server does not understand EHLO, in which case there are no capabilities to read.

Parameters

  • name (?string) — The name to give. The one from the options, or the local hostname, when not given.

Returns Reply

Raises SmtpError if the server refuses both greetings.

SmtpClient.start_tls()

mail.SmtpClient.start_tls() -> SmtpClient

Negotiates TLS over a connection that started in the clear, and introduces the client again, because everything the server said before the handshake has to be thrown away.

Returns SmtpClient — itself.

Raises SmtpError if the server refuses.

SmtpClient.login()

mail.SmtpClient.login(mechanism: ?string) -> SmtpClient

Authenticates, choosing the strongest mechanism both ends know.

Parameters

  • mechanism (?string) — Forces one rather than choosing.

Returns SmtpClient — itself.

Raises AuthenticationError if there is no mechanism in common, if the credentials are refused, or if the only ones on offer would put the password on an unencrypted connection.

SmtpClient.authenticate()

mail.SmtpClient.authenticate(mechanism) -> SmtpClient

Runs one authentication exchange with a mechanism already built.

Parameters

  • mechanism (Mechanism)

Returns SmtpClient — itself.

Raises AuthenticationError if the server refuses.

SmtpClient.mail_from()

mail.SmtpClient.mail_from(sender, options: ?dict) -> Reply

Starts a transaction by naming the sender.

Parameters

  • sender (string|Address) — The return path. An empty string is the null sender a bounce is sent from.
  • options (?dict) — size to declare the message’s size, and body for 8BITMIME or BINARYMIME.

Returns Reply

Raises SmtpError if the server refuses the sender.

SmtpClient.rcpt_to()

mail.SmtpClient.rcpt_to(recipient, options: ?dict) -> Reply

Adds one recipient to the transaction.

Parameters

  • recipient (string|Address)
  • options (?dict) — notify and original for DSN.

Returns Reply

Raises SmtpError if the server refuses the recipient.

SmtpClient.data()

mail.SmtpClient.data(body) -> Reply

Hands over the message itself and ends the transaction.

Line endings are normalised and a leading dot on any line is doubled, so a body containing a line with one dot on it cannot end the message early.

Parameters

  • body (string|bytes)

Returns Reply — the server’s verdict on the whole message.

Raises SmtpError if the server refuses either the command or the message.

SmtpClient.bdat()

mail.SmtpClient.bdat(chunk, last: bool) -> Reply

Hands the message over in chunks, which is what CHUNKING is for.

Nothing is escaped, because a chunk carries its own length and has no terminator to collide with. Only works where the server offers CHUNKING.

Parameters

  • chunk (string|bytes)
  • last (bool) — Whether this is the final chunk.

Returns Reply

Raises StateError if the server does not offer CHUNKING.

Raises SmtpError if the server refuses.

SmtpClient.send()

mail.SmtpClient.send(message, options: ?dict) -> Reply

Sends one message: the sender, the recipients, and the message itself, in one transaction.

The sender comes from the message’s From and the recipients from its To, Cc and Bcc unless the options say otherwise. Bcc is removed before the message goes, which is the whole point of it.

server.send(note, nil)
server.send(note, { from: 'bounces@example.com' })

Parameters

  • message (Message)
  • options (?dict) — from and to to override the envelope, and anything mail_from() and rcpt_to() accept.

Returns Reply — the server’s verdict.

Raises StateError if there is no sender or no recipient.

Raises SmtpError if the server refuses any part of it.

SmtpClient.send_raw()

mail.SmtpClient.send_raw(sender, recipients: list, body, options: ?dict) -> Reply

Sends a message that is already bytes, to recipients given here.

Parameters

  • sender (string|Address)
  • recipients (list)
  • body (string|bytes)
  • options (?dict)

Returns Reply

Raises StateError if there are no recipients.

SmtpClient.rset()

mail.SmtpClient.rset() -> Reply

Abandons whatever transaction is in progress.

Returns Reply

SmtpClient.noop()

mail.SmtpClient.noop() -> Reply

Asks the server for nothing, which is how a connection is kept from going idle.

Returns Reply

SmtpClient.verify()

mail.SmtpClient.verify(address: string) -> Reply

Asks whether the server knows an address.

Most servers on the public internet refuse to answer this, because answering it hands an address list to whoever asks. A refusal is returned rather than raised, since it is an answer of a kind.

Parameters

  • address (string)

Returns Reply

SmtpClient.quit()

mail.SmtpClient.quit() -> Reply|nil

Says goodbye and closes the connection.

Returns Reply|nil — the server’s farewell, or nil when it had already gone.

SmtpClient.close()

mail.SmtpClient.close() -> SmtpClient

Closes the connection without saying goodbye.

Returns SmtpClient — itself.

SmtpClient.to_string()

mail.SmtpClient.to_string()

2026, Richard Ore and Zuri contributors