Skip to content

net.imap #

A sequence set names the messages a command applies to (RFC 3501 section 9, sequence-set). It is a comma separated list of numbers and ranges, and * stands for the highest number in the mailbox.

Ranges matter: a mailbox with fifty thousand messages is addressed as 1:50000, where a list of every number would be a command line no server is obliged to accept. Modified UTF-7 is how IMAP carries mailbox names that are not plain US-ASCII (RFC 3501 section 5.1.3). It differs from UTF-7 on three points: & shifts into base64 rather than +, the base64 alphabet ends in , instead of / so that the popular hierarchy delimiter stays usable, and printable US-ASCII must never be encoded.

Names travel encoded and are handed to the caller decoded, so a program using this module works in ordinary UTF-8 throughout.

Constants #

const default_port = 143

The port a server listens on depends on whether TLS is negotiated up front.

const default_ssl_port = 993

fn new_client #

fn new_client(config Config) !&Client

new_client opens a session, greets the server, upgrades the connection if asked to, and logs in.

fn parse_seq_set #

fn parse_seq_set(s string) !SeqSet

parse_seq_set reads a set back from its wire form, such as 2,4:7,9:*.

fn seq_all #

fn seq_all() SeqSet

seq_all is the set every message in the mailbox belongs to, 1:*.

fn seq_range #

fn seq_range(start u32, stop u32) SeqSet

seq_range builds a set holding the single range start:stop.

fn seq_set #

fn seq_set(numbers []u32) SeqSet

seq_set builds a set from a list of numbers.

fn utf7_decode #

fn utf7_decode(s string) !string

utf7_decode reads a mailbox name back out of modified UTF-7.

Bytes above US-ASCII are passed through as they are: some servers send raw UTF-8 despite the convention, and refusing it would hide their mailboxes.

fn utf7_encode #

fn utf7_encode(s string) string

utf7_encode renders a mailbox name in modified UTF-7.

fn AuthMethod.from #

fn AuthMethod.from[W](input W) !AuthMethod

fn Status.from #

fn Status.from[W](input W) !Status

fn TokenKind.from #

fn TokenKind.from[W](input W) !TokenKind

enum AuthMethod #

enum AuthMethod {
	login
	plain
}

AuthMethod selects how new_client authenticates configured credentials.

enum Status #

enum Status {
	ok
	no
	bad
	bye
	preauth
}

Status is the outcome a tagged completion reports.

struct Address #

struct Address {
pub:
	name string
	// mailbox is the part before the `@`, or the group name when `host` is
	// empty and this address opens a group.
	mailbox string
	// host is the part after the `@`.
	host string
}

Address is one sender or recipient, as an envelope carries it. name is the display name, empty when the message gave none.

fn (Address) addr #

fn (a &Address) addr() string

addr renders the address as someone@example.com, and is empty for the markers that open and close a group.

struct BodyStructure #

struct BodyStructure {
pub:
	media_type        string
	media_subtype     string
	params            map[string]string
	id                string
	description       string
	encoding          string
	message_envelope  ?Envelope
	message_structure ?&BodyStructure
	// size is the part's length in octets, and `lines` its line count for a
	// text part.
	size  u32
	lines u32
	parts []BodyStructure
}

BodyStructure describes one MIME part. A multipart has parts filled in and a media type of multipart. A message/rfc822 part retains the attached message's envelope and MIME tree in message_envelope and message_structure.

fn (BodyStructure) mime_type #

fn (b &BodyStructure) mime_type() string

mime_type renders the pair as it appears in a Content-Type header.

struct Client #

struct Client {
	Config
mut:
	conn     net.TcpConn
	ssl_conn &ssl.SSLConn = unsafe { nil }
	dec      ?&Decoder
	tag_seq  int
	// transport_open is separate from is_open: an unsolicited BYE makes the
	// session unusable before the local socket has necessarily been closed.
	transport_open      bool
	authenticated       bool
	logging_out         bool
	server_capabilities []string
pub mut:
	is_open   bool
	encrypted bool
	// selected names the mailbox the session is working in, and is empty when
	// none has been selected.
	selected string
	// exists and recent track the counts the server reports, which it may do
	// during any command as messages arrive or are removed by another client.
	exists u32
	recent u32
}

