Capítulo 29 21 min de leitura

E-mail (SMTP / IMAP / POP3)

Os módulos stdlib::mail do Glide cobrem o lado cliente completo do e-mail: construa uma mensagem com Mail (RFC 5322 + MIME), envie-a com SmtpClient / SmtpSession, e leia caixas de entrada com ImapSession (IMAP4rev1) ou Pop3Session (POP3). Todos os transportes operam sobre TCP puro ou TLS via stdlib::net.

Importação

Cada protocolo vive no seu próprio submódulo. Importe o que precisar:

import stdlib::mail::message::*;   // Mail, Attachment, parse_mail
import stdlib::mail::smtp::*;      // SmtpClient, SmtpSession, SmtpReply
import stdlib::mail::imap::*;      // ImapSession, ImapResponse
import stdlib::mail::pop3::*;      // Pop3Session, Pop3MsgInfo

message é a dependência comum: o SMTP envia um *Mail, e a recuperação via IMAP/POP3 se combina com parse_mail para decodificar o que chega. Os módulos de e-mail dependem de stdlib::net::tcp / stdlib::net::tls, stdlib::base64, e (para message) stdlib::random, stdlib::time e stdlib::hashmap.

Visão geral da superfície pública

Módulo Tipos Funções / métodos
message Mail, Attachment Mail::new, Mail.to_rfc5322, parse_mail
smtp SmtpClient, SmtpSession, SmtpReply SmtpClient::new, .send, .connect; SmtpSession.send_mail, .quit, .close
imap ImapSession, ImapResponse ImapSession::connect, .login, .select, .fetch, .store_flag, .search, .search_unseen, .search_from, .append, .idle_start, .idle_next, .idle_stop, .logout, .close
pop3 Pop3Session, Pop3MsgInfo Pop3Session::connect, .login, .stat, .list, .retrieve, .delete, .noop, .quit, .close

O tipo Mail

Uma mensagem que você constrói em memória e serializa para bytes de rede, ou analisa de volta a partir de bytes brutos recebidos via IMAP/POP3.

Attachment (struct)

pub struct Attachment {
    pub filename:  string,
    pub mime_type: string,
    pub data:      string,
}

Um único anexo MIME. data contém os bytes brutos (sem codificação); to_rfc5322 codifica em base64 e quebra o resultado em colunas de 76 caracteres, com Content-Disposition: attachment; filename="...". Não há construtor — aloque diretamente e preencha os campos, depois empurre no mail.attachments:

import stdlib::mail::message::*;

fn main() -> i32 {
    let att: *Attachment = malloc(sizeof(Attachment)) as *Attachment;
    att.filename = "report.csv";
    att.mime_type = "text/csv";
    att.data = "a,b\n1,2";

    let m: *Mail = Mail::new();
    m.from = "[email protected]";
    m.to.push("[email protected]");
    m.subject = "report";
    m.body = "see attached";
    m.attachments.push(att);

    let wire: string = m.to_rfc5322();        // multipart/mixed
    println!("len", wire.len());
    println!("has base64:", wire.contains("base64"));
    return 0;
}

Mail (struct)

pub struct Mail {
    pub from:        string,
    pub to:          *Vector<string>,
    pub cc:          *Vector<string>,
    pub bcc:         *Vector<string>,
    pub subject:     string,
    pub body:        string,             // corpo em texto puro
    pub html:        ?string,            // alternativa HTML opcional
    pub attachments: *Vector<*Attachment>,
    pub headers:     *HashMap<string>,   // cabeçalhos extras (Reply-To, etc.)
    pub date:        string,             // vazio = preenchido automaticamente em to_rfc5322
    pub message_id:  string,             // vazio = preenchido automaticamente
}
Campo Tipo Observações
from string Endereço do remetente. Emitido como From: quando não vazio.
to / cc / bcc *Vector<string> Listas de destinatários. to/cc aparecem como cabeçalhos; `bcc` jamais é escrito nos cabeçalhos — é adicionado apenas como RCPT TO (veja SMTP abaixo).
subject string Cabeçalho Subject: quando não vazio.
body string Corpo em texto puro. ASCII é enviado como 7bit; não-ASCII é codificado em quoted-printable.
html ?string Quando some(...), a mensagem se torna multipart/alternative (texto + HTML).
attachments *Vector<*Attachment> Quando não vazio, a mensagem se torna multipart/mixed.
headers *HashMap<string> Cabeçalhos extras arbitrários (ex.: Reply-To). Emitidos literalmente após os cabeçalhos padrão.
date string Vazio preenche automaticamente a data RFC 5322 atual no momento da serialização.
message_id string Vazio gera automaticamente <aleatório@domínio-do-from>.

