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