1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
package mail
import (
"container/list"
"io"
"strings"
"github.com/emersion/go-message"
)
// A PartHeader is a mail part header. It contains convenience functions to get
// and set header fields.
type PartHeader interface {
// Add adds the key, value pair to the header.
Add(key, value string)
// Del deletes the values associated with key.
Del(key string)
// Get gets the first value associated with the given key. If there are no
// values associated with the key, Get returns "".
Get(key string) string
// Set sets the header entries associated with key to the single element
// value. It replaces any existing values associated with key.
Set(key, value string)
}
// A Part is either a mail text or an attachment. Header is either a InlineHeader
// or an AttachmentHeader.
type Part struct {
Header PartHeader
Body io.Reader
}
// A Reader reads a mail message.
type Reader struct {
Header Header
e *message.Entity
readers *list.List
}
// NewReader creates a new mail reader.
func NewReader(e *message.Entity) *Reader {
mr := e.MultipartReader()
if mr == nil {
// Artificially create a multipart entity
// With this header, no error will be returned by message.NewMultipart
var h message.Header
h.Set("Content-Type", "multipart/mixed")
me, _ := message.NewMultipart(h, []*message.Entity{e})
mr = me.MultipartReader()
}
l := list.New()
l.PushBack(mr)
return &Reader{Header{e.Header}, e, l}
}
// CreateReader reads a mail header from r and returns a new mail reader.
//
// If the message uses an unknown transfer encoding or charset, CreateReader
// returns an error that verifies message.IsUnknownCharset, but also returns a
// Reader that can be used.
func CreateReader(r io.Reader) (*Reader, error) {
e, err := message.Read(r)
if err != nil && !message.IsUnknownCharset(err) {
return nil, err
}
return NewReader(e), err
}
// NextPart returns the next mail part. If there is no more part, io.EOF is
// returned as error.
//
// The returned Part.Body must be read completely before the next call to
// NextPart, otherwise it will be discarded.
//
// If the part uses an unknown transfer encoding or charset, NextPart returns an
// error that verifies message.IsUnknownCharset, but also returns a Part that
// can be used.
func (r *Reader) NextPart() (*Part, error) {
for r.readers.Len() > 0 {
e := r.readers.Back()
mr := e.Value.(message.MultipartReader)
p, err := mr.NextPart()
if err == io.EOF {
// This whole multipart entity has been read, continue with the next one
r.readers.Remove(e)
continue
} else if err != nil && !message.IsUnknownCharset(err) {
return nil, err
}
if pmr := p.MultipartReader(); pmr != nil {
// This is a multipart part, read it
r.readers.PushBack(pmr)
} else {
// This is a non-multipart part, return a mail part
mp := &Part{Body: p.Body}
t, _, _ := p.Header.ContentType()
disp, _, _ := p.Header.ContentDisposition()
if disp == "inline" || (disp != "attachment" && strings.HasPrefix(t, "text/")) {
mp.Header = &InlineHeader{p.Header}
} else {
mp.Header = &AttachmentHeader{p.Header}
}
return mp, err
}
}
return nil, io.EOF
}
// Close finishes the reader.
func (r *Reader) Close() error {
for r.readers.Len() > 0 {
e := r.readers.Back()
mr := e.Value.(message.MultipartReader)
if err := mr.Close(); err != nil {
return err
}
r.readers.Remove(e)
}
return nil
}
|