Mail::new

pub fn new() -> *Mail

Constrói um Mail vazio com todos os vetores e o mapa de cabeçalhos alocados, html = none(), e date/message_id vazios (preenchidos automaticamente depois). Preencha os campos públicos diretamente, depois chame to_rfc5322.

Mail.to_rfc5322

pub fn to_rfc5322(self: *Mail) -> string

Serializa para o formato de rede RFC 5322: cabeçalhos, uma linha em branco, depois o corpo ou as seções MIME. O layout se adapta ao que estiver definido:

body html attachments Content-Type resultante
definido none vazio text/plain; charset=UTF-8
definido some vazio multipart/alternative (texto + HTML)
definido none não vazio multipart/mixed (texto + anexos)
definido some não vazio multipart/mixed envolvendo multipart/alternative + anexos

Os cabeçalhos Date, Message-Id e MIME-Version são sempre emitidos; Date/Message-Id ausentes são preenchidos automaticamente. Corpos (e partes HTML) que contêm caracteres não-ASCII são codificados automaticamente em quoted-printable; partes puramente ASCII são enviadas como 7bit literalmente. Os separadores MIME são aleatórios a cada chamada (===_glide_outer_…).

import stdlib::mail::message::*;

fn main() -> i32 {
    let m: *Mail = Mail::new();
    m.from = "[email protected]";
    m.to.push("[email protected]");
    m.cc.push("[email protected]");
    m.subject = "hello";
    m.body = "Plain text body.";
    m.html = some("<p>HTML body.</p>");

    let att: *Attachment = malloc(sizeof(Attachment)) as *Attachment;
    att.filename = "doc.txt";
    att.mime_type = "text/plain";
    att.data = "x";
    m.attachments.push(att);

    m.headers.insert("Reply-To", "[email protected]");

    let wire: string = m.to_rfc5322();   // multipart/mixed { alternative, attachment }
    println!("wire bytes:", wire.len());
    return 0;
}

O próximo programa mostra o comportamento de preenchimento automático e a seleção de quoted-printable. Um date/message_id vazio é preenchido no momento da serialização; um corpo com caracteres não-ASCII muda a codificação de 7bit para quoted-printable. Defina os campos explicitamente para sobrescrever:

import stdlib::mail::message::*;

fn main() -> i32 {
    let m: *Mail = Mail::new();
    m.from = "[email protected]";
    m.to.push("[email protected]");
    m.subject = "cafe";
    m.body = "caf\u{00e9}";              // não-ASCII -> quoted-printable
    m.headers.insert("Reply-To", "[email protected]");

    let w1: string = m.to_rfc5322();
    println!("auto date:", w1.contains("Date: "));
    println!("auto msgid:", w1.contains("Message-Id: <"));
    println!("qp encoding:", w1.contains("quoted-printable"));

    m.date = "Tue, 01 Jan 2026 00:00:00 +0000";
    m.message_id = "[email protected]";
    let w2: string = m.to_rfc5322();
    println!("explicit msgid:", w2.contains("Message-Id: <[email protected]>"));
    return 0;
}

parse_mail

pub fn parse_mail(raw: string) -> !*Mail

