11// Capability is a known string for with the ENABLED command and response and
12// CAPABILITY responses. Servers could send unknown values. Always in upper case.
22 CapAuthSCRAMSHA256Plus Capability = "AUTH=SCRAM-SHA-256-PLUS" //
../rfc/7677:80
23 CapAuthSCRAMSHA256 Capability = "AUTH=SCRAM-SHA-256"
25 CapAuthSCRAMSHA1 Capability = "AUTH=SCRAM-SHA-1"
39 CapUTF8Only Capability = "UTF8=ONLY"
40 CapUTF8Accept Capability = "UTF8=ACCEPT"
58// Status is the tagged final result of a command.
62 BAD Status = "BAD" // Syntax error.
63 NO Status = "NO" // Command failed.
64 OK Status = "OK" // Command succeeded.
67// Response is a response to an IMAP command including any preceding untagged
68// responses. Response implements the error interface through result.
70// See [UntaggedResponseGet] and [UntaggedResponseList] to retrieve specific types
71// of untagged responses.
78 ErrMissing = errors.New("no response of type") // Returned by UntaggedResponseGet.
79 ErrMultiple = errors.New("multiple responses of type") // Idem.
82// UntaggedResponseGet returns the single untagged response of type T. Only
83// [ErrMissing] or [ErrMultiple] can be returned as error.
84func UntaggedResponseGet[T Untagged](resp Response) (T, error) {
87 for _, e := range resp.Untagged {
88 if tt, ok := e.(T); ok {
101// UntaggedResponseList returns all untagged responses of type T.
102func UntaggedResponseList[T Untagged](resp Response) []T {
104 for _, e := range resp.Untagged {
105 if tt, ok := e.(T); ok {
112// Result is the final response for a command, indicating success or failure.
115 Code Code // Set if response code is present.
116 Text string // Any remaining text.
119func (r Result) Error() string {
120 s := fmt.Sprintf("IMAP result %s", r.Status)
122 s += "[" + r.Code.CodeString() + "]"
130// Code represents a response code with optional arguments, i.e. the data between [] in the response line.
135// CodeWord is a response code without parameters, always in upper case.
138func (c CodeWord) CodeString() string {
142// CodeOther is an unrecognized response code with parameters.
143type CodeParams struct {
144 Code string // Always in upper case.
148func (c CodeParams) CodeString() string {
149 return c.Code + " " + strings.Join(c.Args, " ")
152// CodeCapability is a CAPABILITY response code with the capabilities supported by the server.
153type CodeCapability []Capability
155func (c CodeCapability) CodeString() string {
156 var s strings.Builder
157 for _, c := range c {
158 s.WriteString(" " + string(c))
160 return "CAPABILITY" + s.String()
163type CodeBadCharset []string
165func (c CodeBadCharset) CodeString() string {
170 return s + " (" + strings.Join([]string(c), " ") + ")"
173type CodePermanentFlags []string
175func (c CodePermanentFlags) CodeString() string {
176 return "PERMANENTFLAGS (" + strings.Join([]string(c), " ") + ")"
179type CodeUIDNext uint32
181func (c CodeUIDNext) CodeString() string {
182 return fmt.Sprintf("UIDNEXT %d", c)
185type CodeUIDValidity uint32
187func (c CodeUIDValidity) CodeString() string {
188 return fmt.Sprintf("UIDVALIDITY %d", c)
191type CodeUnseen uint32
193func (c CodeUnseen) CodeString() string {
194 return fmt.Sprintf("UNSEEN %d", c)
197// "APPENDUID" response code.
198type CodeAppendUID struct {
203func (c CodeAppendUID) CodeString() string {
204 return fmt.Sprintf("APPENDUID %d %s", c.UIDValidity, c.UIDs.String())
207// "COPYUID" response code.
208type CodeCopyUID struct {
209 DestUIDValidity uint32
214func (c CodeCopyUID) CodeString() string {
215 str := func(l []NumRange) string {
216 var s strings.Builder
217 for i, e := range l {
221 s.WriteString(fmt.Sprintf("%d", e.First))
223 s.WriteString(fmt.Sprintf(":%d", *e.Last))
228 return fmt.Sprintf("COPYUID %d %s %s", c.DestUIDValidity, str(c.From), str(c.To))
232type CodeModified NumSet
234func (c CodeModified) CodeString() string {
235 return fmt.Sprintf("MODIFIED %s", NumSet(c).String())
239type CodeHighestModSeq int64
241func (c CodeHighestModSeq) CodeString() string {
242 return fmt.Sprintf("HIGHESTMODSEQ %d", c)
245// "INPROGRESS" response code.
246type CodeInProgress struct {
247 Tag string // Nil is empty string.
252func (c CodeInProgress) CodeString() string {
253 // ABNF allows inprogress-tag/state with all nil values. Doesn't seem useful enough
255 if c.Tag == "" && c.Current == nil && c.Goal == nil {
259 // todo: quote tag properly
262 if c.Current != nil {
263 current = fmt.Sprintf("%d", *c.Current)
266 goal = fmt.Sprintf("%d", *c.Goal)
268 return fmt.Sprintf("INPROGRESS (%q %s %s)", c.Tag, current, goal)
271// "BADEVENT" response code, with the events that are supported, for the NOTIFY
273type CodeBadEvent []string
275func (c CodeBadEvent) CodeString() string {
276 return fmt.Sprintf("BADEVENT (%s)", strings.Join([]string(c), " "))
279// "METADATA LONGENTRIES number" response for GETMETADATA command.
280type CodeMetadataLongEntries uint32
282func (c CodeMetadataLongEntries) CodeString() string {
283 return fmt.Sprintf("METADATA LONGENTRIES %d", c)
286// "METADATA (MAXSIZE number)" response for SETMETADATA command.
287type CodeMetadataMaxSize uint32
289func (c CodeMetadataMaxSize) CodeString() string {
290 return fmt.Sprintf("METADATA (MAXSIZE %d)", c)
293// "METADATA (TOOMANY)" response for SETMETADATA command.
294type CodeMetadataTooMany struct{}
296func (c CodeMetadataTooMany) CodeString() string {
297 return "METADATA (TOOMANY)"
300// "METADATA (NOPRIVATE)" response for SETMETADATA command.
301type CodeMetadataNoPrivate struct{}
303func (c CodeMetadataNoPrivate) CodeString() string {
304 return "METADATA (NOPRIVATE)"
308func astring(s string) string {
312 for _, c := range s {
313 if c <= ' ' || c >= 0x7f || c == '(' || c == ')' || c == '{' || c == '%' || c == '*' || c == '"' || c == '\\' {
320// imap "string", i.e. double-quoted string or syncliteral.
321func stringx(s string) string {
322 var r strings.Builder
324 for _, c := range s {
325 if c == '\x00' || c == '\r' || c == '\n' {
326 return syncliteral(s)
328 if c == '\\' || c == '"' {
331 r.WriteString(string(c))
337// sync literal, i.e. {<num>}\r\n<num bytes>.
338func syncliteral(s string) string {
339 return fmt.Sprintf("{%d}\r\n", len(s)) + s
342// Untagged is a parsed untagged response. See types starting with Untagged.
343// todo: make an interface that the untagged responses implement?
346type UntaggedBye struct {
347 Code Code // Set if response code is present.
348 Text string // Any remaining text.
350type UntaggedPreauth struct {
351 Code Code // Set if response code is present.
352 Text string // Any remaining text.
354type UntaggedExpunge uint32
355type UntaggedExists uint32
356type UntaggedRecent uint32
358// UntaggedCapability lists all capabilities the server implements.
359type UntaggedCapability []Capability
361// UntaggedEnabled indicates the capabilities that were enabled on the connection
362// by the server, typically in response to an ENABLE command.
363type UntaggedEnabled []Capability
365type UntaggedResult Result
366type UntaggedFlags []string
367type UntaggedList struct {
371 Separator byte // 0 for NIL
373 Extended []MboxListExtendedItem
374 OldName string // If present, taken out of Extended.
376type UntaggedFetch struct {
381// UntaggedUIDFetch is like UntaggedFetch, but with UIDs instead of message
382// sequence numbers, and returned instead of regular fetch responses when UIDONLY
384type UntaggedUIDFetch struct {
388type UntaggedSearch []uint32
390type UntaggedSearchModSeq struct {
396type UntaggedStatus struct {
398 Attrs map[StatusAttr]int64 // Upper case status attributes.
401// Unsolicited response, indicating an annotation has changed.
402type UntaggedMetadataKeys struct {
405 Mailbox string // Empty means not specific to mailbox.
407 // Keys that have changed. To get values (or determine absence), the server must be
412// Annotation is a metadata server of mailbox annotation.
413type Annotation struct {
415 // Nil is represented by IsString false and a nil Value.
420type UntaggedMetadataAnnotations struct {
423 Mailbox string // Empty means not specific to mailbox.
424 Annotations []Annotation
427type StatusAttr string
432 StatusMessages StatusAttr = "MESSAGES"
433 StatusUIDNext StatusAttr = "UIDNEXT"
434 StatusUIDValidity StatusAttr = "UIDVALIDITY"
435 StatusUnseen StatusAttr = "UNSEEN"
436 StatusDeleted StatusAttr = "DELETED"
437 StatusSize StatusAttr = "SIZE"
438 StatusRecent StatusAttr = "RECENT"
439 StatusAppendLimit StatusAttr = "APPENDLIMIT"
440 StatusHighestModSeq StatusAttr = "HIGHESTMODSEQ"
441 StatusDeletedStorage StatusAttr = "DELETED-STORAGE"
444type UntaggedNamespace struct {
445 Personal, Other, Shared []NamespaceDescr
447type UntaggedLsub struct {
455// Fields are optional and zero if absent.
456type UntaggedEsearch struct {
467 Exts []EsearchDataExt
470// UntaggedVanished is used in QRESYNC to send UIDs that have been removed.
471type UntaggedVanished struct {
476// UntaggedQuotaroot lists the roots for which quota can be present.
477type UntaggedQuotaroot []string
479// UntaggedQuota holds the quota for a quota root.
480type UntaggedQuota struct {
483 // Always has at least one. Any QUOTA=RES-* capability not mentioned has no limit
484 // or this quota root.
485 Resources []QuotaResource
490// QuotaResourceName is the name of a resource type. More can be defined in the
491// future and encountered in the wild. Always in upper case.
492type QuotaResourceName string
495 QuotaResourceStorage = "STORAGE"
496 QuotaResourceMesssage = "MESSAGE"
497 QuotaResourceMailbox = "MAILBOX"
498 QuotaResourceAnnotationStorage = "ANNOTATION-STORAGE"
501type QuotaResource struct {
502 Name QuotaResourceName
503 Usage int64 // Currently in use. Count or disk size in 1024 byte blocks.
504 Limit int64 // Maximum allowed usage.
509type UntaggedID map[string]string
511// Extended data in an ESEARCH response.
512type EsearchDataExt struct {
517type NamespaceDescr struct {
521 Separator byte // If 0 then separator was absent.
522 Exts []NamespaceExtension
525type NamespaceExtension struct {
532// FetchAttr represents a FETCH response attribute.
533type FetchAttr interface {
534 Attr() string // Name of attribute in upper case, e.g. "UID".
538 SearchResult bool // True if "$", in which case Ranges is irrelevant.
542func (ns NumSet) IsZero() bool {
543 return !ns.SearchResult && ns.Ranges == nil
546func (ns NumSet) String() string {
550 var r strings.Builder
551 for i, x := range ns.Ranges {
555 r.WriteString(x.String())
560func ParseNumSet(s string) (ns NumSet, rerr error) {
561 c := Proto{br: bufio.NewReader(strings.NewReader(s))}
562 defer c.recover(&rerr)
563 ns = c.xsequenceSet()
567func ParseUIDRange(s string) (nr NumRange, rerr error) {
568 c := Proto{br: bufio.NewReader(strings.NewReader(s))}
569 defer c.recover(&rerr)
574// NumRange is a single number or range.
575type NumRange struct {
576 First uint32 // 0 for "*".
577 Last *uint32 // Nil if absent, 0 for "*".
580func (nr NumRange) String() string {
585 r += fmt.Sprintf("%d", nr.First)
595 r += fmt.Sprintf("%d", v)
600type TaggedExtComp struct {
602 Comps []TaggedExtComp // Used for both space-separated and ().
605type TaggedExtVal struct {
610 Comp *TaggedExtComp // If SimpleNumber and SimpleSeqSet is nil, this is a Comp. But Comp is optional and can also be nil. Not great.
613type MboxListExtendedItem struct {
620// "FLAGS" fetch response.
621type FetchFlags []string
623func (f FetchFlags) Attr() string { return "FLAGS" }
625// "ENVELOPE" fetch response.
626type FetchEnvelope Envelope
628func (f FetchEnvelope) Attr() string { return "ENVELOPE" }
630// Envelope holds the basic email message fields.
631type Envelope struct {
634 From, Sender, ReplyTo, To, CC, BCC []Address
635 InReplyTo, MessageID string
638// Address is an address field in an email message, e.g. To.
640 Name, Adl, Mailbox, Host string
643// "INTERNALDATE" fetch response.
644type FetchInternalDate struct {
648func (f FetchInternalDate) Attr() string { return "INTERNALDATE" }
650// "SAVEDATE" fetch response.
651type FetchSaveDate struct {
654 SaveDate *time.Time // nil means absent for message.
657func (f FetchSaveDate) Attr() string { return "SAVEDATE" }
659// "RFC822.SIZE" fetch response.
660type FetchRFC822Size int64
662func (f FetchRFC822Size) Attr() string { return "RFC822.SIZE" }
664// "RFC822" fetch response.
665type FetchRFC822 string
667func (f FetchRFC822) Attr() string { return "RFC822" }
669// "RFC822.HEADER" fetch response.
670type FetchRFC822Header string
672func (f FetchRFC822Header) Attr() string { return "RFC822.HEADER" }
674// "RFC82.TEXT" fetch response.
675type FetchRFC822Text string
677func (f FetchRFC822Text) Attr() string { return "RFC822.TEXT" }
679// "BODYSTRUCTURE" fetch response.
680type FetchBodystructure struct {
684 Body any // BodyType*
687func (f FetchBodystructure) Attr() string { return f.RespAttr }
689// "BODY" fetch response.
690type FetchBody struct {
699func (f FetchBody) Attr() string { return f.RespAttr }
701// BodyFields is part of a FETCH BODY[] response.
702type BodyFields struct {
704 ContentID, ContentDescr, CTE string
708// BodyTypeMpart represents the body structure a multipart message, with
709// subparts and the multipart media subtype. Used in a FETCH response.
710type BodyTypeMpart struct {
713 Bodies []any // BodyTypeBasic, BodyTypeMsg, BodyTypeText
715 Ext *BodyExtensionMpart
718// BodyTypeBasic represents basic information about a part, used in a FETCH
720type BodyTypeBasic struct {
723 MediaType, MediaSubtype string
724 BodyFields BodyFields
725 Ext *BodyExtension1Part
728// BodyTypeMsg represents an email message as a body structure, used in a FETCH
730type BodyTypeMsg struct {
733 MediaType, MediaSubtype string
734 BodyFields BodyFields
736 Bodystructure any // One of the BodyType*
738 Ext *BodyExtension1Part
741// BodyTypeText represents a text part as a body structure, used in a FETCH
743type BodyTypeText struct {
746 MediaType, MediaSubtype string
747 BodyFields BodyFields
749 Ext *BodyExtension1Part
752// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
755// Fields in this struct are optional in IMAP4, and can be NIL or contain a value.
756// The first field is always present, otherwise the "parent" struct would have a
757// nil *BodyExtensionMpart. The second and later fields are nil when absent. For
758// non-reference types (e.g. strings), an IMAP4 NIL is represented as a pointer to
759// (*T)(nil). For reference types (e.g. slices), an IMAP4 NIL is represented by a
761type BodyExtensionMpart struct {
766 DispositionParams *[][2]string
769 More []BodyExtension // Nil if absent.
772// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
775// Fields in this struct are optional in IMAP4, and can be NIL or contain a value.
776// The first field is always present, otherwise the "parent" struct would have a
777// nil *BodyExtensionMpart. The second and later fields are nil when absent. For
778// non-reference types (e.g. strings), an IMAP4 NIL is represented as a pointer to
779// (*T)(nil). For reference types (e.g. slices), an IMAP4 NIL is represented by a
781type BodyExtension1Part struct {
786 DispositionParams *[][2]string
789 More []BodyExtension // Nil means absent.
792// BodyExtension has the additional extension fields for future expansion of
794type BodyExtension struct {
800// "BINARY" fetch response.
801type FetchBinary struct {
803 Parts []uint32 // Can be nil.
807func (f FetchBinary) Attr() string { return f.RespAttr }
809// "BINARY.SIZE" fetch response.
810type FetchBinarySize struct {
816func (f FetchBinarySize) Attr() string { return f.RespAttr }
818// "UID" fetch response.
821func (f FetchUID) Attr() string { return "UID" }
823// "MODSEQ" fetch response.
824type FetchModSeq int64
826func (f FetchModSeq) Attr() string { return "MODSEQ" }
828// "PREVIEW" fetch response.
829type FetchPreview struct {
835func (f FetchPreview) Attr() string { return "PREVIEW" }