131 lines
3.3 KiB
Go
131 lines
3.3 KiB
Go
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 TextHeader
|
|
// 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
|
|
h := make(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.IsUnknownEncoding, 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.IsUnknownEncoding(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.IsUnknownEncoding, 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.IsUnknownEncoding(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 strings.HasPrefix(t, "text/") && disp != "attachment" {
|
|
mp.Header = TextHeader{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
|
|
}
|