Standard library · Network

net/email

Sending email over SMTP and reading it over IMAP and POP3.

import net.email

This package speaks the three mail protocols directly over net/tcp and tls: SMTP to send, POP3 and IMAP to read. Use it for password reset mails, alerts and reports from a server, or a script that reads a support inbox. It is a protocol client, not a mail library: it builds simple text and HTML messages, but it has no attachments and no MIME parser, so a fetched message comes back as raw lines.

The pieces:

  • BuildMessage writes the message text: the headers plus a text body, an HTML body, or both as multipart/alternative.
  • Send does a whole SMTP delivery to one recipient in one call. For several recipients, Bcc or a useful error message, drive an SMTPClient step by step; every step returns true or false and leaves the server's answer in last_reply.
  • POP3 (POP3Client) downloads messages and can delete them; IMAP (IMAPClient) leaves them on the server and lets you search, so it is the one to use for a shared mailbox.

Things people get wrong:

  • Send always encrypts. With use_tls true it uses TLS from the first byte (port 465); with false it upgrades with STARTTLS (port 587) and fails if the server cannot. It logs in with AUTH PLAIN when a user name is given.
  • BuildMessage writes only From, To, Subject and the content headers. It adds no Date or Message-ID and does not encode a non-ASCII subject; see the first guide. Write line breaks in bodies as \r\n: bare \n is passed through, and some servers reject it.
  • IMAP Login puts the user name and password in double quotes without escaping. A password that contains " or \ fails; use Command with an IMAP literal or LoginXOAuth2 for those.
  • Fetch returns the reply lines only, not whether the command worked; call Command when you need the status.

The protocol examples talk to a tiny fake server in a thread of the same program, so you can run them offline and see every command that goes over the wire.

What BuildMessage writes

BuildMessage(from, to, subject, text, html) returns the full message text that SMTP sends. With only a text body it is a plain text/plain message, with only HTML it is text/html, and with both it becomes multipart/alternative so that each mail program shows the version it prefers. Bodies are UTF-8, so any language works in the body.

net-email-plain-message.salam
import net.email
import str
import encoding

func show(message: str):
    each line in str.Split(message, "\r\n"):
        println line
    end
    println "-----"
end

func main:
    text := email.BuildMessage("shop@example.com", "sara@example.com", "Your code", "Your login code is 4821.", "")
    show(text)
    html := email.BuildMessage("shop@example.com", "sara@example.com", "Welcome", "", "<h1>Welcome, Sara</h1>")
    show(html)
    both := email.BuildMessage("shop@example.com", "sara@example.com", "News", "Plain version", "<p>HTML version</p>")
    parts := str.Split(both, "Content-Type: ")
    each i, part in parts:
        if i > 0: println "Content-Type", i, "is", str.Split(part, ";")[0] end
    end
    subject := "=?UTF-8?B?" + encoding.Base64Encode("Café menu") + "?="
    println "encoded subject:", subject
end
OutputFrom: shop@example.com To: sara@example.com Subject: Your code MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Your login code is 4821. ----- From: shop@example.com To: sara@example.com Subject: Welcome MIME-Version: 1.0 Content-Type: text/html; charset=utf-8 <h1>Welcome, Sara</h1> ----- Content-Type 1 is multipart/alternative Content-Type 2 is text/plain Content-Type 3 is text/html encoded subject: =?UTF-8?B?Q2Fmw6kgbWVudQ==?=

The subject is written as it is. Plain ASCII subjects are always safe; for a subject in another script, encode it the way mail headers expect, =?UTF-8?B? plus the Base64 of the text plus ?=, as the last lines do, and pass that string as the subject. To add a Date or Message-ID header, put the line in front of what BuildMessage returns, for example "Date: " + time.FormatHTTPDate(time.Now()) + "\r\n" + message; mail without a date is more likely to be marked as spam.

An SMTP session step by step

