diff options
Diffstat (limited to 'src/pkg/net/smtp/smtp.go')
-rw-r--r-- | src/pkg/net/smtp/smtp.go | 357 |
1 files changed, 0 insertions, 357 deletions
diff --git a/src/pkg/net/smtp/smtp.go b/src/pkg/net/smtp/smtp.go deleted file mode 100644 index 87dea442c..000000000 --- a/src/pkg/net/smtp/smtp.go +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321. -// It also implements the following extensions: -// 8BITMIME RFC 1652 -// AUTH RFC 2554 -// STARTTLS RFC 3207 -// Additional extensions may be handled by clients. -package smtp - -import ( - "crypto/tls" - "encoding/base64" - "errors" - "io" - "net" - "net/textproto" - "strings" -) - -// A Client represents a client connection to an SMTP server. -type Client struct { - // Text is the textproto.Conn used by the Client. It is exported to allow for - // clients to add extensions. - Text *textproto.Conn - // keep a reference to the connection so it can be used to create a TLS - // connection later - conn net.Conn - // whether the Client is using TLS - tls bool - serverName string - // map of supported extensions - ext map[string]string - // supported auth mechanisms - auth []string - localName string // the name to use in HELO/EHLO - didHello bool // whether we've said HELO/EHLO - helloError error // the error from the hello -} - -// Dial returns a new Client connected to an SMTP server at addr. -// The addr must include a port number. -func Dial(addr string) (*Client, error) { - conn, err := net.Dial("tcp", addr) - if err != nil { - return nil, err - } - host, _, _ := net.SplitHostPort(addr) - return NewClient(conn, host) -} - -// NewClient returns a new Client using an existing connection and host as a -// server name to be used when authenticating. -func NewClient(conn net.Conn, host string) (*Client, error) { - text := textproto.NewConn(conn) - _, _, err := text.ReadResponse(220) - if err != nil { - text.Close() - return nil, err - } - c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"} - return c, nil -} - -// Close closes the connection. -func (c *Client) Close() error { - return c.Text.Close() -} - -// hello runs a hello exchange if needed. -func (c *Client) hello() error { - if !c.didHello { - c.didHello = true - err := c.ehlo() - if err != nil { - c.helloError = c.helo() - } - } - return c.helloError -} - -// Hello sends a HELO or EHLO to the server as the given host name. -// Calling this method is only necessary if the client needs control -// over the host name used. The client will introduce itself as "localhost" -// automatically otherwise. If Hello is called, it must be called before -// any of the other methods. -func (c *Client) Hello(localName string) error { - if c.didHello { - return errors.New("smtp: Hello called after other methods") - } - c.localName = localName - return c.hello() -} - -// cmd is a convenience function that sends a command and returns the response -func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) { - id, err := c.Text.Cmd(format, args...) - if err != nil { - return 0, "", err - } - c.Text.StartResponse(id) - defer c.Text.EndResponse(id) - code, msg, err := c.Text.ReadResponse(expectCode) - return code, msg, err -} - -// helo sends the HELO greeting to the server. It should be used only when the -// server does not support ehlo. -func (c *Client) helo() error { - c.ext = nil - _, _, err := c.cmd(250, "HELO %s", c.localName) - return err -} - -// ehlo sends the EHLO (extended hello) greeting to the server. It -// should be the preferred greeting for servers that support it. -func (c *Client) ehlo() error { - _, msg, err := c.cmd(250, "EHLO %s", c.localName) - if err != nil { - return err - } - ext := make(map[string]string) - extList := strings.Split(msg, "\n") - if len(extList) > 1 { - extList = extList[1:] - for _, line := range extList { - args := strings.SplitN(line, " ", 2) - if len(args) > 1 { - ext[args[0]] = args[1] - } else { - ext[args[0]] = "" - } - } - } - if mechs, ok := ext["AUTH"]; ok { - c.auth = strings.Split(mechs, " ") - } - c.ext = ext - return err -} - -// StartTLS sends the STARTTLS command and encrypts all further communication. -// Only servers that advertise the STARTTLS extension support this function. -func (c *Client) StartTLS(config *tls.Config) error { - if err := c.hello(); err != nil { - return err - } - _, _, err := c.cmd(220, "STARTTLS") - if err != nil { - return err - } - c.conn = tls.Client(c.conn, config) - c.Text = textproto.NewConn(c.conn) - c.tls = true - return c.ehlo() -} - -// Verify checks the validity of an email address on the server. -// If Verify returns nil, the address is valid. A non-nil return -// does not necessarily indicate an invalid address. Many servers -// will not verify addresses for security reasons. -func (c *Client) Verify(addr string) error { - if err := c.hello(); err != nil { - return err - } - _, _, err := c.cmd(250, "VRFY %s", addr) - return err -} - -// Auth authenticates a client using the provided authentication mechanism. -// A failed authentication closes the connection. -// Only servers that advertise the AUTH extension support this function. -func (c *Client) Auth(a Auth) error { - if err := c.hello(); err != nil { - return err - } - encoding := base64.StdEncoding - mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth}) - if err != nil { - c.Quit() - return err - } - resp64 := make([]byte, encoding.EncodedLen(len(resp))) - encoding.Encode(resp64, resp) - code, msg64, err := c.cmd(0, "AUTH %s %s", mech, resp64) - for err == nil { - var msg []byte - switch code { - case 334: - msg, err = encoding.DecodeString(msg64) - case 235: - // the last message isn't base64 because it isn't a challenge - msg = []byte(msg64) - default: - err = &textproto.Error{Code: code, Msg: msg64} - } - if err == nil { - resp, err = a.Next(msg, code == 334) - } - if err != nil { - // abort the AUTH - c.cmd(501, "*") - c.Quit() - break - } - if resp == nil { - break - } - resp64 = make([]byte, encoding.EncodedLen(len(resp))) - encoding.Encode(resp64, resp) - code, msg64, err = c.cmd(0, string(resp64)) - } - return err -} - -// Mail issues a MAIL command to the server using the provided email address. -// If the server supports the 8BITMIME extension, Mail adds the BODY=8BITMIME -// parameter. -// This initiates a mail transaction and is followed by one or more Rcpt calls. -func (c *Client) Mail(from string) error { - if err := c.hello(); err != nil { - return err - } - cmdStr := "MAIL FROM:<%s>" - if c.ext != nil { - if _, ok := c.ext["8BITMIME"]; ok { - cmdStr += " BODY=8BITMIME" - } - } - _, _, err := c.cmd(250, cmdStr, from) - return err -} - -// Rcpt issues a RCPT command to the server using the provided email address. -// A call to Rcpt must be preceded by a call to Mail and may be followed by -// a Data call or another Rcpt call. -func (c *Client) Rcpt(to string) error { - _, _, err := c.cmd(25, "RCPT TO:<%s>", to) - return err -} - -type dataCloser struct { - c *Client - io.WriteCloser -} - -func (d *dataCloser) Close() error { - d.WriteCloser.Close() - _, _, err := d.c.Text.ReadResponse(250) - return err -} - -// Data issues a DATA command to the server and returns a writer that -// can be used to write the data. The caller should close the writer -// before calling any more methods on c. -// A call to Data must be preceded by one or more calls to Rcpt. -func (c *Client) Data() (io.WriteCloser, error) { - _, _, err := c.cmd(354, "DATA") - if err != nil { - return nil, err - } - return &dataCloser{c, c.Text.DotWriter()}, nil -} - -var testHookStartTLS func(*tls.Config) // nil, except for tests - -// SendMail connects to the server at addr, switches to TLS if -// possible, authenticates with the optional mechanism a if possible, -// and then sends an email from address from, to addresses to, with -// message msg. -func SendMail(addr string, a Auth, from string, to []string, msg []byte) error { - c, err := Dial(addr) - if err != nil { - return err - } - defer c.Close() - if err = c.hello(); err != nil { - return err - } - if ok, _ := c.Extension("STARTTLS"); ok { - config := &tls.Config{ServerName: c.serverName} - if testHookStartTLS != nil { - testHookStartTLS(config) - } - if err = c.StartTLS(config); err != nil { - return err - } - } - if a != nil && c.ext != nil { - if _, ok := c.ext["AUTH"]; ok { - if err = c.Auth(a); err != nil { - return err - } - } - } - if err = c.Mail(from); err != nil { - return err - } - for _, addr := range to { - if err = c.Rcpt(addr); err != nil { - return err - } - } - w, err := c.Data() - if err != nil { - return err - } - _, err = w.Write(msg) - if err != nil { - return err - } - err = w.Close() - if err != nil { - return err - } - return c.Quit() -} - -// Extension reports whether an extension is support by the server. -// The extension name is case-insensitive. If the extension is supported, -// Extension also returns a string that contains any parameters the -// server specifies for the extension. -func (c *Client) Extension(ext string) (bool, string) { - if err := c.hello(); err != nil { - return false, "" - } - if c.ext == nil { - return false, "" - } - ext = strings.ToUpper(ext) - param, ok := c.ext[ext] - return ok, param -} - -// Reset sends the RSET command to the server, aborting the current mail -// transaction. -func (c *Client) Reset() error { - if err := c.hello(); err != nil { - return err - } - _, _, err := c.cmd(250, "RSET") - return err -} - -// Quit sends the QUIT command and closes the connection to the server. -func (c *Client) Quit() error { - if err := c.hello(); err != nil { - return err - } - _, _, err := c.cmd(221, "QUIT") - if err != nil { - return err - } - return c.Text.Close() -} |