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 -[![Build Status](https://travis-ci.org/go-gomail/gomail.svg?branch=v2)](https://travis-ci.org/go-gomail/gomail) [![Code Coverage](http://gocover.io/_badge/gopkg.in/gomail.v2)](http://gocover.io/gopkg.in/gomail.v2) [![Documentation](https://godoc.org/gopkg.in/gomail.v2?status.svg)](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. - -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](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 +[![Build Status](https://travis-ci.org/go-mail/mail.svg?branch=master)](https://travis-ci.org/go-mail/mail) [![Code Coverage](http://gocover.io/_badge/github.com/go-mail/mail)](http://gocover.io/github.com/go-mail/mail) [![Documentation](https://godoc.org/github.com/go-mail/mail?status.svg)](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 {