Client is a connection to an IMAP server.

fn (Client) connect #

fn (mut c Client) connect() !

connect opens the transport and reads the server greeting, without logging in. new_client calls it; call it directly only to drive a session by hand.

fn (Client) login #

fn (mut c Client) login() !

login authenticates with the LOGIN command, and does nothing when no username was configured.

fn (Client) login_plain #

fn (mut c Client) login_plain() !

login_plain authenticates with the SASL PLAIN mechanism instead, which some servers require and others prefer.

fn (Client) capability #

fn (mut c Client) capability() ![]string

capability returns the extensions the server advertises.

fn (Client) supports #

fn (mut c Client) supports(name string) !bool

supports reports whether the server advertises name, which is how an optional command should be guarded before it is sent.

fn (Client) noop #

fn (mut c Client) noop() !

noop asks the server for nothing. It keeps the connection alive, and gives the server the chance to report anything that changed in the mailbox.

fn (Client) list_mailboxes #

fn (mut c Client) list_mailboxes(reference string, pattern string) ![]MailboxInfo

list_mailboxes returns the mailboxes matching pattern below reference.

The usual call is list_mailboxes('', '*'), which lists everything the account can see. % matches within one level of the hierarchy where * crosses levels.

fn (Client) list_subscribed #

fn (mut c Client) list_subscribed(reference string, pattern string) ![]MailboxInfo

list_subscribed returns the subscribed mailboxes matching pattern, which is the set a mail client would show by default.

fn (Client) select_mailbox #

fn (mut c Client) select_mailbox(name string) !Mailbox

select_mailbox opens a mailbox for reading and writing, and returns its state. Later fetches, searches and stores act on it.

fn (Client) examine_mailbox #

fn (mut c Client) examine_mailbox(name string) !Mailbox

examine_mailbox opens a mailbox read-only. It behaves like select_mailbox, except that nothing the session does marks messages as seen or otherwise alters the mailbox.

fn (Client) status #

fn (mut c Client) status(name string, items []string) !MailboxStatus

status reports on a mailbox without selecting it, which is how a client polls for new mail while working in another mailbox.

items names what to ask for: MESSAGES, RECENT, UIDNEXT, UIDVALIDITY and UNSEEN.

fn (Client) create_mailbox #

fn (mut c Client) create_mailbox(name string) !

create_mailbox creates a mailbox.

fn (Client) delete_mailbox #

fn (mut c Client) delete_mailbox(name string) !

delete_mailbox deletes a mailbox and everything in it.

fn (Client) subscribe #

fn (mut c Client) subscribe(name string) !

subscribe adds a mailbox to the set a client shows by default.

fn (Client) unsubscribe #

fn (mut c Client) unsubscribe(name string) !

unsubscribe removes it from that set, without touching the mailbox itself.

fn (Client) rename_mailbox #

fn (mut c Client) rename_mailbox(from string, to string) !

rename_mailbox renames a mailbox, along with everything below it in the hierarchy.

fn (Client) append #

fn (mut c Client) append(mailbox string, flags []string, stamp time.Time, body []u8) !

append adds a message to a mailbox, which is how a sent message is filed into a Sent folder or a draft is saved.

flags are set on the new message, commonly \Seen for a message the user has already read. stamp becomes its internal date; pass the zero time to let the server use the moment of delivery.

fn (Client) search #

fn (mut c Client) search(criteria string) !SeqSet

search returns the sequence numbers of the messages in the selected mailbox matching criteria, which is an IMAP search key such as UNSEEN, FROM "someone@example.com" or SINCE 1-Jan-2026.

fn (Client) fetch #

fn (mut c Client) fetch(set SeqSet, items string) ![]Message

fetch retrieves items for each message in set.

items is an IMAP fetch specification. BODY.PEEK[] takes the whole message without marking it read, BODY.PEEK[HEADER] takes just the headers, and (UID FLAGS ENVELOPE) takes metadata alone.

fn (Client) uid_fetch #