Send does everything in one call, but driving the client yourself lets you add several recipients, use another login method, or see where a server says no. The steps are always the same: DialSMTP (or DialSMTPTLS), Hello, an Auth... call on a real server, MailFrom, one RcptTo per recipient, Data with the message, and Quit. Each step returns true or false and leaves the server's answer in last_reply.

net-email-smtp-session.salam
import net.email
import net.tcp

mut server := tcp.Server {}
mut seen := Vector {} as Vector<str>

func next_line(conn: tcp.Conn, buf &: str): str:
    until buf.find("\r\n") < 0:
        buf += tcp.Read(conn, 4096)
    end
    at := buf.find("\r\n")
    line := buf.substr(0, at)
    buf = buf.substr(at + 2, buf.len() - at - 2)
    ret line
end

func mail_server():
    conn := tcp.Accept(server)
    mut buf := ""
    _ := tcp.Write(conn, "220 mail.test ready\r\n")
    mut open := true
    until open:
        line := next_line(conn, buf)
        seen.push(line)
        if line == "DATA":
            _ := tcp.Write(conn, "354 end with a dot\r\n")
            mut body := next_line(conn, buf)
            until body != ".":
                body = next_line(conn, buf)
            end
            _ := tcp.Write(conn, "250 queued as 1001\r\n")
        else line == "QUIT":
            _ := tcp.Write(conn, "221 bye\r\n")
            open = false
        else:
            _ := tcp.Write(conn, "250 ok\r\n")
        end
    end
    tcp.Close(conn)
end

func main:
    server = tcp.Bind(0)
    t := spawn(mail_server)
    mut c := email.DialSMTP("127.0.0.1", tcp.Port(server))
    println "connected:", c.ok
    _ := email.Hello(c, "shop.test")
    _ := email.MailFrom(c, "orders@shop.test")
    each rcpt in ["sara@example.com", "reza@example.com"]:
        _ := email.RcptTo(c, rcpt)
    end
    message := email.BuildMessage("orders@shop.test", "sara@example.com, reza@example.com", "Order 1001", "Your order has shipped.", "")
    println "accepted:", email.Data(c, message), c.last_reply.trim()
    email.Quit(c)
    join(t)
    tcp.CloseServer(server)
    println "the server saw:"
    each line in seen:
        println " ", line
    end
end
Outputconnected: true accepted: true 250 queued as 1001 the server saw: EHLO shop.test MAIL FROM:<orders@shop.test> RCPT TO:<sara@example.com> RCPT TO:<reza@example.com> DATA QUIT

The To header in the message and the RcptTo calls are separate: the header is what people see, the RcptTo list is where the mail is delivered. A Bcc recipient is simply one who gets a RcptTo but is not in the header.

Send with use_tls false still switches to TLS with STARTTLS before it logs in, so it cannot talk to a local test server without TLS such as MailHog. For those, use the steps above without StartTLS.

Reading a mailbox with POP3

POP3 is the simple way to fetch mail: log in with User and Pass, ask for the count and size with Stat, list the messages with POP3List, fetch one with Retr, and mark it for deletion with Dele. Deletions happen when POP3Quit ends the session.

net-email-pop3-inbox.salam
import net.email
import net.tcp
import str

mut server := tcp.Server {}

func next_line(conn: tcp.Conn, buf &: str): str:
    until buf.find("\r\n") < 0:
        buf += tcp.Read(conn, 4096)
    end
    at := buf.find("\r\n")
    line := buf.substr(0, at)
    buf = buf.substr(at + 2, buf.len() - at - 2)
    ret line
end

