smtp.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package mail
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "io"
  6. "net"
  7. "net/smtp"
  8. "strings"
  9. "time"
  10. )
  11. // A Dialer is a dialer to an SMTP server.
  12. type Dialer struct {
  13. // Host represents the host of the SMTP server.
  14. Host string
  15. // Port represents the port of the SMTP server.
  16. Port int
  17. // Username is the username to use to authenticate to the SMTP server.
  18. Username string
  19. // Password is the password to use to authenticate to the SMTP server.
  20. Password string
  21. // Auth represents the authentication mechanism used to authenticate to the
  22. // SMTP server.
  23. Auth smtp.Auth
  24. // SSL defines whether an SSL connection is used. It should be false in
  25. // most cases since the authentication mechanism should use the STARTTLS
  26. // extension instead.
  27. SSL bool
  28. // TLSConfig represents the TLS configuration used for the TLS (when the
  29. // STARTTLS extension is used) or SSL connection.
  30. TLSConfig *tls.Config
  31. // StartTLSPolicy represents the TLS security level required to
  32. // communicate with the SMTP server.
  33. //
  34. // This defaults to OpportunisticStartTLS for backwards compatibility,
  35. // but we recommend MandatoryStartTLS for all modern SMTP servers.
  36. //
  37. // This option has no effect if SSL is set to true.
  38. StartTLSPolicy StartTLSPolicy
  39. // LocalName is the hostname sent to the SMTP server with the HELO command.
  40. // By default, "localhost" is sent.
  41. LocalName string
  42. // Timeout to use for read/write operations. Defaults to 10 seconds, can
  43. // be set to 0 to disable timeouts.
  44. Timeout time.Duration
  45. // Whether we should retry mailing if the connection returned an error,
  46. // defaults to true.
  47. RetryFailure bool
  48. }
  49. // NewDialer returns a new SMTP Dialer. The given parameters are used to connect
  50. // to the SMTP server.
  51. func NewDialer(host string, port int, username, password string) *Dialer {
  52. return &Dialer{
  53. Host: host,
  54. Port: port,
  55. Username: username,
  56. Password: password,
  57. SSL: port == 465,
  58. Timeout: 10 * time.Second,
  59. RetryFailure: true,
  60. }
  61. }
  62. // NewPlainDialer returns a new SMTP Dialer. The given parameters are used to
  63. // connect to the SMTP server.
  64. //
  65. // Deprecated: Use NewDialer instead.
  66. func NewPlainDialer(host string, port int, username, password string) *Dialer {
  67. return NewDialer(host, port, username, password)
  68. }
  69. // NetDialTimeout specifies the DialTimeout function to establish a connection
  70. // to the SMTP server. This can be used to override dialing in the case that a
  71. // proxy or other special behavior is needed.
  72. var NetDialTimeout = net.DialTimeout
  73. // Dial dials and authenticates to an SMTP server. The returned SendCloser
  74. // should be closed when done using it.
  75. func (d *Dialer) Dial() (SendCloser, error) {
  76. conn, err := NetDialTimeout("tcp", addr(d.Host, d.Port), d.Timeout)
  77. if err != nil {
  78. return nil, err
  79. }
  80. if d.SSL {
  81. conn = tlsClient(conn, d.tlsConfig())
  82. }
  83. c, err := smtpNewClient(conn, d.Host)
  84. if err != nil {
  85. return nil, err
  86. }
  87. if d.Timeout > 0 {
  88. conn.SetDeadline(time.Now().Add(d.Timeout))
  89. }
  90. if d.LocalName != "" {
  91. if err := c.Hello(d.LocalName); err != nil {
  92. return nil, err
  93. }
  94. }
  95. if !d.SSL && d.StartTLSPolicy != NoStartTLS {
  96. ok, _ := c.Extension("STARTTLS")
  97. if !ok && d.StartTLSPolicy == MandatoryStartTLS {
  98. err := StartTLSUnsupportedError{
  99. Policy: d.StartTLSPolicy}
  100. return nil, err
  101. }
  102. if ok {
  103. if err := c.StartTLS(d.tlsConfig()); err != nil {
  104. c.Close()
  105. return nil, err
  106. }
  107. }
  108. }
  109. if d.Auth == nil && d.Username != "" {
  110. if ok, auths := c.Extension("AUTH"); ok {
  111. if strings.Contains(auths, "CRAM-MD5") {
  112. d.Auth = smtp.CRAMMD5Auth(d.Username, d.Password)
  113. } else if strings.Contains(auths, "LOGIN") &&
  114. !strings.Contains(auths, "PLAIN") {
  115. d.Auth = &loginAuth{
  116. username: d.Username,
  117. password: d.Password,
  118. host: d.Host,
  119. }
  120. } else {
  121. d.Auth = smtp.PlainAuth("", d.Username, d.Password, d.Host)
  122. }
  123. }
  124. }
  125. if d.Auth != nil {
  126. if err = c.Auth(d.Auth); err != nil {
  127. c.Close()
  128. return nil, err
  129. }
  130. }
  131. return &smtpSender{c, conn, d}, nil
  132. }
  133. func (d *Dialer) tlsConfig() *tls.Config {
  134. if d.TLSConfig == nil {
  135. return &tls.Config{ServerName: d.Host}
  136. }
  137. return d.TLSConfig
  138. }
  139. // StartTLSPolicy constants are valid values for Dialer.StartTLSPolicy.
  140. type StartTLSPolicy int
  141. const (
  142. // OpportunisticStartTLS means that SMTP transactions are encrypted if
  143. // STARTTLS is supported by the SMTP server. Otherwise, messages are
  144. // sent in the clear. This is the default setting.
  145. OpportunisticStartTLS StartTLSPolicy = iota
  146. // MandatoryStartTLS means that SMTP transactions must be encrypted.
  147. // SMTP transactions are aborted unless STARTTLS is supported by the
  148. // SMTP server.
  149. MandatoryStartTLS
  150. // NoStartTLS means encryption is disabled and messages are sent in the
  151. // clear.
  152. NoStartTLS = -1
  153. )
  154. func (policy *StartTLSPolicy) String() string {
  155. switch *policy {
  156. case OpportunisticStartTLS:
  157. return "OpportunisticStartTLS"
  158. case MandatoryStartTLS:
  159. return "MandatoryStartTLS"
  160. case NoStartTLS:
  161. return "NoStartTLS"
  162. default:
  163. return fmt.Sprintf("StartTLSPolicy:%v", *policy)
  164. }
  165. }
  166. // StartTLSUnsupportedError is returned by Dial when connecting to an SMTP
  167. // server that does not support STARTTLS.
  168. type StartTLSUnsupportedError struct {
  169. Policy StartTLSPolicy
  170. }
  171. func (e StartTLSUnsupportedError) Error() string {
  172. return "gomail: " + e.Policy.String() + " required, but " +
  173. "SMTP server does not support STARTTLS"
  174. }
  175. func addr(host string, port int) string {
  176. return fmt.Sprintf("%s:%d", host, port)
  177. }
  178. // DialAndSend opens a connection to the SMTP server, sends the given emails and
  179. // closes the connection.
  180. func (d *Dialer) DialAndSend(m ...*Message) error {
  181. s, err := d.Dial()
  182. if err != nil {
  183. return err
  184. }
  185. defer s.Close()
  186. return Send(s, m...)
  187. }
  188. type smtpSender struct {
  189. smtpClient
  190. conn net.Conn
  191. d *Dialer
  192. }
  193. func (c *smtpSender) retryError(err error) bool {
  194. if !c.d.RetryFailure {
  195. return false
  196. }
  197. if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
  198. return true
  199. }
  200. return err == io.EOF
  201. }
  202. func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
  203. if c.d.Timeout > 0 {
  204. c.conn.SetDeadline(time.Now().Add(c.d.Timeout))
  205. }
  206. if err := c.Mail(from); err != nil {
  207. if c.retryError(err) {
  208. // This is probably due to a timeout, so reconnect and try again.
  209. sc, derr := c.d.Dial()
  210. if derr == nil {
  211. if s, ok := sc.(*smtpSender); ok {
  212. *c = *s
  213. return c.Send(from, to, msg)
  214. }
  215. }
  216. }
  217. return err
  218. }
  219. for _, addr := range to {
  220. if err := c.Rcpt(addr); err != nil {
  221. return err
  222. }
  223. }
  224. w, err := c.Data()
  225. if err != nil {
  226. return err
  227. }
  228. if _, err = msg.WriteTo(w); err != nil {
  229. w.Close()
  230. return err
  231. }
  232. return w.Close()
  233. }
  234. func (c *smtpSender) Close() error {
  235. return c.Quit()
  236. }
  237. // Stubbed out for tests.
  238. var (
  239. tlsClient = tls.Client
  240. smtpNewClient = func(conn net.Conn, host string) (smtpClient, error) {
  241. return smtp.NewClient(conn, host)
  242. }
  243. )
  244. type smtpClient interface {
  245. Hello(string) error
  246. Extension(string) (bool, string)
  247. StartTLS(*tls.Config) error
  248. Auth(smtp.Auth) error
  249. Mail(string) error
  250. Rcpt(string) error
  251. Data() (io.WriteCloser, error)
  252. Quit() error
  253. Close() error
  254. }