Analisa uma mensagem RFC 5322 bruta em um Mail. Separa o bloco de cabeçalhos do corpo no primeiro \r\n\r\n, desdobra linhas de continuação (com SP/TAB inicial) e decodifica os campos principais: From, To, Cc, Subject, Date e Message-Id (os colchetes angulares são removidos). Listas To/Cc reconhecidas são separadas por vírgula nos vetores de destinatários. Qualquer outro cabeçalho (incluindo Content-Type / Content-Transfer-Encoding) é preservado em headers. Retorna erro com "mail: missing header/body separator" se nenhuma linha em branco for encontrada.

import stdlib::mail::message::*;

fn main() -> !i32 {
    let raw: string = "From: [email protected]\r\nTo: [email protected], [email protected]\r\nSubject: hi\r\nMessage-Id: <abc@x>\r\nX-Custom: yes\r\n\r\nbody here";
    let m: *Mail = parse_mail(raw)?;
    println!("from:", m.from);
    println!("subject:", m.subject);
    println!("message-id:", m.message_id);   // "abc@x" — colchetes removidos
    println!("to count:", m.to.len());        // 2 — separado por vírgula
    println!("custom:", m.headers.get("X-Custom"));
    println!("body:", m.body);

    // Separador de linha em branco ausente → erro.
    let bad: !*Mail = parse_mail("From: x\r\nno blank line");
    println!("err surfaces:", !bad.ok);
    return ok(0);
}

SMTP — envio

Conecte, opcionalmente faça upgrade via STARTTLS ou envolva desde o primeiro byte (porta 465), opcionalmente autentique, depois execute a máquina de estados MAIL FROM / RCPT TO / DATA / QUIT.

Item Assinatura Descrição
SmtpClient::new pub fn new(host: string, port: i32) -> *SmtpClient Constrói um cliente; configure os campos depois de send/connect.
SmtpClient.send pub fn send(self: *SmtpClient, mail: *Mail) -> !i32 Tudo-em-um: conecta, envia uma mensagem, faz quit, fecha.
SmtpClient.connect pub fn connect(self: *SmtpClient) -> !*SmtpSession Abre uma sessão reutilizável posicionada antes de MAIL FROM.
SmtpSession.send_mail pub fn send_mail(self: *SmtpSession, mail: *Mail) -> !i32 Executa a conversa por mensagem.
SmtpSession.quit pub fn quit(self: *SmtpSession) -> !i32 Envia QUIT, espera 221.
SmtpSession.close pub fn close(self: *SmtpSession) Encerra o socket; idempotente.

SmtpClient (struct)

pub struct SmtpClient {
    pub host:         string,
    pub port:         i32,
    pub use_tls:      bool,     // TLS implícito desde o primeiro byte (porta 465 / SMTPS)
    pub use_starttls: bool,     // upgrade STARTTLS após EHLO (porta 587)
    pub username:     ?string,
    pub password:     ?string,
    pub timeout_ms:   i32,
    pub tls_insecure: bool,     // ignora validação de certificado — apenas em dev local
    pub auth_method:  string,   // "auto" | "plain" | "login"
}
Campo Padrão (após new) Significado
use_tls false Envolve o socket em TLS imediatamente. Mutuamente exclusivo com use_starttls.
use_starttls false Envia STARTTLS após EHLO, depois refaz EHLO sobre o canal protegido.
username / password none() Quando ambos são some, a sessão se autentica após (STARTTLS+)EHLO.
timeout_ms 0 Timeout de leitura/escrita do socket; 0 mantém o padrão do stream.
tls_insecure false Desativa a validação de certificado. Use apenas contra servidores de dev local.
auth_method "auto" "auto" escolhe a partir do anúncio do EHLO (prefere PLAIN, usa LOGIN como fallback); "plain" / "login" forçam um mecanismo específico.

SmtpClient::new

pub fn new(host: string, port: i32) -> *SmtpClient

Constrói um cliente com todos os booleanos false, credenciais none(), auth_method = "auto" e timeout_ms = 0. Configure os campos públicos antes de chamar send ou connect.