fn (mut c Client) uid_fetch(set SeqSet, items string) ![]Message

uid_fetch behaves like fetch but addresses messages by UID.

fn (Client) store #

fn (mut c Client) store(set SeqSet, action string, flags []string) ![]Message

store changes the flags of the messages in set, and returns what the server reports the new flags to be.

action is +FLAGS to add, -FLAGS to remove, or FLAGS to replace. Appending .SILENT tells the server not to echo the result back, in which case the returned list is empty.

fn (Client) uid_store #

fn (mut c Client) uid_store(set SeqSet, action string, flags []string) ![]Message

uid_store behaves like store but addresses messages by UID.

fn (Client) mark_seen #

fn (mut c Client) mark_seen(set SeqSet) !

mark_seen flags messages as read.

fn (Client) mark_deleted #

fn (mut c Client) mark_deleted(set SeqSet) !

mark_deleted flags messages for deletion. They go on being readable until expunge runs.

fn (Client) copy #

fn (mut c Client) copy(set SeqSet, dest string) !

copy copies messages into another mailbox, leaving the originals in place.

fn (Client) uid_copy #

fn (mut c Client) uid_copy(set SeqSet, dest string) !

uid_copy behaves like copy but addresses messages by UID.

fn (Client) move #

fn (mut c Client) move(set SeqSet, dest string) !

move moves messages into another mailbox in one step (RFC 6851), which a copy followed by a delete cannot do without a window where the message exists twice or not at all.

It needs the MOVE capability; check supports('MOVE') first when the server is not known.

fn (Client) uid_move #

fn (mut c Client) uid_move(set SeqSet, dest string) !

uid_move behaves like move but addresses messages by UID.

fn (Client) expunge #

fn (mut c Client) expunge() ![]u32

expunge permanently removes every message flagged \Deleted from the selected mailbox, and returns the sequence numbers the server reported as removed.

The numbers are reported one at a time, each already renumbered by the removals before it, which is why the same number can appear twice.

fn (Client) check #

fn (mut c Client) check() !

check asks the server to bring its own housekeeping up to date. It is not a NOOP: it may take real time, and it is the right thing to call before a long idle period.

fn (Client) close_mailbox #

fn (mut c Client) close_mailbox() !

close_mailbox leaves the selected mailbox, silently expunging any message flagged \Deleted on the way out.

fn (Client) unselect #

fn (mut c Client) unselect() !

unselect leaves the selected mailbox without expunging anything (RFC 3691), which is what a client wants when the user simply navigated away.

It needs the UNSELECT capability.

fn (Client) logout #

fn (mut c Client) logout() !

logout ends the session politely, giving the server the chance to close the connection itself.

fn (Client) close #

fn (mut c Client) close() !

close logs out and tears down the connection. It is safe to call on a session that is already closed.

fn (Client) command #

fn (mut c Client) command(cmd string) !string

command sends one command with a fresh tag and returns the completion text, so that a caller can reach an extension this module does not wrap. The tag is added here; pass the command without one.

struct Config #

struct Config {
pub:
	server                 string
	port                   int
	username               string
	password               string
	ssl                    bool
	starttls               bool
	timeout                time.Duration
	validate               bool = true
	verify                 string
	cert                   string
	cert_key               string
	in_memory_verification bool
	allow_insecure_auth    bool
	auth_method            AuthMethod
}

Config holds the settings used to open a session.

Leave port unset to take 993 when ssl is true and 143 otherwise. Set ssl for a connection encrypted from the first byte, or starttls to upgrade a plain connection once it is open. The two are mutually exclusive. TLS certificates are validated by default. A TLS connection with validation enabled requires verify to name a PEM CA bundle; set in_memory_verification when it contains the PEM data itself. cert and cert_key configure a client certificate when the server requires one. Credentials are refused on an unencrypted connection unless allow_insecure_auth is explicitly set.

struct Envelope #

struct Envelope {
pub:
	date        string
	subject     string
	from        []Address
	sender      []Address
	reply_to    []Address
	to          []Address
	cc          []Address
	bcc         []Address
	in_reply_to string
	message_id  string
}

