imap

package module
v0.0.0-...-3a59f94 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

README

go-ruby-net-imap/net-imap

net-imap — go-ruby-net-imap

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of the deterministic, interpreter-independent core of Ruby's Net::IMAP — the IMAP4rev1 command builder and the response-grammar parser from MRI 4.0.5 (net-imap 0.6.2). It builds the exact tagged-command octets a client sends and parses the untagged / tagged / continuation responses a server returns into a typed value model — without any Ruby runtime, and without opening a socket.

It is the IMAP backend for go-embedded-ruby, but is a standalone, reusable module — a sibling of go-ruby-net-smtp and go-ruby-regexp.

What it is — and isn't — the socket/TLS seam. Encoding a command line (atom / quoted-string / literal argument encoding, sequence-sets, fetch-att lists) and parsing the IMAP response grammar (parenthesised lists, literals, NIL, numbers, quoted strings, the FETCH attribute and ENVELOPE grammars, resp-text-codes) is fully deterministic and needs no interpreter, so it lives here as pure Go. Reading bytes off a TLS socket — including reading exactly N bytes for a literal — is the host's job: the Reader takes a line-reader and a literal-reader callback, and the host (rbgo) wires those to its own connection.

Features

Faithful port of Net::IMAP's command builder + response parser, validated byte-for-byte against the ruby binary on every supported platform:

  • Command build — tagged commands with monotonic tags (generate_tagRUBY0001): LOGIN / SELECT / EXAMINE / LIST / LSUB / STATUS / FETCH / SEARCH / STORE / COPY / UID … / APPEND / CREATE / DELETE / RENAME / SUBSCRIBE / UNSUBSCRIBE / EXPUNGE / CHECK / CLOSE / UNSELECT / IDLE (+ IdleDone) / AUTHENTICATE / LOGOUT / CAPABILITY / NOOP / STARTTLS, with MRI's exact argument encoding (send_string_data: empty → "", multiline / non-ASCII → literal {n}\r\n…, specials → quoted string, else a bare atom), SequenceSet coalescing ([3,1,2]1:3, n:*), and fetch-att / flag lists.
  • Helpers — modified-UTF-7 mailbox-name EncodeUTF7 / DecodeUTF7 (RFC 3501 §5.1.3, & shift); FormatDate / FormatDatetime and MessageSet; and the pure SASL initial-response encoders SASLPlain / SASLLoginUser / SASLLoginPassword / SASLCramMD5 / SASLXOAuth2 (+ SASLEncode), mirroring the Net::IMAP::SASL authenticators for the AUTHENTICATE exchange.
  • Response parse — the IMAP response grammar: tagged (a001 OK …), untagged (* …) and continuation (+ …) responses; the data responses n EXISTS / n RECENT / n EXPUNGE / FLAGS / LIST / LSUB / STATUS / SEARCH / CAPABILITY / n FETCH (…); parenthesised lists, literals, quoted strings, NIL, numbers; the FETCH attribute grammar (FLAGS / ENVELOPE / INTERNALDATE / RFC822* / UID / RFC822.SIZE / MODSEQ / BODY[…] / BODYSTRUCTURE); and resp-text-codes ([UIDVALIDITY n], [PERMANENTFLAGS …], [CAPABILITY …], [BADCHARSET …], [APPENDUID n uid], [COPYUID n src dst], …).
  • Typed value modelTaggedResponse / UntaggedResponse / ContinuationRequest, ResponseText / ResponseCode, MailboxList, StatusData, FetchData, Envelope, Address, Flag, mirroring the Net::IMAP::* structs member-for-member.

CGO-free, dependency-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) and three OSes.

Install

go get github.com/go-ruby-net-imap/net-imap

Usage — building commands

package main

import (
	"fmt"

	imap "github.com/go-ruby-net-imap/net-imap"
)

