From f2755982c3bb40acc071d84ecd3581943f5799ff Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 27 Mar 2018 21:53:46 +0200 Subject: [PATCH] tech: migrates to none deprecated mail lib gomail is missing a maintainer so we are switching to an active fork ref https://github.com/go-gomail/gomail/issues/108 closes #7189 --- Gopkg.lock | 217 +++--------------- Gopkg.toml | 2 +- pkg/services/notifications/mailer.go | 2 +- .../gopkg.in/{gomail.v2 => mail.v2}/LICENSE | 0 .../gopkg.in/{gomail.v2 => mail.v2}/auth.go | 2 +- vendor/gopkg.in/{gomail.v2 => mail.v2}/doc.go | 5 +- vendor/gopkg.in/mail.v2/errors.go | 16 ++ .../{gomail.v2 => mail.v2}/message.go | 22 +- .../gopkg.in/{gomail.v2 => mail.v2}/mime.go | 2 +- .../{gomail.v2 => mail.v2}/mime_go14.go | 2 +- .../gopkg.in/{gomail.v2 => mail.v2}/send.go | 8 +- .../gopkg.in/{gomail.v2 => mail.v2}/smtp.go | 122 ++++++++-- .../{gomail.v2 => mail.v2}/writeto.go | 13 +- 13 files changed, 186 insertions(+), 227 deletions(-) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/LICENSE (100%) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/auth.go (98%) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/doc.go (57%) create mode 100644 vendor/gopkg.in/mail.v2/errors.go rename vendor/gopkg.in/{gomail.v2 => mail.v2}/message.go (91%) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/mime.go (95%) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/mime_go14.go (96%) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/send.go (94%) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/smtp.go (55%) rename vendor/gopkg.in/{gomail.v2 => mail.v2}/writeto.go (96%) diff --git a/Gopkg.lock b/Gopkg.lock index d447223795e..a35f5b23cda 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -27,37 +27,7 @@ [[projects]] name = "github.com/aws/aws-sdk-go" - packages = [ - "aws", - "aws/awserr", - "aws/awsutil", - "aws/client", - "aws/client/metadata", - "aws/corehandlers", - "aws/credentials", - "aws/credentials/ec2rolecreds", - "aws/credentials/endpointcreds", - "aws/credentials/stscreds", - "aws/defaults", - "aws/ec2metadata", - "aws/endpoints", - "aws/request", - "aws/session", - "aws/signer/v4", - "internal/shareddefaults", - "private/protocol", - "private/protocol/ec2query", - "private/protocol/query", - "private/protocol/query/queryutil", - "private/protocol/rest", - "private/protocol/restxml", - "private/protocol/xml/xmlutil", - "service/cloudwatch", - "service/ec2", - "service/ec2/ec2iface", - "service/s3", - "service/sts" - ] + packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/ec2query","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/cloudwatch","service/ec2","service/ec2/ec2iface","service/s3","service/sts"] revision = "decd990ddc5dcdf2f73309cbcab90d06b996ca28" version = "v1.12.67" @@ -105,10 +75,7 @@ [[projects]] name = "github.com/denisenkom/go-mssqldb" - packages = [ - ".", - "internal/cp" - ] + packages = [".","internal/cp"] revision = "270bc3860bb94dd3a3ffd047377d746c5e276726" [[projects]] @@ -150,12 +117,7 @@ [[projects]] branch = "master" name = "github.com/go-macaron/session" - packages = [ - ".", - "memcache", - "postgres", - "redis" - ] + packages = [".","memcache","postgres","redis"] revision = "b8e286a0dba8f4999042d6b258daf51b31d08938" [[projects]] @@ -190,13 +152,7 @@ [[projects]] branch = "master" name = "github.com/golang/protobuf" - packages = [ - "proto", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp" - ] + packages = ["proto","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"] revision = "c65a0412e71e8b9b3bfd22925720d23c0f054237" [[projects]] @@ -265,10 +221,7 @@ [[projects]] name = "github.com/klauspost/compress" - packages = [ - "flate", - "gzip" - ] + packages = ["flate","gzip"] revision = "6c8db69c4b49dd4df1fff66996cf556176d0b9bf" version = "v1.2.1" @@ -299,10 +252,7 @@ [[projects]] branch = "master" name = "github.com/lib/pq" - packages = [ - ".", - "oid" - ] + packages = [".","oid"] revision = "61fe37aa2ee24fabcdbe5c4ac1d4ac566f88f345" [[projects]] @@ -337,11 +287,7 @@ [[projects]] name = "github.com/opentracing/opentracing-go" - packages = [ - ".", - "ext", - "log" - ] + packages = [".","ext","log"] revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" version = "v1.0.2" @@ -353,12 +299,7 @@ [[projects]] name = "github.com/prometheus/client_golang" - packages = [ - "api", - "api/prometheus/v1", - "prometheus", - "prometheus/promhttp" - ] + packages = ["api","api/prometheus/v1","prometheus","prometheus/promhttp"] revision = "967789050ba94deca04a5e84cce8ad472ce313c1" version = "v0.9.0-pre1" @@ -371,22 +312,13 @@ [[projects]] branch = "master" name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model" - ] + packages = ["expfmt","internal/bitbucket.org/ww/goautoneg","model"] revision = "89604d197083d4781071d3c65855d24ecfb0a563" [[projects]] branch = "master" name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/util", - "nfsd", - "xfs" - ] + packages = [".","internal/util","nfsd","xfs"] revision = "85fadb6e89903ef7cca6f6a804474cd5ea85b6e1" [[projects]] @@ -403,21 +335,13 @@ [[projects]] name = "github.com/smartystreets/assertions" - packages = [ - ".", - "internal/go-render/render", - "internal/oglematchers" - ] + packages = [".","internal/go-render/render","internal/oglematchers"] revision = "0b37b35ec7434b77e77a4bb29b79677cced992ea" version = "1.8.1" [[projects]] name = "github.com/smartystreets/goconvey" - packages = [ - "convey", - "convey/gotest", - "convey/reporting" - ] + packages = ["convey","convey/gotest","convey/reporting"] revision = "9e8dc3f972df6c8fcc0375ef492c24d0bb204857" version = "1.6.3" @@ -429,21 +353,7 @@ [[projects]] name = "github.com/uber/jaeger-client-go" - packages = [ - ".", - "config", - "internal/baggage", - "internal/baggage/remote", - "internal/spanlog", - "log", - "rpcmetrics", - "thrift-gen/agent", - "thrift-gen/baggage", - "thrift-gen/jaeger", - "thrift-gen/sampling", - "thrift-gen/zipkincore", - "utils" - ] + packages = [".","config","internal/baggage","internal/baggage/remote","internal/spanlog","log","rpcmetrics","thrift-gen/agent","thrift-gen/baggage","thrift-gen/jaeger","thrift-gen/sampling","thrift-gen/zipkincore","utils"] revision = "3ac96c6e679cb60a74589b0d0aa7c70a906183f7" version = "v2.11.2" @@ -455,10 +365,7 @@ [[projects]] name = "github.com/yudai/gojsondiff" - packages = [ - ".", - "formatter" - ] + packages = [".","formatter"] revision = "7b1b7adf999dab73a6eb02669c3d82dbb27a3dd6" version = "1.0.0" @@ -471,37 +378,19 @@ [[projects]] branch = "master" name = "golang.org/x/crypto" - packages = [ - "md4", - "pbkdf2" - ] + packages = ["md4","pbkdf2"] revision = "3d37316aaa6bd9929127ac9a527abf408178ea7b" [[projects]] branch = "master" name = "golang.org/x/net" - packages = [ - "context", - "context/ctxhttp", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "lex/httplex", - "trace" - ] + packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"] revision = "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" [[projects]] branch = "master" name = "golang.org/x/oauth2" - packages = [ - ".", - "google", - "internal", - "jws", - "jwt" - ] + packages = [".","google","internal","jws","jwt"] revision = "b28fcf2b08a19742b43084fb40ab78ac6c3d8067" [[projects]] @@ -519,39 +408,12 @@ [[projects]] branch = "master" name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable" - ] + packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3" [[projects]] name = "google.golang.org/appengine" - packages = [ - ".", - "cloudsql", - "internal", - "internal/app_identity", - "internal/base", - "internal/datastore", - "internal/log", - "internal/modules", - "internal/remote_api", - "internal/urlfetch", - "urlfetch" - ] + packages = [".","cloudsql","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"] revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" version = "v1.0.0" @@ -563,32 +425,7 @@ [[projects]] name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "grpclb/grpc_lb_v1/messages", - "grpclog", - "health", - "health/grpc_health_v1", - "internal", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - "transport" - ] + packages = [".","balancer","balancer/base","balancer/roundrobin","codes","connectivity","credentials","encoding","grpclb/grpc_lb_v1/messages","grpclog","health","health/grpc_health_v1","internal","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/passthrough","stats","status","tap","transport"] revision = "6b51017f791ae1cfbec89c52efdf444b13b550ef" version = "v1.9.2" @@ -610,12 +447,6 @@ revision = "567b2bfa514e796916c4747494d6ff5132a1dfce" version = "v1" -[[projects]] - branch = "v2" - name = "gopkg.in/gomail.v2" - packages = ["."] - revision = "81ebce5c23dfd25c6c67194b37d3dd3f338c98b1" - [[projects]] name = "gopkg.in/ini.v1" packages = ["."] @@ -628,6 +459,12 @@ revision = "75f2e9b42e99652f0d82b28ccb73648f44615faa" version = "v1.2.4" +[[projects]] + branch = "v2" + name = "gopkg.in/mail.v2" + packages = ["."] + revision = "5bc5c8bb07bd8d2803831fbaf8cbd630fcde2c68" + [[projects]] name = "gopkg.in/redis.v2" packages = ["."] @@ -643,6 +480,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "8a9e651fb8ea49dfd3c6ddc99bd3242b39e453ea9edd11321da79bd2c865e9d1" + inputs-digest = "ad3c71fd3244369c313978e9e7464c7116faee764386439a17de0707a08103aa" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 350da50fc4b..a9f79c402df 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -172,7 +172,7 @@ ignored = [ name = "golang.org/x/sync" [[constraint]] - name = "gopkg.in/gomail.v2" + name = "gopkg.in/mail.v2" branch = "v2" [[constraint]] diff --git a/pkg/services/notifications/mailer.go b/pkg/services/notifications/mailer.go index 7fbf39ee41d..05c2e53c748 100644 --- a/pkg/services/notifications/mailer.go +++ b/pkg/services/notifications/mailer.go @@ -17,7 +17,7 @@ import ( "github.com/grafana/grafana/pkg/log" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" - "gopkg.in/gomail.v2" + gomail "gopkg.in/mail.v2" ) var mailQueue chan *Message 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/gomail.v2/auth.go b/vendor/gopkg.in/mail.v2/auth.go similarity index 98% rename from vendor/gopkg.in/gomail.v2/auth.go rename to vendor/gopkg.in/mail.v2/auth.go index d28b83ab7d5..b8c0dde7f23 100644 --- a/vendor/gopkg.in/gomail.v2/auth.go +++ b/vendor/gopkg.in/mail.v2/auth.go @@ -1,4 +1,4 @@ -package gomail +package mail import ( "bytes" 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 a8f5091f541..d65bf359160 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 00000000000..770da8c3854 --- /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 91% rename from vendor/gopkg.in/gomail.v2/message.go rename to vendor/gopkg.in/mail.v2/message.go index 4bffb1e7ff6..d1f66159c98 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" @@ -18,6 +18,7 @@ type Message struct { encoding Encoding hEncoder mimeEncoder buf bytes.Buffer + boundary string } type header map[string][]string @@ -97,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) @@ -183,9 +189,15 @@ func (m *Message) GetHeader(field string) []string { } // SetBody sets the body of the message. It replaces any content previously set -// by SetBody, AddAlternative or AddAlternativeWriter. +// by SetBody, SetBodyWriter, AddAlternative or AddAlternativeWriter. func (m *Message) SetBody(contentType, body string, settings ...PartSetting) { - m.parts = []*part{m.newPart(contentType, newCopier(body), settings)} + 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. @@ -226,8 +238,8 @@ func (m *Message) newPart(contentType string, f func(io.Writer) error, settings } // A PartSetting can be used as an argument in Message.SetBody, -// Message.AddAlternative or Message.AddAlternativeWriter to configure the part -// added to a message. +// 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 diff --git a/vendor/gopkg.in/gomail.v2/mime.go b/vendor/gopkg.in/mail.v2/mime.go similarity index 95% rename from vendor/gopkg.in/gomail.v2/mime.go rename to vendor/gopkg.in/mail.v2/mime.go index 194d4a769a8..d95ea2eb240 100644 --- a/vendor/gopkg.in/gomail.v2/mime.go +++ b/vendor/gopkg.in/mail.v2/mime.go @@ -1,6 +1,6 @@ // +build go1.5 -package gomail +package mail import ( "mime" diff --git a/vendor/gopkg.in/gomail.v2/mime_go14.go b/vendor/gopkg.in/mail.v2/mime_go14.go similarity index 96% rename from vendor/gopkg.in/gomail.v2/mime_go14.go rename to vendor/gopkg.in/mail.v2/mime_go14.go index 3dc26aa2ae0..bdb605dcca3 100644 --- a/vendor/gopkg.in/gomail.v2/mime_go14.go +++ b/vendor/gopkg.in/mail.v2/mime_go14.go @@ -1,6 +1,6 @@ // +build !go1.5 -package gomail +package mail import "gopkg.in/alexcesaro/quotedprintable.v3" diff --git a/vendor/gopkg.in/gomail.v2/send.go b/vendor/gopkg.in/mail.v2/send.go similarity index 94% rename from vendor/gopkg.in/gomail.v2/send.go rename to vendor/gopkg.in/mail.v2/send.go index 9115ebe7267..62e67f0b81a 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. @@ -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,7 +108,7 @@ func addAddress(list []string, addr string) []string { } func parseAddress(field string) (string, error) { - addr, err := mail.ParseAddress(field) + addr, err := stdmail.ParseAddress(field) if err != nil { return "", fmt.Errorf("gomail: invalid address %q: %v", field, err) } diff --git a/vendor/gopkg.in/gomail.v2/smtp.go b/vendor/gopkg.in/mail.v2/smtp.go similarity index 55% rename from vendor/gopkg.in/gomail.v2/smtp.go rename to vendor/gopkg.in/mail.v2/smtp.go index 2aa49c8b612..547e04d16b0 100644 --- a/vendor/gopkg.in/gomail.v2/smtp.go +++ b/vendor/gopkg.in/mail.v2/smtp.go @@ -1,4 +1,4 @@ -package gomail +package mail import ( "crypto/tls" @@ -27,23 +27,39 @@ type Dialer struct { // 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 + // 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, + Host: host, + Port: port, + Username: username, + Password: password, + SSL: port == 465, + Timeout: 10 * time.Second, + RetryFailure: true, } } @@ -55,10 +71,15 @@ 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), 10*time.Second) + conn, err := NetDialTimeout("tcp", addr(d.Host, d.Port), d.Timeout) if err != nil { return nil, err } @@ -72,14 +93,25 @@ func (d *Dialer) Dial() (SendCloser, error) { 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 { - if ok, _ := c.Extension("STARTTLS"); ok { + 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 @@ -111,7 +143,7 @@ func (d *Dialer) Dial() (SendCloser, error) { } } - return &smtpSender{c, d}, nil + return &smtpSender{c, conn, d}, nil } func (d *Dialer) tlsConfig() *tls.Config { @@ -121,6 +153,47 @@ func (d *Dialer) tlsConfig() *tls.Config { 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) } @@ -139,12 +212,29 @@ func (d *Dialer) DialAndSend(m ...*Message) error { type smtpSender struct { smtpClient - d *Dialer + 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 err == io.EOF { + if c.retryError(err) { // This is probably due to a timeout, so reconnect and try again. sc, derr := c.d.Dial() if derr == nil { @@ -154,6 +244,7 @@ func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error { } } } + return err } @@ -182,9 +273,8 @@ func (c *smtpSender) Close() error { // Stubbed out for tests. var ( - netDialTimeout = net.DialTimeout - tlsClient = tls.Client - smtpNewClient = func(conn net.Conn, host string) (smtpClient, error) { + tlsClient = tls.Client + smtpNewClient = func(conn net.Conn, host string) (smtpClient, error) { return smtp.NewClient(conn, host) } ) diff --git a/vendor/gopkg.in/gomail.v2/writeto.go b/vendor/gopkg.in/mail.v2/writeto.go similarity index 96% rename from vendor/gopkg.in/gomail.v2/writeto.go rename to vendor/gopkg.in/mail.v2/writeto.go index 9fb6b86e80b..9086e13c37d 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" @@ -28,15 +28,15 @@ 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.writePart(part, m.charset) @@ -77,8 +77,11 @@ type messageWriter struct { err error } -func (w *messageWriter) openMultipart(mimeType string) { +func (w *messageWriter) openMultipart(mimeType, boundary string) { mw := multipart.NewWriter(w) + if boundary != "" { + mw.SetBoundary(boundary) + } contentType := "multipart/" + mimeType + ";\r\n boundary=" + mw.Boundary() w.writers[w.depth] = mw