Envelope is the parsed header of a message, which a server can produce without the client fetching and parsing the header itself.

struct Mailbox #

struct Mailbox {
pub:
	name string
	// exists is the number of messages in the mailbox.
	exists u32
	// recent is how many of them arrived since the last session looked.
	recent u32
	// unseen is the sequence number of the first unread message, zero when the
	// server did not report one.
	unseen u32
	// flags are the flags this mailbox can hold.
	flags []string
	// permanent_flags are the ones a STORE can make stick. A `\*` among them
	// means the mailbox accepts new keywords.
	permanent_flags []string
	// uid_validity changes when the server can no longer promise that UIDs
	// from an earlier session still name the same messages.
	uid_validity u32
	// uid_next is the UID the next arriving message is expected to take.
	uid_next u32
	// read_only is true for a mailbox opened with `examine`, and also for one
	// opened with `select` that the server would only give out read-only.
	read_only bool
}

Mailbox is the state of a mailbox at the moment it was selected.

struct MailboxInfo #

struct MailboxInfo {
pub:
	name string
	// delimiter separates the levels of a hierarchical name, and is empty when
	// the server presents a flat namespace.
	delimiter string
	// attributes are the server's notes about the mailbox, such as `\Noselect`
	// for a name that only exists to hold children.
	attributes []string
}

MailboxInfo is one entry of a mailbox listing.

struct MailboxStatus #

struct MailboxStatus {
pub:
	name         string
	messages     u32
	recent       u32
	uid_next     u32
	uid_validity u32
	unseen       u32
}

MailboxStatus is what STATUS reports about a mailbox without selecting it.

struct Message #

struct Message {
pub:
	seq   u32
	uid   u32
	flags []string
	// size is what RFC822.SIZE reported.
	size u32
	// internal_date is when the server took delivery, which is not the Date
	// header and does not change when a message is copied.
	internal_date time.Time
	envelope      ?Envelope
	structure     ?BodyStructure
	// sections holds every body section the fetch asked for, keyed by the
	// specification the server echoed back: `BODY[]` for the whole message,
	// `BODY[HEADER]` for the header block, `BODY[1.2]` for one MIME part.
	sections map[string]string
}

Message is one fetched message, holding whatever the fetch asked for.

seq is its sequence number in the selected mailbox and is always set. The rest is filled in only when the fetch requested it.

fn (Message) body #

fn (m &Message) body() string

body returns the whole message when it was fetched, and the lone section when exactly one was, which is what a fetch of a single section wants.

A fetch of several sections has no one answer, so it gives back nothing; read sections directly in that case.

struct SeqRange #

struct SeqRange {
pub:
	start u32
	stop  u32
}

SeqRange is one number or one range of them. A single number has start equal to stop. A stop of zero means *, so {5, 0} reads 5:* and {0, 0} reads *.

fn (SeqRange) str #

fn (r SeqRange) str() string

str renders one range, collapsing n:n back to n.

struct SeqSet #

struct SeqSet {
pub mut:
	ranges []SeqRange
}

SeqSet is a set of message numbers, held as ranges that are kept sorted and merged so that the rendered form stays short.

fn (SeqSet) add #

fn (mut s SeqSet) add(n u32)

add inserts one number.

fn (SeqSet) add_range #

fn (mut s SeqSet) add_range(start u32, stop u32)

add_range inserts a range, in either order.

fn (SeqSet) len #

fn (s &SeqSet) len() int

len is the number of ranges the set is stored as, not the number of messages it names.

fn (SeqSet) is_empty #

fn (s &SeqSet) is_empty() bool

is_empty reports whether the set names nothing, in which case no command should be sent at all.

fn (SeqSet) contains #

fn (s &SeqSet) contains(n u32) bool

contains reports whether n is named by the set. It answers for a concrete number; * is not resolvable without knowing the mailbox.

fn (SeqSet) numbers #

fn (s &SeqSet) numbers() ![]u32

numbers expands the set. It fails on a set holding *, whose end is only known to the server.

fn (SeqSet) str #

fn (s SeqSet) str() string

str renders the set as a server reads it.