func pop_server():
    mails := ["Subject: Welcome\r\n\r\nHello Sara.", "Subject: Invoice 12\r\n\r\nTotal: 40 USD\r\n..end of invoice"]
    conn := tcp.Accept(server)
    mut buf := ""
    _ := tcp.Write(conn, "+OK ready\r\n")
    mut line := next_line(conn, buf)
    until line != "QUIT":
        if line == "STAT":
            _ := tcp.Write(conn, "+OK 2 " + (mails[0].len() + mails[1].len()) + "\r\n")
        else line == "LIST":
            _ := tcp.Write(conn, "+OK\r\n1 " + mails[0].len() + "\r\n2 " + mails[1].len() + "\r\n.\r\n")
        else str.StartsWith(line, "RETR "):
            n := line.substr(5, line.len() - 5).to_int()
            _ := tcp.Write(conn, "+OK\r\n" + mails[n - 1] + "\r\n.\r\n")
        else:
            _ := tcp.Write(conn, "+OK\r\n")
        end
        line = next_line(conn, buf)
    end
    _ := tcp.Write(conn, "+OK bye\r\n")
    tcp.Close(conn)
end

func main:
    server = tcp.Bind(0)
    t := spawn(pop_server)
    mut c := email.DialPOP3("127.0.0.1", tcp.Port(server))
    if not (c.ok and email.User(c, "sara") and email.Pass(c, "secret")):
        println "login failed"
        ret 1
    end
    println "stat:", email.Stat(c)
    each entry in email.POP3List(c):
        parts := str.Split(entry, " ")
        number := parts[0].to_int()
        text := email.Retr(c, number)
        subject := str.Split(text, "\r\n")[0]
        println "message", number, "(" + parts[1] + " bytes):", subject
    end
    println "last line of 2:", str.Split(email.Retr(c, 2), "\r\n")[3]
    println "delete 1:", email.Dele(c, 1)
    email.POP3Quit(c)
    join(t)
    tcp.CloseServer(server)
    ret 0
end
Outputstat: +OK 2 85 message 1 (31 bytes): Subject: Welcome message 2 (54 bytes): Subject: Invoice 12 last line of 2: .end of invoice delete 1: true

In POP3 a line of the message that starts with a dot is sent with an extra dot. Retr removes it again, which is why the stored ..end of invoice comes back as .end of invoice.

Finding unread mail with IMAP

IMAP keeps the mail on the server and lets you search it. After Login, Command sends any IMAP command and returns its lines and final status, and Fetch asks for parts of chosen messages. The usual pattern for new mail is SELECT a mailbox, SEARCH UNSEEN for the numbers of unread messages, then Fetch what you need of them.

net-email-imap-unread.salam
import net.email
import net.tcp
import str

mut server := tcp.Server {}

func next_line(conn: tcp.Conn, buf &: str): str:
    until buf.find("\r\n") < 0:
        buf += tcp.Read(conn, 4096)
    end
    at := buf.find("\r\n")
    line := buf.substr(0, at)
    buf = buf.substr(at + 2, buf.len() - at - 2)
    ret line
end

func imap_server():
    conn := tcp.Accept(server)
    mut buf := ""
    _ := tcp.Write(conn, "* OK IMAP ready\r\n")
    mut done := false
    until not done:
        line := next_line(conn, buf)
        tag := str.Split(line, " ")[0]
        if str.Contains(line, " SELECT "):
            _ := tcp.Write(conn, "* 3 EXISTS\r\n* 2 RECENT\r\n")
        else str.Contains(line, " SEARCH "):
            _ := tcp.Write(conn, "* SEARCH 2 3\r\n")
        else str.Contains(line, " FETCH "):
            _ := tcp.Write(conn, "* 2 FETCH (FLAGS (\\Recent))\r\n* 3 FETCH (FLAGS (\\Recent \\Flagged))\r\n")
        else str.Contains(line, " LOGOUT"):
            _ := tcp.Write(conn, "* BYE\r\n")
            done = true
        end
        _ := tcp.Write(conn, tag + " OK done\r\n")
    end
    tcp.Close(conn)
end

func main:
    server = tcp.Bind(0)
    t := spawn(imap_server)
    mut c := email.DialIMAP("127.0.0.1", tcp.Port(server))
    println "logged in:", c.ok and email.Login(c, "sara", "secret")
    selected := email.Command(c, "SELECT \"INBOX\"")
    each line in selected.lines:
        if str.EndsWith(line, " EXISTS"): println "inbox:", line end
    end
    unread := email.Command(c, "SEARCH UNSEEN")
    println "unread:", unread.lines[0], "status:", unread.status
    each line in email.Fetch(c, "2:3", "FLAGS"):
        println " ", line
    end
    email.Logout(c)
    join(t)
    tcp.CloseServer(server)
