// 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.//// The smtp package is frozen and is not accepting new features.// Some external packages provide more functionality. See://// https://godoc.org/?q=smtp
package smtpimport ()// A Client represents a client connection to an SMTP server.typeClientstruct {// 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, as in "mail.example.com:smtp".func ( string) (*Client, error) { , := net.Dial("tcp", )if != nil {returnnil, } , , := net.SplitHostPort()returnNewClient(, )}// NewClient returns a new [Client] using an existing connection and host as a// server name to be used when authenticating.func ( net.Conn, string) (*Client, error) { := textproto.NewConn() , , := .ReadResponse(220)if != nil { .Close()returnnil, } := &Client{Text: , conn: , serverName: , localName: "localhost"} _, .tls = .(*tls.Conn)return , nil}// Close closes the connection.func ( *Client) () error {return .Text.Close()}// hello runs a hello exchange if needed.func ( *Client) () error {if !.didHello { .didHello = true := .ehlo()if != nil { .helloError = .helo() } }return .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 ( *Client) ( string) error {if := validateLine(); != nil {return }if .didHello {returnerrors.New("smtp: Hello called after other methods") } .localName = return .hello()}// cmd is a convenience function that sends a command and returns the responsefunc ( *Client) ( int, string, ...any) (int, string, error) { , := .Text.Cmd(, ...)if != nil {return0, "", } .Text.StartResponse()defer .Text.EndResponse() , , := .Text.ReadResponse()return , , }// helo sends the HELO greeting to the server. It should be used only when the// server does not support ehlo.func ( *Client) () error { .ext = nil , , := .cmd(250, "HELO %s", .localName)return}// ehlo sends the EHLO (extended hello) greeting to the server. It// should be the preferred greeting for servers that support it.func ( *Client) () error { , , := .cmd(250, "EHLO %s", .localName)if != nil {return } := make(map[string]string) := strings.Split(, "\n")iflen() > 1 { = [1:]for , := range { , , := strings.Cut(, " ") [] = } }if , := ["AUTH"]; { .auth = strings.Split(, " ") } .ext = return}// StartTLS sends the STARTTLS command and encrypts all further communication.// Only servers that advertise the STARTTLS extension support this function.func ( *Client) ( *tls.Config) error {if := .hello(); != nil {return } , , := .cmd(220, "STARTTLS")if != nil {return } .conn = tls.Client(.conn, ) .Text = textproto.NewConn(.conn) .tls = truereturn .ehlo()}// TLSConnectionState returns the client's TLS connection state.// The return values are their zero values if [Client.StartTLS] did// not succeed.func ( *Client) () ( tls.ConnectionState, bool) { , := .conn.(*tls.Conn)if ! {return }return .ConnectionState(), true}// 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 ( *Client) ( string) error {if := validateLine(); != nil {return }if := .hello(); != nil {return } , , := .cmd(250, "VRFY %s", )return}// 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 ( *Client) ( Auth) error {if := .hello(); != nil {return } := base64.StdEncoding , , := .Start(&ServerInfo{.serverName, .tls, .auth})if != nil { .Quit()return } := make([]byte, .EncodedLen(len())) .Encode(, ) , , := .cmd(0, "%s", strings.TrimSpace(fmt.Sprintf("AUTH %s %s", , )))for == nil {var []byteswitch {case334: , = .DecodeString()case235:// the last message isn't base64 because it isn't a challenge = []byte()default: = &textproto.Error{Code: , Msg: } }if == nil { , = .Next(, == 334) }if != nil {// abort the AUTH .cmd(501, "*") .Quit()break }if == nil {break } = make([]byte, .EncodedLen(len())) .Encode(, ) , , = .cmd(0, "%s", ) }return}// 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. If the server supports the SMTPUTF8 extension, Mail adds the// SMTPUTF8 parameter.// This initiates a mail transaction and is followed by one or more [Client.Rcpt] calls.func ( *Client) ( string) error {if := validateLine(); != nil {return }if := .hello(); != nil {return } := "MAIL FROM:<%s>"if .ext != nil {if , := .ext["8BITMIME"]; { += " BODY=8BITMIME" }if , := .ext["SMTPUTF8"]; { += " SMTPUTF8" } } , , := .cmd(250, , )return}// Rcpt issues a RCPT command to the server using the provided email address.// A call to Rcpt must be preceded by a call to [Client.Mail] and may be followed by// a [Client.Data] call or another Rcpt call.func ( *Client) ( string) error {if := validateLine(); != nil {return } , , := .cmd(25, "RCPT TO:<%s>", )return}type dataCloser struct { c *Clientio.WriteCloser}func ( *dataCloser) () error { .WriteCloser.Close() , , := .c.Text.ReadResponse(250)return}// Data issues a DATA command to the server and returns a writer that// can be used to write the mail headers and body. 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 [Client.Rcpt].func ( *Client) () (io.WriteCloser, error) { , , := .cmd(354, "DATA")if != nil {returnnil, }return &dataCloser{, .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.// The addr must include a port, as in "mail.example.com:smtp".//// The addresses in the to parameter are the SMTP RCPT addresses.//// The msg parameter should be an RFC 822-style email with headers// first, a blank line, and then the message body. The lines of msg// should be CRLF terminated. The msg headers should usually include// fields such as "From", "To", "Subject", and "Cc". Sending "Bcc"// messages is accomplished by including an email address in the to// parameter but not including it in the msg headers.//// The SendMail function and the net/smtp package are low-level// mechanisms and provide no support for DKIM signing, MIME// attachments (see the mime/multipart package), or other mail// functionality. Higher-level packages exist outside of the standard// library.func ( string, Auth, string, []string, []byte) error {if := validateLine(); != nil {return }for , := range {if := validateLine(); != nil {return } } , := Dial()if != nil {return }defer .Close()if = .hello(); != nil {return }if , := .Extension("STARTTLS"); { := &tls.Config{ServerName: .serverName}iftestHookStartTLS != nil {testHookStartTLS() }if = .StartTLS(); != nil {return } }if != nil && .ext != nil {if , := .ext["AUTH"]; ! {returnerrors.New("smtp: server doesn't support AUTH") }if = .Auth(); != nil {return } }if = .Mail(); != nil {return }for , := range {if = .Rcpt(); != nil {return } } , := .Data()if != nil {return } _, = .Write()if != nil {return } = .Close()if != nil {return }return .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 ( *Client) ( string) (bool, string) {if := .hello(); != nil {returnfalse, "" }if .ext == nil {returnfalse, "" } = strings.ToUpper() , := .ext[]return , }// Reset sends the RSET command to the server, aborting the current mail// transaction.func ( *Client) () error {if := .hello(); != nil {return } , , := .cmd(250, "RSET")return}// Noop sends the NOOP command to the server. It does nothing but check// that the connection to the server is okay.func ( *Client) () error {if := .hello(); != nil {return } , , := .cmd(250, "NOOP")return}// Quit sends the QUIT command and closes the connection to the server.func ( *Client) () error {if := .hello(); != nil {return } , , := .cmd(221, "QUIT")if != nil {return }return .Text.Close()}// validateLine checks to see if a line has CR or LF as per RFC 5321.func validateLine( string) error {ifstrings.ContainsAny(, "\n\r") {returnerrors.New("smtp: A line must not contain CR or LF") }returnnil}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.