SmtpClient.send

pub fn send(self: *SmtpClient, mail: *Mail) -> !i32

Tudo-em-um: abre uma conexão, executa toda a conversa SMTP para mail, faz quit e fecha — retornando ok(0) em caso de sucesso. Para múltiplas mensagens em uma única conexão, use connect.

import stdlib::mail::message::*;
import stdlib::mail::smtp::*;

fn main() -> i32 {
    let c: *SmtpClient = SmtpClient::new("smtp.example.com", 587);
    c.use_starttls = true;
    c.username = some("alice");
    c.password = some("hunter2");
    c.timeout_ms = 10000;
    c.auth_method = "auto";

    let m: *Mail = Mail::new();
    m.from = "[email protected]";
    m.to.push("[email protected]");
    m.bcc.push("[email protected]");   // entregue, mas oculto dos cabeçalhos
    m.subject = "ping";
    m.body = "hi";

    let r: !i32 = c.send(m);
    if !r.ok { println!(r.err); return 1; }
    return 0;
}

Para comunicar-se com um servidor de dev local com certificado autoassinado e forçar um mecanismo de autenticação específico, defina tls_insecure e auth_method:

import stdlib::mail::message::*;
import stdlib::mail::smtp::*;

fn main() -> i32 {
    let c: *SmtpClient = SmtpClient::new("localhost", 587);
    c.use_starttls = true;
    c.tls_insecure = true;        // ignora validação de certificado — apenas em dev
    c.auth_method = "login";      // força AUTH LOGIN
    c.username = some("dev");
    c.password = some("dev");

    let m: *Mail = Mail::new();
    m.from = "dev@localhost";
    m.to.push("inbox@localhost");
    m.subject = "test";
    m.body = "x";

    let r: !i32 = c.send(m);
    if !r.ok { println!(r.err); return 1; }
    return 0;
}

SmtpClient.connect

pub fn connect(self: *SmtpClient) -> !*SmtpSession

Abre uma sessão e executa: conexão TCP → leitura do banner → EHLO → STARTTLS opcional (+ re-EHLO) → AUTH opcional. O *SmtpSession retornado está posicionado logo antes de MAIL FROM e pode ser reutilizado em muitas chamadas send_mail até que você execute quit / close.

SmtpReply (struct)

pub struct SmtpReply {
    pub code: i32,      // status numérico (ex.: 250, 354, 535)
    pub text: string,   // última linha de resposta (respostas multilinha colapsam para a linha final)
}

O valor bruto da resposta analisado da rede. Raramente você o constrói; ele aparece em mensagens de erro construídas a partir do código de status e texto do servidor (ex.: "smtp: 535 5.7.8 auth failed").

SmtpSession (struct)

pub struct SmtpSession {
    pub host:       string,
    // tcp / tls / is_tls são estado privado do transporte
    pub auth_mechs: string,   // mecanismos anunciados pelo servidor no EHLO, em minúsculas
}

auth_mechs contém os mecanismos AUTH separados por espaço do EHLO mais recente (ex.: "plain login cram-md5"), ou "" se o EHLO ainda não foi executado ou o servidor não anunciou nenhum AUTH.

SmtpSession.send_mail

pub fn send_mail(self: *SmtpSession, mail: *Mail) -> !i32

Executa MAIL FROM / RCPT TO (uma vez por destinatário em to + cc + bcc) / DATA, depois transmite mail.to_rfc5322() (com dot-stuffing conforme RFC 5321 — qualquer linha do corpo que comece com . é duplicada) terminado por \r\n.\r\n. Retorna ok(0) em uma resposta final 2xx, caso contrário err("smtp: <código> <texto>").

SmtpSession.quit / SmtpSession.close

pub fn quit(self: *SmtpSession) -> !i32
pub fn close(self: *SmtpSession)