end
Outputlogged in: true inbox: * 3 EXISTS unread: * SEARCH 2 3 status: A3 OK done * 2 FETCH (FLAGS (\Recent)) * 3 FETCH (FLAGS (\Recent \Flagged))

Fetch puts the parentheses around the item list itself, so pass FLAGS or BODY[HEADER], not (FLAGS).

Reading subjects and senders with IMAP

To list a mailbox without downloading whole messages, fetch only the header fields you need with BODY.PEEK[HEADER.FIELDS (SUBJECT FROM)]; PEEK keeps the messages unread. The server sends each header block as an IMAP literal ({57} followed by that many bytes), and Fetch returns it split into lines: the * 2 FETCH line, the header lines, the empty line that ends the headers and a closing ). The program walks those lines and prints one row per message.

net-email-imap-headers.salam
import net.email
import net.tcp
import str

mut server := tcp.Server {}

func next_line(conn: tcp.Conn, buf &: str): str:
    until buf.find("\r\n") < 0:
        buf += tcp.Read(conn, 4096)
    end
    at := buf.find("\r\n")
    line := buf.substr(0, at)
    buf = buf.substr(at + 2, buf.len() - at - 2)
    ret line
end

func imap_server():
    conn := tcp.Accept(server)
    mut buf := ""
    _ := tcp.Write(conn, "* OK IMAP ready\r\n")
    mut done := false
    until not done:
        line := next_line(conn, buf)
        tag := str.Split(line, " ")[0]
        if str.Contains(line, " FETCH "):
            first := "Subject: Invoice 12\r\nFrom: billing@shop.test\r\n\r\n"
            second := "Subject: Lunch?\r\nFrom: reza@example.com\r\n\r\n"
            _ := tcp.Write(conn, "* 2 FETCH (BODY[HEADER.FIELDS (SUBJECT FROM)] {" + first.len() + "}\r\n" + first + ")\r\n")
            _ := tcp.Write(conn, "* 3 FETCH (BODY[HEADER.FIELDS (SUBJECT FROM)] {" + second.len() + "}\r\n" + second + ")\r\n")
        else str.Contains(line, " LOGOUT"):
            _ := tcp.Write(conn, "* BYE\r\n")
            done = true
        end
        _ := tcp.Write(conn, tag + " OK done\r\n")
    end
    tcp.Close(conn)
end

func main:
    server = tcp.Bind(0)
    t := spawn(imap_server)
    mut c := email.DialIMAP("127.0.0.1", tcp.Port(server))
    _ := email.Login(c, "sara", "secret") and email.Select(c, "INBOX")
    lines := email.Fetch(c, "2:3", "BODY.PEEK[HEADER.FIELDS (SUBJECT FROM)]")
    println "lines received:", lines.len()
    mut subject := ""
    mut from := ""
    each line in lines:
        if str.StartsWith(line, "Subject: "):
            subject = line.substr(9, line.len() - 9)
        else str.StartsWith(line, "From: "):
            from = line.substr(6, line.len() - 6)
        else line == ")":
            println from, "wrote:", subject
        end
    end
    email.Logout(c)
    join(t)
    tcp.CloseServer(server)
end
Outputlines received: 10 billing@shop.test wrote: Invoice 12 reza@example.com wrote: Lunch?

Header values can be folded over several lines and encoded as =?UTF-8?B?...?=; the lines come back exactly as the server sent them, so decode them yourself if your mail is not plain ASCII.

Sending through Gmail

Gmail, like most providers, accepts only encrypted connections and does not take your normal password. Turn on two step verification and create an app password, or use an OAuth token with AuthXOAuth2. Port 465 with use_tls true connects with TLS from the start. The program reads the address and secrets from environment variables so they stay out of the source:

MAIL_USER=you@gmail.com MAIL_APP_PASSWORD='abcd efgh ijkl mnop' salam run net-email-gmail.salam
net-email-gmail.salam
import net.email
import os

func main:
    user := os.Env("MAIL_USER")
    password := os.Env("MAIL_APP_PASSWORD")
    sent := email.Send("smtp.gmail.com", 465, true, user, password, user, "sara@example.com", "Weekly report", "The report is ready.", "<p>The <b>report</b> is ready.</p>")
    println "sent with a password:", sent
    mut c := email.DialSMTPTLS("smtp.gmail.com", 465)
    if not (c.ok and email.Hello(c, "example.com")):
        println "could not connect:", c.last_reply
        ret 1
    end
    if not email.AuthXOAuth2(c, user, os.Env("MAIL_OAUTH_TOKEN")):
        println "login refused:", c.last_reply
        email.Quit(c)
        ret 1
    end
    ok := email.MailFrom(c, user) and email.RcptTo(c, "sara@example.com") and email.Data(c, email.BuildMessage(user, "sara@example.com", "Hello", "Sent with an OAuth token.", ""))
    println "sent with a token:", ok
    email.Quit(c)
    ret 0
end

Types

POP3Client

struct POP3Client
A POP3 client for taking email away.
NameTypeDescription
conntcp.ConnThe connection.
securebool = falseIt is encrypted.
tls_idxint = -1The internal TLS index.
okbool = falseThe connection is up.
rxbufstr = ""The data not yet read.
net-email-Clients.salam
import net.email

func main:
    mut smtp := email.DialSMTPTLS("smtp.example.com", 465)
    if smtp.ok and email.Hello(smtp, "example.com") and email.AuthLogin(smtp, "ali", "secret"):
        _ := email.MailFrom(smtp, "ali@example.com")
        _ := email.RcptTo(smtp, "sara@example.com")
        _ := email.Data(smtp, email.BuildMessage("ali@example.com", "sara@example.com", "hello", "some text", ""))
    end
    email.Quit(smtp)
    mut smtp2 := email.DialSMTP("smtp.example.com", 587)
    _ := email.StartTLS(smtp2, "smtp.example.com")
    _ := email.AuthPlain(smtp2, "ali", "secret")
    _ := email.AuthXOAuth2(smtp2, "ali", "token")
    println smtp2.last_reply
    _ := email.Send("smtp.example.com", 465, true, "ali", "secret", "ali@example.com", "sara@example.com", "hello", "some text", "")
    mut imap := email.DialIMAPTLS("imap.example.com", 993)
    _ := email.Login(imap, "ali", "secret")
    _ := email.LoginXOAuth2(imap, "ali", "token")
    println email.List(imap, "", "*")
    _ := email.Select(imap, "INBOX")
    println email.Fetch(imap, "1:5", "(FLAGS BODY[HEADER.FIELDS (SUBJECT)])")
    reply := email.Command(imap, "NOOP")
    println reply.ok, reply.status, reply.lines.len()
    email.Logout(imap)
    _ := email.DialIMAP("imap.example.com", 143)
    mut pop := email.DialPOP3TLS("pop.example.com", 995)
    _ := email.User(pop, "ali")
    _ := email.Pass(pop, "secret")
    println email.Stat(pop), email.POP3List(pop)
    println email.Retr(pop, 1)
    _ := email.Dele(pop, 1)
    email.POP3Quit(pop)
    _ := email.DialPOP3("pop.example.com", 110)
end

SMTPClient

struct SMTPClient
An SMTP client for sending email.
NameTypeDescription
conntcp.ConnThe connection.
securebool = falseIt is encrypted.
tls_idxint = -1The internal TLS index.
okbool = falseThe connection is up.
rxbufstr = ""The data not yet read.
last_replystr = ""The last reply of the server; for debugging.

The example for this item is shown with the ones above: net-email-Clients.salam

IMAPClient

