1package imapclient
2
3import (
4 "bufio"
5 "errors"
6 "fmt"
7 "strings"
8 "time"
9)
10
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.
13type Capability string
14
15const (
16 CapIMAP4rev1 Capability = "IMAP4REV1" // ../rfc/3501:1310
17 CapIMAP4rev2 Capability = "IMAP4REV2" // ../rfc/9051:1219
18 CapLoginDisabled Capability = "LOGINDISABLED" // ../rfc/3501:3792 ../rfc/9051:5436
19 CapStartTLS Capability = "STARTTLS" // ../rfc/3501:1327 ../rfc/9051:1238
20 CapAuthPlain Capability = "AUTH=PLAIN" // ../rfc/3501:1327 ../rfc/9051:1238
21 CapAuthExternal Capability = "AUTH=EXTERNAL" // ../rfc/4422:1575
22 CapAuthSCRAMSHA256Plus Capability = "AUTH=SCRAM-SHA-256-PLUS" // ../rfc/7677:80
23 CapAuthSCRAMSHA256 Capability = "AUTH=SCRAM-SHA-256"
24 CapAuthSCRAMSHA1Plus Capability = "AUTH=SCRAM-SHA-1-PLUS" // ../rfc/5802:465
25 CapAuthSCRAMSHA1 Capability = "AUTH=SCRAM-SHA-1"
26 CapAuthCRAMMD5 Capability = "AUTH=CRAM-MD5" // ../rfc/2195:80
27 CapLiteralPlus Capability = "LITERAL+" // ../rfc/2088:45
28 CapLiteralMinus Capability = "LITERAL-" // ../rfc/7888:26 ../rfc/9051:847 Default since IMAP4rev2
29 CapIdle Capability = "IDLE" // ../rfc/2177:69 ../rfc/9051:3542 Default since IMAP4rev2
30 CapNamespace Capability = "NAMESPACE" // ../rfc/2342:130 ../rfc/9051:135 Default since IMAP4rev2
31 CapBinary Capability = "BINARY" // ../rfc/3516:100
32 CapUnselect Capability = "UNSELECT" // ../rfc/3691:78 ../rfc/9051:3667 Default since IMAP4rev2
33 CapUidplus Capability = "UIDPLUS" // ../rfc/4315:36 ../rfc/9051:8015 Default since IMAP4rev2
34 CapEsearch Capability = "ESEARCH" // ../rfc/4731:69 ../rfc/9051:8016 Default since IMAP4rev2
35 CapEnable Capability = "ENABLE" // ../rfc/5161:52 ../rfc/9051:8016 Default since IMAP4rev2
36 CapListExtended Capability = "LIST-EXTENDED" // ../rfc/5258:150 ../rfc/9051:7987 Syntax except multiple mailboxes default since IMAP4rev2
37 CapSpecialUse Capability = "SPECIAL-USE" // ../rfc/6154:156 ../rfc/9051:8021 Special-use attributes in LIST responses by default since IMAP4rev2
38 CapMove Capability = "MOVE" // ../rfc/6851:87 ../rfc/9051:8018 Default since IMAP4rev2
39 CapUTF8Only Capability = "UTF8=ONLY"
40 CapUTF8Accept Capability = "UTF8=ACCEPT"
41 CapCondstore Capability = "CONDSTORE" // ../rfc/7162:411
42 CapQresync Capability = "QRESYNC" // ../rfc/7162:1376
43 CapID Capability = "ID" // ../rfc/2971:80
44 CapMetadata Capability = "METADATA" // ../rfc/5464:124
45 CapMetadataServer Capability = "METADATA-SERVER" // ../rfc/5464:124
46 CapSaveDate Capability = "SAVEDATE" // ../rfc/8514
47 CapCreateSpecialUse Capability = "CREATE-SPECIAL-USE" // ../rfc/6154:296
48 CapCompressDeflate Capability = "COMPRESS=DEFLATE" // ../rfc/4978:65
49 CapListMetadata Capability = "LIST-METADATA" // ../rfc/9590:73
50 CapMultiAppend Capability = "MULTIAPPEND" // ../rfc/3502:33
51 CapReplace Capability = "REPLACE" // ../rfc/8508:155
52 CapPreview Capability = "PREVIEW" // ../rfc/8970:114
53 CapMultiSearch Capability = "MULTISEARCH" // ../rfc/7377:187
54 CapNotify Capability = "NOTIFY" // ../rfc/5465:195
55 CapUIDOnly Capability = "UIDONLY" // ../rfc/9586:129
56)
57
58// Status is the tagged final result of a command.
59type Status string
60
61const (
62 BAD Status = "BAD" // Syntax error.
63 NO Status = "NO" // Command failed.
64 OK Status = "OK" // Command succeeded.
65)
66
67// Response is a response to an IMAP command including any preceding untagged
68// responses. Response implements the error interface through result.
69//
70// See [UntaggedResponseGet] and [UntaggedResponseList] to retrieve specific types
71// of untagged responses.
72type Response struct {
73 Untagged []Untagged
74 Result
75}
76
77var (
78 ErrMissing = errors.New("no response of type") // Returned by UntaggedResponseGet.
79 ErrMultiple = errors.New("multiple responses of type") // Idem.
80)
81
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) {
85 var t T
86 var have bool
87 for _, e := range resp.Untagged {
88 if tt, ok := e.(T); ok {
89 if have {
90 return t, ErrMultiple
91 }
92 t = tt
93 }
94 }
95 if !have {
96 return t, ErrMissing
97 }
98 return t, nil
99}
100
101// UntaggedResponseList returns all untagged responses of type T.
102func UntaggedResponseList[T Untagged](resp Response) []T {
103 var l []T
104 for _, e := range resp.Untagged {
105 if tt, ok := e.(T); ok {
106 l = append(l, tt)
107 }
108 }
109 return l
110}
111
112// Result is the final response for a command, indicating success or failure.
113type Result struct {
114 Status Status
115 Code Code // Set if response code is present.
116 Text string // Any remaining text.
117}
118
119func (r Result) Error() string {
120 s := fmt.Sprintf("IMAP result %s", r.Status)
121 if r.Code != nil {
122 s += "[" + r.Code.CodeString() + "]"
123 }
124 if r.Text != "" {
125 s += " " + r.Text
126 }
127 return s
128}
129
130// Code represents a response code with optional arguments, i.e. the data between [] in the response line.
131type Code interface {
132 CodeString() string
133}
134
135// CodeWord is a response code without parameters, always in upper case.
136type CodeWord string
137
138func (c CodeWord) CodeString() string {
139 return string(c)
140}
141
142// CodeOther is an unrecognized response code with parameters.
143type CodeParams struct {
144 Code string // Always in upper case.
145 Args []string
146}
147
148func (c CodeParams) CodeString() string {
149 return c.Code + " " + strings.Join(c.Args, " ")
150}
151
152// CodeCapability is a CAPABILITY response code with the capabilities supported by the server.
153type CodeCapability []Capability
154
155func (c CodeCapability) CodeString() string {
156 var s strings.Builder
157 for _, c := range c {
158 s.WriteString(" " + string(c))
159 }
160 return "CAPABILITY" + s.String()
161}
162
163type CodeBadCharset []string
164
165func (c CodeBadCharset) CodeString() string {
166 s := "BADCHARSET"
167 if len(c) == 0 {
168 return s
169 }
170 return s + " (" + strings.Join([]string(c), " ") + ")"
171}
172
173type CodePermanentFlags []string
174
175func (c CodePermanentFlags) CodeString() string {
176 return "PERMANENTFLAGS (" + strings.Join([]string(c), " ") + ")"
177}
178
179type CodeUIDNext uint32
180
181func (c CodeUIDNext) CodeString() string {
182 return fmt.Sprintf("UIDNEXT %d", c)
183}
184
185type CodeUIDValidity uint32
186
187func (c CodeUIDValidity) CodeString() string {
188 return fmt.Sprintf("UIDVALIDITY %d", c)
189}
190
191type CodeUnseen uint32
192
193func (c CodeUnseen) CodeString() string {
194 return fmt.Sprintf("UNSEEN %d", c)
195}
196
197// "APPENDUID" response code.
198type CodeAppendUID struct {
199 UIDValidity uint32
200 UIDs NumRange
201}
202
203func (c CodeAppendUID) CodeString() string {
204 return fmt.Sprintf("APPENDUID %d %s", c.UIDValidity, c.UIDs.String())
205}
206
207// "COPYUID" response code.
208type CodeCopyUID struct {
209 DestUIDValidity uint32
210 From []NumRange
211 To []NumRange
212}
213
214func (c CodeCopyUID) CodeString() string {
215 str := func(l []NumRange) string {
216 var s strings.Builder
217 for i, e := range l {
218 if i > 0 {
219 s.WriteString(",")
220 }
221 s.WriteString(fmt.Sprintf("%d", e.First))
222 if e.Last != nil {
223 s.WriteString(fmt.Sprintf(":%d", *e.Last))
224 }
225 }
226 return s.String()
227 }
228 return fmt.Sprintf("COPYUID %d %s %s", c.DestUIDValidity, str(c.From), str(c.To))
229}
230
231// For CONDSTORE.
232type CodeModified NumSet
233
234func (c CodeModified) CodeString() string {
235 return fmt.Sprintf("MODIFIED %s", NumSet(c).String())
236}
237
238// For CONDSTORE.
239type CodeHighestModSeq int64
240
241func (c CodeHighestModSeq) CodeString() string {
242 return fmt.Sprintf("HIGHESTMODSEQ %d", c)
243}
244
245// "INPROGRESS" response code.
246type CodeInProgress struct {
247 Tag string // Nil is empty string.
248 Current *uint32
249 Goal *uint32
250}
251
252func (c CodeInProgress) CodeString() string {
253 // ABNF allows inprogress-tag/state with all nil values. Doesn't seem useful enough
254 // to keep track of.
255 if c.Tag == "" && c.Current == nil && c.Goal == nil {
256 return "INPROGRESS"
257 }
258
259 // todo: quote tag properly
260 current := "nil"
261 goal := "nil"
262 if c.Current != nil {
263 current = fmt.Sprintf("%d", *c.Current)
264 }
265 if c.Goal != nil {
266 goal = fmt.Sprintf("%d", *c.Goal)
267 }
268 return fmt.Sprintf("INPROGRESS (%q %s %s)", c.Tag, current, goal)
269}
270
271// "BADEVENT" response code, with the events that are supported, for the NOTIFY
272// extension.
273type CodeBadEvent []string
274
275func (c CodeBadEvent) CodeString() string {
276 return fmt.Sprintf("BADEVENT (%s)", strings.Join([]string(c), " "))
277}
278
279// "METADATA LONGENTRIES number" response for GETMETADATA command.
280type CodeMetadataLongEntries uint32
281
282func (c CodeMetadataLongEntries) CodeString() string {
283 return fmt.Sprintf("METADATA LONGENTRIES %d", c)
284}
285
286// "METADATA (MAXSIZE number)" response for SETMETADATA command.
287type CodeMetadataMaxSize uint32
288
289func (c CodeMetadataMaxSize) CodeString() string {
290 return fmt.Sprintf("METADATA (MAXSIZE %d)", c)
291}
292
293// "METADATA (TOOMANY)" response for SETMETADATA command.
294type CodeMetadataTooMany struct{}
295
296func (c CodeMetadataTooMany) CodeString() string {
297 return "METADATA (TOOMANY)"
298}
299
300// "METADATA (NOPRIVATE)" response for SETMETADATA command.
301type CodeMetadataNoPrivate struct{}
302
303func (c CodeMetadataNoPrivate) CodeString() string {
304 return "METADATA (NOPRIVATE)"
305}
306
307// atom or string.
308func astring(s string) string {
309 if len(s) == 0 {
310 return stringx(s)
311 }
312 for _, c := range s {
313 if c <= ' ' || c >= 0x7f || c == '(' || c == ')' || c == '{' || c == '%' || c == '*' || c == '"' || c == '\\' {
314 return stringx(s)
315 }
316 }
317 return s
318}
319
320// imap "string", i.e. double-quoted string or syncliteral.
321func stringx(s string) string {
322 var r strings.Builder
323 r.WriteString(`"`)
324 for _, c := range s {
325 if c == '\x00' || c == '\r' || c == '\n' {
326 return syncliteral(s)
327 }
328 if c == '\\' || c == '"' {
329 r.WriteString(`\`)
330 }
331 r.WriteString(string(c))
332 }
333 r.WriteString(`"`)
334 return r.String()
335}
336
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
340}
341
342// Untagged is a parsed untagged response. See types starting with Untagged.
343// todo: make an interface that the untagged responses implement?
344type Untagged any
345
346type UntaggedBye struct {
347 Code Code // Set if response code is present.
348 Text string // Any remaining text.
349}
350type UntaggedPreauth struct {
351 Code Code // Set if response code is present.
352 Text string // Any remaining text.
353}
354type UntaggedExpunge uint32
355type UntaggedExists uint32
356type UntaggedRecent uint32
357
358// UntaggedCapability lists all capabilities the server implements.
359type UntaggedCapability []Capability
360
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
364
365type UntaggedResult Result
366type UntaggedFlags []string
367type UntaggedList struct {
368 // ../rfc/9051:6690
369
370 Flags []string
371 Separator byte // 0 for NIL
372 Mailbox string
373 Extended []MboxListExtendedItem
374 OldName string // If present, taken out of Extended.
375}
376type UntaggedFetch struct {
377 Seq uint32
378 Attrs []FetchAttr
379}
380
381// UntaggedUIDFetch is like UntaggedFetch, but with UIDs instead of message
382// sequence numbers, and returned instead of regular fetch responses when UIDONLY
383// is enabled.
384type UntaggedUIDFetch struct {
385 UID uint32
386 Attrs []FetchAttr
387}
388type UntaggedSearch []uint32
389
390type UntaggedSearchModSeq struct {
391 // ../rfc/7162:1101
392
393 Nums []uint32
394 ModSeq int64
395}
396type UntaggedStatus struct {
397 Mailbox string
398 Attrs map[StatusAttr]int64 // Upper case status attributes.
399}
400
401// Unsolicited response, indicating an annotation has changed.
402type UntaggedMetadataKeys struct {
403 // ../rfc/5464:716
404
405 Mailbox string // Empty means not specific to mailbox.
406
407 // Keys that have changed. To get values (or determine absence), the server must be
408 // queried.
409 Keys []string
410}
411
412// Annotation is a metadata server of mailbox annotation.
413type Annotation struct {
414 Key string
415 // Nil is represented by IsString false and a nil Value.
416 IsString bool
417 Value []byte
418}
419
420type UntaggedMetadataAnnotations struct {
421 // ../rfc/5464:683
422
423 Mailbox string // Empty means not specific to mailbox.
424 Annotations []Annotation
425}
426
427type StatusAttr string
428
429// ../rfc/9051:7059 ../9208:712
430
431const (
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"
442)
443
444type UntaggedNamespace struct {
445 Personal, Other, Shared []NamespaceDescr
446}
447type UntaggedLsub struct {
448 // ../rfc/3501:4833
449
450 Flags []string
451 Separator byte
452 Mailbox string
453}
454
455// Fields are optional and zero if absent.
456type UntaggedEsearch struct {
457 Tag string // ../rfc/9051:6546
458 Mailbox string // For MULTISEARCH. ../rfc/7377:437
459 UIDValidity uint32 // For MULTISEARCH, ../rfc/7377:438
460
461 UID bool
462 Min uint32
463 Max uint32
464 All NumSet
465 Count *uint32
466 ModSeq int64
467 Exts []EsearchDataExt
468}
469
470// UntaggedVanished is used in QRESYNC to send UIDs that have been removed.
471type UntaggedVanished struct {
472 Earlier bool
473 UIDs NumSet
474}
475
476// UntaggedQuotaroot lists the roots for which quota can be present.
477type UntaggedQuotaroot []string
478
479// UntaggedQuota holds the quota for a quota root.
480type UntaggedQuota struct {
481 Root string
482
483 // Always has at least one. Any QUOTA=RES-* capability not mentioned has no limit
484 // or this quota root.
485 Resources []QuotaResource
486}
487
488// Resource types ../rfc/9208:533
489
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
493
494const (
495 QuotaResourceStorage = "STORAGE"
496 QuotaResourceMesssage = "MESSAGE"
497 QuotaResourceMailbox = "MAILBOX"
498 QuotaResourceAnnotationStorage = "ANNOTATION-STORAGE"
499)
500
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.
505}
506
507// ../rfc/2971:184
508
509type UntaggedID map[string]string
510
511// Extended data in an ESEARCH response.
512type EsearchDataExt struct {
513 Tag string
514 Value TaggedExtVal
515}
516
517type NamespaceDescr struct {
518 // ../rfc/9051:6769
519
520 Prefix string
521 Separator byte // If 0 then separator was absent.
522 Exts []NamespaceExtension
523}
524
525type NamespaceExtension struct {
526 // ../rfc/9051:6773
527
528 Key string
529 Values []string
530}
531
532// FetchAttr represents a FETCH response attribute.
533type FetchAttr interface {
534 Attr() string // Name of attribute in upper case, e.g. "UID".
535}
536
537type NumSet struct {
538 SearchResult bool // True if "$", in which case Ranges is irrelevant.
539 Ranges []NumRange
540}
541
542func (ns NumSet) IsZero() bool {
543 return !ns.SearchResult && ns.Ranges == nil
544}
545
546func (ns NumSet) String() string {
547 if ns.SearchResult {
548 return "$"
549 }
550 var r strings.Builder
551 for i, x := range ns.Ranges {
552 if i > 0 {
553 r.WriteString(",")
554 }
555 r.WriteString(x.String())
556 }
557 return r.String()
558}
559
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()
564 return
565}
566
567func ParseUIDRange(s string) (nr NumRange, rerr error) {
568 c := Proto{br: bufio.NewReader(strings.NewReader(s))}
569 defer c.recover(&rerr)
570 nr = c.xuidrange()
571 return
572}
573
574// NumRange is a single number or range.
575type NumRange struct {
576 First uint32 // 0 for "*".
577 Last *uint32 // Nil if absent, 0 for "*".
578}
579
580func (nr NumRange) String() string {
581 var r string
582 if nr.First == 0 {
583 r += "*"
584 } else {
585 r += fmt.Sprintf("%d", nr.First)
586 }
587 if nr.Last == nil {
588 return r
589 }
590 r += ":"
591 v := *nr.Last
592 if v == 0 {
593 r += "*"
594 } else {
595 r += fmt.Sprintf("%d", v)
596 }
597 return r
598}
599
600type TaggedExtComp struct {
601 String string
602 Comps []TaggedExtComp // Used for both space-separated and ().
603}
604
605type TaggedExtVal struct {
606 // ../rfc/9051:7111
607
608 Number *int64
609 SeqSet *NumSet
610 Comp *TaggedExtComp // If SimpleNumber and SimpleSeqSet is nil, this is a Comp. But Comp is optional and can also be nil. Not great.
611}
612
613type MboxListExtendedItem struct {
614 // ../rfc/9051:6699
615
616 Tag string
617 Val TaggedExtVal
618}
619
620// "FLAGS" fetch response.
621type FetchFlags []string
622
623func (f FetchFlags) Attr() string { return "FLAGS" }
624
625// "ENVELOPE" fetch response.
626type FetchEnvelope Envelope
627
628func (f FetchEnvelope) Attr() string { return "ENVELOPE" }
629
630// Envelope holds the basic email message fields.
631type Envelope struct {
632 Date string
633 Subject string
634 From, Sender, ReplyTo, To, CC, BCC []Address
635 InReplyTo, MessageID string
636}
637
638// Address is an address field in an email message, e.g. To.
639type Address struct {
640 Name, Adl, Mailbox, Host string
641}
642
643// "INTERNALDATE" fetch response.
644type FetchInternalDate struct {
645 Date time.Time
646}
647
648func (f FetchInternalDate) Attr() string { return "INTERNALDATE" }
649
650// "SAVEDATE" fetch response.
651type FetchSaveDate struct {
652 // ../rfc/8514:265
653
654 SaveDate *time.Time // nil means absent for message.
655}
656
657func (f FetchSaveDate) Attr() string { return "SAVEDATE" }
658
659// "RFC822.SIZE" fetch response.
660type FetchRFC822Size int64
661
662func (f FetchRFC822Size) Attr() string { return "RFC822.SIZE" }
663
664// "RFC822" fetch response.
665type FetchRFC822 string
666
667func (f FetchRFC822) Attr() string { return "RFC822" }
668
669// "RFC822.HEADER" fetch response.
670type FetchRFC822Header string
671
672func (f FetchRFC822Header) Attr() string { return "RFC822.HEADER" }
673
674// "RFC82.TEXT" fetch response.
675type FetchRFC822Text string
676
677func (f FetchRFC822Text) Attr() string { return "RFC822.TEXT" }
678
679// "BODYSTRUCTURE" fetch response.
680type FetchBodystructure struct {
681 // ../rfc/9051:6355
682
683 RespAttr string
684 Body any // BodyType*
685}
686
687func (f FetchBodystructure) Attr() string { return f.RespAttr }
688
689// "BODY" fetch response.
690type FetchBody struct {
691 // ../rfc/9051:6756 ../rfc/9051:6985
692
693 RespAttr string
694 Section string // todo: parse more ../rfc/9051:6985
695 Offset int32
696 Body string
697}
698
699func (f FetchBody) Attr() string { return f.RespAttr }
700
701// BodyFields is part of a FETCH BODY[] response.
702type BodyFields struct {
703 Params [][2]string
704 ContentID, ContentDescr, CTE string
705 Octets int32
706}
707
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 {
711 // ../rfc/9051:6411
712
713 Bodies []any // BodyTypeBasic, BodyTypeMsg, BodyTypeText
714 MediaSubtype string
715 Ext *BodyExtensionMpart
716}
717
718// BodyTypeBasic represents basic information about a part, used in a FETCH
719// response.
720type BodyTypeBasic struct {
721 // ../rfc/9051:6407
722
723 MediaType, MediaSubtype string
724 BodyFields BodyFields
725 Ext *BodyExtension1Part
726}
727
728// BodyTypeMsg represents an email message as a body structure, used in a FETCH
729// response.
730type BodyTypeMsg struct {
731 // ../rfc/9051:6415
732
733 MediaType, MediaSubtype string
734 BodyFields BodyFields
735 Envelope Envelope
736 Bodystructure any // One of the BodyType*
737 Lines int64
738 Ext *BodyExtension1Part
739}
740
741// BodyTypeText represents a text part as a body structure, used in a FETCH
742// response.
743type BodyTypeText struct {
744 // ../rfc/9051:6418
745
746 MediaType, MediaSubtype string
747 BodyFields BodyFields
748 Lines int64
749 Ext *BodyExtension1Part
750}
751
752// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
753// multiparts.
754//
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
760// pointer to nil.
761type BodyExtensionMpart struct {
762 // ../rfc/9051:5986 ../rfc/3501:4161 ../rfc/9051:6371 ../rfc/3501:4599
763
764 Params [][2]string
765 Disposition **string
766 DispositionParams *[][2]string
767 Language *[]string
768 Location **string
769 More []BodyExtension // Nil if absent.
770}
771
772// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
773// non-multiparts.
774//
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
780// pointer to nil.
781type BodyExtension1Part struct {
782 // ../rfc/9051:6023 ../rfc/3501:4191 ../rfc/9051:6366 ../rfc/3501:4584
783
784 MD5 *string
785 Disposition **string
786 DispositionParams *[][2]string
787 Language *[]string
788 Location **string
789 More []BodyExtension // Nil means absent.
790}
791
792// BodyExtension has the additional extension fields for future expansion of
793// extensions.
794type BodyExtension struct {
795 String *string
796 Number *int64
797 More []BodyExtension
798}
799
800// "BINARY" fetch response.
801type FetchBinary struct {
802 RespAttr string
803 Parts []uint32 // Can be nil.
804 Data string
805}
806
807func (f FetchBinary) Attr() string { return f.RespAttr }
808
809// "BINARY.SIZE" fetch response.
810type FetchBinarySize struct {
811 RespAttr string
812 Parts []uint32
813 Size int64
814}
815
816func (f FetchBinarySize) Attr() string { return f.RespAttr }
817
818// "UID" fetch response.
819type FetchUID uint32
820
821func (f FetchUID) Attr() string { return "UID" }
822
823// "MODSEQ" fetch response.
824type FetchModSeq int64
825
826func (f FetchModSeq) Attr() string { return "MODSEQ" }
827
828// "PREVIEW" fetch response.
829type FetchPreview struct {
830 Preview *string
831}
832
833// ../rfc/8970:146
834
835func (f FetchPreview) Attr() string { return "PREVIEW" }
836