diff --git a/Gopkg.lock b/Gopkg.lock
index ca84780ef1..bd40362115 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -587,7 +587,7 @@
[[projects]]
branch = "master"
- digest = "1:9ea97158ea06d60b1f324aa384f6a4cbd596a81b3cae994e17108b195f398d01"
+ digest = "1:d39e7c7677b161c2dd4c635a2ac196460608c7d8ba5337cc8cae5825a2681f8f"
name = "github.com/prometheus/procfs"
packages = [
".",
@@ -596,7 +596,7 @@
"xfs",
]
pruneopts = "UT"
- revision = "aa55a523dc0a8297edf51bb75e8eec13eb3be45d"
+ revision = "1dc9a6cbc91aacc3e8b2d63db4d2e957a5394ac4"
[[projects]]
digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758"
@@ -952,12 +952,12 @@
version = "v1.3"
[[projects]]
- digest = "1:eebd52aee67d9f1e0af1859a584e14b1817581dd5f90d4585418c27b6a93c966"
- name = "gopkg.in/gomail.v2"
+ digest = "1:3806f369b846160fcbde19bdcf93790868defe7c58d1bb6bc8d974c5b8f8dc1e"
+ name = "gopkg.in/mail.v2"
packages = ["."]
pruneopts = "UT"
- revision = "41f3572897373c5538c50a2402db15db079fa4fd"
- version = "2.0.0"
+ revision = "f59b9b83a4e522098e3d3eb94e6f81850ad6e973"
+ version = "v2.3.1"
[[projects]]
digest = "1:c805e517269b0ba4c21ded5836019ed7d16953d4026cb7d00041d039c7906be9"
@@ -1047,7 +1047,7 @@
"golang.org/x/crypto/bcrypt",
"golang.org/x/image/bmp",
"golang.org/x/net/html/charset",
- "gopkg.in/gomail.v2",
+ "gopkg.in/mail.v2",
"gopkg.in/natefinch/lumberjack.v2",
"gopkg.in/olivere/elastic.v5",
"gopkg.in/yaml.v2",
diff --git a/services/mailservice/mail.go b/services/mailservice/mail.go
index e9b689f208..6cb1f66bec 100644
--- a/services/mailservice/mail.go
+++ b/services/mailservice/mail.go
@@ -14,7 +14,7 @@ import (
"net/smtp"
"time"
- "gopkg.in/gomail.v2"
+ gomail "gopkg.in/mail.v2"
"net/http"
diff --git a/vendor/github.com/prometheus/procfs/Makefile b/vendor/github.com/prometheus/procfs/Makefile
index 40540b02ef..947d7d8fa7 100644
--- a/vendor/github.com/prometheus/procfs/Makefile
+++ b/vendor/github.com/prometheus/procfs/Makefile
@@ -23,5 +23,8 @@ update_fixtures: fixtures.ttar sysfs/fixtures.ttar
rm -v $(dir $*)fixtures/.unpacked
./ttar -C $(dir $*) -c -f $*fixtures.ttar fixtures/
+.PHONY: build
+build:
+
.PHONY: test
test: fixtures/.unpacked sysfs/fixtures/.unpacked common-test
diff --git a/vendor/gopkg.in/gomail.v2/.travis.yml b/vendor/gopkg.in/gomail.v2/.travis.yml
deleted file mode 100644
index 24edf22cc9..0000000000
--- a/vendor/gopkg.in/gomail.v2/.travis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-language: go
-
-go:
- - 1.2
- - 1.3
- - 1.4
- - 1.5
- - tip
diff --git a/vendor/gopkg.in/gomail.v2/CHANGELOG.md b/vendor/gopkg.in/gomail.v2/CHANGELOG.md
deleted file mode 100644
index a797ab4c09..0000000000
--- a/vendor/gopkg.in/gomail.v2/CHANGELOG.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Change Log
-All notable changes to this project will be documented in this file.
-This project adheres to [Semantic Versioning](http://semver.org/).
-
-## [2.0.0] - 2015-09-02
-
-- Mailer has been removed. It has been replaced by Dialer and Sender.
-- `File` type and the `CreateFile` and `OpenFile` functions have been removed.
-- `Message.Attach` and `Message.Embed` have a new signature.
-- `Message.GetBodyWriter` has been removed. Use `Message.AddAlternativeWriter`
-instead.
-- `Message.Export` has been removed. `Message.WriteTo` can be used instead.
-- `Message.DelHeader` has been removed.
-- The `Bcc` header field is no longer sent. It is far more simpler and
-efficient: the same message is sent to all recipients instead of sending a
-different email to each Bcc address.
-- LoginAuth has been removed. `NewPlainDialer` now implements the LOGIN
-authentication mechanism when needed.
-- Go 1.2 is now required instead of Go 1.3. No external dependency are used when
-using Go 1.5.
diff --git a/vendor/gopkg.in/gomail.v2/README.md b/vendor/gopkg.in/gomail.v2/README.md
deleted file mode 100644
index 18cb881300..0000000000
--- a/vendor/gopkg.in/gomail.v2/README.md
+++ /dev/null
@@ -1,97 +0,0 @@
-# Gomail
-[](https://travis-ci.org/go-gomail/gomail) [](http://gocover.io/gopkg.in/gomail.v2) [](https://godoc.org/gopkg.in/gomail.v2)
-
-## Introduction
-
-Gomail is a simple and efficient package to send emails. It is well tested and
-documented.
-
-It is versioned using [gopkg.in](https://gopkg.in) so I promise
-they will never be backward incompatible changes within each version.
-
-It requires Go 1.2 or newer. With Go 1.5, no external dependencies are used.
-
-
-## Features
-
-Gomail supports:
-- Attachments
-- Embedded images
-- HTML and text templates
-- Automatic encoding of special characters
-- SSL and TLS
-- Sending multiple emails with the same SMTP connection
-- Any method to send emails: SMTP, postfix (not included but easily doable), etc
-
-
-## Documentation
-
-https://godoc.org/gopkg.in/gomail.v2
-
-
-## Download
-
- go get gopkg.in/gomail.v2
-
-
-## Examples
-
-See the [examples in the documentation](https://godoc.org/gopkg.in/gomail.v2#example-package).
-
-
-## FAQ
-
-### x509: certificate signed by unknown authority
-
-If you get this error it means the certificate used by the SMTP server is not
-considered valid by the client running Gomail. As a quick workaround you can
-bypass the verification of the server's certificate chain and host name by using
-`SetTLSConfig`:
-
- d := gomail.NewPlainDialer("smtp.example.com", "user", "123456", 587)
- d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
-
-Note, however, that this is insecure and should not be used in production.
-
-
-## Contribute
-
-Contributions are more than welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for
-more info.
-
-
-## Change log
-
-See [CHANGELOG.md](CHANGELOG.md).
-
-
-## License
-
-[MIT](LICENSE)
-
-
-## Contact
-
-You can ask questions on the [Gomail
-thread](https://groups.google.com/d/topic/golang-nuts/jMxZHzvvEVg/discussion)
-in the Go mailing-list.
-
-
-## Support
-
-If you want to support the development of Gomail, I gladly accept donations.
-
-I will give 100% of the money I receive to
-[Enfants, Espoir Du Monde](http://www.eedm.fr/).
-EEDM is a French NGO which helps children in Bangladesh, Cameroun, Haiti, India
-and Madagascar.
-
-All its members are volunteers so its operating costs are only
-1.9%. So your money will directly helps children of these countries.
-
-As an added bonus, your donations will also tip me by lowering my taxes :smile:
-
-I will send an email with the receipt of the donation to EEDM annually to all
-donors.
-
-[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PYQKC7VFVXCFG)
diff --git a/vendor/gopkg.in/gomail.v2/auth.go b/vendor/gopkg.in/gomail.v2/auth.go
deleted file mode 100644
index 4bcdd06202..0000000000
--- a/vendor/gopkg.in/gomail.v2/auth.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package gomail
-
-import (
- "bytes"
- "errors"
- "fmt"
- "net/smtp"
-)
-
-// plainAuth is an smtp.Auth that implements the PLAIN authentication mechanism.
-// It fallbacks to the LOGIN mechanism if it is the only mechanism advertised
-// by the server.
-type plainAuth struct {
- username string
- password string
- host string
- login bool
-}
-
-func (a *plainAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
- if server.Name != a.host {
- return "", nil, errors.New("gomail: wrong host name")
- }
-
- var plain, login bool
- for _, a := range server.Auth {
- switch a {
- case "PLAIN":
- plain = true
- case "LOGIN":
- login = true
- }
- }
-
- if !server.TLS && !plain && !login {
- return "", nil, errors.New("gomail: unencrypted connection")
- }
-
- if !plain && login {
- a.login = true
- return "LOGIN", nil, nil
- }
-
- return "PLAIN", []byte("\x00" + a.username + "\x00" + a.password), nil
-}
-
-func (a *plainAuth) Next(fromServer []byte, more bool) ([]byte, error) {
- if !a.login {
- if more {
- return nil, errors.New("gomail: unexpected server challenge")
- }
- return nil, nil
- }
-
- if !more {
- return nil, nil
- }
-
- switch {
- case bytes.Equal(fromServer, []byte("Username:")):
- return []byte(a.username), nil
- case bytes.Equal(fromServer, []byte("Password:")):
- return []byte(a.password), nil
- default:
- return nil, fmt.Errorf("gomail: unexpected server challenge: %s", fromServer)
- }
-}
diff --git a/vendor/gopkg.in/gomail.v2/mime_go14.go b/vendor/gopkg.in/gomail.v2/mime_go14.go
deleted file mode 100644
index 246e2e5e5a..0000000000
--- a/vendor/gopkg.in/gomail.v2/mime_go14.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// +build !go1.5
-
-package gomail
-
-import "gopkg.in/alexcesaro/quotedprintable.v3"
-
-var newQPWriter = quotedprintable.NewWriter
-
-type mimeEncoder struct {
- quotedprintable.WordEncoder
-}
-
-var (
- bEncoding = mimeEncoder{quotedprintable.BEncoding}
- qEncoding = mimeEncoder{quotedprintable.QEncoding}
-)
diff --git a/vendor/gopkg.in/gomail.v2/smtp.go b/vendor/gopkg.in/gomail.v2/smtp.go
deleted file mode 100644
index cf773a1027..0000000000
--- a/vendor/gopkg.in/gomail.v2/smtp.go
+++ /dev/null
@@ -1,175 +0,0 @@
-package gomail
-
-import (
- "crypto/tls"
- "fmt"
- "io"
- "net"
- "net/smtp"
-)
-
-// A Dialer is a dialer to an SMTP server.
-type Dialer struct {
- // Host represents the host of the SMTP server.
- Host string
- // Port represents the port of the SMTP server.
- Port int
- // Auth represents the authentication mechanism used to authenticate to the
- // SMTP server.
- Auth smtp.Auth
- // SSL defines whether an SSL connection is used. It should be false in
- // most cases since the authentication mechanism should use the STARTTLS
- // extension instead.
- SSL bool
- // TSLConfig represents the TLS configuration used for the TLS (when the
- // STARTTLS extension is used) or SSL connection.
- TLSConfig *tls.Config
-}
-
-// NewPlainDialer returns a Dialer. The given parameters are used to connect to
-// the SMTP server via a PLAIN authentication mechanism.
-//
-// It fallbacks to the LOGIN mechanism if it is the only mechanism advertised by
-// the server.
-func NewPlainDialer(host string, port int, username, password string) *Dialer {
- return &Dialer{
- Host: host,
- Port: port,
- Auth: &plainAuth{
- username: username,
- password: password,
- host: host,
- },
- SSL: port == 465,
- }
-}
-
-// Dial dials and authenticates to an SMTP server. The returned SendCloser
-// should be closed when done using it.
-func (d *Dialer) Dial() (SendCloser, error) {
- c, err := d.dial()
- if err != nil {
- return nil, err
- }
-
- if d.Auth != nil {
- if ok, _ := c.Extension("AUTH"); ok {
- if err = c.Auth(d.Auth); err != nil {
- c.Close()
- return nil, err
- }
- }
- }
-
- return &smtpSender{c}, nil
-}
-
-func (d *Dialer) dial() (smtpClient, error) {
- if d.SSL {
- return d.sslDial()
- }
- return d.starttlsDial()
-}
-
-func (d *Dialer) starttlsDial() (smtpClient, error) {
- c, err := smtpDial(addr(d.Host, d.Port))
- if err != nil {
- return nil, err
- }
-
- if ok, _ := c.Extension("STARTTLS"); ok {
- if err := c.StartTLS(d.tlsConfig()); err != nil {
- c.Close()
- return nil, err
- }
- }
-
- return c, nil
-}
-
-func (d *Dialer) sslDial() (smtpClient, error) {
- conn, err := tlsDial("tcp", addr(d.Host, d.Port), d.tlsConfig())
- if err != nil {
- return nil, err
- }
-
- return newClient(conn, d.Host)
-}
-
-func (d *Dialer) tlsConfig() *tls.Config {
- if d.TLSConfig == nil {
- return &tls.Config{ServerName: d.Host}
- }
-
- return d.TLSConfig
-}
-
-func addr(host string, port int) string {
- return fmt.Sprintf("%s:%d", host, port)
-}
-
-// DialAndSend opens a connection to the SMTP server, sends the given emails and
-// closes the connection.
-func (d *Dialer) DialAndSend(m ...*Message) error {
- s, err := d.Dial()
- if err != nil {
- return err
- }
- defer s.Close()
-
- return Send(s, m...)
-}
-
-type smtpSender struct {
- smtpClient
-}
-
-func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
- if err := c.Mail(from); err != nil {
- return err
- }
-
- for _, addr := range to {
- if err := c.Rcpt(addr); err != nil {
- return err
- }
- }
-
- w, err := c.Data()
- if err != nil {
- return err
- }
-
- if _, err = msg.WriteTo(w); err != nil {
- w.Close()
- return err
- }
-
- return w.Close()
-}
-
-func (c *smtpSender) Close() error {
- return c.Quit()
-}
-
-// Stubbed out for tests.
-var (
- smtpDial = func(addr string) (smtpClient, error) {
- return smtp.Dial(addr)
- }
- tlsDial = tls.Dial
- newClient = func(conn net.Conn, host string) (smtpClient, error) {
- return smtp.NewClient(conn, host)
- }
-)
-
-type smtpClient interface {
- Extension(string) (bool, string)
- StartTLS(*tls.Config) error
- Auth(smtp.Auth) error
- Mail(string) error
- Rcpt(string) error
- Data() (io.WriteCloser, error)
- Quit() error
- Close() error
-}
diff --git a/vendor/gopkg.in/mail.v2/.gitignore b/vendor/gopkg.in/mail.v2/.gitignore
new file mode 100644
index 0000000000..cc4721cfe5
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/.gitignore
@@ -0,0 +1,17 @@
+
+
+# Binaries for programs and plugins
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+
+# IDE's
+.idea/
diff --git a/vendor/gopkg.in/mail.v2/.travis.yml b/vendor/gopkg.in/mail.v2/.travis.yml
new file mode 100644
index 0000000000..397d1660c2
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/.travis.yml
@@ -0,0 +1,25 @@
+language: go
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - 1.7
+ - 1.8
+ - 1.9
+ - master
+
+# safelist
+branches:
+ only:
+ - master
+ - v2
+
+notifications:
+ email: false
+
+before_install:
+ - mkdir -p $GOPATH/src/gopkg.in &&
+ ln -s ../github.com/go-mail/mail $GOPATH/src/gopkg.in/mail.v2
diff --git a/vendor/gopkg.in/mail.v2/CHANGELOG.md b/vendor/gopkg.in/mail.v2/CHANGELOG.md
new file mode 100644
index 0000000000..cdd898af87
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/CHANGELOG.md
@@ -0,0 +1,88 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## *Unreleased*
+
+## [2.3.1] - 2018-11-12
+
+### Fixed
+
+- #39: Reverts addition of Go modules `go.mod` manifest.
+
+## [2.3.0] - 2018-11-10
+
+### Added
+
+- #12: Adds `SendError` to provide additional info about the cause and index of
+ a failed attempt to transmit a batch of messages.
+- go-gomail#78: Adds new `Message` methods for attaching and embedding
+ `io.Reader`s: `AttachReader` and `EmbedReader`.
+
+### Fixed
+
+- #26: Fixes RFC 1341 compliance by properly capitalizing the
+ `MIME-Version` header.
+- #30: Fixes IO errors being silently dropped in `Message.WriteTo`.
+
+## [2.2.0] - 2018-03-01
+
+### Added
+
+- #20: Adds `Message.SetBoundary` to allow specifying a custom MIME boundary.
+- #22: Adds `Message.SetBodyWriter` to make it easy to use text/template and
+ html/template for message bodies. Contributed by Quantcast.
+- #25: Adds `Dialer.StartTLSPolicy` so that `MandatoryStartTLS` can be required,
+ or `NoStartTLS` can disable it. Contributed by Quantcast.
+
+## [2.1.0] - 2017-12-14
+
+### Added
+
+- go-gomail#40: Adds `Dialer.LocalName` field to allow specifying the hostname
+ sent with SMTP's HELO command.
+- go-gomail#47: `Message.SetBody`, `Message.AddAlternative`, and
+ `Message.AddAlternativeWriter` allow specifying the encoding of message parts.
+- `Dialer.Dial`'s returned `SendCloser` automatically redials after a timeout.
+- go-gomail#55, go-gomail#56: Adds `Rename` to allow specifying filename
+ of an attachment.
+- go-gomail#100: Exports `NetDialTimeout` to allow setting a custom dialer.
+- go-gomail#70: Adds `Dialer.Timeout` field to allow specifying a timeout for
+ dials, reads, and writes.
+
+### Changed
+
+- go-gomail#52: `Dialer.Dial` automatically uses CRAM-MD5 when available.
+- `Dialer.Dial` specifies a default timeout of 10 seconds.
+- Gomail is forked from to
+ .
+
+### Deprecated
+
+- go-gomail#52: `NewPlainDialer` is deprecated in favor of `NewDialer`.
+
+### Fixed
+
+- go-gomail#41, go-gomail#42: Fixes a panic when a `Message` contains a
+ nil header.
+- go-gomail#44: Fixes `AddAlternativeWriter` replacing the message body instead
+ of adding a body part.
+- go-gomail#53: Folds long header lines for RFC 2047 compliance.
+- go-gomail#54: Fixes `Message.FormatAddress` when name is blank.
+
+## [2.0.0] - 2015-09-02
+
+- Mailer has been removed. It has been replaced by Dialer and Sender.
+- `File` type and the `CreateFile` and `OpenFile` functions have been removed.
+- `Message.Attach` and `Message.Embed` have a new signature.
+- `Message.GetBodyWriter` has been removed. Use `Message.AddAlternativeWriter`
+instead.
+- `Message.Export` has been removed. `Message.WriteTo` can be used instead.
+- `Message.DelHeader` has been removed.
+- The `Bcc` header field is no longer sent. It is far more simpler and
+efficient: the same message is sent to all recipients instead of sending a
+different email to each Bcc address.
+- LoginAuth has been removed. `NewPlainDialer` now implements the LOGIN
+authentication mechanism when needed.
+- Go 1.2 is now required instead of Go 1.3. No external dependency are used when
+using Go 1.5.
diff --git a/vendor/gopkg.in/gomail.v2/CONTRIBUTING.md b/vendor/gopkg.in/mail.v2/CONTRIBUTING.md
similarity index 100%
rename from vendor/gopkg.in/gomail.v2/CONTRIBUTING.md
rename to vendor/gopkg.in/mail.v2/CONTRIBUTING.md
diff --git a/vendor/gopkg.in/gomail.v2/LICENSE b/vendor/gopkg.in/mail.v2/LICENSE
similarity index 100%
rename from vendor/gopkg.in/gomail.v2/LICENSE
rename to vendor/gopkg.in/mail.v2/LICENSE
diff --git a/vendor/gopkg.in/mail.v2/README.md b/vendor/gopkg.in/mail.v2/README.md
new file mode 100644
index 0000000000..8cc31b6f59
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/README.md
@@ -0,0 +1,129 @@
+# Gomail
+[](https://travis-ci.org/go-mail/mail) [](http://gocover.io/github.com/go-mail/mail) [](https://godoc.org/github.com/go-mail/mail)
+
+This is an actively maintained fork of [Gomail][1] and includes fixes and
+improvements for a number of outstanding issues. The current progress is
+as follows:
+
+ - [x] Timeouts and retries can be specified outside of the 10 second default.
+ - [x] Proxying is supported through specifying a custom [NetDialTimeout][2].
+ - [ ] Filenames are properly encoded for non-ASCII characters.
+ - [ ] Email addresses are properly encoded for non-ASCII characters.
+ - [ ] Embedded files and attachments are tested for their existence.
+ - [ ] An `io.Reader` can be supplied when embedding and attaching files.
+
+See [Transitioning Existing Codebases][3] for more information on switching.
+
+[1]: https://github.com/go-gomail/gomail
+[2]: https://godoc.org/gopkg.in/mail.v2#NetDialTimeout
+[3]: #transitioning-existing-codebases
+
+## Introduction
+
+Gomail is a simple and efficient package to send emails. It is well tested and
+documented.
+
+Gomail can only send emails using an SMTP server. But the API is flexible and it
+is easy to implement other methods for sending emails using a local Postfix, an
+API, etc.
+
+It requires Go 1.2 or newer. With Go 1.5, no external dependencies are used.
+
+
+## Features
+
+Gomail supports:
+- Attachments
+- Embedded images
+- HTML and text templates
+- Automatic encoding of special characters
+- SSL and TLS
+- Sending multiple emails with the same SMTP connection
+
+
+## Documentation
+
+https://godoc.org/github.com/go-mail/mail
+
+
+## Download
+
+If you're already using a dependency manager, like [dep][dep], use the following
+import path:
+
+```
+github.com/go-mail/mail
+```
+
+If you *aren't* using vendoring, `go get` the [Gopkg.in](http://gopkg.in)
+import path:
+
+```
+gopkg.in/mail.v2
+```
+
+[dep]: https://github.com/golang/dep#readme
+
+## Examples
+
+See the [examples in the documentation](https://godoc.org/github.com/go-mail/mail#example-package).
+
+
+## FAQ
+
+### x509: certificate signed by unknown authority
+
+If you get this error it means the certificate used by the SMTP server is not
+considered valid by the client running Gomail. As a quick workaround you can
+bypass the verification of the server's certificate chain and host name by using
+`SetTLSConfig`:
+
+```go
+package main
+
+import (
+ "crypto/tls"
+
+ "gopkg.in/mail.v2"
+)
+
+func main() {
+ d := mail.NewDialer("smtp.example.com", 587, "user", "123456")
+ d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
+
+ // Send emails using d.
+}
+```
+
+Note, however, that this is insecure and should not be used in production.
+
+### Transitioning Existing Codebases
+
+If you're already using the original Gomail, switching is as easy as updating
+the import line to:
+
+```
+import gomail "gopkg.in/mail.v2"
+```
+
+## Contribute
+
+Contributions are more than welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for
+more info.
+
+
+## Change log
+
+See [CHANGELOG.md](CHANGELOG.md).
+
+
+## License
+
+[MIT](LICENSE)
+
+
+## Support & Contact
+
+You can ask questions on the [Gomail
+thread](https://groups.google.com/d/topic/golang-nuts/jMxZHzvvEVg/discussion)
+in the Go mailing-list.
diff --git a/vendor/gopkg.in/mail.v2/auth.go b/vendor/gopkg.in/mail.v2/auth.go
new file mode 100644
index 0000000000..b8c0dde7f2
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/auth.go
@@ -0,0 +1,49 @@
+package mail
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "net/smtp"
+)
+
+// loginAuth is an smtp.Auth that implements the LOGIN authentication mechanism.
+type loginAuth struct {
+ username string
+ password string
+ host string
+}
+
+func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
+ if !server.TLS {
+ advertised := false
+ for _, mechanism := range server.Auth {
+ if mechanism == "LOGIN" {
+ advertised = true
+ break
+ }
+ }
+ if !advertised {
+ return "", nil, errors.New("gomail: unencrypted connection")
+ }
+ }
+ if server.Name != a.host {
+ return "", nil, errors.New("gomail: wrong host name")
+ }
+ return "LOGIN", nil, nil
+}
+
+func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
+ if !more {
+ return nil, nil
+ }
+
+ switch {
+ case bytes.Equal(fromServer, []byte("Username:")):
+ return []byte(a.username), nil
+ case bytes.Equal(fromServer, []byte("Password:")):
+ return []byte(a.password), nil
+ default:
+ return nil, fmt.Errorf("gomail: unexpected server challenge: %s", fromServer)
+ }
+}
diff --git a/vendor/gopkg.in/gomail.v2/doc.go b/vendor/gopkg.in/mail.v2/doc.go
similarity index 57%
rename from vendor/gopkg.in/gomail.v2/doc.go
rename to vendor/gopkg.in/mail.v2/doc.go
index a8f5091f54..d65bf35916 100644
--- a/vendor/gopkg.in/gomail.v2/doc.go
+++ b/vendor/gopkg.in/mail.v2/doc.go
@@ -1,5 +1,6 @@
// Package gomail provides a simple interface to compose emails and to mail them
// efficiently.
//
-// More info on Github: https://github.com/go-gomail/gomail
-package gomail
+// More info on Github: https://github.com/go-mail/mail
+//
+package mail
diff --git a/vendor/gopkg.in/mail.v2/errors.go b/vendor/gopkg.in/mail.v2/errors.go
new file mode 100644
index 0000000000..770da8c385
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/errors.go
@@ -0,0 +1,16 @@
+package mail
+
+import "fmt"
+
+// A SendError represents the failure to transmit a Message, detailing the cause
+// of the failure and index of the Message within a batch.
+type SendError struct {
+ // Index specifies the index of the Message within a batch.
+ Index uint
+ Cause error
+}
+
+func (err *SendError) Error() string {
+ return fmt.Sprintf("gomail: could not send email %d: %v",
+ err.Index+1, err.Cause)
+}
diff --git a/vendor/gopkg.in/gomail.v2/message.go b/vendor/gopkg.in/mail.v2/message.go
similarity index 64%
rename from vendor/gopkg.in/gomail.v2/message.go
rename to vendor/gopkg.in/mail.v2/message.go
index 2f75368bda..9f7f7bde45 100644
--- a/vendor/gopkg.in/gomail.v2/message.go
+++ b/vendor/gopkg.in/mail.v2/message.go
@@ -1,4 +1,4 @@
-package gomail
+package mail
import (
"bytes"
@@ -11,20 +11,22 @@ import (
// Message represents an email.
type Message struct {
header header
- parts []part
+ parts []*part
attachments []*file
embedded []*file
charset string
encoding Encoding
hEncoder mimeEncoder
buf bytes.Buffer
+ boundary string
}
type header map[string][]string
type part struct {
- header header
- copier func(io.Writer) error
+ contentType string
+ copier func(io.Writer) error
+ encoding Encoding
}
// NewMessage creates a new message. It uses UTF-8 and quoted-printable encoding
@@ -96,6 +98,11 @@ const (
Unencoded Encoding = "8bit"
)
+// SetBoundary sets a custom multipart boundary.
+func (m *Message) SetBoundary(boundary string) {
+ m.boundary = boundary
+}
+
// SetHeader sets a value to the given header field.
func (m *Message) SetHeader(field string, value ...string) {
m.encodeHeader(value)
@@ -126,6 +133,10 @@ func (m *Message) SetAddressHeader(field, address, name string) {
// FormatAddress formats an address and a name as a valid RFC 5322 address.
func (m *Message) FormatAddress(address, name string) string {
+ if name == "" {
+ return address
+ }
+
enc := m.encodeString(name)
if enc == name {
m.buf.WriteByte('"')
@@ -177,53 +188,66 @@ func (m *Message) GetHeader(field string) []string {
return m.header[field]
}
-// SetBody sets the body of the message.
-func (m *Message) SetBody(contentType, body string) {
- m.parts = []part{
- {
- header: m.getPartHeader(contentType),
- copier: func(w io.Writer) error {
- _, err := io.WriteString(w, body)
- return err
- },
- },
- }
+// SetBody sets the body of the message. It replaces any content previously set
+// by SetBody, SetBodyWriter, AddAlternative or AddAlternativeWriter.
+func (m *Message) SetBody(contentType, body string, settings ...PartSetting) {
+ m.SetBodyWriter(contentType, newCopier(body), settings...)
+}
+
+// SetBodyWriter sets the body of the message. It can be useful with the
+// text/template or html/template packages.
+func (m *Message) SetBodyWriter(contentType string, f func(io.Writer) error, settings ...PartSetting) {
+ m.parts = []*part{m.newPart(contentType, f, settings)}
}
// AddAlternative adds an alternative part to the message.
//
// It is commonly used to send HTML emails that default to the plain text
-// version for backward compatibility.
-//
-// More info: http://en.wikipedia.org/wiki/MIME#Alternative
-func (m *Message) AddAlternative(contentType, body string) {
- m.parts = append(m.parts,
- part{
- header: m.getPartHeader(contentType),
- copier: func(w io.Writer) error {
- _, err := io.WriteString(w, body)
- return err
- },
- },
- )
+// version for backward compatibility. AddAlternative appends the new part to
+// the end of the message. So the plain text part should be added before the
+// HTML part. See http://en.wikipedia.org/wiki/MIME#Alternative
+func (m *Message) AddAlternative(contentType, body string, settings ...PartSetting) {
+ m.AddAlternativeWriter(contentType, newCopier(body), settings...)
+}
+
+func newCopier(s string) func(io.Writer) error {
+ return func(w io.Writer) error {
+ _, err := io.WriteString(w, s)
+ return err
+ }
}
// AddAlternativeWriter adds an alternative part to the message. It can be
// useful with the text/template or html/template packages.
-func (m *Message) AddAlternativeWriter(contentType string, f func(io.Writer) error) {
- m.parts = []part{
- {
- header: m.getPartHeader(contentType),
- copier: f,
- },
- }
+func (m *Message) AddAlternativeWriter(contentType string, f func(io.Writer) error, settings ...PartSetting) {
+ m.parts = append(m.parts, m.newPart(contentType, f, settings))
}
-func (m *Message) getPartHeader(contentType string) header {
- return map[string][]string{
- "Content-Type": {contentType + "; charset=" + m.charset},
- "Content-Transfer-Encoding": {string(m.encoding)},
+func (m *Message) newPart(contentType string, f func(io.Writer) error, settings []PartSetting) *part {
+ p := &part{
+ contentType: contentType,
+ copier: f,
+ encoding: m.encoding,
}
+
+ for _, s := range settings {
+ s(p)
+ }
+
+ return p
+}
+
+// A PartSetting can be used as an argument in Message.SetBody,
+// Message.SetBodyWriter, Message.AddAlternative or Message.AddAlternativeWriter
+// to configure the part added to a message.
+type PartSetting func(*part)
+
+// SetPartEncoding sets the encoding of the part added to the message. By
+// default, parts use the same encoding than the message.
+func SetPartEncoding(e Encoding) PartSetting {
+ return PartSetting(func(p *part) {
+ p.encoding = e
+ })
}
type file struct {
@@ -252,6 +276,14 @@ func SetHeader(h map[string][]string) FileSetting {
}
}
+// Rename is a file setting to set the name of the attachment if the name is
+// different than the filename on disk.
+func Rename(name string) FileSetting {
+ return func(f *file) {
+ f.Name = name
+ }
+}
+
// SetCopyFunc is a file setting to replace the function that runs when the
// message is sent. It should copy the content of the file to the io.Writer.
//
@@ -263,8 +295,28 @@ func SetCopyFunc(f func(io.Writer) error) FileSetting {
}
}
-func (m *Message) appendFile(list []*file, name string, settings []FileSetting) []*file {
- f := &file{
+// AttachReader attaches a file using an io.Reader
+func (m *Message) AttachReader(name string, r io.Reader, settings ...FileSetting) {
+ m.attachments = m.appendFile(m.attachments, fileFromReader(name, r), settings)
+}
+
+// Attach attaches the files to the email.
+func (m *Message) Attach(filename string, settings ...FileSetting) {
+ m.attachments = m.appendFile(m.attachments, fileFromFilename(filename), settings)
+}
+
+// EmbedReader embeds the images to the email.
+func (m *Message) EmbedReader(name string, r io.Reader, settings ...FileSetting) {
+ m.embedded = m.appendFile(m.embedded, fileFromReader(name, r), settings)
+}
+
+// Embed embeds the images to the email.
+func (m *Message) Embed(filename string, settings ...FileSetting) {
+ m.embedded = m.appendFile(m.embedded, fileFromFilename(filename), settings)
+}
+
+func fileFromFilename(name string) *file {
+ return &file{
Name: filepath.Base(name),
Header: make(map[string][]string),
CopyFunc: func(w io.Writer) error {
@@ -279,7 +331,22 @@ func (m *Message) appendFile(list []*file, name string, settings []FileSetting)
return h.Close()
},
}
+}
+func fileFromReader(name string, r io.Reader) *file {
+ return &file{
+ Name: filepath.Base(name),
+ Header: make(map[string][]string),
+ CopyFunc: func(w io.Writer) error {
+ if _, err := io.Copy(w, r); err != nil {
+ return err
+ }
+ return nil
+ },
+ }
+}
+
+func (m *Message) appendFile(list []*file, f *file, settings []FileSetting) []*file {
for _, s := range settings {
s(f)
}
@@ -290,13 +357,3 @@ func (m *Message) appendFile(list []*file, name string, settings []FileSetting)
return append(list, f)
}
-
-// Attach attaches the files to the email.
-func (m *Message) Attach(filename string, settings ...FileSetting) {
- m.attachments = m.appendFile(m.attachments, filename, settings)
-}
-
-// Embed embeds the images to the email.
-func (m *Message) Embed(filename string, settings ...FileSetting) {
- m.embedded = m.appendFile(m.embedded, filename, settings)
-}
diff --git a/vendor/gopkg.in/gomail.v2/mime.go b/vendor/gopkg.in/mail.v2/mime.go
similarity index 51%
rename from vendor/gopkg.in/gomail.v2/mime.go
rename to vendor/gopkg.in/mail.v2/mime.go
index 51cba724b6..d95ea2eb24 100644
--- a/vendor/gopkg.in/gomail.v2/mime.go
+++ b/vendor/gopkg.in/mail.v2/mime.go
@@ -1,10 +1,11 @@
// +build go1.5
-package gomail
+package mail
import (
"mime"
"mime/quotedprintable"
+ "strings"
)
var newQPWriter = quotedprintable.NewWriter
@@ -14,6 +15,7 @@ type mimeEncoder struct {
}
var (
- bEncoding = mimeEncoder{mime.BEncoding}
- qEncoding = mimeEncoder{mime.QEncoding}
+ bEncoding = mimeEncoder{mime.BEncoding}
+ qEncoding = mimeEncoder{mime.QEncoding}
+ lastIndexByte = strings.LastIndexByte
)
diff --git a/vendor/gopkg.in/mail.v2/mime_go14.go b/vendor/gopkg.in/mail.v2/mime_go14.go
new file mode 100644
index 0000000000..bdb605dcca
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/mime_go14.go
@@ -0,0 +1,25 @@
+// +build !go1.5
+
+package mail
+
+import "gopkg.in/alexcesaro/quotedprintable.v3"
+
+var newQPWriter = quotedprintable.NewWriter
+
+type mimeEncoder struct {
+ quotedprintable.WordEncoder
+}
+
+var (
+ bEncoding = mimeEncoder{quotedprintable.BEncoding}
+ qEncoding = mimeEncoder{quotedprintable.QEncoding}
+ lastIndexByte = func(s string, c byte) int {
+ for i := len(s) - 1; i >= 0; i-- {
+
+ if s[i] == c {
+ return i
+ }
+ }
+ return -1
+ }
+)
diff --git a/vendor/gopkg.in/gomail.v2/send.go b/vendor/gopkg.in/mail.v2/send.go
similarity index 87%
rename from vendor/gopkg.in/gomail.v2/send.go
rename to vendor/gopkg.in/mail.v2/send.go
index 3e67265097..62e67f0b81 100644
--- a/vendor/gopkg.in/gomail.v2/send.go
+++ b/vendor/gopkg.in/mail.v2/send.go
@@ -1,10 +1,10 @@
-package gomail
+package mail
import (
"errors"
"fmt"
"io"
- "net/mail"
+ stdmail "net/mail"
)
// Sender is the interface that wraps the Send method.
@@ -20,7 +20,7 @@ type SendCloser interface {
Close() error
}
-// A SendFunc is a function that sends emails to the given adresses.
+// A SendFunc is a function that sends emails to the given addresses.
//
// The SendFunc type is an adapter to allow the use of ordinary functions as
// email senders. If f is a function with the appropriate signature, SendFunc(f)
@@ -36,7 +36,7 @@ func (f SendFunc) Send(from string, to []string, msg io.WriterTo) error {
func Send(s Sender, msg ...*Message) error {
for i, m := range msg {
if err := send(s, m); err != nil {
- return fmt.Errorf("gomail: could not send email %d: %v", i+1, err)
+ return &SendError{Cause: err, Index: uint(i)}
}
}
@@ -108,10 +108,9 @@ func addAddress(list []string, addr string) []string {
}
func parseAddress(field string) (string, error) {
- a, err := mail.ParseAddress(field)
- if a == nil {
- return "", err
+ addr, err := stdmail.ParseAddress(field)
+ if err != nil {
+ return "", fmt.Errorf("gomail: invalid address %q: %v", field, err)
}
-
- return a.Address, err
+ return addr.Address, nil
}
diff --git a/vendor/gopkg.in/mail.v2/smtp.go b/vendor/gopkg.in/mail.v2/smtp.go
new file mode 100644
index 0000000000..547e04d16b
--- /dev/null
+++ b/vendor/gopkg.in/mail.v2/smtp.go
@@ -0,0 +1,292 @@
+package mail
+
+import (
+ "crypto/tls"
+ "fmt"
+ "io"
+ "net"
+ "net/smtp"
+ "strings"
+ "time"
+)
+
+// A Dialer is a dialer to an SMTP server.
+type Dialer struct {
+ // Host represents the host of the SMTP server.
+ Host string
+ // Port represents the port of the SMTP server.
+ Port int
+ // Username is the username to use to authenticate to the SMTP server.
+ Username string
+ // Password is the password to use to authenticate to the SMTP server.
+ Password string
+ // Auth represents the authentication mechanism used to authenticate to the
+ // SMTP server.
+ Auth smtp.Auth
+ // SSL defines whether an SSL connection is used. It should be false in
+ // most cases since the authentication mechanism should use the STARTTLS
+ // extension instead.
+ SSL bool
+ // TLSConfig represents the TLS configuration used for the TLS (when the
+ // STARTTLS extension is used) or SSL connection.
+ TLSConfig *tls.Config
+ // StartTLSPolicy represents the TLS security level required to
+ // communicate with the SMTP server.
+ //
+ // This defaults to OpportunisticStartTLS for backwards compatibility,
+ // but we recommend MandatoryStartTLS for all modern SMTP servers.
+ //
+ // This option has no effect if SSL is set to true.
+ StartTLSPolicy StartTLSPolicy
+ // LocalName is the hostname sent to the SMTP server with the HELO command.
+ // By default, "localhost" is sent.
+ LocalName string
+ // Timeout to use for read/write operations. Defaults to 10 seconds, can
+ // be set to 0 to disable timeouts.
+ Timeout time.Duration
+ // Whether we should retry mailing if the connection returned an error,
+ // defaults to true.
+ RetryFailure bool
+}
+
+// NewDialer returns a new SMTP Dialer. The given parameters are used to connect
+// to the SMTP server.
+func NewDialer(host string, port int, username, password string) *Dialer {
+ return &Dialer{
+ Host: host,
+ Port: port,
+ Username: username,
+ Password: password,
+ SSL: port == 465,
+ Timeout: 10 * time.Second,
+ RetryFailure: true,
+ }
+}
+
+// NewPlainDialer returns a new SMTP Dialer. The given parameters are used to
+// connect to the SMTP server.
+//
+// Deprecated: Use NewDialer instead.
+func NewPlainDialer(host string, port int, username, password string) *Dialer {
+ return NewDialer(host, port, username, password)
+}
+
+// NetDialTimeout specifies the DialTimeout function to establish a connection
+// to the SMTP server. This can be used to override dialing in the case that a
+// proxy or other special behavior is needed.
+var NetDialTimeout = net.DialTimeout
+
+// Dial dials and authenticates to an SMTP server. The returned SendCloser
+// should be closed when done using it.
+func (d *Dialer) Dial() (SendCloser, error) {
+ conn, err := NetDialTimeout("tcp", addr(d.Host, d.Port), d.Timeout)
+ if err != nil {
+ return nil, err
+ }
+
+ if d.SSL {
+ conn = tlsClient(conn, d.tlsConfig())
+ }
+
+ c, err := smtpNewClient(conn, d.Host)
+ if err != nil {
+ return nil, err
+ }
+
+ if d.Timeout > 0 {
+ conn.SetDeadline(time.Now().Add(d.Timeout))
+ }
+
+ if d.LocalName != "" {
+ if err := c.Hello(d.LocalName); err != nil {
+ return nil, err
+ }
+ }
+
+ if !d.SSL && d.StartTLSPolicy != NoStartTLS {
+ ok, _ := c.Extension("STARTTLS")
+ if !ok && d.StartTLSPolicy == MandatoryStartTLS {
+ err := StartTLSUnsupportedError{
+ Policy: d.StartTLSPolicy}
+ return nil, err
+ }
+
+ if ok {
+ if err := c.StartTLS(d.tlsConfig()); err != nil {
+ c.Close()
+ return nil, err
+ }
+ }
+ }
+
+ if d.Auth == nil && d.Username != "" {
+ if ok, auths := c.Extension("AUTH"); ok {
+ if strings.Contains(auths, "CRAM-MD5") {
+ d.Auth = smtp.CRAMMD5Auth(d.Username, d.Password)
+ } else if strings.Contains(auths, "LOGIN") &&
+ !strings.Contains(auths, "PLAIN") {
+ d.Auth = &loginAuth{
+ username: d.Username,
+ password: d.Password,
+ host: d.Host,
+ }
+ } else {
+ d.Auth = smtp.PlainAuth("", d.Username, d.Password, d.Host)
+ }
+ }
+ }
+
+ if d.Auth != nil {
+ if err = c.Auth(d.Auth); err != nil {
+ c.Close()
+ return nil, err
+ }
+ }
+
+ return &smtpSender{c, conn, d}, nil
+}
+
+func (d *Dialer) tlsConfig() *tls.Config {
+ if d.TLSConfig == nil {
+ return &tls.Config{ServerName: d.Host}
+ }
+ return d.TLSConfig
+}
+
+// StartTLSPolicy constants are valid values for Dialer.StartTLSPolicy.
+type StartTLSPolicy int
+
+const (
+ // OpportunisticStartTLS means that SMTP transactions are encrypted if
+ // STARTTLS is supported by the SMTP server. Otherwise, messages are
+ // sent in the clear. This is the default setting.
+ OpportunisticStartTLS StartTLSPolicy = iota
+ // MandatoryStartTLS means that SMTP transactions must be encrypted.
+ // SMTP transactions are aborted unless STARTTLS is supported by the
+ // SMTP server.
+ MandatoryStartTLS
+ // NoStartTLS means encryption is disabled and messages are sent in the
+ // clear.
+ NoStartTLS = -1
+)
+
+func (policy *StartTLSPolicy) String() string {
+ switch *policy {
+ case OpportunisticStartTLS:
+ return "OpportunisticStartTLS"
+ case MandatoryStartTLS:
+ return "MandatoryStartTLS"
+ case NoStartTLS:
+ return "NoStartTLS"
+ default:
+ return fmt.Sprintf("StartTLSPolicy:%v", *policy)
+ }
+}
+
+// StartTLSUnsupportedError is returned by Dial when connecting to an SMTP
+// server that does not support STARTTLS.
+type StartTLSUnsupportedError struct {
+ Policy StartTLSPolicy
+}
+
+func (e StartTLSUnsupportedError) Error() string {
+ return "gomail: " + e.Policy.String() + " required, but " +
+ "SMTP server does not support STARTTLS"
+}
+
+func addr(host string, port int) string {
+ return fmt.Sprintf("%s:%d", host, port)
+}
+
+// DialAndSend opens a connection to the SMTP server, sends the given emails and
+// closes the connection.
+func (d *Dialer) DialAndSend(m ...*Message) error {
+ s, err := d.Dial()
+ if err != nil {
+ return err
+ }
+ defer s.Close()
+
+ return Send(s, m...)
+}
+
+type smtpSender struct {
+ smtpClient
+ conn net.Conn
+ d *Dialer
+}
+
+func (c *smtpSender) retryError(err error) bool {
+ if !c.d.RetryFailure {
+ return false
+ }
+
+ if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
+ return true
+ }
+
+ return err == io.EOF
+}
+
+func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
+ if c.d.Timeout > 0 {
+ c.conn.SetDeadline(time.Now().Add(c.d.Timeout))
+ }
+
+ if err := c.Mail(from); err != nil {
+ if c.retryError(err) {
+ // This is probably due to a timeout, so reconnect and try again.
+ sc, derr := c.d.Dial()
+ if derr == nil {
+ if s, ok := sc.(*smtpSender); ok {
+ *c = *s
+ return c.Send(from, to, msg)
+ }
+ }
+ }
+
+ return err
+ }
+
+ for _, addr := range to {
+ if err := c.Rcpt(addr); err != nil {
+ return err
+ }
+ }
+
+ w, err := c.Data()
+ if err != nil {
+ return err
+ }
+
+ if _, err = msg.WriteTo(w); err != nil {
+ w.Close()
+ return err
+ }
+
+ return w.Close()
+}
+
+func (c *smtpSender) Close() error {
+ return c.Quit()
+}
+
+// Stubbed out for tests.
+var (
+ tlsClient = tls.Client
+ smtpNewClient = func(conn net.Conn, host string) (smtpClient, error) {
+ return smtp.NewClient(conn, host)
+ }
+)
+
+type smtpClient interface {
+ Hello(string) error
+ Extension(string) (bool, string)
+ StartTLS(*tls.Config) error
+ Auth(smtp.Auth) error
+ Mail(string) error
+ Rcpt(string) error
+ Data() (io.WriteCloser, error)
+ Quit() error
+ Close() error
+}
diff --git a/vendor/gopkg.in/gomail.v2/writeto.go b/vendor/gopkg.in/mail.v2/writeto.go
similarity index 64%
rename from vendor/gopkg.in/gomail.v2/writeto.go
rename to vendor/gopkg.in/mail.v2/writeto.go
index 57a1dd7f1e..faf6124d6b 100644
--- a/vendor/gopkg.in/gomail.v2/writeto.go
+++ b/vendor/gopkg.in/mail.v2/writeto.go
@@ -1,4 +1,4 @@
-package gomail
+package mail
import (
"encoding/base64"
@@ -7,6 +7,7 @@ import (
"mime"
"mime/multipart"
"path/filepath"
+ "strings"
"time"
)
@@ -18,8 +19,8 @@ func (m *Message) WriteTo(w io.Writer) (int64, error) {
}
func (w *messageWriter) writeMessage(m *Message) {
- if _, ok := m.header["Mime-Version"]; !ok {
- w.writeString("Mime-Version: 1.0\r\n")
+ if _, ok := m.header["MIME-Version"]; !ok {
+ w.writeString("MIME-Version: 1.0\r\n")
}
if _, ok := m.header["Date"]; !ok {
w.writeHeader("Date", m.FormatDate(now()))
@@ -27,19 +28,18 @@ func (w *messageWriter) writeMessage(m *Message) {
w.writeHeaders(m.header)
if m.hasMixedPart() {
- w.openMultipart("mixed")
+ w.openMultipart("mixed", m.boundary)
}
if m.hasRelatedPart() {
- w.openMultipart("related")
+ w.openMultipart("related", m.boundary)
}
if m.hasAlternativePart() {
- w.openMultipart("alternative")
+ w.openMultipart("alternative", m.boundary)
}
for _, part := range m.parts {
- w.writeHeaders(part.header)
- w.writeBody(part.copier, m.encoding)
+ w.writePart(part, m.charset)
}
if m.hasAlternativePart() {
w.closeMultipart()
@@ -77,9 +77,12 @@ type messageWriter struct {
err error
}
-func (w *messageWriter) openMultipart(mimeType string) {
+func (w *messageWriter) openMultipart(mimeType, boundary string) {
mw := multipart.NewWriter(w)
- contentType := "multipart/" + mimeType + "; boundary=" + mw.Boundary()
+ if boundary != "" {
+ mw.SetBoundary(boundary)
+ }
+ contentType := "multipart/" + mimeType + ";\r\n boundary=" + mw.Boundary()
w.writers[w.depth] = mw
if w.depth == 0 {
@@ -104,6 +107,14 @@ func (w *messageWriter) closeMultipart() {
}
}
+func (w *messageWriter) writePart(p *part, charset string) {
+ w.writeHeaders(map[string][]string{
+ "Content-Type": {p.contentType + "; charset=" + charset},
+ "Content-Transfer-Encoding": {string(p.encoding)},
+ })
+ w.writeBody(p.copier, p.encoding)
+}
+
func (w *messageWriter) addFiles(files []*file, isAttachment bool) {
for _, f := range files {
if _, ok := f.Header["Content-Type"]; !ok {
@@ -150,30 +161,90 @@ func (w *messageWriter) Write(p []byte) (int, error) {
}
func (w *messageWriter) writeString(s string) {
- n, _ := io.WriteString(w.w, s)
+ if w.err != nil { // do nothing when in error
+ return
+ }
+ var n int
+ n, w.err = io.WriteString(w.w, s)
w.n += int64(n)
}
-func (w *messageWriter) writeStrings(a []string, sep string) {
- if len(a) > 0 {
- w.writeString(a[0])
- if len(a) == 1 {
- return
- }
- }
- for _, s := range a[1:] {
- w.writeString(sep)
- w.writeString(s)
- }
-}
-
func (w *messageWriter) writeHeader(k string, v ...string) {
w.writeString(k)
+ if len(v) == 0 {
+ w.writeString(":\r\n")
+ return
+ }
w.writeString(": ")
- w.writeStrings(v, ", ")
+
+ // Max header line length is 78 characters in RFC 5322 and 76 characters
+ // in RFC 2047. So for the sake of simplicity we use the 76 characters
+ // limit.
+ charsLeft := 76 - len(k) - len(": ")
+
+ for i, s := range v {
+ // If the line is already too long, insert a newline right away.
+ if charsLeft < 1 {
+ if i == 0 {
+ w.writeString("\r\n ")
+ } else {
+ w.writeString(",\r\n ")
+ }
+ charsLeft = 75
+ } else if i != 0 {
+ w.writeString(", ")
+ charsLeft -= 2
+ }
+
+ // While the header content is too long, fold it by inserting a newline.
+ for len(s) > charsLeft {
+ s = w.writeLine(s, charsLeft)
+ charsLeft = 75
+ }
+ w.writeString(s)
+ if i := lastIndexByte(s, '\n'); i != -1 {
+ charsLeft = 75 - (len(s) - i - 1)
+ } else {
+ charsLeft -= len(s)
+ }
+ }
w.writeString("\r\n")
}
+func (w *messageWriter) writeLine(s string, charsLeft int) string {
+ // If there is already a newline before the limit. Write the line.
+ if i := strings.IndexByte(s, '\n'); i != -1 && i < charsLeft {
+ w.writeString(s[:i+1])
+ return s[i+1:]
+ }
+
+ for i := charsLeft - 1; i >= 0; i-- {
+ if s[i] == ' ' {
+ w.writeString(s[:i])
+ w.writeString("\r\n ")
+ return s[i+1:]
+ }
+ }
+
+ // We could not insert a newline cleanly so look for a space or a newline
+ // even if it is after the limit.
+ for i := 75; i < len(s); i++ {
+ if s[i] == ' ' {
+ w.writeString(s[:i])
+ w.writeString("\r\n ")
+ return s[i+1:]
+ }
+ if s[i] == '\n' {
+ w.writeString(s[:i+1])
+ return s[i+1:]
+ }
+ }
+
+ // Too bad, no space or newline in the whole string. Just write everything.
+ w.writeString(s)
+ return ""
+}
+
func (w *messageWriter) writeHeaders(h map[string][]string) {
if w.depth == 0 {
for k, v := range h {