func main() {
	b := imap.NewBuilder("RUBY") // tags RUBY0001, RUBY0002, …

	c, _ := b.Login("joe", "pa ss")
	fmt.Printf("%q\n", c.Bytes) // "RUBY0001 LOGIN joe \"pa ss\"\r\n"

	set, _ := imap.NewSequenceSet(1, imap.SeqRange{Lo: 3, Hi: 5})
	f, _ := b.Fetch(set, "FLAGS", "UID")
	fmt.Printf("%q\n", f.Bytes) // "RUBY0002 FETCH 1,3:5 (FLAGS UID)\r\n"

	// A command with a literal stops Bytes after "{n}\r\n"; the host writes each
	// segment's Data after the server's continuation request, then its Tail.
	a, _ := b.Append("INBOX", []imap.Flag{"Seen"}, nil, "From: …\r\n\r\nbody")
	fmt.Printf("%q + %d literal(s)\n", a.Bytes, len(a.Literals))
}

Usage — parsing responses

r, _ := imap.ParseResponse("* 12 FETCH (FLAGS (\\Seen) UID 4827313)\r\n")
u := r.(*imap.UntaggedResponse)         // Name == "FETCH"
fd := u.Data.(*imap.FetchData)          // Seqno == 12
flags, _ := fd.Attr.Get("FLAGS")        // []imap.Flag{"Seen"}
uid, _ := fd.Attr.Get("UID")            // int64(4827313)
_ = flags
_ = uid

ParseResponse is byte-faithful to MRI for the IMAP4rev1 grammar. The BODYSTRUCTURE attribute is returned as a uniform, fully-recursive *BodyStructure tree (loss-free — every body-fld is captured); its BodyType() method reports the MRI class it maps to (BodyTypeText / BodyTypeBasic / BodyTypeMessage / BodyTypeMultipart), which the host (rbgo) uses to instantiate the right Ruby struct over the field tree.

The socket / literal seam

The transport is a seam the host supplies. Reader assembles a complete response buffer — a line plus any embedded literals — from two callbacks, then parses it (mirroring Net::IMAP::ResponseReader without touching a socket):

rd := imap.NewReader(
	conn.ReadLine,             // func() (string, error)        — a CRLF-terminated line
	conn.ReadExactly,          // func(n int) (string, error)   — exactly n literal bytes
)
resp, err := rd.ReadResponse() // frames {n}\r\n literals, then ParseResponse

Response value model

IMAP wire form Go type returned
a001 OK … *TaggedResponse
* … *UntaggedResponse
+ … *ContinuationRequest
n EXISTS/RECENT/EXPUNGE int64 (in UntaggedResponse.Data)
OK/NO/BAD/BYE/PREAUTH … *ResponseText (Code + Text)
FLAGS (…) []Flag
LIST/LSUB … *MailboxList
STATUS … *StatusData
SEARCH … []int64
CAPABILITY … []string
n FETCH (…) *FetchData (*Envelope, []Flag, int64, *BodyStructure, …)

Tests & coverage

The suite pairs deterministic, ruby-free tests (which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate) with a differential MRI oracle: command bytes are compared against Net::IMAP#send_command (via a socket-free put_string probe) and the per-string send_string_data encoding, and a corpus of canned server responses is parsed by both this package and MRI's ResponseParser and projected to a canonical line for comparison. The oracle scripts $stdout.binmode so Windows text-mode never pollutes the bytes, and skip themselves where ruby is absent.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-net-imap/net-imap authors.

Documentation

Overview

Package imap is a pure-Go (CGO-free) reimplementation of the deterministic, interpreter-independent core of Ruby's Net::IMAP (MRI 4.0.5, net-imap 0.6.2): the IMAP4rev1 command builder and the response-grammar parser. The socket and TLS transport is a seam the host (go-embedded-ruby / rbgo) supplies; this package never opens a connection.

What it is — and isn't

Building a tagged command line (atom / quoted-string / literal argument encoding, sequence-sets, fetch-att lists) and parsing the IMAP response grammar (tagged / untagged / continuation responses, parenthesised lists, literals, NIL, numbers, quoted strings, the FETCH attribute and ENVELOPE grammars, resp-text-codes) is fully deterministic and needs no interpreter, so it lives here as pure Go. Reading bytes off a TLS socket — including reading exactly N bytes for a literal — is the host's job; the Reader takes a line-reader and a literal-reader callback, and the host wires those to its own connection.