quit envia QUIT e espera um 221. close encerra o socket TCP/TLS subjacente e libera a sessão; é idempotente. Sempre use os dois em par — quit é o encerramento educado do protocolo, close libera o recurso.

import stdlib::mail::message::*;
import stdlib::mail::smtp::*;

fn build(i: i32) -> *Mail {
    let m: *Mail = Mail::new();
    m.from = "[email protected]";
    m.to.push("[email protected]");
    m.subject = "msg";
    m.body = "hello";
    return m;
}

fn main() -> i32 {
    let c: *SmtpClient = SmtpClient::new("smtp.example.com", 465);
    c.use_tls = true;

    let r: !*SmtpSession = c.connect();
    if !r.ok { println!(r.err); return 1; }
    let s: *SmtpSession = r.val;
    println!("host:", s.host);
    println!("advertised:", s.auth_mechs);

    for let i: i32 = 0; i < 3; i++ {
        let m: *Mail = build(i);
        let sr: !i32 = s.send_mail(m);
        if !sr.ok { println!(sr.err); }
    }
    let _q: !i32 = s.quit();
    s.close();
    return 0;
}

IMAP — leitura e marcação de mensagens

Um cliente IMAP4rev1 (RFC 3501) minimalista: LOGIN / SELECT / FETCH / STORE / SEARCH / APPEND / IDLE / LOGOUT baseado em tags, sobre TLS implícito (993) ou TCP puro (143). Cada comando recebe uma tag única (A1, A2, …); a sessão lê linhas sem tag * … até a linha com tag An OK/NO/BAD correspondente.

ImapSession (struct)

pub struct ImapSession {
    pub host: string,
    // tcp / tls / is_tls / rx_buf / tag_counter / idle_tag são estado privado
}

ImapResponse (struct)

pub struct ImapResponse {
    pub untagged: *Vector<string>,   // cada linha "* ..." do comando
    pub tagged:   string,            // a linha final com tag ("A3 OK ...")
    pub literal:  string,            // payload literal {N} capturado (ex.: corpo do FETCH)
}

O resultado estruturado de um comando. select, search e fetch analisam isso por você; a struct é exposta para que você possa inspecionar as linhas brutas quando necessário.

Conexão e autenticação

Método Assinatura Descrição
connect pub fn connect(host: string, port: i32, use_tls: bool) -> !*ImapSession Abre uma sessão. use_tls=true → TLS implícito (993); false → TCP puro (143). Valida o banner * OK.
login pub fn login(self: *ImapSession, user: string, pass: string) -> !i32 Autentica com o comando LOGIN. A maioria dos provedores exige uma senha de aplicativo.
select pub fn select(self: *ImapSession, mailbox: string) -> !i32 Seleciona uma caixa de correio em modo leitura-escrita; retorna a contagem de mensagens de * N EXISTS.
logout pub fn logout(self: *ImapSession) -> !i32 Envia LOGOUT para liberar o estado do servidor.
close pub fn close(self: *ImapSession) Encerra o socket e libera a sessão. Idempotente.

Leitura e marcação

Método Assinatura Descrição
fetch pub fn fetch(self: *ImapSession, seq: i32) -> !string Faz FETCH do corpo RFC 5322 completo (BODY[]) para o número de sequência seq. Passe o resultado para parse_mail.
store_flag pub fn store_flag(self: *ImapSession, seq: i32, flag: string, add: bool) -> !i32 +FLAGS quando add=true, -FLAGS quando false. Flags comuns: \\Seen, \\Deleted, \\Flagged.
search pub fn search(self: *ImapSession, criteria: string) -> !*Vector<i32> Executa SEARCH; criteria é acrescentado literalmente (RFC 3501 §6.4.4). Retorna os números de sequência correspondentes.
search_unseen pub fn search_unseen(self: *ImapSession) -> !*Vector<i32> Atalho para search("UNSEEN").
search_from pub fn search_from(self: *ImapSession, addr: string) -> !*Vector<i32> Atalho para search("FROM \"<addr>\"") (busca por substring no cabeçalho From).
append pub fn append(self: *ImapSession, mailbox: string, flags: string, message: string) -> !i32 Faz APPEND de uma mensagem RFC 5322 bruta na mailbox. flags é separado por espaço (sem parênteses), "" para nenhum.

