Update vendor/

This commit is contained in:
2018-06-28 01:09:56 +01:00
parent 3e5ab5bb0a
commit 21c6e571d8
108 changed files with 121110 additions and 1144 deletions

24
vendor/github.com/emersion/go-message/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

5
vendor/github.com/emersion/go-message/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,5 @@
language: go
go:
- 1.8
script: bash <(curl -sL https://gist.github.com/emersion/49d4dda535497002639626bd9e16480c/raw/codecov-go.sh)
after_script: bash <(curl -s https://codecov.io/bash)

21
vendor/github.com/emersion/go-message/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 emersion
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

22
vendor/github.com/emersion/go-message/README.md generated vendored Normal file
View File

@@ -0,0 +1,22 @@
# go-message
[![GoDoc](https://godoc.org/github.com/emersion/go-message?status.svg)](https://godoc.org/github.com/emersion/go-message)
[![Build Status](https://travis-ci.org/emersion/go-message.svg?branch=master)](https://travis-ci.org/emersion/go-message)
[![codecov](https://codecov.io/gh/emersion/go-message/branch/master/graph/badge.svg)](https://codecov.io/gh/emersion/go-message)
[![Go Report Card](https://goreportcard.com/badge/github.com/emersion/go-message)](https://goreportcard.com/report/github.com/emersion/go-message)
[![Unstable](https://img.shields.io/badge/stability-unstable-yellow.svg)](https://github.com/emersion/stability-badges#unstable)
A Go library for the Internet Message Format. It implements:
* [RFC 5322](https://tools.ietf.org/html/rfc5322): Internet Message Format
* [RFC 2045](https://tools.ietf.org/html/rfc2045), [RFC 2046](https://tools.ietf.org/html/rfc2046) and [RFC 2047](https://tools.ietf.org/html/rfc2047): Multipurpose Internet Mail Extensions
* [RFC 2183](https://tools.ietf.org/html/rfc2183): Content-Disposition Header Field
## Features
* Streaming API
* Automatic encoding and charset handling
* A [`mail`](https://godoc.org/github.com/emersion/go-message/mail) subpackage to read and write mail messages
## License
MIT

View File

@@ -0,0 +1,57 @@
// Package charset provides functions to decode and encode charsets.
package charset
import (
"fmt"
"io"
"strings"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/encoding/traditionalchinese"
)
var charsets = map[string]encoding.Encoding{
"big5": traditionalchinese.Big5,
"euc-jp": japanese.EUCJP,
"gbk": simplifiedchinese.GBK,
"gb2312": simplifiedchinese.GBK, // as GBK is a superset of HZGB2312, so just use GBK
"gb18030": simplifiedchinese.GB18030, // GB18030 Use for parse QQ business mail message
"iso-2022-jp": japanese.ISO2022JP,
"iso-8859-1": charmap.ISO8859_1,
"iso-8859-2": charmap.ISO8859_2,
"iso-8859-3": charmap.ISO8859_3,
"iso-8859-4": charmap.ISO8859_4,
"iso-8859-9": charmap.ISO8859_9,
"iso-8859-10": charmap.ISO8859_10,
"iso-8859-13": charmap.ISO8859_13,
"iso-8859-14": charmap.ISO8859_14,
"iso-8859-15": charmap.ISO8859_15,
"iso-8859-16": charmap.ISO8859_16,
"koi8-r": charmap.KOI8R,
"shift_jis": japanese.ShiftJIS,
"windows-1250": charmap.Windows1250,
"windows-1251": charmap.Windows1251,
"windows-1252": charmap.Windows1252,
}
// Reader returns an io.Reader that converts the provided charset to UTF-8.
func Reader(charset string, input io.Reader) (io.Reader, error) {
charset = strings.ToLower(charset)
// "ascii" is not in the spec but is common
if charset == "utf-8" || charset == "us-ascii" || charset == "ascii" {
return input, nil
}
if enc, ok := charsets[charset]; ok {
return enc.NewDecoder().Reader(input), nil
}
return nil, fmt.Errorf("unhandled charset %q", charset)
}
// RegisterEncoding registers an encoding. This is intended to be called from
// the init function in packages that want to support additional charsets.
func RegisterEncoding(name string, enc encoding.Encoding) {
charsets[name] = enc
}

View File

@@ -0,0 +1,22 @@
package charset
import (
"mime"
)
var wordDecoder = &mime.WordDecoder{CharsetReader: Reader}
// DecodeHeader decodes an internationalized header field. If it fails, it
// returns the input string and the error.
func DecodeHeader(s string) (string, error) {
dec, err := wordDecoder.DecodeHeader(s)
if err != nil {
return s, err
}
return dec, nil
}
// EncodeHeader encodes an internationalized header field.
func EncodeHeader(s string) string {
return mime.QEncoding.Encode("utf-8", s)
}

47
vendor/github.com/emersion/go-message/encoding.go generated vendored Normal file
View File

@@ -0,0 +1,47 @@
package message
import (
"encoding/base64"
"fmt"
"io"
"mime/quotedprintable"
"strings"
"github.com/emersion/go-textwrapper"
)
func encodingReader(enc string, r io.Reader) (io.Reader, error) {
var dec io.Reader
switch strings.ToLower(enc) {
case "quoted-printable":
dec = quotedprintable.NewReader(r)
case "base64":
dec = base64.NewDecoder(base64.StdEncoding, r)
case "7bit", "8bit", "binary", "":
dec = r
default:
return nil, fmt.Errorf("unhandled encoding %q", enc)
}
return dec, nil
}
type nopCloser struct {
io.Writer
}
func (nopCloser) Close() error { return nil }
func encodingWriter(enc string, w io.Writer) io.WriteCloser {
var wc io.WriteCloser
switch strings.ToLower(enc) {
case "quoted-printable":
wc = quotedprintable.NewWriter(w)
case "base64":
wc = base64.NewEncoder(base64.StdEncoding, textwrapper.NewRFC822(w))
case "7bit", "8bit":
wc = nopCloser{textwrapper.New(w, "\r\n", 1000)}
default: // "binary"
wc = nopCloser{w}
}
return wc
}

132
vendor/github.com/emersion/go-message/entity.go generated vendored Normal file
View File

@@ -0,0 +1,132 @@
package message
import (
"bufio"
"io"
"mime/multipart"
"net/textproto"
"strings"
"github.com/emersion/go-message/charset"
)
type unknownEncodingError struct {
error
}
// IsUnknownEncoding returns a boolean indicating whether the error is known to
// report that the transfer encoding or the charset advertised by the entity is
// unknown.
func IsUnknownEncoding(err error) bool {
_, ok := err.(unknownEncodingError)
return ok
}
// An Entity is either a whole message or a one of the parts in the body of a
// multipart entity.
type Entity struct {
Header Header // The entity's header.
Body io.Reader // The decoded entity's body.
mediaType string
mediaParams map[string]string
}
// New makes a new message with the provided header and body. The entity's
// transfer encoding and charset are automatically decoded to UTF-8.
//
// If the message uses an unknown transfer encoding or charset, New returns an
// error that verifies IsUnknownEncoding, but also returns an Entity that can
// be read.
func New(header Header, body io.Reader) (*Entity, error) {
var err error
enc := header.Get("Content-Transfer-Encoding")
if decoded, encErr := encodingReader(enc, body); encErr != nil {
err = unknownEncodingError{encErr}
} else {
body = decoded
}
mediaType, mediaParams, _ := header.ContentType()
if ch, ok := mediaParams["charset"]; ok {
if converted, charsetErr := charset.Reader(ch, body); charsetErr != nil {
err = unknownEncodingError{charsetErr}
} else {
body = converted
}
}
return &Entity{
Header: header,
Body: body,
mediaType: mediaType,
mediaParams: mediaParams,
}, err
}
// NewMultipart makes a new multipart message with the provided header and
// parts. The Content-Type header must begin with "multipart/".
//
// If the message uses an unknown transfer encoding, NewMultipart returns an
// error that verifies IsUnknownEncoding, but also returns an Entity that can
// be read.
func NewMultipart(header Header, parts []*Entity) (*Entity, error) {
r := &multipartBody{
header: header,
parts: parts,
}
return New(header, r)
}
// Read reads a message from r. The message's encoding and charset are
// automatically decoded to UTF-8. Note that this function only reads the
// message header.
//
// If the message uses an unknown transfer encoding or charset, Read returns an
// error that verifies IsUnknownEncoding, but also returns an Entity that can
// be read.
func Read(r io.Reader) (*Entity, error) {
br := bufio.NewReader(r)
h, err := textproto.NewReader(br).ReadMIMEHeader()
if err != nil {
return nil, err
}
return New(Header(h), br)
}
// MultipartReader returns a MultipartReader that reads parts from this entity's
// body. If this entity is not multipart, it returns nil.
func (e *Entity) MultipartReader() MultipartReader {
if !strings.HasPrefix(e.mediaType, "multipart/") {
return nil
}
if mb, ok := e.Body.(*multipartBody); ok {
return mb
}
return &multipartReader{multipart.NewReader(e.Body, e.mediaParams["boundary"])}
}
// writeBodyTo writes this entity's body to w (without the header).
func (e *Entity) writeBodyTo(w *Writer) error {
var err error
if mb, ok := e.Body.(*multipartBody); ok {
err = mb.writeBodyTo(w)
} else {
_, err = io.Copy(w, e.Body)
}
return err
}
// WriteTo writes this entity's header and body to w.
func (e *Entity) WriteTo(w io.Writer) error {
ew, err := CreateWriter(w, e.Header)
if err != nil {
return err
}
defer ew.Close()
return e.writeBodyTo(ew)
}

154
vendor/github.com/emersion/go-message/header.go generated vendored Normal file
View File

@@ -0,0 +1,154 @@
package message
import (
"mime"
"net/textproto"
"strings"
"github.com/emersion/go-message/charset"
)
const maxHeaderLen = 76
func parseHeaderWithParams(s string) (f string, params map[string]string, err error) {
f, params, err = mime.ParseMediaType(s)
if err != nil {
return s, nil, err
}
for k, v := range params {
params[k], _ = charset.DecodeHeader(v)
}
return
}
func formatHeaderWithParams(f string, params map[string]string) string {
encParams := make(map[string]string)
for k, v := range params {
encParams[k] = charset.EncodeHeader(v)
}
return mime.FormatMediaType(f, encParams)
}
// formatHeaderField formats a header field, ensuring each line is no longer
// than 76 characters. It tries to fold lines at whitespace characters if
// possible. If the header contains a word longer than this limit, it will be
// split.
func formatHeaderField(k, v string) string {
s := k + ": "
if v == "" {
return s + "\r\n"
}
first := true
for len(v) > 0 {
maxlen := maxHeaderLen
if first {
maxlen -= len(s)
}
// We'll need to fold before i
foldBefore := maxlen + 1
foldAt := len(v)
var folding string
if foldBefore > len(v) {
// We reached the end of the string
if v[len(v)-1] != '\n' {
// If there isn't already a trailing CRLF, insert one
folding = "\r\n"
}
} else {
// Find the closest whitespace before i
foldAt = strings.LastIndexAny(v[:foldBefore], " \t\n")
if foldAt == 0 {
// The whitespace we found was the previous folding WSP
foldAt = foldBefore - 1
} else if foldAt < 0 {
// We didn't find any whitespace, we have to insert one
foldAt = foldBefore - 2
}
switch v[foldAt] {
case ' ', '\t':
if v[foldAt-1] != '\n' {
folding = "\r\n" // The next char will be a WSP, don't need to insert one
}
case '\n':
folding = "" // There is already a CRLF, nothing to do
default:
folding = "\r\n " // Another char, we need to insert CRLF + WSP
}
}
s += v[:foldAt] + folding
v = v[foldAt:]
first = false
}
return s
}
// A Header represents the key-value pairs in a message header.
type Header map[string][]string
// Add adds the key, value pair to the header. It appends to any existing values
// associated with key.
func (h Header) Add(key, value string) {
textproto.MIMEHeader(h).Add(key, value)
}
// Set sets the header entries associated with key to the single element value.
// It replaces any existing values associated with key.
func (h Header) Set(key, value string) {
textproto.MIMEHeader(h).Set(key, value)
}
// Get gets the first value associated with the given key. If there are no
// values associated with the key, Get returns "".
func (h Header) Get(key string) string {
return textproto.MIMEHeader(h).Get(key)
}
// Del deletes the values associated with key.
func (h Header) Del(key string) {
textproto.MIMEHeader(h).Del(key)
}
// ContentType parses the Content-Type header field.
//
// If no Content-Type is specified, it returns "text/plain".
func (h Header) ContentType() (t string, params map[string]string, err error) {
v := h.Get("Content-Type")
if v == "" {
return "text/plain", nil, nil
}
return parseHeaderWithParams(v)
}
// SetContentType formats the Content-Type header field.
func (h Header) SetContentType(t string, params map[string]string) {
h.Set("Content-Type", formatHeaderWithParams(t, params))
}
// ContentDescription parses the Content-Description header field.
func (h Header) ContentDescription() (string, error) {
return charset.DecodeHeader(h.Get("Content-Description"))
}
// SetContentDescription parses the Content-Description header field.
func (h Header) SetContentDescription(desc string) {
h.Set("Content-Description", charset.EncodeHeader(desc))
}
// ContentDisposition parses the Content-Disposition header field, as defined in
// RFC 2183.
func (h Header) ContentDisposition() (disp string, params map[string]string, err error) {
return parseHeaderWithParams(h.Get("Content-Disposition"))
}
// SetContentDisposition formats the Content-Disposition header field, as
// defined in RFC 2183.
func (h Header) SetContentDisposition(disp string, params map[string]string) {
h.Set("Content-Disposition", formatHeaderWithParams(disp, params))
}

37
vendor/github.com/emersion/go-message/mail/address.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
package mail
import (
"net/mail"
"strings"
)
// Address represents a single mail address.
type Address mail.Address
// String formats the address as a valid RFC 5322 address. If the address's name
// contains non-ASCII characters the name will be rendered according to
// RFC 2047.
func (a *Address) String() string {
return ((*mail.Address)(a)).String()
}
func parseAddressList(s string) ([]*Address, error) {
list, err := mail.ParseAddressList(s)
if err != nil {
return nil, err
}
addrs := make([]*Address, len(list))
for i, a := range list {
addrs[i] = (*Address)(a)
}
return addrs, nil
}
func formatAddressList(l []*Address) string {
formatted := make([]string, len(l))
for i, a := range l {
formatted[i] = a.String()
}
return strings.Join(formatted, ", ")
}

View File

@@ -0,0 +1,38 @@
package mail
import (
"github.com/emersion/go-message"
)
// An AttachmentHeader represents an attachment's header.
type AttachmentHeader struct {
message.Header
}
// NewAttachmentHeader creates a new AttachmentHeader.
func NewAttachmentHeader() AttachmentHeader {
h := AttachmentHeader{make(message.Header)}
h.Set("Content-Disposition", "attachment")
h.Set("Content-Transfer-Encoding", "base64")
return h
}
// Filename parses the attachment's filename.
func (h AttachmentHeader) Filename() (string, error) {
_, params, err := h.ContentDisposition()
filename, ok := params["filename"]
if !ok {
// Using "name" in Content-Type is discouraged
_, params, err = h.ContentType()
filename = params["name"]
}
return filename, err
}
// SetFilename formats the attachment's filename.
func (h AttachmentHeader) SetFilename(filename string) {
dispParams := map[string]string{"filename": filename}
h.SetContentDisposition("attachment", dispParams)
}

57
vendor/github.com/emersion/go-message/mail/header.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
package mail
import (
"net/mail"
"time"
"github.com/emersion/go-message"
"github.com/emersion/go-message/charset"
)
const dateLayout = "Mon, 02 Jan 2006 15:04:05 -0700"
// A Header is a mail header.
type Header struct {
message.Header
}
// NewHeader creates a new mail header.
func NewHeader() Header {
return Header{make(message.Header)}
}
// AddressList parses the named header field as a list of addresses. If the
// header is missing, it returns nil.
func (h Header) AddressList(key string) ([]*Address, error) {
v := h.Get(key)
if v == "" {
return nil, nil
}
return parseAddressList(v)
}
// SetAddressList formats the named header to the provided list of addresses.
func (h Header) SetAddressList(key string, addrs []*Address) {
h.Set(key, formatAddressList(addrs))
}
// Date parses the Date header field.
func (h Header) Date() (time.Time, error) {
return mail.Header(h.Header).Date()
}
// SetDate formats the Date header field.
func (h Header) SetDate(t time.Time) {
h.Set("Date", t.Format(dateLayout))
}
// Subject parses the Subject header field. If there is an error, the raw field
// value is returned alongside the error.
func (h Header) Subject() (string, error) {
return charset.DecodeHeader(h.Get("Subject"))
}
// SetSubject formats the Subject header field.
func (h Header) SetSubject(s string) {
h.Set("Subject", charset.EncodeHeader(s))
}

9
vendor/github.com/emersion/go-message/mail/mail.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// Package mail implements reading and writing mail messages.
//
// This package assumes that a mail message contains one or more text parts and
// zero or more attachment parts. Each text part represents a different version
// of the message content (e.g. a different type, a different language and so
// on).
//
// RFC 5322 defines the Internet Message Format.
package mail

130
vendor/github.com/emersion/go-message/mail/reader.go generated vendored Normal file
View File

@@ -0,0 +1,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 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
}

18
vendor/github.com/emersion/go-message/mail/text.go generated vendored Normal file
View File

@@ -0,0 +1,18 @@
package mail
import (
"github.com/emersion/go-message"
)
// A TextHeader represents a message text header.
type TextHeader struct {
message.Header
}
// NewTextHeader creates a new message text header.
func NewTextHeader() TextHeader {
h := TextHeader{make(message.Header)}
h.Set("Content-Disposition", "inline")
h.Set("Content-Transfer-Encoding", "quoted-printable")
return h
}

73
vendor/github.com/emersion/go-message/mail/writer.go generated vendored Normal file
View File

@@ -0,0 +1,73 @@
package mail
import (
"io"
"github.com/emersion/go-message"
)
// A Writer writes a mail message. A mail message contains one or more text
// parts and zero or more attachments.
type Writer struct {
mw *message.Writer
}
// CreateWriter writes a mail header to w and creates a new Writer.
func CreateWriter(w io.Writer, header Header) (*Writer, error) {
header.Set("Content-Type", "multipart/mixed")
mw, err := message.CreateWriter(w, header.Header)
if err != nil {
return nil, err
}
return &Writer{mw}, nil
}
// CreateText creates a TextWriter. One or more parts representing the same text
// in different formats can be written to a TextWriter.
func (w *Writer) CreateText() (*TextWriter, error) {
h := make(message.Header)
h.Set("Content-Type", "multipart/alternative")
mw, err := w.mw.CreatePart(h)
if err != nil {
return nil, err
}
return &TextWriter{mw}, nil
}
// CreateSingleText creates a new single text part with the provided header. The
// body of the part should be written to the returned io.WriteCloser. Only one
// single text part should be written, use CreateText if you want multiple text
// parts.
func (w *Writer) CreateSingleText(header TextHeader) (io.WriteCloser, error) {
return w.mw.CreatePart(header.Header)
}
// CreateAttachment creates a new attachment with the provided header. The body
// of the part should be written to the returned io.WriteCloser.
func (w *Writer) CreateAttachment(header AttachmentHeader) (io.WriteCloser, error) {
return w.mw.CreatePart(header.Header)
}
// Close finishes the Writer.
func (w *Writer) Close() error {
return w.mw.Close()
}
// TextWriter writes a mail message's text.
type TextWriter struct {
mw *message.Writer
}
// CreatePart creates a new text part with the provided header. The body of the
// part should be written to the returned io.WriteCloser.
func (w *TextWriter) CreatePart(header TextHeader) (io.WriteCloser, error) {
return w.mw.CreatePart(header.Header)
}
// Close finishes the TextWriter.
func (w *TextWriter) Close() error {
return w.mw.Close()
}

5
vendor/github.com/emersion/go-message/message.go generated vendored Normal file
View File

@@ -0,0 +1,5 @@
// Package message implements reading and writing multipurpose messages.
//
// RFC 2045, RFC 2046 and RFC 2047 defines MIME, and RFC 2183 defines the
// Content-Disposition header field.
package message

110
vendor/github.com/emersion/go-message/multipart.go generated vendored Normal file
View File

@@ -0,0 +1,110 @@
package message
import (
"io"
"mime/multipart"
)
// MultipartReader is an iterator over parts in a MIME multipart body.
type MultipartReader interface {
io.Closer
// NextPart returns the next part in the multipart or an error. When there are
// no more parts, the error io.EOF is returned.
//
// Entity.Body must be read completely before the next call to NextPart,
// otherwise it will be discarded.
NextPart() (*Entity, error)
}
type multipartReader struct {
r *multipart.Reader
}
// NextPart implements MultipartReader.
func (r *multipartReader) NextPart() (*Entity, error) {
p, err := r.r.NextPart()
if err != nil {
return nil, err
}
return New(Header(p.Header), p)
}
// Close implements io.Closer.
func (r *multipartReader) Close() error {
return nil
}
type multipartBody struct {
header Header
parts []*Entity
r *io.PipeReader
w *Writer
i int
}
// Read implements io.Reader.
func (m *multipartBody) Read(p []byte) (n int, err error) {
if m.r == nil {
r, w := io.Pipe()
m.r = r
m.w = newWriter(w, m.header)
// Prevent calls to NextPart to succeed
m.i = len(m.parts)
go func() {
if err := m.writeBodyTo(m.w); err != nil {
w.CloseWithError(err)
return
}
if err := m.w.Close(); err != nil {
w.CloseWithError(err)
return
}
w.Close()
}()
}
return m.r.Read(p)
}
// Close implements io.Closer.
func (m *multipartBody) Close() error {
if m.r != nil {
m.r.Close()
}
return nil
}
// NextPart implements MultipartReader.
func (m *multipartBody) NextPart() (*Entity, error) {
if m.i >= len(m.parts) {
return nil, io.EOF
}
part := m.parts[m.i]
m.i++
return part, nil
}
func (m *multipartBody) writeBodyTo(w *Writer) error {
for _, p := range m.parts {
pw, err := w.CreatePart(p.Header)
if err != nil {
return err
}
if err := p.writeBodyTo(pw); err != nil {
return err
}
if err := pw.Close(); err != nil {
return err
}
}
return nil
}

116
vendor/github.com/emersion/go-message/writer.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
package message
import (
"errors"
"io"
"mime/multipart"
"net/textproto"
"sort"
"strings"
)
// From https://golang.org/src/mime/multipart/writer.go?s=2140:2215#L76
func writeHeader(w io.Writer, header Header) error {
keys := make([]string, 0, len(header))
for k := range header {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
for _, v := range header[k] {
if _, err := io.WriteString(w, formatHeaderField(k, v)); err != nil {
return err
}
}
}
_, err := io.WriteString(w, "\r\n")
return err
}
// A Writer formats entities.
type Writer struct {
w io.Writer
c io.Closer
mw *multipart.Writer
}
// newWriter creates a new Writer writing to w with the provided header. Nothing
// is written to w when it is called. header is modified in-place.
func newWriter(w io.Writer, header Header) *Writer {
ww := &Writer{w: w}
mediaType, mediaParams, _ := header.ContentType()
if strings.HasPrefix(mediaType, "multipart/") {
ww.mw = multipart.NewWriter(ww.w)
// Do not set ww's io.Closer for now: if this is a multipart entity but
// CreatePart is not used (only Write is used), then the final boundary
// is expected to be written by the user too. In this case, ww.Close
// shouldn't write the final boundary.
if mediaParams["boundary"] != "" {
ww.mw.SetBoundary(mediaParams["boundary"])
} else {
mediaParams["boundary"] = ww.mw.Boundary()
header.SetContentType(mediaType, mediaParams)
}
header.Del("Content-Transfer-Encoding")
} else {
wc := encodingWriter(header.Get("Content-Transfer-Encoding"), ww.w)
ww.w = wc
ww.c = wc
}
return ww
}
// CreateWriter creates a new Writer writing to w. If header contains an
// encoding, data written to the Writer will automatically be encoded with it.
func CreateWriter(w io.Writer, header Header) (*Writer, error) {
ww := newWriter(w, header)
if err := writeHeader(w, header); err != nil {
return nil, err
}
return ww, nil
}
// Write implements io.Writer.
func (w *Writer) Write(b []byte) (int, error) {
return w.w.Write(b)
}
// Close implements io.Closer.
func (w *Writer) Close() error {
if w.c != nil {
return w.c.Close()
}
return nil
}
// CreatePart returns a Writer to a new part in this multipart entity. If this
// entity is not multipart, it fails. The body of the part should be written to
// the returned io.WriteCloser.
func (w *Writer) CreatePart(header Header) (*Writer, error) {
if w.mw == nil {
return nil, errors.New("cannot create a part in a non-multipart message")
}
if w.c == nil {
// We know that the user calls CreatePart so Close should write the final
// boundary
w.c = w.mw
}
// cw -> ww -> pw -> w.mw -> w.w
ww := &struct{ io.Writer }{nil}
cw := newWriter(ww, header)
pw, err := w.mw.CreatePart(textproto.MIMEHeader(header))
if err != nil {
return nil, err
}
ww.Writer = pw
return cw, nil
}