Update vendor/
This commit is contained in:
24
vendor/github.com/emersion/go-message/.gitignore
generated
vendored
Normal file
24
vendor/github.com/emersion/go-message/.gitignore
generated
vendored
Normal 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
5
vendor/github.com/emersion/go-message/.travis.yml
generated
vendored
Normal 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
21
vendor/github.com/emersion/go-message/LICENSE
generated
vendored
Normal 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
22
vendor/github.com/emersion/go-message/README.md
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# go-message
|
||||
|
||||
[](https://godoc.org/github.com/emersion/go-message)
|
||||
[](https://travis-ci.org/emersion/go-message)
|
||||
[](https://codecov.io/gh/emersion/go-message)
|
||||
[](https://goreportcard.com/report/github.com/emersion/go-message)
|
||||
[](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
|
57
vendor/github.com/emersion/go-message/charset/charset.go
generated
vendored
Normal file
57
vendor/github.com/emersion/go-message/charset/charset.go
generated
vendored
Normal 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
|
||||
}
|
22
vendor/github.com/emersion/go-message/charset/header.go
generated
vendored
Normal file
22
vendor/github.com/emersion/go-message/charset/header.go
generated
vendored
Normal 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
47
vendor/github.com/emersion/go-message/encoding.go
generated
vendored
Normal 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
132
vendor/github.com/emersion/go-message/entity.go
generated
vendored
Normal 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
154
vendor/github.com/emersion/go-message/header.go
generated
vendored
Normal 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
37
vendor/github.com/emersion/go-message/mail/address.go
generated
vendored
Normal 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, ", ")
|
||||
}
|
38
vendor/github.com/emersion/go-message/mail/attachment.go
generated
vendored
Normal file
38
vendor/github.com/emersion/go-message/mail/attachment.go
generated
vendored
Normal 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
57
vendor/github.com/emersion/go-message/mail/header.go
generated
vendored
Normal 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
9
vendor/github.com/emersion/go-message/mail/mail.go
generated
vendored
Normal 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
130
vendor/github.com/emersion/go-message/mail/reader.go
generated
vendored
Normal 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
18
vendor/github.com/emersion/go-message/mail/text.go
generated
vendored
Normal 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
73
vendor/github.com/emersion/go-message/mail/writer.go
generated
vendored
Normal 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
5
vendor/github.com/emersion/go-message/message.go
generated
vendored
Normal 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
110
vendor/github.com/emersion/go-message/multipart.go
generated
vendored
Normal 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
116
vendor/github.com/emersion/go-message/writer.go
generated
vendored
Normal 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
|
||||
}
|
Reference in New Issue
Block a user