Os critérios de search são repassados diretamente, então você pode combinar palavras-chave: "ALL", "UNSEEN", "SEEN", "FLAGGED", "DELETED", "RECENT", "FROM \"a@x\"", "SUBJECT \"meeting\"", "SINCE 1-Jan-2026", "BEFORE 31-Dec-2026", "LARGER 1024", "SMALLER 100000", "BODY \"keyword\"", "TEXT \"keyword\"", "FROM x SUBJECT y" (justaposição = AND), "OR FROM x FROM y", "NOT DELETED".

import stdlib::mail::message::*;
import stdlib::mail::imap::*;

fn main() -> i32 {
    let r: !*ImapSession = ImapSession::connect("imap.example.com", 993, true);
    if !r.ok { println!(r.err); return 1; }
    let p: *ImapSession = r.val;
    println!("host:", p.host);

    let lr: !i32 = p.login("[email protected]", "app-password");
    if !lr.ok { println!(lr.err); p.close(); return 1; }

    let count: !i32 = p.select("INBOX");
    if count.ok && count.val > 0 {
        let raw: !string = p.fetch(1);
        if raw.ok {
            let m: !*Mail = parse_mail(raw.val);
            if m.ok { println!("subject:", m.val.subject); }
        }
        let _f: !i32 = p.store_flag(1, "\\Seen", true);   // marca como lido
    }

    let un: !*Vector<i32> = p.search_unseen();
    if un.ok { println!("unread:", un.val.len()); }

    let fr: !*Vector<i32> = p.search_from("[email protected]");
    if fr.ok { println!("from alice:", fr.val.len()); }

    let hits: !*Vector<i32> = p.search("UNSEEN SUBJECT \"alert\"");
    if hits.ok {
        for let i: i32 = 0; i < hits.val.len(); i++ {
            println!("msg #", hits.val.get(i));
        }
    }

    let _lo: !i32 = p.logout();
    p.close();
    return 0;
}

IDLE (notificações push) e APPEND

Método Assinatura Descrição
idle_start pub fn idle_start(self: *ImapSession) -> !i32 Entra em IDLE (RFC 2177); o servidor começa a enviar atualizações sem tag até você parar.
idle_next pub fn idle_next(self: *ImapSession) -> !string Bloqueia aguardando a próxima linha sem tag (ex.: * 5 EXISTS). Respeita qualquer timeout do socket.
idle_stop pub fn idle_stop(self: *ImapSession) -> !i32 Envia DONE e drena de volta ao modo de comando. Retorna erro se chamado sem idle_start.

append grava uma mensagem em uma caixa de correio sem enviá-la — ideal para salvar uma cópia em Sent após um envio via SMTP, ou armazenar uma entrada em Drafts. Combine com to_rfc5322 para que os bytes gravados sejam exatamente o que você transmitiria:

import stdlib::mail::message::*;
import stdlib::mail::imap::*;

fn main() -> i32 {
    let r: !*ImapSession = ImapSession::connect("imap.example.com", 993, true);
    if !r.ok { println!(r.err); return 1; }
    let p: *ImapSession = r.val;
    let _l: !i32 = p.login("[email protected]", "pw");

    // Faz APPEND de um rascunho na caixa Drafts.
    let m: *Mail = Mail::new();
    m.from = "[email protected]";
    m.to.push("[email protected]");
    m.subject = "draft";
    m.body = "WIP";
    let _a: !i32 = p.append("Drafts", "\\Seen", m.to_rfc5322());

    // Aguarda novos e-mails, depois volta ao modo de comando.
    let _is: !i32 = p.idle_start();
    while true {
        let line: !string = p.idle_next();
        if !line.ok { break; }
        if line.val.contains("EXISTS") { break; }   // novo e-mail chegou
    }
    let _ie: !i32 = p.idle_stop();

    let _lo: !i32 = p.logout();
    p.close();
    return 0;
}