Index

Constants

View Source
const CRLF = "\r\n"

CRLF is the IMAP line terminator.

View Source
const Star = int64(0)

Star is the sequence value "*" (the largest message in the mailbox).

Variables

View Source
var ErrInvalidData = errors.New("imap: invalid command data")

ErrInvalidData is returned by command builders for an argument outside the set of types IMAP commands can serialise.

View Source
var ErrParse = errors.New("imap: parse error")

ErrParse is returned (wrapped) when a response buffer does not match the IMAP response grammar.

Functions

func DecodeUTF7

func DecodeUTF7(s string) string

DecodeUTF7 decodes a modified-UTF-7 mailbox name to UTF-8, mirroring Net::IMAP.decode_utf7. "&-" decodes to a literal "&"; "&…-" decodes its modified-base64 payload as UTF-16BE; everything else passes through.

func EncodeUTF7

func EncodeUTF7(s string) string

EncodeUTF7 encodes a UTF-8 string to a modified-UTF-7 mailbox name, mirroring Net::IMAP.encode_utf7. Printable ASCII (0x20–0x7e) passes through, with a bare "&" doubled to "&-"; runs of other characters are shifted into a "&…-" block of modified base64 over their UTF-16BE code units.

func FormatDate

func FormatDate(t time.Time) string

FormatDate mirrors Net::IMAP.format_date: an unquoted `DD-Mon-YYYY` (e.g. "30-Jun-2026"), used for the SEARCH date keys.

func FormatDatetime

func FormatDatetime(t time.Time) string

FormatDatetime mirrors Net::IMAP.format_datetime: an unquoted `DD-Mon-YYYY HH:MM +ZZZZ` (minute precision, no seconds), distinct from the APPEND date-time which carries seconds. It uses the time's own zone offset.

func IdleDone

func IdleDone() string

