mail.smtp.client
import mail
Everything here is re-exported by
import mailis enough and the names are called asmail.*. Importingmail.smtp.clienton 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(), soechoandprint()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(), soechoandprint()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) — Asconnect().
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.
| option | default | what it does |
|---|---|---|
username, password | none | authenticate when both are given |
token | none | authenticate with a bearer token instead |
tls | require | require, prefer or disable, when not already encrypted |
tls_config | a default one | the trust settings for TLS |
mechanisms | all of them | restricts which are acceptable |
client_name | the local hostname | the name to greet the server with |
timeout | 30000 | milliseconds 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) —sizeto declare the message’s size, andbodyfor8BITMIMEorBINARYMIME.
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) —notifyandoriginalfor 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) —fromandtoto override the envelope, and anythingmail_from()andrcpt_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