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