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 {
157 for _, c := range c {
160 return "CAPABILITY" + s
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 {
217 for i, e := range l {
221 s += fmt.Sprintf("%d", e.First)
223 s += 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 {
323 for _, c := range s {
324 if c == '\x00' || c == '\r' || c == '\n' {
325 return syncliteral(s)
327 if c == '\\' || c == '"' {
336// sync literal, i.e. {<num>}\r\n<num bytes>.
337func syncliteral(s string) string {
338 return fmt.Sprintf("{%d}\r\n", len(s)) + s
341// Untagged is a parsed untagged response. See types starting with Untagged.
342// todo: make an interface that the untagged responses implement?
345type UntaggedBye struct {
346 Code Code // Set if response code is present.
347 Text string // Any remaining text.
349type UntaggedPreauth struct {
350 Code Code // Set if response code is present.
351 Text string // Any remaining text.
353type UntaggedExpunge uint32
354type UntaggedExists uint32
355type UntaggedRecent uint32
357// UntaggedCapability lists all capabilities the server implements.
358type UntaggedCapability []Capability
360// UntaggedEnabled indicates the capabilities that were enabled on the connection
361// by the server, typically in response to an ENABLE command.
362type UntaggedEnabled []Capability
364type UntaggedResult Result
365type UntaggedFlags []string
366type UntaggedList struct {
370 Separator byte // 0 for NIL
372 Extended []MboxListExtendedItem
373 OldName string // If present, taken out of Extended.
375type UntaggedFetch struct {
380// UntaggedUIDFetch is like UntaggedFetch, but with UIDs instead of message
381// sequence numbers, and returned instead of regular fetch responses when UIDONLY
383type UntaggedUIDFetch struct {
387type UntaggedSearch []uint32
389type UntaggedSearchModSeq struct {
395type UntaggedStatus struct {
397 Attrs map[StatusAttr]int64 // Upper case status attributes.
400// Unsolicited response, indicating an annotation has changed.
401type UntaggedMetadataKeys struct {
404 Mailbox string // Empty means not specific to mailbox.
406 // Keys that have changed. To get values (or determine absence), the server must be
411// Annotation is a metadata server of mailbox annotation.
412type Annotation struct {
414 // Nil is represented by IsString false and a nil Value.
419type UntaggedMetadataAnnotations struct {
422 Mailbox string // Empty means not specific to mailbox.
423 Annotations []Annotation
426type StatusAttr string
431 StatusMessages StatusAttr = "MESSAGES"
432 StatusUIDNext StatusAttr = "UIDNEXT"
433 StatusUIDValidity StatusAttr = "UIDVALIDITY"
434 StatusUnseen StatusAttr = "UNSEEN"
435 StatusDeleted StatusAttr = "DELETED"
436 StatusSize StatusAttr = "SIZE"
437 StatusRecent StatusAttr = "RECENT"
438 StatusAppendLimit StatusAttr = "APPENDLIMIT"
439 StatusHighestModSeq StatusAttr = "HIGHESTMODSEQ"
440 StatusDeletedStorage StatusAttr = "DELETED-STORAGE"
443type UntaggedNamespace struct {
444 Personal, Other, Shared []NamespaceDescr
446type UntaggedLsub struct {
454// Fields are optional and zero if absent.
455type UntaggedEsearch struct {
466 Exts []EsearchDataExt
469// UntaggedVanished is used in QRESYNC to send UIDs that have been removed.
470type UntaggedVanished struct {
475// UntaggedQuotaroot lists the roots for which quota can be present.
476type UntaggedQuotaroot []string
478// UntaggedQuota holds the quota for a quota root.
479type UntaggedQuota struct {
482 // Always has at least one. Any QUOTA=RES-* capability not mentioned has no limit
483 // or this quota root.
484 Resources []QuotaResource
489// QuotaResourceName is the name of a resource type. More can be defined in the
490// future and encountered in the wild. Always in upper case.
491type QuotaResourceName string
494 QuotaResourceStorage = "STORAGE"
495 QuotaResourceMesssage = "MESSAGE"
496 QuotaResourceMailbox = "MAILBOX"
497 QuotaResourceAnnotationStorage = "ANNOTATION-STORAGE"
500type QuotaResource struct {
501 Name QuotaResourceName
502 Usage int64 // Currently in use. Count or disk size in 1024 byte blocks.
503 Limit int64 // Maximum allowed usage.
508type UntaggedID map[string]string
510// Extended data in an ESEARCH response.
511type EsearchDataExt struct {
516type NamespaceDescr struct {
520 Separator byte // If 0 then separator was absent.
521 Exts []NamespaceExtension
524type NamespaceExtension struct {
531// FetchAttr represents a FETCH response attribute.
532type FetchAttr interface {
533 Attr() string // Name of attribute in upper case, e.g. "UID".
537 SearchResult bool // True if "$", in which case Ranges is irrelevant.
541func (ns NumSet) IsZero() bool {
542 return !ns.SearchResult && ns.Ranges == nil
545func (ns NumSet) String() string {
550 for i, x := range ns.Ranges {
559func ParseNumSet(s string) (ns NumSet, rerr error) {
560 c := Proto{br: bufio.NewReader(strings.NewReader(s))}
561 defer c.recover(&rerr)
562 ns = c.xsequenceSet()
566func ParseUIDRange(s string) (nr NumRange, rerr error) {
567 c := Proto{br: bufio.NewReader(strings.NewReader(s))}
568 defer c.recover(&rerr)
573// NumRange is a single number or range.
574type NumRange struct {
575 First uint32 // 0 for "*".
576 Last *uint32 // Nil if absent, 0 for "*".
579func (nr NumRange) String() string {
584 r += fmt.Sprintf("%d", nr.First)
594 r += fmt.Sprintf("%d", v)
599type TaggedExtComp struct {
601 Comps []TaggedExtComp // Used for both space-separated and ().
604type TaggedExtVal struct {
609 Comp *TaggedExtComp // If SimpleNumber and SimpleSeqSet is nil, this is a Comp. But Comp is optional and can also be nil. Not great.
612type MboxListExtendedItem struct {
619// "FLAGS" fetch response.
620type FetchFlags []string
622func (f FetchFlags) Attr() string { return "FLAGS" }
624// "ENVELOPE" fetch response.
625type FetchEnvelope Envelope
627func (f FetchEnvelope) Attr() string { return "ENVELOPE" }
629// Envelope holds the basic email message fields.
630type Envelope struct {
633 From, Sender, ReplyTo, To, CC, BCC []Address
634 InReplyTo, MessageID string
637// Address is an address field in an email message, e.g. To.
639 Name, Adl, Mailbox, Host string
642// "INTERNALDATE" fetch response.
643type FetchInternalDate struct {
647func (f FetchInternalDate) Attr() string { return "INTERNALDATE" }
649// "SAVEDATE" fetch response.
650type FetchSaveDate struct {
653 SaveDate *time.Time // nil means absent for message.
656func (f FetchSaveDate) Attr() string { return "SAVEDATE" }
658// "RFC822.SIZE" fetch response.
659type FetchRFC822Size int64
661func (f FetchRFC822Size) Attr() string { return "RFC822.SIZE" }
663// "RFC822" fetch response.
664type FetchRFC822 string
666func (f FetchRFC822) Attr() string { return "RFC822" }
668// "RFC822.HEADER" fetch response.
669type FetchRFC822Header string
671func (f FetchRFC822Header) Attr() string { return "RFC822.HEADER" }
673// "RFC82.TEXT" fetch response.
674type FetchRFC822Text string
676func (f FetchRFC822Text) Attr() string { return "RFC822.TEXT" }
678// "BODYSTRUCTURE" fetch response.
679type FetchBodystructure struct {
683 Body any // BodyType*
686func (f FetchBodystructure) Attr() string { return f.RespAttr }
688// "BODY" fetch response.
689type FetchBody struct {
698func (f FetchBody) Attr() string { return f.RespAttr }
700// BodyFields is part of a FETCH BODY[] response.
701type BodyFields struct {
703 ContentID, ContentDescr, CTE string
707// BodyTypeMpart represents the body structure a multipart message, with
708// subparts and the multipart media subtype. Used in a FETCH response.
709type BodyTypeMpart struct {
712 Bodies []any // BodyTypeBasic, BodyTypeMsg, BodyTypeText
714 Ext *BodyExtensionMpart
717// BodyTypeBasic represents basic information about a part, used in a FETCH
719type BodyTypeBasic struct {
722 MediaType, MediaSubtype string
723 BodyFields BodyFields
724 Ext *BodyExtension1Part
727// BodyTypeMsg represents an email message as a body structure, used in a FETCH
729type BodyTypeMsg struct {
732 MediaType, MediaSubtype string
733 BodyFields BodyFields
735 Bodystructure any // One of the BodyType*
737 Ext *BodyExtension1Part
740// BodyTypeText represents a text part as a body structure, used in a FETCH
742type BodyTypeText struct {
745 MediaType, MediaSubtype string
746 BodyFields BodyFields
748 Ext *BodyExtension1Part
751// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
754// Fields in this struct are optional in IMAP4, and can be NIL or contain a value.
755// The first field is always present, otherwise the "parent" struct would have a
756// nil *BodyExtensionMpart. The second and later fields are nil when absent. For
757// non-reference types (e.g. strings), an IMAP4 NIL is represented as a pointer to
758// (*T)(nil). For reference types (e.g. slices), an IMAP4 NIL is represented by a
760type BodyExtensionMpart struct {
765 DispositionParams *[][2]string
768 More []BodyExtension // Nil if absent.
771// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
774// Fields in this struct are optional in IMAP4, and can be NIL or contain a value.
775// The first field is always present, otherwise the "parent" struct would have a
776// nil *BodyExtensionMpart. The second and later fields are nil when absent. For
777// non-reference types (e.g. strings), an IMAP4 NIL is represented as a pointer to
778// (*T)(nil). For reference types (e.g. slices), an IMAP4 NIL is represented by a
780type BodyExtension1Part struct {
785 DispositionParams *[][2]string
788 More []BodyExtension // Nil means absent.
791// BodyExtension has the additional extension fields for future expansion of
793type BodyExtension struct {
799// "BINARY" fetch response.
800type FetchBinary struct {
802 Parts []uint32 // Can be nil.
806func (f FetchBinary) Attr() string { return f.RespAttr }
808// "BINARY.SIZE" fetch response.
809type FetchBinarySize struct {
815func (f FetchBinarySize) Attr() string { return f.RespAttr }
817// "UID" fetch response.
820func (f FetchUID) Attr() string { return "UID" }
822// "MODSEQ" fetch response.
823type FetchModSeq int64
825func (f FetchModSeq) Attr() string { return "MODSEQ" }
827// "PREVIEW" fetch response.
828type FetchPreview struct {
834func (f FetchPreview) Attr() string { return "PREVIEW" }