struct IMAPClient
An IMAP client for reading a mailbox.
NameTypeDescription
conntcp.ConnThe connection.
securebool = falseIt is encrypted.
tls_idxint = -1The internal TLS index.
okbool = falseThe connection is up.
rxbufstr = ""The data not yet read.
tag_counterint = 0The command tag counter.

The example for this item is shown with the ones above: net-email-Clients.salam

IMAPReply

struct IMAPReply
The reply to one IMAP command.
NameTypeDescription
okbool = falseThe reply was OK.
statusstr = ""The closing status line.
linesVectorThe lines of the reply.

The example for this item is shown with the ones above: net-email-Clients.salam

Functions

DialPOP3

func DialPOP3(host: str, port: int): POP3Client
Connects to a POP3 server, usually port 110.

The example for this item is shown with the ones above: net-email-Clients.salam

DialPOP3TLS

func DialPOP3TLS(host: str, port: int): POP3Client
Connects encrypted, usually port 995.

The example for this item is shown with the ones above: net-email-Clients.salam

User

func User(c &: POP3Client, username: str): bool
Sends the user name.

The example for this item is shown with the ones above: net-email-Clients.salam

Pass

func Pass(c &: POP3Client, password: str): bool
Sends the password.

The example for this item is shown with the ones above: net-email-Clients.salam

Stat

func Stat(c &: POP3Client): str
The raw +OK reply with the number of messages and their total size in bytes.

The example for this item is shown with the ones above: net-email-Clients.salam

POP3List

func POP3List(c &: POP3Client): Vector<str>
One line per message: its number and size in bytes.

The example for this item is shown with the ones above: net-email-Clients.salam

Retr

func Retr(c &: POP3Client, msgnum: int): str
The complete text of one message, with the POP3 dot-stuffing removed.

The example for this item is shown with the ones above: net-email-Clients.salam

Dele

func Dele(c &: POP3Client, msgnum: int): bool
Marks the message for deletion; it goes when you log out.

The example for this item is shown with the ones above: net-email-Clients.salam

POP3Quit

func POP3Quit(c &: POP3Client)
Logs out and makes the deletions final.

The example for this item is shown with the ones above: net-email-Clients.salam

DialSMTP

func DialSMTP(host: str, port: int): SMTPClient
Connects to an SMTP server without encryption, usually port 587 or 25; ok is true when the server greets with 220.

The example for this item is shown with the ones above: net-email-Clients.salam

DialSMTPTLS

func DialSMTPTLS(host: str, port: int): SMTPClient
Connects encrypted from the start, usually port 465.

The example for this item is shown with the ones above: net-email-Clients.salam

Hello

func Hello(c &: SMTPClient, domain: str): bool
Sends the EHLO greeting with the domain name of the sender.

The example for this item is shown with the ones above: net-email-Clients.salam

StartTLS

func StartTLS(c &: SMTPClient, host: str): bool
Encrypts a plain connection with STARTTLS.

The example for this item is shown with the ones above: net-email-Clients.salam

AuthLogin

func AuthLogin(c &: SMTPClient, username: str, password: str): bool
Logs in with the LOGIN method.

The example for this item is shown with the ones above: net-email-Clients.salam

AuthPlain

func AuthPlain(c &: SMTPClient, username: str, password: str): bool
Logs in with the PLAIN method, sending the user name and password in one Base64 line.

The example for this item is shown with the ones above: net-email-Clients.salam

AuthXOAuth2

func AuthXOAuth2(c &: SMTPClient, username: str, access_token: str): bool
Logs in with an OAuth token.

The example for this item is shown with the ones above: net-email-Clients.salam

MailFrom

func MailFrom(c &: SMTPClient, addr: str): bool
Declares the envelope sender address, without angle brackets; bounces go there.

The example for this item is shown with the ones above: net-email-Clients.salam

RcptTo

func RcptTo(c &: SMTPClient, addr: str): bool
Adds one envelope recipient; call it again for each further one. This, not the To header, decides who gets the mail.

The example for this item is shown with the ones above: net-email-Clients.salam

