mail
import mail
Mail, from the message to the socket.
mail builds and reads messages, and speaks the three protocols that
move them: SMTP to send, IMAP and POP3 to read. It also runs the other
end, with an SMTP server that accepts mail and an IMAP server that
serves it. All of it is written in Zuri.
import mail
var note = mail.message()
.set_from('reports@example.com')
.add_to('ann@example.com')
.set_subject('Quarterly report')
.set_text('The numbers are in.')
mail.send('smtp://mail.example.com', note, {
username: 'reports',
password: secret,
})
What is here
message() | starts a message, built up one call at a time |
parse() | reads one back |
send() | sends one, opening and closing the connection |
Message | a message, or one part of one |
Address | one address and the name written with it |
Headers | the header block, in order and case-insensitive |
SmtpClient | a connection to a server that sends mail |
ImapClient | a connection to a server that stores it |
Pop3Client | the older way of collecting it |
SmtpServer | the receiving end of SMTP |
ImapServer | the serving end of IMAP |
MailStore | where an ImapServer keeps mail |
MailError | the root of every error raised here |
Reading mail
import mail
var inbox = mail.imap('imaps://mail.example.com', {
username: 'ann',
password: secret,
})
inbox.select('INBOX')
for id in inbox.search('UNSEEN') {
echo inbox.fetch_message(id).subject()
}
inbox.logout()
Running a server
The servers are the same shape as http.HttpServer: bind, listen, and
hand each connection to a worker. An SmtpServer decides what to accept
through handlers, and an ImapServer answers from a MailStore, of
which two ship: one on disk in Maildir format and one in memory.
Security
Every client can start in TLS or negotiate it with STARTTLS, and
refuses to send credentials over a connection that is neither.
mail.dkim signs outgoing mail and verifies incoming mail.
The mail API
Every public name in mail, wherever it is declared. Each links to the
page that documents it.
| Name | Kind | Summary |
|---|---|---|
mail.ALLOWED_FLAGS | constant | |
mail.Address | class | One address, with the display name that was written alongside it. |
mail.Attachment | class | Something a message carries alongside what it says: a file to offer, or an image the HTML shows. |
mail.AuthenticationError | class | The credentials were not accepted, or no mechanism both ends understand was on offer. |
mail.BASE_CAPABILITIES | constant | |
mail.BodyPart | class | What one message is made of, without fetching it: the type of each part, its size, and where in the message… |
mail.ConnectionClosed | class | The connection closed while a conversation was still going on, or a command was issued on one that had… |
mail.ContentDisposition | class | What a part is for: shown where it sits, or offered as a file. |
mail.ContentType | class | A media type and the parameters that came with it. |
mail.DEFAULT_CHARSET | constant | |
mail.DEFAULT_MAX_RECIPIENTS | constant | |
mail.DEFAULT_MAX_SIZE | constant | |
mail.DEFAULT_TIMEOUT | constant | |
mail.DEFAULT_TYPE | constant | |
mail.DELIMITER | constant | |
mail.Entry | class | One message in the mailbox, as list() reports it. |
mail.Envelope | class | The addresses and dates out of a message’s headers, as the server parsed them, so a list of messages can be… |
mail.Group | class | A named group of addresses, as Managers: ann@example.com, bob@example.com; writes one. |
mail.Headers | class | The headers of a message or of one of its parts. |
mail.IMAP_SCHEMES | constant | |
mail.INDEX_FILE | constant | |
mail.INFO_SEPARATOR | constant | |
mail.ImapClient | class | A connection to a server that stores mail. |
mail.ImapError | class | The IMAP server answered a command with NO or BAD. |
mail.ImapServer | class | A server that serves mail. |
mail.ImapSession | class | One connection, and what it has got as far as doing. |
mail.MAILDIR_FLAGS | constant | |
mail.MAILDIR_PARTS | constant | |
mail.MAX_DATA_LINE | constant | |
mail.MAX_ERRORS | constant | |
mail.MAX_REPLY_LINE | constant | |
mail.MECHANISMS | constant | |
mail.MailError | class | Base class for every error this module raises. |
mail.MailStore | class | What a store has to be able to do. |
mail.Mailbox | class | An open mailbox, and what the server said about it when it opened. |
mail.MailboxError | class | Something went wrong in a mail store: a mailbox that does not exist, one that cannot be created, a message… |
mail.MailboxInfo | class | One mailbox as list() reports it: its name, the character that separates the levels of it, and what the… |
mail.MaildirStore | class | A store that keeps mail on disk in the Maildir layout. |
mail.MemoryStore | class | A store that keeps everything in the process and nothing on disk. |
mail.Message | class | One message, or one part of one. |
mail.MessageError | class | The bytes handed over are not a message, or are one that contradicts itself: a header with no colon in it, a… |
mail.MessageInfo | class | What a FETCH returned about one message. |
mail.POP3_SCHEMES | constant | |
mail.Pop3Client | class | A connection to a server that hands mail over. |
mail.Pop3Error | class | The POP3 server answered a command with -ERR. |
mail.ProtocolError | class | The server said something the protocol does not allow: a greeting that is not a greeting, a response with no… |
mail.Reply | class | One reply from the server: its code, and whatever it said with it. |
mail.SMTP_SCHEMES | constant | |
mail.STATES | constant | |
mail.SmtpClient | class | A connection to a server that sends mail. |
mail.SmtpError | class | The server rejected a command, and said so with a reply code. |
mail.SmtpPermanentError | class | A 5xx reply: the server will not accept the message, and trying again changes nothing. |
mail.SmtpServer | class | A server that accepts mail. |
mail.SmtpSession | class | One connection, and everything known about it so far. |
mail.SmtpTransientError | class | A 4xx reply: the server could not accept the message now, and the sender should try again later. |
mail.StateError | class | A command was issued that makes no sense in the state the connection is in: a fetch before a mailbox has been… |
mail.StoredMessage | class | One message in a store. |
mail.address.address | function | Builds an address without parsing anything. |
mail.address.format_list | function | Writes a list of addresses as a header value. |
mail.address.parse | function | Reads exactly one address. |
mail.address.parse_groups | function | Reads every address in a header value, keeping the groups. |
mail.address.parse_list | function | Reads every address in a header value. |
mail.attachment | function | Starts an attachment from what is in it. |
mail.dkim.ALGORITHMS | constant | The signing algorithms this implements. |
mail.dkim.CANONICALISATIONS | constant | The canonicalisations this implements, on either headers or body. |
mail.dkim.DEFAULT_HEADERS | constant | The headers signed when the signer is not told which to sign. |
mail.dkim.DkimError | class | Raised when a signature cannot be built: an algorithm this does not implement, a key that will not parse, a… |
mail.dkim.Result | class | What checking one signature came to. |
mail.dkim.Signature | class | One DKIM-Signature header, read into its parts. |
mail.dkim.Signer | class | Signs outgoing messages for one domain with one key. |
mail.dkim.canonicalise_body | function | Canonicalises a body. |
mail.dkim.canonicalise_header | function | Canonicalises one header. |
mail.dkim.is_signed | function | Whether a message carries at least one signature that checks out. |
mail.dkim.public_key_pem | function | Turns the p= value of a key record into a PEM the crypto module will accept. |
mail.dkim.verify | function | Checks every signature on a message. |
mail.encoding.TRANSFER_ENCODINGS | constant | |
mail.encoding.decode_base64 | function | Decodes base64, ignoring the line breaks and stray whitespace a message carries it with. |
mail.encoding.decode_body | function | Decodes a part’s body the way its Content-Transfer-Encoding says. |
mail.encoding.decode_parameters | function | Puts the parameters of a header back together. |
mail.encoding.decode_quoted_printable | function | Decodes quoted-printable. |
mail.encoding.decode_text | function | Turns bytes into text, reading them as the character set names. |
mail.encoding.decode_words | function | Reads the encoded words out of a header value and puts back the text they stand for. |
mail.encoding.encode_base64 | function | Encodes data as base64, wrapped into lines. |
mail.encoding.encode_body | function | Encodes a part’s body the way its Content-Transfer-Encoding says. |
mail.encoding.encode_parameter | function | Writes one parameter of a header, choosing the form its value needs. |
mail.encoding.encode_quoted_printable | function | Encodes data as quoted-printable. |
mail.encoding.encode_word | function | Encodes text as one or more RFC 2047 encoded words, so that a header can carry characters a header is not… |
mail.encoding.guess_encoding | function | Picks the transfer encoding a body should use. |
mail.encoding.is_ascii | function | Whether every byte is plain ASCII, which decides whether a header or a body needs encoding at all. |
mail.format_addresses | function | Writes a list of addresses as a header value. |
mail.headers.ADDRESS_HEADERS | constant | |
mail.headers.LINE_WIDTH | constant | |
mail.headers.fold | function | Writes one header, folded so that no line runs past the width. |
mail.headers.unfold | function | |
mail.imap | function | Opens a connection to a server that stores mail. |
mail.message | function | Starts a message. |
mail.multipart | function | |
mail.new_message_id | function | An identifier for a message, unique enough that no two ever collide. |
mail.parse | function | Reads a message. |
mail.parse_address | function | Reads one address out of text. |
mail.parse_address_groups | function | Reads every address out of text, keeping the groups as groups. |
mail.parse_address_list | function | Reads every address out of text, with any groups flattened into their members. |
mail.parser.Literal | class | A run of bytes a server sent as a literal. |
mail.parser.Reader | class | Reads responses off a connection, one at a time. |
mail.parser.Response | class | One response from the server. |
mail.parser.STATUSES | constant | |
mail.parser.SYSTEM_FLAGS | constant | |
mail.parser.quote | function | Writes a value the way a command has to carry it. |
mail.parser.tokenize | function | Reads a run of IMAP values out of text. |
mail.pool.Cluster | class | A running pool: the socket, the workers, and the loop feeding them. |
mail.pool.serve | function | Starts a pool and runs it until something closes it. |
mail.pool.start | function | Starts a pool without running the accept loop, so the address is known before the first connection. |
mail.pool.worker_main | function | What one worker isolate runs: build a server of its own, then serve whatever connections the acceptor hands… |
mail.pop3 | function | Opens a connection to a server that hands mail over. |
mail.sasl.Anonymous | class | ANONYMOUS: no identity at all, with an optional note saying who is knocking. |
mail.sasl.CramMd5 | class | CRAM-MD5: a keyed digest of the server’s challenge, so the password never travels. |
mail.sasl.External | class | EXTERNAL: the connection already established who this is, usually with a client certificate. |
mail.sasl.Login | class | LOGIN: the username and the password again, one prompt at a time. |
mail.sasl.Mechanism | class | What every mechanism looks like from the outside. |
mail.sasl.NEEDS_TLS | constant | The mechanisms that put the password itself on the wire, and so must never be used without TLS. |
mail.sasl.OAuthBearer | class | OAUTHBEARER: the standardised form of the same idea, from RFC 7628. |
mail.sasl.PREFERENCE | constant | The mechanisms this implements, strongest first, which is the order a client picks from what a server offers. |
mail.sasl.Plain | class | PLAIN: the username and the password, separated by zero bytes. |
mail.sasl.Scram | class | SCRAM-SHA-1 and SCRAM-SHA-256: the password is never sent, the server never has to store it, and the… |
mail.sasl.XOAuth2 | class | XOAUTH2: a bearer token rather than a password, which is what Google and Microsoft accept. |
mail.sasl.choose | function | Picks the mechanism to use. |
mail.sasl.of | function | Builds a mechanism by name. |
mail.sasl.prepare | function | Prepares a username or password the way RFC 4013 says to, so that two spellings of the same characters… |
mail.sasl.response | function | The answer to a CRAM-MD5 challenge. |
mail.send | function | Sends one message, opening the connection and closing it again. |
mail.smtp | function | Opens a connection to a server that sends mail. |
mail.stream.LineStream | class | A connection that reads and writes lines. |
mail.stream.MAX_LINE | constant | |
mail.stream.TLS_MODES | constant | How TLS is treated on a connection that did not start encrypted: require refuses to go on without it,… |
mail.stream.connect | function | Opens a connection to a host, in TLS or in the clear. |
mail.stream.endpoint | function | Reads a connection string into the pieces needed to open it. |
mail.stream.start_tls | function | Wraps a connection that started in the clear in TLS, which is what every STARTTLS comes down to. |
mail.text_part | function | Builds one text part. |
Submodules
| Module | Reached as | Summary |
|---|---|---|
mail.address | mail.address.* | Reading and writing the addresses in a mail header. |
mail.content | mail.* | The two headers that say what a part holds and what to do with it. |
mail.dkim | mail.dkim.* | DomainKeys Identified Mail: signing outgoing messages so a receiver can tell they came from the domain they… |
mail.encoding | mail.encoding.* | The encodings a message uses to get eight bit data through a seven bit pipe. |
mail.errors | mail.* | Every error the mail stack raises, under one root. |
mail.headers | mail.headers.* | The header block of a message: what is in it, in what order, and how it is written back out. |
mail.imap | mail.* | IMAP: reading mail where it is kept, rather than taking it away. |
mail.message | mail.* | A mail message: its headers, its body, and the tree of parts a body turns into once there is more than one… |
mail.pool | mail.pool.* | Running a mail server on more than one connection at a time. |
mail.pop3 | mail.* | POP3: the client end of collecting mail. |
mail.sasl | mail.sasl.* | The authentication mechanisms all three mail protocols share. |
mail.smtp | mail.* | SMTP: the protocol that moves mail from where it was written to where it is kept. |
mail.stream | mail.stream.* | A line-oriented connection, which is what all three mail protocols are underneath. |
Functions
parse_address()
mail.parse_address(text: string) -> Address
Reads one address out of text.
import mail
echo mail.parse_address('Ann <ann@example.com>').address
ann@example.com
Parameters
text(string)
Returns Address
Raises MessageError if the text holds no address, or several.
parse_address_list()
mail.parse_address_list(text: string) -> list
Reads every address out of text, with any groups flattened into their members.
Parameters
text(string)
Returns list — of Address
parse_address_groups()
mail.parse_address_groups(text: string) -> list
Reads every address out of text, keeping the groups as groups.
Parameters
text(string)
Returns list — of Address and Group
format_addresses()
mail.format_addresses(people: list) -> string
Writes a list of addresses as a header value.
Parameters
people(list) —Addressvalues,Groupvalues, or text to be parsed first.
Returns string
send()
mail.send(url: string, note, options: ?dict) -> Reply
Sends one message, opening the connection and closing it again.
This is the whole of sending mail for a program that sends one at a
time. A program sending many wants an SmtpClient of its own, kept open
across all of them.
import mail
mail.send('smtp://mail.example.com', mail.message()
.set_from('reports@example.com')
.add_to('ann@example.com')
.set_subject('Quarterly report')
.set_text('The numbers are in.'),
{ username: 'reports', password: secret })
Parameters
url(string) —smtp://hostorsmtps://host.note(Message)options(?dict) — EverythingSmtpClient.connect()takes, andfromandtoto override the envelope.
Returns Reply — the server’s verdict on the message.
Raises SmtpError if the server refuses any part of it.
smtp()
mail.smtp(url: string, options: ?dict) -> SmtpClient
Opens a connection to a server that sends mail.
Parameters
url(string)options(?dict)
Returns SmtpClient
imap()
mail.imap(url: string, options: ?dict) -> ImapClient
Opens a connection to a server that stores mail.
Parameters
url(string)options(?dict)
Returns ImapClient
pop3()
mail.pop3(url: string, options: ?dict) -> Pop3Client
Opens a connection to a server that hands mail over.
Parameters
url(string)options(?dict)
Returns Pop3Client
2026, Richard Ore and Zuri contributors