10// Capability is a known string for with the ENABLED and CAPABILITY command.
14 CapIMAP4rev1 Capability = "IMAP4rev1"
15 CapIMAP4rev2 Capability = "IMAP4rev2"
16 CapLoginDisabled Capability = "LOGINDISABLED"
17 CapStarttls Capability = "STARTTLS"
18 CapAuthPlain Capability = "AUTH=PLAIN"
19 CapLiteralPlus Capability = "LITERAL+"
20 CapLiteralMinus Capability = "LITERAL-"
21 CapIdle Capability = "IDLE"
22 CapNamespace Capability = "NAMESPACE"
23 CapBinary Capability = "BINARY"
24 CapUnselect Capability = "UNSELECT"
25 CapUidplus Capability = "UIDPLUS"
26 CapEsearch Capability = "ESEARCH"
27 CapEnable Capability = "ENABLE"
28 CapSave Capability = "SAVE"
29 CapListExtended Capability = "LIST-EXTENDED"
30 CapSpecialUse Capability = "SPECIAL-USE"
31 CapMove Capability = "MOVE"
32 CapUTF8Only Capability = "UTF8=ONLY"
33 CapUTF8Accept Capability = "UTF8=ACCEPT"
47// Status is the tagged final result of a command.
51 BAD Status = "BAD" // Syntax error.
52 NO Status = "NO" // Command failed.
53 OK Status = "OK" // Command succeeded.
56// Result is the final response for a command, indicating success or failure.
62// CodeArg represents a response code with arguments, i.e. the data between [] in the response line.
63type CodeArg interface {
67// CodeOther is a valid but unrecognized response code.
68type CodeOther struct {
73func (c CodeOther) CodeString() string {
74 return c.Code + " " + strings.Join(c.Args, " ")
77// CodeWords is a code with space-separated string parameters. E.g. CAPABILITY.
78type CodeWords struct {
83func (c CodeWords) CodeString() string {
85 for _, w := range c.Args {
91// CodeList is a code with a list with space-separated strings as parameters. E.g. BADCHARSET, PERMANENTFLAGS.
94 Args []string // If nil, no list was present. List can also be empty.
97func (c CodeList) CodeString() string {
102 return s + "(" + strings.Join(c.Args, " ") + ")"
105// CodeUint is a code with a uint32 parameter, e.g. UIDNEXT and UIDVALIDITY.
106type CodeUint struct {
111func (c CodeUint) CodeString() string {
112 return fmt.Sprintf("%s %d", c.Code, c.Num)
115// "APPENDUID" response code.
116type CodeAppendUID struct {
121func (c CodeAppendUID) CodeString() string {
122 return fmt.Sprintf("APPENDUID %d %s", c.UIDValidity, c.UIDs.String())
125// "COPYUID" response code.
126type CodeCopyUID struct {
127 DestUIDValidity uint32
132func (c CodeCopyUID) CodeString() string {
133 str := func(l []NumRange) string {
135 for i, e := range l {
139 s += fmt.Sprintf("%d", e.First)
141 s += fmt.Sprintf(":%d", *e.Last)
146 return fmt.Sprintf("COPYUID %d %s %s", c.DestUIDValidity, str(c.From), str(c.To))
150type CodeModified NumSet
152func (c CodeModified) CodeString() string {
153 return fmt.Sprintf("MODIFIED %s", NumSet(c).String())
157type CodeHighestModSeq int64
159func (c CodeHighestModSeq) CodeString() string {
160 return fmt.Sprintf("HIGHESTMODSEQ %d", c)
163// "INPROGRESS" response code.
164type CodeInProgress struct {
165 Tag string // Nil is empty string.
170func (c CodeInProgress) CodeString() string {
171 // ABNF allows inprogress-tag/state with all nil values. Doesn't seem useful enough
173 if c.Tag == "" && c.Current == nil && c.Goal == nil {
177 // todo: quote tag properly
180 if c.Current != nil {
181 current = fmt.Sprintf("%d", *c.Current)
184 goal = fmt.Sprintf("%d", *c.Goal)
186 return fmt.Sprintf("INPROGRESS (%q %s %s)", c.Tag, current, goal)
189// RespText represents a response line minus the leading tag.
190type RespText struct {
191 Code string // The first word between [] after the status.
192 CodeArg CodeArg // Set if code has a parameter.
193 More string // Any remaining text.
197func astring(s string) string {
201 for _, c := range s {
202 if c <= ' ' || c >= 0x7f || c == '(' || c == ')' || c == '{' || c == '%' || c == '*' || c == '"' || c == '\\' {
209// imap "string", i.e. double-quoted string or syncliteral.
210func stringx(s string) string {
212 for _, c := range s {
213 if c == '\x00' || c == '\r' || c == '\n' {
214 return syncliteral(s)
216 if c == '\\' || c == '"' {
225// sync literal, i.e. {<num>}\r\n<num bytes>.
226func syncliteral(s string) string {
227 return fmt.Sprintf("{%d}\r\n", len(s)) + s
230// Untagged is a parsed untagged response. See types starting with Untagged.
231// todo: make an interface that the untagged responses implement?
234type UntaggedBye RespText
235type UntaggedPreauth RespText
236type UntaggedExpunge uint32
237type UntaggedExists uint32
238type UntaggedRecent uint32
239type UntaggedCapability []string
240type UntaggedEnabled []string
241type UntaggedResult Result
242type UntaggedFlags []string
243type UntaggedList struct {
246 Separator byte // 0 for NIL
248 Extended []MboxListExtendedItem
249 OldName string // If present, taken out of Extended.
251type UntaggedFetch struct {
255type UntaggedSearch []uint32
258type UntaggedSearchModSeq struct {
262type UntaggedStatus struct {
264 Attrs map[StatusAttr]int64 // Upper case status attributes.
268type UntaggedMetadataKeys struct {
269 Mailbox string // Empty means not specific to mailbox.
271 // Keys that have changed. To get values (or determine absence), the server must be
276// Annotation is a metadata server of mailbox annotation.
277type Annotation struct {
279 // Nil is represented by IsString false and a nil Value.
285type UntaggedMetadataAnnotations struct {
286 Mailbox string // Empty means not specific to mailbox.
287 Annotations []Annotation
291type StatusAttr string
294 StatusMessages StatusAttr = "MESSAGES"
295 StatusUIDNext StatusAttr = "UIDNEXT"
296 StatusUIDValidity StatusAttr = "UIDVALIDITY"
297 StatusUnseen StatusAttr = "UNSEEN"
298 StatusDeleted StatusAttr = "DELETED"
299 StatusSize StatusAttr = "SIZE"
300 StatusRecent StatusAttr = "RECENT"
301 StatusAppendLimit StatusAttr = "APPENDLIMIT"
302 StatusHighestModSeq StatusAttr = "HIGHESTMODSEQ"
303 StatusDeletedStorage StatusAttr = "DELETED-STORAGE"
306type UntaggedNamespace struct {
307 Personal, Other, Shared []NamespaceDescr
309type UntaggedLsub struct {
316// Fields are optional and zero if absent.
317type UntaggedEsearch struct {
328 Exts []EsearchDataExt
331// UntaggedVanished is used in QRESYNC to send UIDs that have been removed.
332type UntaggedVanished struct {
337// UntaggedQuotaroot lists the roots for which quota can be present.
338type UntaggedQuotaroot []string
340// UntaggedQuota holds the quota for a quota root.
341type UntaggedQuota struct {
344 // Always has at least one. Any QUOTA=RES-* capability not mentioned has no limit
345 // or this quota root.
346 Resources []QuotaResource
351// QuotaResourceName is the name of a resource type. More can be defined in the
352// future and encountered in the wild. Always in upper case.
353type QuotaResourceName string
356 QuotaResourceStorage = "STORAGE"
357 QuotaResourceMesssage = "MESSAGE"
358 QuotaResourceMailbox = "MAILBOX"
359 QuotaResourceAnnotationStorage = "ANNOTATION-STORAGE"
362type QuotaResource struct {
363 Name QuotaResourceName
364 Usage int64 // Currently in use. Count or disk size in 1024 byte blocks.
365 Limit int64 // Maximum allowed usage.
370type UntaggedID map[string]string
372// Extended data in an ESEARCH response.
373type EsearchDataExt struct {
378type NamespaceDescr struct {
381 Separator byte // If 0 then separator was absent.
382 Exts []NamespaceExtension
385type NamespaceExtension struct {
391// FetchAttr represents a FETCH response attribute.
392type FetchAttr interface {
393 Attr() string // Name of attribute.
397 SearchResult bool // True if "$", in which case Ranges is irrelevant.
401func (ns NumSet) IsZero() bool {
402 return !ns.SearchResult && ns.Ranges == nil
405func (ns NumSet) String() string {
410 for i, x := range ns.Ranges {
419func ParseNumSet(s string) (ns NumSet, rerr error) {
420 c := Conn{br: bufio.NewReader(strings.NewReader(s))}
421 defer c.recover(&rerr)
422 ns = c.xsequenceSet()
426func ParseUIDRange(s string) (nr NumRange, rerr error) {
427 c := Conn{br: bufio.NewReader(strings.NewReader(s))}
428 defer c.recover(&rerr)
433// NumRange is a single number or range.
434type NumRange struct {
435 First uint32 // 0 for "*".
436 Last *uint32 // Nil if absent, 0 for "*".
439func (nr NumRange) String() string {
444 r += fmt.Sprintf("%d", nr.First)
454 r += fmt.Sprintf("%d", v)
459type TaggedExtComp struct {
461 Comps []TaggedExtComp // Used for both space-separated and ().
464type TaggedExtVal struct {
468 Comp *TaggedExtComp // If SimpleNumber and SimpleSeqSet is nil, this is a Comp. But Comp is optional and can also be nil. Not great.
471type MboxListExtendedItem struct {
477// "FLAGS" fetch response.
478type FetchFlags []string
480func (f FetchFlags) Attr() string { return "FLAGS" }
482// "ENVELOPE" fetch response.
483type FetchEnvelope Envelope
485func (f FetchEnvelope) Attr() string { return "ENVELOPE" }
487// Envelope holds the basic email message fields.
488type Envelope struct {
491 From, Sender, ReplyTo, To, CC, BCC []Address
492 InReplyTo, MessageID string
495// Address is an address field in an email message, e.g. To.
497 Name, Adl, Mailbox, Host string
500// "INTERNALDATE" fetch response.
501type FetchInternalDate struct {
505func (f FetchInternalDate) Attr() string { return "INTERNALDATE" }
508type FetchSaveDate struct {
509 SaveDate *time.Time // nil means absent for message.
512func (f FetchSaveDate) Attr() string { return "SAVEDATE" }
514// "RFC822.SIZE" fetch response.
515type FetchRFC822Size int64
517func (f FetchRFC822Size) Attr() string { return "RFC822.SIZE" }
519// "RFC822" fetch response.
520type FetchRFC822 string
522func (f FetchRFC822) Attr() string { return "RFC822" }
524// "RFC822.HEADER" fetch response.
525type FetchRFC822Header string
527func (f FetchRFC822Header) Attr() string { return "RFC822.HEADER" }
529// "RFC82.TEXT" fetch response.
530type FetchRFC822Text string
532func (f FetchRFC822Text) Attr() string { return "RFC822.TEXT" }
534// "BODYSTRUCTURE" fetch response.
535type FetchBodystructure struct {
538 Body any // BodyType*
541func (f FetchBodystructure) Attr() string { return f.RespAttr }
543// "BODY" fetch response.
544type FetchBody struct {
552func (f FetchBody) Attr() string { return f.RespAttr }
554// BodyFields is part of a FETCH BODY[] response.
555type BodyFields struct {
557 ContentID, ContentDescr, CTE string
561// BodyTypeMpart represents the body structure a multipart message, with
562// subparts and the multipart media subtype. Used in a FETCH response.
563type BodyTypeMpart struct {
565 Bodies []any // BodyTypeBasic, BodyTypeMsg, BodyTypeText
567 Ext *BodyExtensionMpart
570// BodyTypeBasic represents basic information about a part, used in a FETCH
572type BodyTypeBasic struct {
574 MediaType, MediaSubtype string
575 BodyFields BodyFields
576 Ext *BodyExtension1Part
579// BodyTypeMsg represents an email message as a body structure, used in a FETCH
581type BodyTypeMsg struct {
583 MediaType, MediaSubtype string
584 BodyFields BodyFields
586 Bodystructure any // One of the BodyType*
588 Ext *BodyExtension1Part
591// BodyTypeText represents a text part as a body structure, used in a FETCH
593type BodyTypeText struct {
595 MediaType, MediaSubtype string
596 BodyFields BodyFields
598 Ext *BodyExtension1Part
601// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
603type BodyExtensionMpart struct {
607 DispositionParams [][2]string
613// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
615type BodyExtension1Part struct {
619 DispositionParams [][2]string
625// BodyExtension has the additional extension fields for future expansion of
627type BodyExtension struct {
633// "BINARY" fetch response.
634type FetchBinary struct {
636 Parts []uint32 // Can be nil.
640func (f FetchBinary) Attr() string { return f.RespAttr }
642// "BINARY.SIZE" fetch response.
643type FetchBinarySize struct {
649func (f FetchBinarySize) Attr() string { return f.RespAttr }
651// "UID" fetch response.
654func (f FetchUID) Attr() string { return "UID" }
656// "MODSEQ" fetch response.
657type FetchModSeq int64
659func (f FetchModSeq) Attr() string { return "MODSEQ" }
661// "PREVIEW" fetch response.
662type FetchPreview struct {
668func (f FetchPreview) Attr() string { return "PREVIEW" }