You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
4.4 KiB

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