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 string
157 for _, c := range c {
158 s += " " + string(c)
159 }
160 return "CAPABILITY" + s
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 s := ""
217 for i, e := range l {
218 if i > 0 {
219 s += ","
220 }
221 s += fmt.Sprintf("%d", e.First)
222 if e.Last != nil {
223 s += fmt.Sprintf(":%d", *e.Last)
224 }
225 }
226 return s
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 r := `"`
323 for _, c := range s {
324 if c == '\x00' || c == '\r' || c == '\n' {
325 return syncliteral(s)
326 }
327 if c == '\\' || c == '"' {
328 r += `\`
329 }
330 r += string(c)
331 }
332 r += `"`
333 return r
334}
335
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
339}
340
341// Untagged is a parsed untagged response. See types starting with Untagged.
342// todo: make an interface that the untagged responses implement?
343type Untagged any
344
345type UntaggedBye struct {
346 Code Code // Set if response code is present.
347 Text string // Any remaining text.
348}
349type UntaggedPreauth struct {
350 Code Code // Set if response code is present.
351 Text string // Any remaining text.
352}
353type UntaggedExpunge uint32
354type UntaggedExists uint32
355type UntaggedRecent uint32
356
357// UntaggedCapability lists all capabilities the server implements.
358type UntaggedCapability []Capability
359
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
363
364type UntaggedResult Result
365type UntaggedFlags []string
366type UntaggedList struct {
367 // ../rfc/9051:6690
368
369 Flags []string
370 Separator byte // 0 for NIL
371 Mailbox string
372 Extended []MboxListExtendedItem
373 OldName string // If present, taken out of Extended.
374}
375type UntaggedFetch struct {
376 Seq uint32
377 Attrs []FetchAttr
378}
379
380// UntaggedUIDFetch is like UntaggedFetch, but with UIDs instead of message
381// sequence numbers, and returned instead of regular fetch responses when UIDONLY
382// is enabled.
383type UntaggedUIDFetch struct {
384 UID uint32
385 Attrs []FetchAttr
386}
387type UntaggedSearch []uint32
388
389type UntaggedSearchModSeq struct {
390 // ../rfc/7162:1101
391
392 Nums []uint32
393 ModSeq int64
394}
395type UntaggedStatus struct {
396 Mailbox string
397 Attrs map[StatusAttr]int64 // Upper case status attributes.
398}
399
400// Unsolicited response, indicating an annotation has changed.
401type UntaggedMetadataKeys struct {
402 // ../rfc/5464:716
403
404 Mailbox string // Empty means not specific to mailbox.
405
406 // Keys that have changed. To get values (or determine absence), the server must be
407 // queried.
408 Keys []string
409}
410
411// Annotation is a metadata server of mailbox annotation.
412type Annotation struct {
413 Key string
414 // Nil is represented by IsString false and a nil Value.
415 IsString bool
416 Value []byte
417}
418
419type UntaggedMetadataAnnotations struct {
420 // ../rfc/5464:683
421
422 Mailbox string // Empty means not specific to mailbox.
423 Annotations []Annotation
424}
425
426type StatusAttr string
427
428// ../rfc/9051:7059 ../9208:712
429
430const (
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"
441)
442
443type UntaggedNamespace struct {
444 Personal, Other, Shared []NamespaceDescr
445}
446type UntaggedLsub struct {
447 // ../rfc/3501:4833
448
449 Flags []string
450 Separator byte
451 Mailbox string
452}
453
454// Fields are optional and zero if absent.
455type UntaggedEsearch struct {
456 Tag string // ../rfc/9051:6546
457 Mailbox string // For MULTISEARCH. ../rfc/7377:437
458 UIDValidity uint32 // For MULTISEARCH, ../rfc/7377:438
459
460 UID bool
461 Min uint32
462 Max uint32
463 All NumSet
464 Count *uint32
465 ModSeq int64
466 Exts []EsearchDataExt
467}
468
469// UntaggedVanished is used in QRESYNC to send UIDs that have been removed.
470type UntaggedVanished struct {
471 Earlier bool
472 UIDs NumSet
473}
474
475// UntaggedQuotaroot lists the roots for which quota can be present.
476type UntaggedQuotaroot []string
477
478// UntaggedQuota holds the quota for a quota root.
479type UntaggedQuota struct {
480 Root string
481
482 // Always has at least one. Any QUOTA=RES-* capability not mentioned has no limit
483 // or this quota root.
484 Resources []QuotaResource
485}
486
487// Resource types ../rfc/9208:533
488
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
492
493const (
494 QuotaResourceStorage = "STORAGE"
495 QuotaResourceMesssage = "MESSAGE"
496 QuotaResourceMailbox = "MAILBOX"
497 QuotaResourceAnnotationStorage = "ANNOTATION-STORAGE"
498)
499
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.
504}
505
506// ../rfc/2971:184
507
508type UntaggedID map[string]string
509
510// Extended data in an ESEARCH response.
511type EsearchDataExt struct {
512 Tag string
513 Value TaggedExtVal
514}
515
516type NamespaceDescr struct {
517 // ../rfc/9051:6769
518
519 Prefix string
520 Separator byte // If 0 then separator was absent.
521 Exts []NamespaceExtension
522}
523
524type NamespaceExtension struct {
525 // ../rfc/9051:6773
526
527 Key string
528 Values []string
529}
530
531// FetchAttr represents a FETCH response attribute.
532type FetchAttr interface {
533 Attr() string // Name of attribute in upper case, e.g. "UID".
534}
535
536type NumSet struct {
537 SearchResult bool // True if "$", in which case Ranges is irrelevant.
538 Ranges []NumRange
539}
540
541func (ns NumSet) IsZero() bool {
542 return !ns.SearchResult && ns.Ranges == nil
543}
544
545func (ns NumSet) String() string {
546 if ns.SearchResult {
547 return "$"
548 }
549 var r string
550 for i, x := range ns.Ranges {
551 if i > 0 {
552 r += ","
553 }
554 r += x.String()
555 }
556 return r
557}
558
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()
563 return
564}
565
566func ParseUIDRange(s string) (nr NumRange, rerr error) {
567 c := Proto{br: bufio.NewReader(strings.NewReader(s))}
568 defer c.recover(&rerr)
569 nr = c.xuidrange()
570 return
571}
572
573// NumRange is a single number or range.
574type NumRange struct {
575 First uint32 // 0 for "*".
576 Last *uint32 // Nil if absent, 0 for "*".
577}
578
579func (nr NumRange) String() string {
580 var r string
581 if nr.First == 0 {
582 r += "*"
583 } else {
584 r += fmt.Sprintf("%d", nr.First)
585 }
586 if nr.Last == nil {
587 return r
588 }
589 r += ":"
590 v := *nr.Last
591 if v == 0 {
592 r += "*"
593 } else {
594 r += fmt.Sprintf("%d", v)
595 }
596 return r
597}
598
599type TaggedExtComp struct {
600 String string
601 Comps []TaggedExtComp // Used for both space-separated and ().
602}
603
604type TaggedExtVal struct {
605 // ../rfc/9051:7111
606
607 Number *int64
608 SeqSet *NumSet
609 Comp *TaggedExtComp // If SimpleNumber and SimpleSeqSet is nil, this is a Comp. But Comp is optional and can also be nil. Not great.
610}
611
612type MboxListExtendedItem struct {
613 // ../rfc/9051:6699
614
615 Tag string
616 Val TaggedExtVal
617}
618
619// "FLAGS" fetch response.
620type FetchFlags []string
621
622func (f FetchFlags) Attr() string { return "FLAGS" }
623
624// "ENVELOPE" fetch response.
625type FetchEnvelope Envelope
626
627func (f FetchEnvelope) Attr() string { return "ENVELOPE" }
628
629// Envelope holds the basic email message fields.
630type Envelope struct {
631 Date string
632 Subject string
633 From, Sender, ReplyTo, To, CC, BCC []Address
634 InReplyTo, MessageID string
635}
636
637// Address is an address field in an email message, e.g. To.
638type Address struct {
639 Name, Adl, Mailbox, Host string
640}
641
642// "INTERNALDATE" fetch response.
643type FetchInternalDate struct {
644 Date time.Time
645}
646
647func (f FetchInternalDate) Attr() string { return "INTERNALDATE" }
648
649// "SAVEDATE" fetch response.
650type FetchSaveDate struct {
651 // ../rfc/8514:265
652
653 SaveDate *time.Time // nil means absent for message.
654}
655
656func (f FetchSaveDate) Attr() string { return "SAVEDATE" }
657
658// "RFC822.SIZE" fetch response.
659type FetchRFC822Size int64
660
661func (f FetchRFC822Size) Attr() string { return "RFC822.SIZE" }
662
663// "RFC822" fetch response.
664type FetchRFC822 string
665
666func (f FetchRFC822) Attr() string { return "RFC822" }
667
668// "RFC822.HEADER" fetch response.
669type FetchRFC822Header string
670
671func (f FetchRFC822Header) Attr() string { return "RFC822.HEADER" }
672
673// "RFC82.TEXT" fetch response.
674type FetchRFC822Text string
675
676func (f FetchRFC822Text) Attr() string { return "RFC822.TEXT" }
677
678// "BODYSTRUCTURE" fetch response.
679type FetchBodystructure struct {
680 // ../rfc/9051:6355
681
682 RespAttr string
683 Body any // BodyType*
684}
685
686func (f FetchBodystructure) Attr() string { return f.RespAttr }
687
688// "BODY" fetch response.
689type FetchBody struct {
690 // ../rfc/9051:6756 ../rfc/9051:6985
691
692 RespAttr string
693 Section string // todo: parse more ../rfc/9051:6985
694 Offset int32
695 Body string
696}
697
698func (f FetchBody) Attr() string { return f.RespAttr }
699
700// BodyFields is part of a FETCH BODY[] response.
701type BodyFields struct {
702 Params [][2]string
703 ContentID, ContentDescr, CTE string
704 Octets int32
705}
706
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 {
710 // ../rfc/9051:6411
711
712 Bodies []any // BodyTypeBasic, BodyTypeMsg, BodyTypeText
713 MediaSubtype string
714 Ext *BodyExtensionMpart
715}
716
717// BodyTypeBasic represents basic information about a part, used in a FETCH
718// response.
719type BodyTypeBasic struct {
720 // ../rfc/9051:6407
721
722 MediaType, MediaSubtype string
723 BodyFields BodyFields
724 Ext *BodyExtension1Part
725}
726
727// BodyTypeMsg represents an email message as a body structure, used in a FETCH
728// response.
729type BodyTypeMsg struct {
730 // ../rfc/9051:6415
731
732 MediaType, MediaSubtype string
733 BodyFields BodyFields
734 Envelope Envelope
735 Bodystructure any // One of the BodyType*
736 Lines int64
737 Ext *BodyExtension1Part
738}
739
740// BodyTypeText represents a text part as a body structure, used in a FETCH
741// response.
742type BodyTypeText struct {
743 // ../rfc/9051:6418
744
745 MediaType, MediaSubtype string
746 BodyFields BodyFields
747 Lines int64
748 Ext *BodyExtension1Part
749}
750
751// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
752// multiparts.
753//
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
759// pointer to nil.
760type BodyExtensionMpart struct {
761 // ../rfc/9051:5986 ../rfc/3501:4161 ../rfc/9051:6371 ../rfc/3501:4599
762
763 Params [][2]string
764 Disposition **string
765 DispositionParams *[][2]string
766 Language *[]string
767 Location **string
768 More []BodyExtension // Nil if absent.
769}
770
771// BodyExtension1Part has the extensible form fields of a BODYSTRUCTURE for
772// non-multiparts.
773//
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
779// pointer to nil.
780type BodyExtension1Part struct {
781 // ../rfc/9051:6023 ../rfc/3501:4191 ../rfc/9051:6366 ../rfc/3501:4584
782
783 MD5 *string
784 Disposition **string
785 DispositionParams *[][2]string
786 Language *[]string
787 Location **string
788 More []BodyExtension // Nil means absent.
789}
790
791// BodyExtension has the additional extension fields for future expansion of
792// extensions.
793type BodyExtension struct {
794 String *string
795 Number *int64
796 More []BodyExtension
797}
798
799// "BINARY" fetch response.
800type FetchBinary struct {
801 RespAttr string
802 Parts []uint32 // Can be nil.
803 Data string
804}
805
806func (f FetchBinary) Attr() string { return f.RespAttr }
807
808// "BINARY.SIZE" fetch response.
809type FetchBinarySize struct {
810 RespAttr string
811 Parts []uint32
812 Size int64
813}
814
815func (f FetchBinarySize) Attr() string { return f.RespAttr }
816
817// "UID" fetch response.
818type FetchUID uint32
819
820func (f FetchUID) Attr() string { return "UID" }
821
822// "MODSEQ" fetch response.
823type FetchModSeq int64
824
825func (f FetchModSeq) Attr() string { return "MODSEQ" }
826
827// "PREVIEW" fetch response.
828type FetchPreview struct {
829 Preview *string
830}
831
832// ../rfc/8970:146
833
834func (f FetchPreview) Attr() string { return "PREVIEW" }
835