smtp.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321.
  5. // It also implements the following extensions:
  6. // 8BITMIME RFC 1652
  7. // AUTH RFC 2554
  8. // STARTTLS RFC 3207
  9. // Additional extensions may be handled by clients.
  10. package smtp
  11. import (
  12. "crypto/tls"
  13. "encoding/base64"
  14. "errors"
  15. "io"
  16. "net"
  17. "net/textproto"
  18. "strings"
  19. )
  20. // A Client represents a client connection to an SMTP server.
  21. type Client struct {
  22. // Text is the textproto.Conn used by the Client. It is exported to allow for
  23. // clients to add extensions.
  24. Text *textproto.Conn
  25. // keep a reference to the connection so it can be used to create a TLS
  26. // connection later
  27. conn net.Conn
  28. // whether the Client is using TLS
  29. tls bool
  30. serverName string
  31. // map of supported extensions
  32. ext map[string]string
  33. // supported auth mechanisms
  34. auth []string
  35. localName string // the name to use in HELO/EHLO
  36. didHello bool // whether we've said HELO/EHLO
  37. helloError error // the error from the hello
  38. }
  39. // Dial returns a new Client connected to an SMTP server at addr.
  40. // The addr must include a port number.
  41. func Dial(addr string) (*Client, error) {
  42. conn, err := net.Dial("tcp", addr)
  43. if err != nil {
  44. return nil, err
  45. }
  46. host, _, _ := net.SplitHostPort(addr)
  47. return NewClient(conn, host)
  48. }
  49. // NewClient returns a new Client using an existing connection and host as a
  50. // server name to be used when authenticating.
  51. func NewClient(conn net.Conn, host string) (*Client, error) {
  52. text := textproto.NewConn(conn)
  53. _, _, err := text.ReadResponse(220)
  54. if err != nil {
  55. text.Close()
  56. return nil, err
  57. }
  58. c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
  59. return c, nil
  60. }
  61. // Close closes the connection.
  62. func (c *Client) Close() error {
  63. return c.Text.Close()
  64. }
  65. // hello runs a hello exchange if needed.
  66. func (c *Client) hello() error {
  67. if !c.didHello {
  68. c.didHello = true
  69. err := c.ehlo()
  70. if err != nil {
  71. c.helloError = c.helo()
  72. }
  73. }
  74. return c.helloError
  75. }
  76. // Hello sends a HELO or EHLO to the server as the given host name.
  77. // Calling this method is only necessary if the client needs control
  78. // over the host name used. The client will introduce itself as "localhost"
  79. // automatically otherwise. If Hello is called, it must be called before
  80. // any of the other methods.
  81. func (c *Client) Hello(localName string) error {
  82. if c.didHello {
  83. return errors.New("smtp: Hello called after other methods")
  84. }
  85. c.localName = localName
  86. return c.hello()
  87. }
  88. // cmd is a convenience function that sends a command and returns the response
  89. func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) {
  90. id, err := c.Text.Cmd(format, args...)
  91. if err != nil {
  92. return 0, "", err
  93. }
  94. c.Text.StartResponse(id)
  95. defer c.Text.EndResponse(id)
  96. code, msg, err := c.Text.ReadResponse(expectCode)
  97. return code, msg, err
  98. }
  99. // helo sends the HELO greeting to the server. It should be used only when the
  100. // server does not support ehlo.
  101. func (c *Client) helo() error {
  102. c.ext = nil
  103. _, _, err := c.cmd(250, "HELO %s", c.localName)
  104. return err
  105. }
  106. // ehlo sends the EHLO (extended hello) greeting to the server. It
  107. // should be the preferred greeting for servers that support it.
  108. func (c *Client) ehlo() error {
  109. _, msg, err := c.cmd(250, "EHLO %s", c.localName)
  110. if err != nil {
  111. return err
  112. }
  113. ext := make(map[string]string)
  114. extList := strings.Split(msg, "\n")
  115. if len(extList) > 1 {
  116. extList = extList[1:]
  117. for _, line := range extList {
  118. args := strings.SplitN(line, " ", 2)
  119. if len(args) > 1 {
  120. ext[args[0]] = args[1]
  121. } else {
  122. ext[args[0]] = ""
  123. }
  124. }
  125. }
  126. if mechs, ok := ext["AUTH"]; ok {
  127. c.auth = strings.Split(mechs, " ")
  128. }
  129. c.ext = ext
  130. return err
  131. }
  132. // StartTLS sends the STARTTLS command and encrypts all further communication.
  133. // Only servers that advertise the STARTTLS extension support this function.
  134. func (c *Client) StartTLS(config *tls.Config) error {
  135. if err := c.hello(); err != nil {
  136. return err
  137. }
  138. _, _, err := c.cmd(220, "STARTTLS")
  139. if err != nil {
  140. return err
  141. }
  142. c.conn = tls.Client(c.conn, config)
  143. c.Text = textproto.NewConn(c.conn)
  144. c.tls = true
  145. return c.ehlo()
  146. }
  147. // Verify checks the validity of an email address on the server.
  148. // If Verify returns nil, the address is valid. A non-nil return
  149. // does not necessarily indicate an invalid address. Many servers
  150. // will not verify addresses for security reasons.
  151. func (c *Client) Verify(addr string) error {
  152. if err := c.hello(); err != nil {
  153. return err
  154. }
  155. _, _, err := c.cmd(250, "VRFY %s", addr)
  156. return err
  157. }
  158. // Auth authenticates a client using the provided authentication mechanism.
  159. // A failed authentication closes the connection.
  160. // Only servers that advertise the AUTH extension support this function.
  161. func (c *Client) Auth(a Auth) error {
  162. if err := c.hello(); err != nil {
  163. return err
  164. }
  165. encoding := base64.StdEncoding
  166. mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth})
  167. if err != nil {
  168. c.Quit()
  169. return err
  170. }
  171. resp64 := make([]byte, encoding.EncodedLen(len(resp)))
  172. encoding.Encode(resp64, resp)
  173. code, msg64, err := c.cmd(0, "AUTH %s %s", mech, resp64)
  174. for err == nil {
  175. var msg []byte
  176. switch code {
  177. case 334:
  178. msg, err = encoding.DecodeString(msg64)
  179. case 235:
  180. // the last message isn't base64 because it isn't a challenge
  181. msg = []byte(msg64)
  182. default:
  183. err = &textproto.Error{Code: code, Msg: msg64}
  184. }
  185. if err == nil {
  186. resp, err = a.Next(msg, code == 334)
  187. }
  188. if err != nil {
  189. // abort the AUTH
  190. c.cmd(501, "*")
  191. c.Quit()
  192. break
  193. }
  194. if resp == nil {
  195. break
  196. }
  197. resp64 = make([]byte, encoding.EncodedLen(len(resp)))
  198. encoding.Encode(resp64, resp)
  199. code, msg64, err = c.cmd(0, string(resp64))
  200. }
  201. return err
  202. }
  203. // Mail issues a MAIL command to the server using the provided email address.
  204. // If the server supports the 8BITMIME extension, Mail adds the BODY=8BITMIME
  205. // parameter.
  206. // This initiates a mail transaction and is followed by one or more Rcpt calls.
  207. func (c *Client) Mail(from string) error {
  208. if err := c.hello(); err != nil {
  209. return err
  210. }
  211. cmdStr := "MAIL FROM:<%s>"
  212. if c.ext != nil {
  213. if _, ok := c.ext["8BITMIME"]; ok {
  214. cmdStr += " BODY=8BITMIME"
  215. }
  216. }
  217. _, _, err := c.cmd(250, cmdStr, from)
  218. return err
  219. }
  220. // Rcpt issues a RCPT command to the server using the provided email address.
  221. // A call to Rcpt must be preceded by a call to Mail and may be followed by
  222. // a Data call or another Rcpt call.
  223. func (c *Client) Rcpt(to string) error {
  224. _, _, err := c.cmd(25, "RCPT TO:<%s>", to)
  225. return err
  226. }
  227. type dataCloser struct {
  228. c *Client
  229. io.WriteCloser
  230. }
  231. func (d *dataCloser) Close() error {
  232. d.WriteCloser.Close()
  233. _, _, err := d.c.Text.ReadResponse(250)
  234. return err
  235. }
  236. // Data issues a DATA command to the server and returns a writer that
  237. // can be used to write the data. The caller should close the writer
  238. // before calling any more methods on c.
  239. // A call to Data must be preceded by one or more calls to Rcpt.
  240. func (c *Client) Data() (io.WriteCloser, error) {
  241. _, _, err := c.cmd(354, "DATA")
  242. if err != nil {
  243. return nil, err
  244. }
  245. return &dataCloser{c, c.Text.DotWriter()}, nil
  246. }
  247. var testHookStartTLS func(*tls.Config) // nil, except for tests
  248. // SendMail connects to the server at addr, switches to TLS if
  249. // possible, authenticates with the optional mechanism a if possible,
  250. // and then sends an email from address from, to addresses to, with
  251. // message msg.
  252. func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
  253. c, err := Dial(addr)
  254. if err != nil {
  255. return err
  256. }
  257. defer c.Close()
  258. if err = c.hello(); err != nil {
  259. return err
  260. }
  261. if ok, _ := c.Extension("STARTTLS"); ok {
  262. config := &tls.Config{ServerName: c.serverName}
  263. if testHookStartTLS != nil {
  264. testHookStartTLS(config)
  265. }
  266. if err = c.StartTLS(config); err != nil {
  267. return err
  268. }
  269. }
  270. if a != nil && c.ext != nil {
  271. if _, ok := c.ext["AUTH"]; ok {
  272. if err = c.Auth(a); err != nil {
  273. return err
  274. }
  275. }
  276. }
  277. if err = c.Mail(from); err != nil {
  278. return err
  279. }
  280. for _, addr := range to {
  281. if err = c.Rcpt(addr); err != nil {
  282. return err
  283. }
  284. }
  285. w, err := c.Data()
  286. if err != nil {
  287. return err
  288. }
  289. _, err = w.Write(msg)
  290. if err != nil {
  291. return err
  292. }
  293. err = w.Close()
  294. if err != nil {
  295. return err
  296. }
  297. return c.Quit()
  298. }
  299. // Extension reports whether an extension is support by the server.
  300. // The extension name is case-insensitive. If the extension is supported,
  301. // Extension also returns a string that contains any parameters the
  302. // server specifies for the extension.
  303. func (c *Client) Extension(ext string) (bool, string) {
  304. if err := c.hello(); err != nil {
  305. return false, ""
  306. }
  307. if c.ext == nil {
  308. return false, ""
  309. }
  310. ext = strings.ToUpper(ext)
  311. param, ok := c.ext[ext]
  312. return ok, param
  313. }
  314. // Reset sends the RSET command to the server, aborting the current mail
  315. // transaction.
  316. func (c *Client) Reset() error {
  317. if err := c.hello(); err != nil {
  318. return err
  319. }
  320. _, _, err := c.cmd(250, "RSET")
  321. return err
  322. }
  323. // Quit sends the QUIT command and closes the connection to the server.
  324. func (c *Client) Quit() error {
  325. if err := c.hello(); err != nil {
  326. return err
  327. }
  328. _, _, err := c.cmd(221, "QUIT")
  329. if err != nil {
  330. return err
  331. }
  332. return c.Text.Close()
  333. }