Explorar o código

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
bergquist %!s(int64=7) %!d(string=hai) anos
pai
achega
f2755982c3

+ 27 - 190
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

+ 1 - 1
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]]

+ 1 - 1
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

+ 0 - 0
vendor/gopkg.in/gomail.v2/LICENSE → vendor/gopkg.in/mail.v2/LICENSE


+ 1 - 1
vendor/gopkg.in/gomail.v2/auth.go → vendor/gopkg.in/mail.v2/auth.go

@@ -1,4 +1,4 @@
-package gomail
+package mail
 
 import (
 	"bytes"

+ 3 - 2
vendor/gopkg.in/gomail.v2/doc.go → 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

+ 16 - 0
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)
+}

+ 17 - 5
vendor/gopkg.in/gomail.v2/message.go → 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

+ 1 - 1
vendor/gopkg.in/gomail.v2/mime.go → vendor/gopkg.in/mail.v2/mime.go

@@ -1,6 +1,6 @@
 // +build go1.5
 
-package gomail
+package mail
 
 import (
 	"mime"

+ 1 - 1
vendor/gopkg.in/gomail.v2/mime_go14.go → 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"
 

+ 4 - 4
vendor/gopkg.in/gomail.v2/send.go → 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)
 	}

+ 106 - 16
vendor/gopkg.in/gomail.v2/smtp.go → 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)
 	}
 )

+ 8 - 5
vendor/gopkg.in/gomail.v2/writeto.go → 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