IdleDone returns the `DONE\r\n` line that terminates an IDLE (it is untagged and carries no tag, mirroring Net::IMAP#idle_done's put_string("DONE\r\n")).

func MessageSet

func MessageSet(nums ...int64) (string, error)

MessageSet renders a list of message sequence numbers / UIDs as the IMAP sequence-set string, mirroring Net::IMAP::MessageSet#to_s (it is a thin alias over SequenceSet for the FETCH/STORE/COPY set argument). 0 means "*". It returns ErrInvalidData for an out-of-range value.

func SASLCramMD5

func SASLCramMD5(username, password, challenge string) string

SASLCramMD5 returns the raw CRAM-MD5 response (RFC 2195) for the given decoded server challenge: "username SP hex(HMAC-MD5(challenge, password))". The caller base64-decodes the server challenge first, then base64-encodes this result with SASLEncode.

func SASLEncode

func SASLEncode(raw string) string

SASLEncode base64-encodes a raw SASL response for the wire, mirroring the strict (no-newline) base64 Net::IMAP writes after a continuation request.

func SASLLoginPassword

func SASLLoginPassword(password string) string

SASLLoginPassword returns the raw LOGIN second response: the password verbatim. Encode with SASLEncode for the wire.

func SASLLoginUser

func SASLLoginUser(username string) string

SASLLoginUser returns the raw LOGIN first response: the username verbatim (Net::IMAP::SASL::LoginAuthenticator's first process result). Encode with SASLEncode for the wire.

func SASLPlain

func SASLPlain(authzid, username, password string) string

SASLPlain returns the raw PLAIN initial response (RFC 4616): the octets authzid NUL authcid NUL passwd. With an empty authzid (the common case) this is "\0user\0pass". Encode with SASLEncode for the wire.

func SASLXOAuth2

func SASLXOAuth2(username, token string) string

SASLXOAuth2 returns the raw XOAUTH2 initial response: the octets "user=USER\x01auth=Bearer TOKEN\x01\x01". Encode with SASLEncode for the wire.

Types

type Address

type Address struct {
	Name    string
	Route   string
	Mailbox string
	Host    string
}

Address is one address from an ENVELOPE. It mirrors Net::IMAP::Address (members: name, route, mailbox, host). A field is "" where the wire form was NIL.

type AppendUIDData

type AppendUIDData struct {
	UIDValidity  int64
	AssignedUIDs string
}

AppendUIDData is the data of an [APPENDUID uidvalidity uid] response code, mirroring Net::IMAP::AppendUIDData (members: uidvalidity, assigned_uids). AssignedUIDs is the sequence-set string the server reported (e.g. "3955").

type Argument

type Argument = any

Argument is a value usable as an IMAP command argument. The accepted dynamic types mirror MRI's send_data dispatch:

nil              -> NIL
string           -> atom / quoted-string / literal (per send_string_data)
int / int64      -> a number
[]Argument       -> a parenthesised list
time.Time        -> a quoted date-time ("DD-Mon-YYYY HH:MM:SS +ZZZZ")
Date             -> a quoted date (DD-Mon-YYYY)
Flag             -> a backslash-prefixed atom (\Seen)
Atom             -> a raw atom, emitted verbatim
QuotedString     -> always a quoted string
Literal          -> always a literal
RawData          -> emitted verbatim with no quoting
*SequenceSet     -> the coalesced sequence-set form (1:3,7)

type Atom

type Atom string

Atom is a command argument emitted verbatim as an atom (Net::IMAP::Atom).

type BodyStructure

type BodyStructure struct {
	// Multipart is true when the body is a multipart/* (a parenthesised list of
	// nested parts followed by the subtype).
	Multipart bool

	// Parts holds the nested parts of a multipart body (nil for singlepart).
	Parts []*BodyStructure
	// MultipartSubtype is the subtype of a multipart body ("MIXED", "ALTERNATIVE").
	MultipartSubtype string

	// Singlepart fields (the body-type-1part grammar):
	MediaType   string // body-fld-type, e.g. "TEXT" / "IMAGE" / "MESSAGE"
	Subtype     string // body-fld-subtype, e.g. "PLAIN"
	Params      *OrderedAttr
	ContentID   string
	Description string
	Encoding    string
	Size        int64
	Lines       int64 // text body line count (TEXT subtype only); -1 if absent

	// Extension holds any remaining body-ext-* items (md5, disposition,
	// language, location, and the per-type extension data) as a generic list of
	// parsed values, in wire order.
	Extension []any
}

BodyStructure is the parsed BODY / BODYSTRUCTURE of a message part.

Boundary versus MRI

MRI builds a tower of typed structs (BodyTypeText, BodyTypeBasic, BodyTypeMessage, BodyTypeMultipart, …) and assigns each body-fld-* slot a named member. This package instead returns one uniform, fully-recursive tree: every field of the body grammar is captured (so no information is lost), but the dispatch to a specific Ruby struct class — and the field-name mapping of the per-type extension data — is left to the host (rbgo), which knows the Ruby class registry. Parts is non-nil only for a multipart body; for a singlepart body the leading nstring/number fields populate the named slots and Extension holds the remaining body-ext-* items verbatim as a generic list.

This is the one documented-partial attribute: the parse is complete and loss-free, but it is not mapped to MRI's BodyType* struct names here.

func (*BodyStructure) BodyType

func (b *BodyStructure) BodyType() string

BodyType returns the name of the MRI Net::IMAP struct class this body maps to, driving the same dispatch ResponseParser does: "BodyTypeMultipart" for a multipart; "BodyTypeMessage" for a MESSAGE/RFC822 part; "BodyTypeText" for a TEXT/* part; otherwise "BodyTypeBasic". rbgo uses this to instantiate the right Ruby class over the loss-free field tree.

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder assembles tagged command lines with monotonic tags, mirroring Net::IMAP#generate_tag ("<prefix><4-digit>"). The zero value is not usable; use NewBuilder.

func NewBuilder

func NewBuilder(prefix string) *Builder

NewBuilder returns a Builder issuing tags "<prefix>0001", "<prefix>0002", … MRI's default prefix is "RUBY".

func (*Builder) Append

func (b *Builder) Append(mailbox string, flags []Flag, dt *Date, message string) (Command, error)

Append builds `TAG APPEND mailbox [(flags)] [datetime] {n}\r\n<message>`. When flags is non-empty it is emitted as a parenthesised flag list; when dt is non-nil it is emitted as a quoted date-time; the message is always a literal.

func (*Builder) Authenticate

func (b *Builder) Authenticate(mechanism string) (Command, error)

Authenticate builds `TAG AUTHENTICATE mech` for the named SASL mechanism (emitted as a raw atom, e.g. PLAIN, LOGIN, CRAM-MD5, XOAUTH2). The continuation exchange (base64 challenge/response) is driven by the host using the SASL encoders in sasl.go.

func (*Builder) Capability

func (b *Builder) Capability() (Command, error)

Capability builds `TAG CAPABILITY`.

func (*Builder) Check

func (b *Builder) Check() (Command, error)

Check builds `TAG CHECK`.

func (*Builder) Close

func (b *Builder) Close() (Command, error)

Close builds `TAG CLOSE`.

func (*Builder) Command

func (b *Builder) Command(cmd string, args ...Argument) (Command, error)

Command builds a tagged command line: TAG SP CMD (SP arg)* CRLF, encoding each argument per MRI's send_data. It returns ErrInvalidData for an unsupported argument type or an out-of-range integer.

func (*Builder) Copy

func (b *Builder) Copy(set *SequenceSet, mailbox string) (Command, error)

Copy builds `TAG COPY set mailbox`.

func (*Builder) Create

func (b *Builder) Create(mailbox string) (Command, error)

Create builds `TAG CREATE mailbox`.

func (*Builder) Delete

func (b *Builder) Delete(mailbox string) (Command, error)

Delete builds `TAG DELETE mailbox`.

func (*Builder) Examine

func (b *Builder) Examine(mailbox string) (Command, error)

Examine builds `TAG EXAMINE mailbox`.

func (*Builder) Expunge

func (b *Builder) Expunge() (Command, error)

Expunge builds `TAG EXPUNGE`.

func (*Builder) Fetch

func (b *Builder) Fetch(set *SequenceSet, atts ...string) (Command, error)

Fetch builds `TAG FETCH set (att …)`. set is the sequence set; atts is the fetch attribute list, each emitted as a raw atom (FLAGS, UID, BODY[TEXT], …).

func (*Builder) Idle

func (b *Builder) Idle() (Command, error)

Idle builds `TAG IDLE` (RFC 2177). The host writes Bytes, waits for the "+ " continuation, then later writes IdleDone() to end the idle.

func (*Builder) List

func (b *Builder) List(refName, mailbox string) (Command, error)

List builds `TAG LIST refname mailbox` (the mailbox glob).

func (*Builder) Login

func (b *Builder) Login(user, password string) (Command, error)

Login builds `TAG LOGIN user pass` (each argument string-encoded).

func (*Builder) Logout

func (b *Builder) Logout() (Command, error)

Logout builds `TAG LOGOUT`.

func (*Builder) Lsub

func (b *Builder) Lsub(refName, mailbox string) (Command, error)

Lsub builds `TAG LSUB refname mailbox`.

func (*Builder) NextTag

func (b *Builder) NextTag() string

NextTag returns the next tag and advances the counter (Net::IMAP#generate_tag).

func (*Builder) Noop

func (b *Builder) Noop() (Command, error)

Noop builds `TAG NOOP`.

func (*Builder) Rename

func (b *Builder) Rename(oldName, newName string) (Command, error)

Rename builds `TAG RENAME old new`.

func (*Builder) Search

func (b *Builder) Search(keys ...Argument) (Command, error)

Search builds `TAG SEARCH key …` from raw search-key tokens (e.g. "FROM", "smith", "SINCE", "1-Feb-2026"). Each token is emitted as a raw atom unless it is already an Argument (a QuotedString, Literal, *SequenceSet, …).

func (*Builder) Select

func (b *Builder) Select(mailbox string) (Command, error)

Select builds `TAG SELECT mailbox`.

func (*Builder) StartTLS

func (b *Builder) StartTLS() (Command, error)

StartTLS builds `TAG STARTTLS`.

func (*Builder) Status

func (b *Builder) Status(mailbox string, attrs ...string) (Command, error)

Status builds `TAG STATUS mailbox (ATTR …)` from the requested status items. Each item is emitted as a raw atom (MESSAGES, UIDNEXT, …).

func (*Builder) Store

func (b *Builder) Store(set *SequenceSet, item string, flags []Flag) (Command, error)

Store builds `TAG STORE set item value`. item is e.g. "+FLAGS" or "FLAGS"; flags is the list of flags, emitted as a parenthesised flag list.

func (*Builder) Subscribe

func (b *Builder) Subscribe(mailbox string) (Command, error)

Subscribe builds `TAG SUBSCRIBE mailbox`.

func (*Builder) UIDCopy

func (b *Builder) UIDCopy(set *SequenceSet, mailbox string) (Command, error)

UIDCopy builds `TAG UID COPY set mailbox`.

func (*Builder) UIDFetch

func (b *Builder) UIDFetch(set *SequenceSet, atts ...string) (Command, error)

UIDFetch builds `TAG UID FETCH set (att …)`.

func (*Builder) UIDSearch

func (b *Builder) UIDSearch(keys ...Argument) (Command, error)

UIDSearch builds `TAG UID SEARCH key …`.

func (*Builder) UIDStore

func (b *Builder) UIDStore(set *SequenceSet, item string, flags []Flag) (Command, error)

UIDStore builds `TAG UID STORE set item value`.

func (*Builder) Unselect

func (b *Builder) Unselect() (Command, error)

Unselect builds `TAG UNSELECT` (RFC 3691).

func (*Builder) Unsubscribe

func (b *Builder) Unsubscribe(mailbox string) (Command, error)

Unsubscribe builds `TAG UNSUBSCRIBE mailbox`.

type Command

type Command struct {
	Tag      string
	Bytes    string
	Literals []LiteralSegment
}

Command is a fully-built tagged command ready to write to the socket. Bytes is the complete octet sequence ("TAG CMD args\r\n"); Tag is the tag the host must match against the eventual tagged response. When the command carries one or more literals, Bytes stops just after the first `{n}\r\n`; the remaining segments live in Literals and the host writes each after receiving a continuation request. For literal-free commands Literals is empty and Bytes is the whole line.

type ContinuationRequest

type ContinuationRequest struct {
	Data    *ResponseText
	RawData string
}

ContinuationRequest is a command continuation request (`+ …`). It mirrors Net::IMAP::ContinuationRequest (members: data, raw_data).

type CopyUIDData

type CopyUIDData struct {
	UIDValidity  int64
	SourceUIDs   string
	AssignedUIDs string
}

CopyUIDData is the data of a [COPYUID uidvalidity src dst] response code, mirroring Net::IMAP::CopyUIDData (members: uidvalidity, source_uids, assigned_uids). The two UID sets are the sequence-set strings the server sent.

type Date

type Date struct {
	Year  int
	Month time.Month
	Day   int
}

Date is a calendar date emitted as the IMAP `DD-Mon-YYYY` quoted date.

type Envelope

type Envelope struct {
	Date      string
	Subject   string
	From      []Address
	Sender    []Address
	ReplyTo   []Address
	To        []Address
	Cc        []Address
	Bcc       []Address
	InReplyTo string
	MessageID string
}

Envelope is the parsed ENVELOPE fetch attribute. It mirrors Net::IMAP::Envelope (members: date, subject, from, sender, reply_to, to, cc, bcc, in_reply_to, message_id). String members are "" where the wire form was NIL; address-list members are nil where the wire form was NIL.

type FetchData

type FetchData struct {
	Seqno int64
	Attr  *OrderedAttr
}

FetchData is the data of a FETCH response. It mirrors Net::IMAP::FetchData (members: seqno, attr). Attr maps each fetch attribute name to its parsed value (see ParseResponse for the per-attribute value types), preserving the order the server sent them.

type Flag

type Flag string

Flag is a system or keyword flag. System flags are reported by their bare name without the leading backslash and capitalised like Ruby's symbol (`\Seen` -> Flag("Seen")); the wildcard `\*` is Flag("*"). Keyword flags (no backslash) are reported verbatim.

type Literal

type Literal string

Literal is a command argument always emitted as a literal (Net::IMAP::Literal): `{n}\r\n` followed by the bytes (the host sends the bytes after the server's continuation request).

type LiteralSegment

type LiteralSegment struct {
	Data string
	Tail string
}

LiteralSegment is the literal payload plus the bytes that follow it (up to and including the next literal's `{n}\r\n`, or the trailing CRLF). The host writes Data after a continuation request, then writes Tail.

type MailboxList

type MailboxList struct {
	Attr  []Flag
	Delim string // hierarchy delimiter, or "" for NIL
	Name  string
}

MailboxList is the data of a LIST or LSUB response. It mirrors Net::IMAP::MailboxList (members: attr, delim, name).

type OrderedAttr

type OrderedAttr struct {
	// contains filtered or unexported fields
}

OrderedAttr is an insertion-ordered string-keyed map of FETCH attributes, mirroring the Ruby Hash that Net::IMAP::FetchData#attr returns (key order is the server's order).

func (*OrderedAttr) Get

func (o *OrderedAttr) Get(key string) (any, bool)

Get returns the value for key and whether it was present.

func (*OrderedAttr) Keys

func (o *OrderedAttr) Keys() []string

Keys returns the attribute names in insertion order.

func (*OrderedAttr) Len

func (o *OrderedAttr) Len() int

Len reports the number of attributes.

func (*OrderedAttr) Set

func (o *OrderedAttr) Set(key string, val any)

Set inserts or replaces key. A repeated key keeps its original position.

type OrderedInts

type OrderedInts struct {
	// contains filtered or unexported fields
}

OrderedInts is an insertion-ordered string-keyed map of integers, used for the STATUS attr hash.

func (*OrderedInts) Get

func (o *OrderedInts) Get(key string) (int64, bool)

Get returns the value for key and whether it was present.

func (*OrderedInts) Keys

func (o *OrderedInts) Keys() []string

Keys returns the item names in insertion order.

func (*OrderedInts) Len

func (o *OrderedInts) Len() int

Len reports the number of items.

func (*OrderedInts) Set

func (o *OrderedInts) Set(key string, val int64)

Set inserts or replaces key, keeping an existing key's position.

type QuotedString

type QuotedString string

QuotedString is a command argument always emitted as a quoted string (Net::IMAP::QuotedString).

type RawData

type RawData string

RawData is a command argument emitted verbatim with no quoting (Net::IMAP::RawData).

type ReadLineFunc

type ReadLineFunc func() (string, error)

ReadLineFunc reads the next CRLF-terminated line from the connection, including the trailing CRLF. It returns the line and nil on success, or a non-nil error (e.g. io.EOF) at end of stream. The host wires this to its buffered socket reader.

type ReadLiteralFunc

type ReadLiteralFunc func(n int) (string, error)

ReadLiteralFunc reads exactly n bytes of literal payload from the connection. The host wires this to a full read of n octets off the socket.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader assembles complete response buffers from a line reader and a literal reader, then parses them. It is the parser-side seam: the host supplies the two transport callbacks and Reader does the IMAP framing (detecting a trailing `{n}\r\n`, reading n literal bytes, and continuing until the line is complete).

func NewReader

func NewReader(readLine ReadLineFunc, readLiteral ReadLiteralFunc) *Reader

NewReader returns a Reader driven by the host's line and literal readers.

func (*Reader) ReadBuffer

func (r *Reader) ReadBuffer() (string, error)

ReadBuffer assembles one complete response buffer: it reads a line, and while that line ends with a `{n}\r\n` literal marker it reads n literal bytes plus the next line, splicing them into the buffer (Net::IMAP::ResponseReader's read_response_buffer). The returned buffer is suitable for ParseResponse.

func (*Reader) ReadResponse

func (r *Reader) ReadResponse() (Response, error)

ReadResponse assembles a complete buffer and parses it.

type Response

type Response interface {
	// contains filtered or unexported methods
}

Response is the common interface of the three response shapes ParseResponse returns: *TaggedResponse, *UntaggedResponse and *ContinuationRequest.

func ParseResponse

func ParseResponse(buf string) (Response, error)

ParseResponse parses one complete response buffer — a single line, with any IMAP literals already spliced in as raw bytes (the Reader produces exactly such a buffer). It returns a *TaggedResponse, *UntaggedResponse or *ContinuationRequest, mirroring Net::IMAP::ResponseParser#parse.

The returned RawData field carries buf verbatim. ParseResponse is byte-faithful to MRI for the IMAP4rev1 response grammar (status responses, EXISTS/RECENT/ EXPUNGE, FLAGS, LIST/LSUB, STATUS, SEARCH, CAPABILITY, FETCH including ENVELOPE / FLAGS / INTERNALDATE / RFC822* / UID / BODY[…] and the resp-text-codes). BODYSTRUCTURE is returned as a *BodyStructure tree (see its doc for the boundary versus MRI's BodyType* struct tower).

type ResponseCode

type ResponseCode struct {
	Name string
	Data any
}

ResponseCode is a bracketed response code such as [ALERT], [UIDVALIDITY 1234] or [PERMANENTFLAGS (\Deleted \Seen \*)]. It mirrors Net::IMAP::ResponseCode (members: name, data). Data is nil, an int64, a string, a []int64, or a []Flag depending on the code.

type ResponseText

type ResponseText struct {
	Code *ResponseCode
	Text string
}

ResponseText is the human-readable text of a status response, with an optional bracketed [resp-text-code]. It mirrors Net::IMAP::ResponseText (members: code, text).

type SeqRange

type SeqRange struct{ Lo, Hi int64 }

SeqRange is a closed range [Lo, Hi] for NewSequenceSet. Use Star for "*".

type SequenceSet

type SequenceSet struct {
	// contains filtered or unexported fields
}

SequenceSet is a set of message sequence numbers or UIDs. It coalesces sorted values into ranges the way Net::IMAP::SequenceSet#valid_string does ([3,1,2] -> "1:3"). A value of 0 stands for "*" (the largest in the mailbox).

func NewSequenceSet

func NewSequenceSet(items ...any) (*SequenceSet, error)

NewSequenceSet builds a coalesced sequence set from individual numbers and ranges. Numbers are int / int64 (0 meaning "*"); ranges are SeqRange. It returns ErrInvalidData for an unsupported element or a number below 0.

func (*SequenceSet) String

func (s *SequenceSet) String() string

String renders the set as the IMAP sequence-set form, e.g. "1:3,7,9:*".

type StatusData

type StatusData struct {
	Mailbox string
	Attr    *OrderedInts
}

StatusData is the data of a STATUS response. It mirrors Net::IMAP::StatusData (members: mailbox, attr). Attr maps each requested status item (MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN, …) to its integer value, preserving the order the server sent them.

type TaggedResponse

type TaggedResponse struct {
	Tag     string
	Name    string
	Data    *ResponseText
	RawData string
}

TaggedResponse is a tagged status response (`a001 OK …`). It mirrors Net::IMAP::TaggedResponse (members: tag, name, data, raw_data).

type UntaggedResponse

type UntaggedResponse struct {
	Name    string
	Data    any
	RawData string
}

UntaggedResponse is an untagged response (`* …`). It mirrors Net::IMAP::UntaggedResponse (members: name, data, raw_data). Data's concrete type depends on Name: int64 for EXISTS/RECENT/EXPUNGE, *ResponseText for OK/NO/BAD/BYE/PREAUTH, []Flag for FLAGS, *MailboxList for LIST/LSUB, *StatusData for STATUS, []int64 for SEARCH, []string for CAPABILITY, and *FetchData for FETCH.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL