1package gomail 2 3import ( 4 "crypto/tls" 5 "fmt" 6 "io" 7 "net" 8 "net/smtp" 9 "strings" 10 "time" 11) 12 13// A Dialer is a dialer to an SMTP server. 14type Dialer struct { 15 // Host represents the host of the SMTP server. 16 Host string 17 // Port represents the port of the SMTP server. 18 Port int 19 // Username is the username to use to authenticate to the SMTP server. 20 Username string 21 // Password is the password to use to authenticate to the SMTP server. 22 Password string 23 // Auth represents the authentication mechanism used to authenticate to the 24 // SMTP server. 25 Auth smtp.Auth 26 // SSL defines whether an SSL connection is used. It should be false in 27 // most cases since the authentication mechanism should use the STARTTLS 28 // extension instead. 29 SSL bool 30 // TSLConfig represents the TLS configuration used for the TLS (when the 31 // STARTTLS extension is used) or SSL connection. 32 TLSConfig *tls.Config 33 // LocalName is the hostname sent to the SMTP server with the HELO command. 34 // By default, "localhost" is sent. 35 LocalName string 36} 37 38// NewDialer returns a new SMTP Dialer. The given parameters are used to connect 39// to the SMTP server. 40func NewDialer(host string, port int, username, password string) *Dialer { 41 return &Dialer{ 42 Host: host, 43 Port: port, 44 Username: username, 45 Password: password, 46 SSL: port == 465, 47 } 48} 49 50// NewPlainDialer returns a new SMTP Dialer. The given parameters are used to 51// connect to the SMTP server. 52// 53// Deprecated: Use NewDialer instead. 54func NewPlainDialer(host string, port int, username, password string) *Dialer { 55 return NewDialer(host, port, username, password) 56} 57 58// Dial dials and authenticates to an SMTP server. The returned SendCloser 59// should be closed when done using it. 60func (d *Dialer) Dial() (SendCloser, error) { 61 conn, err := netDialTimeout("tcp", addr(d.Host, d.Port), 10*time.Second) 62 if err != nil { 63 return nil, err 64 } 65 66 if d.SSL { 67 conn = tlsClient(conn, d.tlsConfig()) 68 } 69 70 c, err := smtpNewClient(conn, d.Host) 71 if err != nil { 72 return nil, err 73 } 74 75 if d.LocalName != "" { 76 if err := c.Hello(d.LocalName); err != nil { 77 return nil, err 78 } 79 } 80 81 if !d.SSL { 82 if ok, _ := c.Extension("STARTTLS"); ok { 83 if err := c.StartTLS(d.tlsConfig()); err != nil { 84 c.Close() 85 return nil, err 86 } 87 } 88 } 89 90 if d.Auth == nil && d.Username != "" { 91 if ok, auths := c.Extension("AUTH"); ok { 92 if strings.Contains(auths, "CRAM-MD5") { 93 d.Auth = smtp.CRAMMD5Auth(d.Username, d.Password) 94 } else if strings.Contains(auths, "LOGIN") && 95 !strings.Contains(auths, "PLAIN") { 96 d.Auth = &loginAuth{ 97 username: d.Username, 98 password: d.Password, 99 host: d.Host, 100 } 101 } else { 102 d.Auth = smtp.PlainAuth("", d.Username, d.Password, d.Host) 103 } 104 } 105 } 106 107 if d.Auth != nil { 108 if err = c.Auth(d.Auth); err != nil { 109 c.Close() 110 return nil, err 111 } 112 } 113 114 return &smtpSender{c, d}, nil 115} 116 117func (d *Dialer) tlsConfig() *tls.Config { 118 if d.TLSConfig == nil { 119 return &tls.Config{ServerName: d.Host} 120 } 121 return d.TLSConfig 122} 123 124func addr(host string, port int) string { 125 return fmt.Sprintf("%s:%d", host, port) 126} 127 128// DialAndSend opens a connection to the SMTP server, sends the given emails and 129// closes the connection. 130func (d *Dialer) DialAndSend(m ...*Message) error { 131 s, err := d.Dial() 132 if err != nil { 133 return err 134 } 135 defer s.Close() 136 137 return Send(s, m...) 138} 139 140type smtpSender struct { 141 smtpClient 142 d *Dialer 143} 144 145func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error { 146 if err := c.Mail(from); err != nil { 147 if err == io.EOF { 148 // This is probably due to a timeout, so reconnect and try again. 149 sc, derr := c.d.Dial() 150 if derr == nil { 151 if s, ok := sc.(*smtpSender); ok { 152 *c = *s 153 return c.Send(from, to, msg) 154 } 155 } 156 } 157 return err 158 } 159 160 for _, addr := range to { 161 if err := c.Rcpt(addr); err != nil { 162 return err 163 } 164 } 165 166 w, err := c.Data() 167 if err != nil { 168 return err 169 } 170 171 if _, err = msg.WriteTo(w); err != nil { 172 w.Close() 173 return err 174 } 175 176 return w.Close() 177} 178 179func (c *smtpSender) Close() error { 180 return c.Quit() 181} 182 183// Stubbed out for tests. 184var ( 185 netDialTimeout = net.DialTimeout 186 tlsClient = tls.Client 187 smtpNewClient = func(conn net.Conn, host string) (smtpClient, error) { 188 return smtp.NewClient(conn, host) 189 } 190) 191 192type smtpClient interface { 193 Hello(string) error 194 Extension(string) (bool, string) 195 StartTLS(*tls.Config) error 196 Auth(smtp.Auth) error 197 Mail(string) error 198 Rcpt(string) error 199 Data() (io.WriteCloser, error) 200 Quit() error 201 Close() error 202} 203