POP3 — recuperação de caixa única

Um cliente POP3 (RFC 1939) cobrindo os oito comandos padrão: USER / PASS / STAT / LIST / RETR / DELE / NOOP / QUIT. Respostas multilinha (LIST, RETR) terminam em uma linha com . isolado e têm o dot-unstuffing feito automaticamente.

Pop3MsgInfo (struct)

pub struct Pop3MsgInfo {
    pub idx:  i32,
    pub size: i32,
}

Um descritor por mensagem. Para list, idx é o número da mensagem e size é a contagem de octetos. Para stat, o único valor retornado reutiliza os campos como (count, total_size_octets)idx é a contagem de mensagens e size é o total de bytes.

Pop3Session (struct)

pub struct Pop3Session {
    pub host: string,
    // tcp / tls / is_tls / rx_buf são estado privado do transporte
}

Métodos

Método Assinatura Descrição
connect pub fn connect(host: string, port: i32, use_tls: bool) -> !*Pop3Session Abre uma sessão. use_tls=true → TLS desde o início (995); false → TCP puro (110). Valida o banner +OK.
login pub fn login(self: *Pop3Session, user: string, pass: string) -> !i32 Autentica via USER + PASS.
stat pub fn stat(self: *Pop3Session) -> !*Pop3MsgInfo STAT: retorna (count, total_size) empacotado em idx / size.
list pub fn list(self: *Pop3Session) -> !*Vector<*Pop3MsgInfo> LIST: um Pop3MsgInfo por mensagem (idx, size).
retrieve pub fn retrieve(self: *Pop3Session, n: i32) -> !string RETR: corpo RFC 5322 completo da mensagem n (com dot-unstuffing).
delete pub fn delete(self: *Pop3Session, n: i32) -> !i32 DELE: marca a mensagem n para exclusão (efetivada no quit).
noop pub fn noop(self: *Pop3Session) -> !i32 Keep-alive NOOP para evitar desconexões por inatividade.
quit pub fn quit(self: *Pop3Session) -> !i32 QUIT e confirma as exclusões pendentes.
close pub fn close(self: *Pop3Session) Encerra o socket. Idempotente.
import stdlib::mail::message::*;
import stdlib::mail::pop3::*;

fn main() -> i32 {
    let r: !*Pop3Session = Pop3Session::connect("pop.example.com", 995, true);
    if !r.ok { println!(r.err); return 1; }
    let p: *Pop3Session = r.val;
    println!("host:", p.host);

    let _l: !i32 = p.login("[email protected]", "app-password");

    let st: !*Pop3MsgInfo = p.stat();
    if st.ok { println!(st.val.idx, "msgs,", st.val.size, "bytes"); }

    let lst: !*Vector<*Pop3MsgInfo> = p.list();
    if lst.ok {
        for let i: i32 = 0; i < lst.val.len(); i++ {
            let info: *Pop3MsgInfo = lst.val.get(i);
            println!("#", info.idx, "size", info.size);
            let raw: !string = p.retrieve(info.idx);
            if raw.ok {
                let m: !*Mail = parse_mail(raw.val);
                if m.ok { println!("subject:", m.val.subject); }
            }
        }
    }

    let _d: !i32 = p.delete(1);   // efetivado no quit
    let _n: !i32 = p.noop();
    let _q: !i32 = p.quit();
    p.close();
    return 0;
}

Veja também

  • net::tcp / net::tls — o transporte sobre o qual toda sessão de e-mail é construída; timeout_ms e tls_insecure mapeiam para controles no nível do stream.
  • base64 — usado internamente para codificação de anexos e tokens de AUTH PLAIN/LOGIN do SMTP.
  • timeMail.to_rfc5322 usa para preencher automaticamente o cabeçalho Date.