Data

func Data(c &: SMTPClient, raw_message: str): bool
Sends the complete text of the message and doubles any leading dot on a line.

The example for this item is shown with the ones above: net-email-Clients.salam

Quit

func Quit(c &: SMTPClient)
Ends the session and closes the connection.

The example for this item is shown with the ones above: net-email-Clients.salam

BuildMessage

func BuildMessage(from: str, to_addr: str, subject: str, text_body: str, html_body: str): str
Builds a complete email message with From, To and Subject headers. When you give both plain text and HTML, the message becomes multipart/alternative so every mail program can show the version that suits it.
net-email-Message.salam
import net.email
import str

func main:
    message := email.BuildMessage("ali@example.com", "sara@example.com", "hello", "plain text", "<p>some <b>nice</b> text</p>")
    each line in str.Split(message, "\r\n"):
        if str.StartsWith(line, "From:") or str.StartsWith(line, "To:") or str.StartsWith(line, "MIME-Version:") or str.StartsWith(line, "Subject:"):
            println line
        end
    end
    println str.Contains(message, "multipart/alternative")
    println email.BuildXOAuth2("ali@example.com", "ya29.token")
end
OutputFrom: ali@example.com To: sara@example.com Subject: hello MIME-Version: 1.0 true dXNlcj1hbGlAZXhhbXBsZS5jb20BYXV0aD1CZWFyZXIgeWEyOS50b2tlbgEB

Send

func Send(host: str, port: int, use_tls: bool, username: str, password: str, from: str, to_addr: str, subject: str, text_body: str, html_body: str): bool
Sends an email to one recipient in one step: it connects, logs in with AUTH PLAIN (when a user name is given) and sends what BuildMessage writes. When use_tls is true it connects encrypted from the start; otherwise it must be able to upgrade with STARTTLS. Returns only true or false.

The example for this item is shown with the ones above: net-email-Clients.salam

BuildXOAuth2

func BuildXOAuth2(username: str, access_token: str): str
Builds the XOAUTH2 login string as base64, for logging in with an OAuth token, as Gmail wants.

The example for this item is shown with the ones above: net-email-Message.salam

DialIMAP

func DialIMAP(host: str, port: int): IMAPClient
Connects to an IMAP server, usually port 143.

The example for this item is shown with the ones above: net-email-Clients.salam

DialIMAPTLS

func DialIMAPTLS(host: str, port: int): IMAPClient
Connects encrypted, usually port 993.

The example for this item is shown with the ones above: net-email-Clients.salam

Command

func Command(c &: IMAPClient, cmd: str): IMAPReply
Sends any IMAP command with the next tag and returns its untagged lines and the tagged status line.

The example for this item is shown with the ones above: net-email-Clients.salam

Login

func Login(c &: IMAPClient, username: str, password: str): bool
Logs in with a user name and password, each put in double quotes without escaping.

The example for this item is shown with the ones above: net-email-Clients.salam

LoginXOAuth2

func LoginXOAuth2(c &: IMAPClient, username: str, access_token: str): bool
Logs in with an OAuth token.

The example for this item is shown with the ones above: net-email-Clients.salam

Select

func Select(c &: IMAPClient, mailbox: str): bool
Picks a mailbox, INBOX for instance, to work in.

The example for this item is shown with the ones above: net-email-Clients.salam

List

func List(c &: IMAPClient, reference: str, pattern: str): Vector<str>
The raw * LIST lines for the mailboxes that match a reference and a pattern, such as "" and "*".

The example for this item is shown with the ones above: net-email-Clients.salam

Fetch

func Fetch(c &: IMAPClient, seq: str, items: str): Vector<str>
Fetches parts of messages: a sequence set such as 2:3 and the items without parentheses. Returns the reply lines without the status.

The example for this item is shown with the ones above: net-email-Clients.salam

Logout

func Logout(c &: IMAPClient)
Logs out and closes the connection.

The example for this item is shown with the ones above: net-email-Clients.salam