1package imap
2
3import (
4	"bytes"
5	"errors"
6	"fmt"
7	"io"
8	"mime"
9	"strconv"
10	"strings"
11	"time"
12)
13
14// System message flags, defined in RFC 3501 section 2.3.2.
15const (
16	SeenFlag     = "\\Seen"
17	AnsweredFlag = "\\Answered"
18	FlaggedFlag  = "\\Flagged"
19	DeletedFlag  = "\\Deleted"
20	DraftFlag    = "\\Draft"
21	RecentFlag   = "\\Recent"
22)
23
24// TryCreateFlag is a special flag in MailboxStatus.PermanentFlags indicating
25// that it is possible to create new keywords by attempting to store those
26// flags in the mailbox.
27const TryCreateFlag = "\\*"
28
29var flags = []string{
30	SeenFlag,
31	AnsweredFlag,
32	FlaggedFlag,
33	DeletedFlag,
34	DraftFlag,
35	RecentFlag,
36}
37
38// A PartSpecifier specifies which parts of the MIME entity should be returned.
39type PartSpecifier string
40
41// Part specifiers described in RFC 3501 page 55.
42const (
43	// Refers to the entire part, including headers.
44	EntireSpecifier PartSpecifier = ""
45	// Refers to the header of the part. Must include the final CRLF delimiting
46	// the header and the body.
47	HeaderSpecifier = "HEADER"
48	// Refers to the text body of the part, omitting the header.
49	TextSpecifier = "TEXT"
50	// Refers to the MIME Internet Message Body header.  Must include the final
51	// CRLF delimiting the header and the body.
52	MIMESpecifier = "MIME"
53)
54
55// CanonicalFlag returns the canonical form of a flag. Flags are case-insensitive.
56//
57// If the flag is defined in RFC 3501, it returns the flag with the case of the
58// RFC. Otherwise, it returns the lowercase version of the flag.
59func CanonicalFlag(flag string) string {
60	flag = strings.ToLower(flag)
61	for _, f := range flags {
62		if strings.ToLower(f) == flag {
63			return f
64		}
65	}
66	return flag
67}
68
69func ParseParamList(fields []interface{}) (map[string]string, error) {
70	params := make(map[string]string)
71
72	var k string
73	for i, f := range fields {
74		p, err := ParseString(f)
75		if err != nil {
76			return nil, errors.New("Parameter list contains a non-string: " + err.Error())
77		}
78
79		if i%2 == 0 {
80			k = p
81		} else {
82			params[k] = p
83			k = ""
84		}
85	}
86
87	if k != "" {
88		return nil, errors.New("Parameter list contains a key without a value")
89	}
90	return params, nil
91}
92
93func FormatParamList(params map[string]string) []interface{} {
94	var fields []interface{}
95	for key, value := range params {
96		fields = append(fields, key, value)
97	}
98	return fields
99}
100
101var wordDecoder = &mime.WordDecoder{
102	CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
103		if CharsetReader != nil {
104			return CharsetReader(charset, input)
105		}
106		return nil, fmt.Errorf("imap: unhandled charset %q", charset)
107	},
108}
109
110func decodeHeader(s string) (string, error) {
111	dec, err := wordDecoder.DecodeHeader(s)
112	if err != nil {
113		return s, err
114	}
115	return dec, nil
116}
117
118func encodeHeader(s string) string {
119	return mime.QEncoding.Encode("utf-8", s)
120}
121
122func parseHeaderParamList(fields []interface{}) (map[string]string, error) {
123	params, err := ParseParamList(fields)
124	if err != nil {
125		return nil, err
126	}
127
128	for k, v := range params {
129		params[k], _ = decodeHeader(v)
130	}
131	return params, nil
132}
133
134func formatHeaderParamList(params map[string]string) []interface{} {
135	encoded := make(map[string]string)
136	for k, v := range params {
137		encoded[k] = encodeHeader(v)
138	}
139	return FormatParamList(encoded)
140}
141
142// A message.
143type Message struct {
144	// The message sequence number. It must be greater than or equal to 1.
145	SeqNum uint32
146	// The mailbox items that are currently filled in. This map's values
147	// should not be used directly, they must only be used by libraries
148	// implementing extensions of the IMAP protocol.
149	Items map[FetchItem]interface{}
150
151	// The message envelope.
152	Envelope *Envelope
153	// The message body structure (either BODYSTRUCTURE or BODY).
154	BodyStructure *BodyStructure
155	// The message flags.
156	Flags []string
157	// The date the message was received by the server.
158	InternalDate time.Time
159	// The message size.
160	Size uint32
161	// The message unique identifier. It must be greater than or equal to 1.
162	Uid uint32
163	// The message body sections.
164	Body map[*BodySectionName]Literal
165
166	// The order in which items were requested. This order must be preserved
167	// because some bad IMAP clients (looking at you, Outlook!) refuse responses
168	// containing items in a different order.
169	itemsOrder []FetchItem
170}
171
172// Create a new empty message that will contain the specified items.
173func NewMessage(seqNum uint32, items []FetchItem) *Message {
174	msg := &Message{
175		SeqNum:     seqNum,
176		Items:      make(map[FetchItem]interface{}),
177		Body:       make(map[*BodySectionName]Literal),
178		itemsOrder: items,
179	}
180
181	for _, k := range items {
182		msg.Items[k] = nil
183	}
184
185	return msg
186}
187
188// Parse a message from fields.
189func (m *Message) Parse(fields []interface{}) error {
190	m.Items = make(map[FetchItem]interface{})
191	m.Body = map[*BodySectionName]Literal{}
192	m.itemsOrder = nil
193
194	var k FetchItem
195	for i, f := range fields {
196		if i%2 == 0 { // It's a key
197			switch f := f.(type) {
198			case string:
199				k = FetchItem(strings.ToUpper(f))
200			case RawString:
201				k = FetchItem(strings.ToUpper(string(f)))
202			default:
203				return fmt.Errorf("cannot parse message: key is not a string, but a %T", f)
204			}
205		} else { // It's a value
206			m.Items[k] = nil
207			m.itemsOrder = append(m.itemsOrder, k)
208
209			switch k {
210			case FetchBody, FetchBodyStructure:
211				bs, ok := f.([]interface{})
212				if !ok {
213					return fmt.Errorf("cannot parse message: BODYSTRUCTURE is not a list, but a %T", f)
214				}
215
216				m.BodyStructure = &BodyStructure{Extended: k == FetchBodyStructure}
217				if err := m.BodyStructure.Parse(bs); err != nil {
218					return err
219				}
220			case FetchEnvelope:
221				env, ok := f.([]interface{})
222				if !ok {
223					return fmt.Errorf("cannot parse message: ENVELOPE is not a list, but a %T", f)
224				}
225
226				m.Envelope = &Envelope{}
227				if err := m.Envelope.Parse(env); err != nil {
228					return err
229				}
230			case FetchFlags:
231				flags, ok := f.([]interface{})
232				if !ok {
233					return fmt.Errorf("cannot parse message: FLAGS is not a list, but a %T", f)
234				}
235
236				m.Flags = make([]string, len(flags))
237				for i, flag := range flags {
238					s, _ := ParseString(flag)
239					m.Flags[i] = CanonicalFlag(s)
240				}
241			case FetchInternalDate:
242				date, _ := f.(string)
243				m.InternalDate, _ = time.Parse(DateTimeLayout, date)
244			case FetchRFC822Size:
245				m.Size, _ = ParseNumber(f)
246			case FetchUid:
247				m.Uid, _ = ParseNumber(f)
248			default:
249				// Likely to be a section of the body
250				// First check that the section name is correct
251				if section, err := ParseBodySectionName(k); err != nil {
252					// Not a section name, maybe an attribute defined in an IMAP extension
253					m.Items[k] = f
254				} else {
255					m.Body[section], _ = f.(Literal)
256				}
257			}
258		}
259	}
260
261	return nil
262}
263
264func (m *Message) formatItem(k FetchItem) []interface{} {
265	v := m.Items[k]
266	var kk interface{} = RawString(k)
267
268	switch k {
269	case FetchBody, FetchBodyStructure:
270		// Extension data is only returned with the BODYSTRUCTURE fetch
271		m.BodyStructure.Extended = k == FetchBodyStructure
272		v = m.BodyStructure.Format()
273	case FetchEnvelope:
274		v = m.Envelope.Format()
275	case FetchFlags:
276		flags := make([]interface{}, len(m.Flags))
277		for i, flag := range m.Flags {
278			flags[i] = RawString(flag)
279		}
280		v = flags
281	case FetchInternalDate:
282		v = m.InternalDate
283	case FetchRFC822Size:
284		v = m.Size
285	case FetchUid:
286		v = m.Uid
287	default:
288		for section, literal := range m.Body {
289			if section.value == k {
290				// This can contain spaces, so we can't pass it as a string directly
291				kk = section.resp()
292				v = literal
293				break
294			}
295		}
296	}
297
298	return []interface{}{kk, v}
299}
300
301func (m *Message) Format() []interface{} {
302	var fields []interface{}
303
304	// First send ordered items
305	processed := make(map[FetchItem]bool)
306	for _, k := range m.itemsOrder {
307		if _, ok := m.Items[k]; ok {
308			fields = append(fields, m.formatItem(k)...)
309			processed[k] = true
310		}
311	}
312
313	// Then send other remaining items
314	for k := range m.Items {
315		if !processed[k] {
316			fields = append(fields, m.formatItem(k)...)
317		}
318	}
319
320	return fields
321}
322
323// GetBody gets the body section with the specified name. Returns nil if it's not found.
324func (m *Message) GetBody(section *BodySectionName) Literal {
325	section = section.resp()
326
327	for s, body := range m.Body {
328		if section.Equal(s) {
329			return body
330		}
331	}
332	return nil
333}
334
335// A body section name.
336// See RFC 3501 page 55.
337type BodySectionName struct {
338	BodyPartName
339
340	// If set to true, do not implicitly set the \Seen flag.
341	Peek bool
342	// The substring of the section requested. The first value is the position of
343	// the first desired octet and the second value is the maximum number of
344	// octets desired.
345	Partial []int
346
347	value FetchItem
348}
349
350func (section *BodySectionName) parse(s string) error {
351	section.value = FetchItem(s)
352
353	if s == "RFC822" {
354		s = "BODY[]"
355	}
356	if s == "RFC822.HEADER" {
357		s = "BODY.PEEK[HEADER]"
358	}
359	if s == "RFC822.TEXT" {
360		s = "BODY[TEXT]"
361	}
362
363	partStart := strings.Index(s, "[")
364	if partStart == -1 {
365		return errors.New("Invalid body section name: must contain an open bracket")
366	}
367
368	partEnd := strings.LastIndex(s, "]")
369	if partEnd == -1 {
370		return errors.New("Invalid body section name: must contain a close bracket")
371	}
372
373	name := s[:partStart]
374	part := s[partStart+1 : partEnd]
375	partial := s[partEnd+1:]
376
377	if name == "BODY.PEEK" {
378		section.Peek = true
379	} else if name != "BODY" {
380		return errors.New("Invalid body section name")
381	}
382
383	b := bytes.NewBufferString(part + string(cr) + string(lf))
384	r := NewReader(b)
385	fields, err := r.ReadFields()
386	if err != nil {
387		return err
388	}
389
390	if err := section.BodyPartName.parse(fields); err != nil {
391		return err
392	}
393
394	if len(partial) > 0 {
395		if !strings.HasPrefix(partial, "<") || !strings.HasSuffix(partial, ">") {
396			return errors.New("Invalid body section name: invalid partial")
397		}
398		partial = partial[1 : len(partial)-1]
399
400		partialParts := strings.SplitN(partial, ".", 2)
401
402		var from, length int
403		if from, err = strconv.Atoi(partialParts[0]); err != nil {
404			return errors.New("Invalid body section name: invalid partial: invalid from: " + err.Error())
405		}
406		section.Partial = []int{from}
407
408		if len(partialParts) == 2 {
409			if length, err = strconv.Atoi(partialParts[1]); err != nil {
410				return errors.New("Invalid body section name: invalid partial: invalid length: " + err.Error())
411			}
412			section.Partial = append(section.Partial, length)
413		}
414	}
415
416	return nil
417}
418
419func (section *BodySectionName) FetchItem() FetchItem {
420	if section.value != "" {
421		return section.value
422	}
423
424	s := "BODY"
425	if section.Peek {
426		s += ".PEEK"
427	}
428
429	s += "[" + section.BodyPartName.string() + "]"
430
431	if len(section.Partial) > 0 {
432		s += "<"
433		s += strconv.Itoa(section.Partial[0])
434
435		if len(section.Partial) > 1 {
436			s += "."
437			s += strconv.Itoa(section.Partial[1])
438		}
439
440		s += ">"
441	}
442
443	return FetchItem(s)
444}
445
446// Equal checks whether two sections are equal.
447func (section *BodySectionName) Equal(other *BodySectionName) bool {
448	if section.Peek != other.Peek {
449		return false
450	}
451	if len(section.Partial) != len(other.Partial) {
452		return false
453	}
454	if len(section.Partial) > 0 && section.Partial[0] != other.Partial[0] {
455		return false
456	}
457	if len(section.Partial) > 1 && section.Partial[1] != other.Partial[1] {
458		return false
459	}
460	return section.BodyPartName.Equal(&other.BodyPartName)
461}
462
463func (section *BodySectionName) resp() *BodySectionName {
464	resp := *section // Copy section
465	if resp.Peek {
466		resp.Peek = false
467	}
468	if len(resp.Partial) == 2 {
469		resp.Partial = []int{resp.Partial[0]}
470	}
471	if !strings.HasPrefix(string(resp.value), string(FetchRFC822)) {
472		resp.value = ""
473	}
474	return &resp
475}
476
477// ExtractPartial returns a subset of the specified bytes matching the partial requested in the
478// section name.
479func (section *BodySectionName) ExtractPartial(b []byte) []byte {
480	if len(section.Partial) != 2 {
481		return b
482	}
483
484	from := section.Partial[0]
485	length := section.Partial[1]
486	to := from + length
487	if from > len(b) {
488		return nil
489	}
490	if to > len(b) {
491		to = len(b)
492	}
493	return b[from:to]
494}
495
496// ParseBodySectionName parses a body section name.
497func ParseBodySectionName(s FetchItem) (*BodySectionName, error) {
498	section := new(BodySectionName)
499	err := section.parse(string(s))
500	return section, err
501}
502
503// A body part name.
504type BodyPartName struct {
505	// The specifier of the requested part.
506	Specifier PartSpecifier
507	// The part path. Parts indexes start at 1.
508	Path []int
509	// If Specifier is HEADER, contains header fields that will/won't be returned,
510	// depending of the value of NotFields.
511	Fields []string
512	// If set to true, Fields is a blacklist of fields instead of a whitelist.
513	NotFields bool
514}
515
516func (part *BodyPartName) parse(fields []interface{}) error {
517	if len(fields) == 0 {
518		return nil
519	}
520
521	name, ok := fields[0].(string)
522	if !ok {
523		return errors.New("Invalid body section name: part name must be a string")
524	}
525
526	args := fields[1:]
527
528	path := strings.Split(strings.ToUpper(name), ".")
529
530	end := 0
531loop:
532	for i, node := range path {
533		switch PartSpecifier(node) {
534		case EntireSpecifier, HeaderSpecifier, MIMESpecifier, TextSpecifier:
535			part.Specifier = PartSpecifier(node)
536			end = i + 1
537			break loop
538		}
539
540		index, err := strconv.Atoi(node)
541		if err != nil {
542			return errors.New("Invalid body part name: " + err.Error())
543		}
544		if index <= 0 {
545			return errors.New("Invalid body part name: index <= 0")
546		}
547
548		part.Path = append(part.Path, index)
549	}
550
551	if part.Specifier == HeaderSpecifier && len(path) > end && path[end] == "FIELDS" && len(args) > 0 {
552		end++
553		if len(path) > end && path[end] == "NOT" {
554			part.NotFields = true
555		}
556
557		names, ok := args[0].([]interface{})
558		if !ok {
559			return errors.New("Invalid body part name: HEADER.FIELDS must have a list argument")
560		}
561
562		for _, namei := range names {
563			if name, ok := namei.(string); ok {
564				part.Fields = append(part.Fields, name)
565			}
566		}
567	}
568
569	return nil
570}
571
572func (part *BodyPartName) string() string {
573	path := make([]string, len(part.Path))
574	for i, index := range part.Path {
575		path[i] = strconv.Itoa(index)
576	}
577
578	if part.Specifier != EntireSpecifier {
579		path = append(path, string(part.Specifier))
580	}
581
582	if part.Specifier == HeaderSpecifier && len(part.Fields) > 0 {
583		path = append(path, "FIELDS")
584
585		if part.NotFields {
586			path = append(path, "NOT")
587		}
588	}
589
590	s := strings.Join(path, ".")
591
592	if len(part.Fields) > 0 {
593		s += " (" + strings.Join(part.Fields, " ") + ")"
594	}
595
596	return s
597}
598
599// Equal checks whether two body part names are equal.
600func (part *BodyPartName) Equal(other *BodyPartName) bool {
601	if part.Specifier != other.Specifier {
602		return false
603	}
604	if part.NotFields != other.NotFields {
605		return false
606	}
607	if len(part.Path) != len(other.Path) {
608		return false
609	}
610	for i, node := range part.Path {
611		if node != other.Path[i] {
612			return false
613		}
614	}
615	if len(part.Fields) != len(other.Fields) {
616		return false
617	}
618	for _, field := range part.Fields {
619		found := false
620		for _, f := range other.Fields {
621			if strings.EqualFold(field, f) {
622				found = true
623				break
624			}
625		}
626		if !found {
627			return false
628		}
629	}
630	return true
631}
632
633// An address.
634type Address struct {
635	// The personal name.
636	PersonalName string
637	// The SMTP at-domain-list (source route).
638	AtDomainList string
639	// The mailbox name.
640	MailboxName string
641	// The host name.
642	HostName string
643}
644
645// Address returns the mailbox address (e.g. "foo@example.org").
646func (addr *Address) Address() string {
647	return addr.MailboxName + "@" + addr.HostName
648}
649
650// Parse an address from fields.
651func (addr *Address) Parse(fields []interface{}) error {
652	if len(fields) < 4 {
653		return errors.New("Address doesn't contain 4 fields")
654	}
655
656	if s, err := ParseString(fields[0]); err == nil {
657		addr.PersonalName, _ = decodeHeader(s)
658	}
659	if s, err := ParseString(fields[1]); err == nil {
660		addr.AtDomainList, _ = decodeHeader(s)
661	}
662	if s, err := ParseString(fields[2]); err == nil {
663		addr.MailboxName, _ = decodeHeader(s)
664	}
665	if s, err := ParseString(fields[3]); err == nil {
666		addr.HostName, _ = decodeHeader(s)
667	}
668
669	return nil
670}
671
672// Format an address to fields.
673func (addr *Address) Format() []interface{} {
674	fields := make([]interface{}, 4)
675
676	if addr.PersonalName != "" {
677		fields[0] = encodeHeader(addr.PersonalName)
678	}
679	if addr.AtDomainList != "" {
680		fields[1] = addr.AtDomainList
681	}
682	if addr.MailboxName != "" {
683		fields[2] = addr.MailboxName
684	}
685	if addr.HostName != "" {
686		fields[3] = addr.HostName
687	}
688
689	return fields
690}
691
692// Parse an address list from fields.
693func ParseAddressList(fields []interface{}) (addrs []*Address) {
694	addrs = make([]*Address, len(fields))
695
696	for i, f := range fields {
697		if addrFields, ok := f.([]interface{}); ok {
698			addr := &Address{}
699			if err := addr.Parse(addrFields); err == nil {
700				addrs[i] = addr
701			}
702		}
703	}
704
705	return
706}
707
708// Format an address list to fields.
709func FormatAddressList(addrs []*Address) (fields []interface{}) {
710	fields = make([]interface{}, len(addrs))
711
712	for i, addr := range addrs {
713		fields[i] = addr.Format()
714	}
715
716	return
717}
718
719// A message envelope, ie. message metadata from its headers.
720// See RFC 3501 page 77.
721type Envelope struct {
722	// The message date.
723	Date time.Time
724	// The message subject.
725	Subject string
726	// The From header addresses.
727	From []*Address
728	// The message senders.
729	Sender []*Address
730	// The Reply-To header addresses.
731	ReplyTo []*Address
732	// The To header addresses.
733	To []*Address
734	// The Cc header addresses.
735	Cc []*Address
736	// The Bcc header addresses.
737	Bcc []*Address
738	// The In-Reply-To header. Contains the parent Message-Id.
739	InReplyTo string
740	// The Message-Id header.
741	MessageId string
742}
743
744// Parse an envelope from fields.
745func (e *Envelope) Parse(fields []interface{}) error {
746	if len(fields) < 10 {
747		return errors.New("ENVELOPE doesn't contain 10 fields")
748	}
749
750	if date, ok := fields[0].(string); ok {
751		e.Date, _ = parseMessageDateTime(date)
752	}
753	if subject, err := ParseString(fields[1]); err == nil {
754		e.Subject, _ = decodeHeader(subject)
755	}
756	if from, ok := fields[2].([]interface{}); ok {
757		e.From = ParseAddressList(from)
758	}
759	if sender, ok := fields[3].([]interface{}); ok {
760		e.Sender = ParseAddressList(sender)
761	}
762	if replyTo, ok := fields[4].([]interface{}); ok {
763		e.ReplyTo = ParseAddressList(replyTo)
764	}
765	if to, ok := fields[5].([]interface{}); ok {
766		e.To = ParseAddressList(to)
767	}
768	if cc, ok := fields[6].([]interface{}); ok {
769		e.Cc = ParseAddressList(cc)
770	}
771	if bcc, ok := fields[7].([]interface{}); ok {
772		e.Bcc = ParseAddressList(bcc)
773	}
774	if inReplyTo, ok := fields[8].(string); ok {
775		e.InReplyTo = inReplyTo
776	}
777	if msgId, ok := fields[9].(string); ok {
778		e.MessageId = msgId
779	}
780
781	return nil
782}
783
784// Format an envelope to fields.
785func (e *Envelope) Format() (fields []interface{}) {
786	return []interface{}{
787		envelopeDateTime(e.Date),
788		encodeHeader(e.Subject),
789		FormatAddressList(e.From),
790		FormatAddressList(e.Sender),
791		FormatAddressList(e.ReplyTo),
792		FormatAddressList(e.To),
793		FormatAddressList(e.Cc),
794		FormatAddressList(e.Bcc),
795		e.InReplyTo,
796		e.MessageId,
797	}
798}
799
800// A body structure.
801// See RFC 3501 page 74.
802type BodyStructure struct {
803	// Basic fields
804
805	// The MIME type (e.g. "text", "image")
806	MIMEType string
807	// The MIME subtype (e.g. "plain", "png")
808	MIMESubType string
809	// The MIME parameters. Values are encoded.
810	Params map[string]string
811
812	// The Content-Id header.
813	Id string
814	// The Content-Description header. This is the raw encoded value.
815	Description string
816	// The Content-Encoding header.
817	Encoding string
818	// The Content-Length header.
819	Size uint32
820
821	// Type-specific fields
822
823	// The children parts, if multipart.
824	Parts []*BodyStructure
825	// The envelope, if message/rfc822.
826	Envelope *Envelope
827	// The body structure, if message/rfc822.
828	BodyStructure *BodyStructure
829	// The number of lines, if text or message/rfc822.
830	Lines uint32
831
832	// Extension data
833
834	// True if the body structure contains extension data.
835	Extended bool
836
837	// The Content-Disposition header field value.
838	Disposition string
839	// The Content-Disposition header field parameters. Values are encoded.
840	DispositionParams map[string]string
841	// The Content-Language header field, if multipart.
842	Language []string
843	// The content URI, if multipart.
844	Location []string
845
846	// The MD5 checksum.
847	MD5 string
848}
849
850func (bs *BodyStructure) Parse(fields []interface{}) error {
851	if len(fields) == 0 {
852		return nil
853	}
854
855	// Initialize params map
856	bs.Params = make(map[string]string)
857
858	switch fields[0].(type) {
859	case []interface{}: // A multipart body part
860		bs.MIMEType = "multipart"
861
862		end := 0
863		for i, fi := range fields {
864			switch f := fi.(type) {
865			case []interface{}: // A part
866				part := new(BodyStructure)
867				if err := part.Parse(f); err != nil {
868					return err
869				}
870				bs.Parts = append(bs.Parts, part)
871			case string:
872				end = i
873			}
874
875			if end > 0 {
876				break
877			}
878		}
879
880		bs.MIMESubType, _ = fields[end].(string)
881		end++
882
883		// GMail seems to return only 3 extension data fields. Parse as many fields
884		// as we can.
885		if len(fields) > end {
886			bs.Extended = true // Contains extension data
887
888			params, _ := fields[end].([]interface{})
889			bs.Params, _ = parseHeaderParamList(params)
890			end++
891		}
892		if len(fields) > end {
893			if disp, ok := fields[end].([]interface{}); ok && len(disp) >= 2 {
894				if s, ok := disp[0].(string); ok {
895					bs.Disposition, _ = decodeHeader(s)
896				}
897				if params, ok := disp[1].([]interface{}); ok {
898					bs.DispositionParams, _ = parseHeaderParamList(params)
899				}
900			}
901			end++
902		}
903		if len(fields) > end {
904			switch langs := fields[end].(type) {
905			case string:
906				bs.Language = []string{langs}
907			case []interface{}:
908				bs.Language, _ = ParseStringList(langs)
909			default:
910				bs.Language = nil
911			}
912			end++
913		}
914		if len(fields) > end {
915			location, _ := fields[end].([]interface{})
916			bs.Location, _ = ParseStringList(location)
917			end++
918		}
919	case string: // A non-multipart body part
920		if len(fields) < 7 {
921			return errors.New("Non-multipart body part doesn't have 7 fields")
922		}
923
924		bs.MIMEType, _ = fields[0].(string)
925		bs.MIMESubType, _ = fields[1].(string)
926
927		params, _ := fields[2].([]interface{})
928		bs.Params, _ = parseHeaderParamList(params)
929
930		bs.Id, _ = fields[3].(string)
931		if desc, err := ParseString(fields[4]); err == nil {
932			bs.Description, _ = decodeHeader(desc)
933		}
934		bs.Encoding, _ = fields[5].(string)
935		bs.Size, _ = ParseNumber(fields[6])
936
937		end := 7
938
939		// Type-specific fields
940		if strings.EqualFold(bs.MIMEType, "message") && strings.EqualFold(bs.MIMESubType, "rfc822") {
941			if len(fields)-end < 3 {
942				return errors.New("Missing type-specific fields for message/rfc822")
943			}
944
945			envelope, _ := fields[end].([]interface{})
946			bs.Envelope = new(Envelope)
947			bs.Envelope.Parse(envelope)
948
949			structure, _ := fields[end+1].([]interface{})
950			bs.BodyStructure = new(BodyStructure)
951			bs.BodyStructure.Parse(structure)
952
953			bs.Lines, _ = ParseNumber(fields[end+2])
954
955			end += 3
956		}
957		if strings.EqualFold(bs.MIMEType, "text") {
958			if len(fields)-end < 1 {
959				return errors.New("Missing type-specific fields for text/*")
960			}
961
962			bs.Lines, _ = ParseNumber(fields[end])
963			end++
964		}
965
966		// GMail seems to return only 3 extension data fields. Parse as many fields
967		// as we can.
968		if len(fields) > end {
969			bs.Extended = true // Contains extension data
970
971			bs.MD5, _ = fields[end].(string)
972			end++
973		}
974		if len(fields) > end {
975			if disp, ok := fields[end].([]interface{}); ok && len(disp) >= 2 {
976				if s, ok := disp[0].(string); ok {
977					bs.Disposition, _ = decodeHeader(s)
978				}
979				if params, ok := disp[1].([]interface{}); ok {
980					bs.DispositionParams, _ = parseHeaderParamList(params)
981				}
982			}
983			end++
984		}
985		if len(fields) > end {
986			switch langs := fields[end].(type) {
987			case string:
988				bs.Language = []string{langs}
989			case []interface{}:
990				bs.Language, _ = ParseStringList(langs)
991			default:
992				bs.Language = nil
993			}
994			end++
995		}
996		if len(fields) > end {
997			location, _ := fields[end].([]interface{})
998			bs.Location, _ = ParseStringList(location)
999			end++
1000		}
1001	}
1002
1003	return nil
1004}
1005
1006func (bs *BodyStructure) Format() (fields []interface{}) {
1007	if strings.EqualFold(bs.MIMEType, "multipart") {
1008		for _, part := range bs.Parts {
1009			fields = append(fields, part.Format())
1010		}
1011
1012		fields = append(fields, bs.MIMESubType)
1013
1014		if bs.Extended {
1015			extended := make([]interface{}, 4)
1016
1017			if bs.Params != nil {
1018				extended[0] = formatHeaderParamList(bs.Params)
1019			}
1020			if bs.Disposition != "" {
1021				extended[1] = []interface{}{
1022					encodeHeader(bs.Disposition),
1023					formatHeaderParamList(bs.DispositionParams),
1024				}
1025			}
1026			if bs.Language != nil {
1027				extended[2] = FormatStringList(bs.Language)
1028			}
1029			if bs.Location != nil {
1030				extended[3] = FormatStringList(bs.Location)
1031			}
1032
1033			fields = append(fields, extended...)
1034		}
1035	} else {
1036		fields = make([]interface{}, 7)
1037		fields[0] = bs.MIMEType
1038		fields[1] = bs.MIMESubType
1039		fields[2] = formatHeaderParamList(bs.Params)
1040
1041		if bs.Id != "" {
1042			fields[3] = bs.Id
1043		}
1044		if bs.Description != "" {
1045			fields[4] = encodeHeader(bs.Description)
1046		}
1047		if bs.Encoding != "" {
1048			fields[5] = bs.Encoding
1049		}
1050
1051		fields[6] = bs.Size
1052
1053		// Type-specific fields
1054		if strings.EqualFold(bs.MIMEType, "message") && strings.EqualFold(bs.MIMESubType, "rfc822") {
1055			var env interface{}
1056			if bs.Envelope != nil {
1057				env = bs.Envelope.Format()
1058			}
1059
1060			var bsbs interface{}
1061			if bs.BodyStructure != nil {
1062				bsbs = bs.BodyStructure.Format()
1063			}
1064
1065			fields = append(fields, env, bsbs, bs.Lines)
1066		}
1067		if strings.EqualFold(bs.MIMEType, "text") {
1068			fields = append(fields, bs.Lines)
1069		}
1070
1071		// Extension data
1072		if bs.Extended {
1073			extended := make([]interface{}, 4)
1074
1075			if bs.MD5 != "" {
1076				extended[0] = bs.MD5
1077			}
1078			if bs.Disposition != "" {
1079				extended[1] = []interface{}{
1080					encodeHeader(bs.Disposition),
1081					formatHeaderParamList(bs.DispositionParams),
1082				}
1083			}
1084			if bs.Language != nil {
1085				extended[2] = FormatStringList(bs.Language)
1086			}
1087			if bs.Location != nil {
1088				extended[3] = FormatStringList(bs.Location)
1089			}
1090
1091			fields = append(fields, extended...)
1092		}
1093	}
1094
1095	return
1096}
1097
1098// Filename parses the body structure's filename, if it's an attachment. An
1099// empty string is returned if the filename isn't specified. An error is
1100// returned if and only if a charset error occurs, in which case the undecoded
1101// filename is returned too.
1102func (bs *BodyStructure) Filename() (string, error) {
1103	raw, ok := bs.DispositionParams["filename"]
1104	if !ok {
1105		// Using "name" in Content-Type is discouraged
1106		raw = bs.Params["name"]
1107	}
1108	return decodeHeader(raw)
1109}
1110
1111// BodyStructureWalkFunc is the type of the function called for each body
1112// structure visited by BodyStructure.Walk. The path argument contains the IMAP
1113// part path (see BodyPartName).
1114//
1115// The function should return true to visit all of the part's children or false
1116// to skip them.
1117type BodyStructureWalkFunc func(path []int, part *BodyStructure) (walkChildren bool)
1118
1119// Walk walks the body structure tree, calling f for each part in the tree,
1120// including bs itself. The parts are visited in DFS pre-order.
1121func (bs *BodyStructure) Walk(f BodyStructureWalkFunc) {
1122	// Non-multipart messages only have part 1
1123	if len(bs.Parts) == 0 {
1124		f([]int{1}, bs)
1125		return
1126	}
1127
1128	bs.walk(f, nil)
1129}
1130
1131func (bs *BodyStructure) walk(f BodyStructureWalkFunc, path []int) {
1132	if !f(path, bs) {
1133		return
1134	}
1135
1136	for i, part := range bs.Parts {
1137		num := i + 1
1138
1139		partPath := append([]int(nil), path...)
1140		partPath = append(partPath, num)
1141
1142		part.walk(f, partPath)
1143	}
1144}
1145