client.go raw

   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
   6  
   7  import (
   8  	"crypto/tls"
   9  	"encoding/base64"
  10  	"errors"
  11  	"fmt"
  12  	"io"
  13  	"net"
  14  	"net/textproto"
  15  	"sort"
  16  	"strconv"
  17  	"strings"
  18  	"time"
  19  
  20  	"github.com/emersion/go-sasl"
  21  )
  22  
  23  // A Client represents a client connection to an SMTP server.
  24  type Client struct {
  25  	// keep a reference to the connection so it can be used to create a TLS
  26  	// connection later
  27  	conn       net.Conn
  28  	text       *textproto.Conn
  29  	serverName string
  30  	lmtp       bool
  31  	ext        map[string]string // supported extensions
  32  	localName  string            // the name to use in HELO/EHLO/LHLO
  33  	didGreet   bool              // whether we've received greeting from server
  34  	greetError error             // the error from the greeting
  35  	didHello   bool              // whether we've said HELO/EHLO/LHLO
  36  	helloError error             // the error from the hello
  37  	rcpts      []string          // recipients accumulated for the current session
  38  
  39  	// Time to wait for command responses (this includes 3xx reply to DATA).
  40  	CommandTimeout time.Duration
  41  	// Time to wait for responses after final dot.
  42  	SubmissionTimeout time.Duration
  43  
  44  	// Logger for all network activity.
  45  	DebugWriter io.Writer
  46  }
  47  
  48  // 30 seconds was chosen as it's the same duration as http.DefaultTransport's
  49  // timeout.
  50  var defaultDialer = net.Dialer{Timeout: 30 * time.Second}
  51  
  52  // Dial returns a new Client connected to an SMTP server at addr. The addr must
  53  // include a port, as in "mail.example.com:smtp".
  54  //
  55  // This function returns a plaintext connection. To enable TLS, use
  56  // DialStartTLS.
  57  func Dial(addr string) (*Client, error) {
  58  	conn, err := defaultDialer.Dial("tcp", addr)
  59  	if err != nil {
  60  		return nil, err
  61  	}
  62  	client := NewClient(conn)
  63  	client.serverName, _, _ = net.SplitHostPort(addr)
  64  	return client, nil
  65  }
  66  
  67  // DialTLS returns a new Client connected to an SMTP server via TLS at addr.
  68  // The addr must include a port, as in "mail.example.com:smtps".
  69  //
  70  // A nil tlsConfig is equivalent to a zero tls.Config.
  71  func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
  72  	tlsDialer := tls.Dialer{
  73  		NetDialer: &defaultDialer,
  74  		Config:    tlsConfig,
  75  	}
  76  	conn, err := tlsDialer.Dial("tcp", addr)
  77  	if err != nil {
  78  		return nil, err
  79  	}
  80  	client := NewClient(conn)
  81  	client.serverName, _, _ = net.SplitHostPort(addr)
  82  	return client, nil
  83  }
  84  
  85  // DialStartTLS returns a new Client connected to an SMTP server via STARTTLS
  86  // at addr. The addr must include a port, as in "mail.example.com:smtp".
  87  //
  88  // A nil tlsConfig is equivalent to a zero tls.Config.
  89  func DialStartTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
  90  	c, err := Dial(addr)
  91  	if err != nil {
  92  		return nil, err
  93  	}
  94  	if err := initStartTLS(c, tlsConfig); err != nil {
  95  		c.Close()
  96  		return nil, err
  97  	}
  98  	return c, nil
  99  }
 100  
 101  // NewClient returns a new Client using an existing connection and host as a
 102  // server name to be used when authenticating.
 103  func NewClient(conn net.Conn) *Client {
 104  	c := &Client{
 105  		localName: "localhost",
 106  		// As recommended by RFC 5321. For DATA command reply (3xx one) RFC
 107  		// recommends a slightly shorter timeout but we do not bother
 108  		// differentiating these.
 109  		CommandTimeout: 5 * time.Minute,
 110  		// 10 minutes + 2 minute buffer in case the server is doing transparent
 111  		// forwarding and also follows recommended timeouts.
 112  		SubmissionTimeout: 12 * time.Minute,
 113  	}
 114  
 115  	c.setConn(conn)
 116  
 117  	return c
 118  }
 119  
 120  // NewClientStartTLS creates a new Client and performs a STARTTLS command.
 121  func NewClientStartTLS(conn net.Conn, tlsConfig *tls.Config) (*Client, error) {
 122  	c := NewClient(conn)
 123  	if err := initStartTLS(c, tlsConfig); err != nil {
 124  		c.Close()
 125  		return nil, err
 126  	}
 127  	return c, nil
 128  }
 129  
 130  func initStartTLS(c *Client, tlsConfig *tls.Config) error {
 131  	if err := c.hello(); err != nil {
 132  		return err
 133  	}
 134  	if ok, _ := c.Extension("STARTTLS"); !ok {
 135  		return errors.New("smtp: server doesn't support STARTTLS")
 136  	}
 137  	if err := c.startTLS(tlsConfig); err != nil {
 138  		return err
 139  	}
 140  	return nil
 141  }
 142  
 143  // NewClientLMTP returns a new LMTP Client (as defined in RFC 2033) using an
 144  // existing connection and host as a server name to be used when authenticating.
 145  func NewClientLMTP(conn net.Conn) *Client {
 146  	c := NewClient(conn)
 147  	c.lmtp = true
 148  	return c
 149  }
 150  
 151  // setConn sets the underlying network connection for the client.
 152  func (c *Client) setConn(conn net.Conn) {
 153  	c.conn = conn
 154  
 155  	var r io.Reader = conn
 156  	var w io.Writer = conn
 157  
 158  	r = &lineLimitReader{
 159  		R: conn,
 160  		// Doubled maximum line length per RFC 5321 (Section 4.5.3.1.6)
 161  		LineLimit: 2000,
 162  	}
 163  
 164  	r = io.TeeReader(r, clientDebugWriter{c})
 165  	w = io.MultiWriter(w, clientDebugWriter{c})
 166  
 167  	rwc := struct {
 168  		io.Reader
 169  		io.Writer
 170  		io.Closer
 171  	}{
 172  		Reader: r,
 173  		Writer: w,
 174  		Closer: conn,
 175  	}
 176  	c.text = textproto.NewConn(rwc)
 177  }
 178  
 179  // Close closes the connection.
 180  func (c *Client) Close() error {
 181  	return c.text.Close()
 182  }
 183  
 184  func (c *Client) greet() error {
 185  	if c.didGreet {
 186  		return c.greetError
 187  	}
 188  
 189  	// Initial greeting timeout. RFC 5321 recommends 5 minutes.
 190  	c.conn.SetDeadline(time.Now().Add(c.CommandTimeout))
 191  	defer c.conn.SetDeadline(time.Time{})
 192  
 193  	c.didGreet = true
 194  	_, _, err := c.readResponse(220)
 195  	if err != nil {
 196  		c.greetError = err
 197  		c.text.Close()
 198  	}
 199  
 200  	return c.greetError
 201  }
 202  
 203  // hello runs a hello exchange if needed.
 204  func (c *Client) hello() error {
 205  	if c.didHello {
 206  		return c.helloError
 207  	}
 208  
 209  	if err := c.greet(); err != nil {
 210  		return err
 211  	}
 212  
 213  	c.didHello = true
 214  	if err := c.ehlo(); err != nil {
 215  		var smtpError *SMTPError
 216  		if errors.As(err, &smtpError) && (smtpError.Code == 500 || smtpError.Code == 502) {
 217  			// The server doesn't support EHLO, fallback to HELO
 218  			c.helloError = c.helo()
 219  		} else {
 220  			c.helloError = err
 221  		}
 222  	}
 223  	return c.helloError
 224  }
 225  
 226  // Hello sends a HELO or EHLO to the server as the given host name.
 227  // Calling this method is only necessary if the client needs control
 228  // over the host name used. The client will introduce itself as "localhost"
 229  // automatically otherwise. If Hello is called, it must be called before
 230  // any of the other methods.
 231  //
 232  // If server returns an error, it will be of type *SMTPError.
 233  func (c *Client) Hello(localName string) error {
 234  	if err := validateLine(localName); err != nil {
 235  		return err
 236  	}
 237  	if c.didHello {
 238  		return errors.New("smtp: Hello called after other methods")
 239  	}
 240  	c.localName = localName
 241  	return c.hello()
 242  }
 243  
 244  func (c *Client) readResponse(expectCode int) (int, string, error) {
 245  	code, msg, err := c.text.ReadResponse(expectCode)
 246  	if protoErr, ok := err.(*textproto.Error); ok {
 247  		err = toSMTPErr(protoErr)
 248  	}
 249  	return code, msg, err
 250  }
 251  
 252  // cmd is a convenience function that sends a command and returns the response
 253  // textproto.Error returned by c.text.ReadResponse is converted into SMTPError.
 254  func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) {
 255  	c.conn.SetDeadline(time.Now().Add(c.CommandTimeout))
 256  	defer c.conn.SetDeadline(time.Time{})
 257  
 258  	id, err := c.text.Cmd(format, args...)
 259  	if err != nil {
 260  		return 0, "", err
 261  	}
 262  	c.text.StartResponse(id)
 263  	defer c.text.EndResponse(id)
 264  
 265  	return c.readResponse(expectCode)
 266  }
 267  
 268  // helo sends the HELO greeting to the server. It should be used only when the
 269  // server does not support ehlo.
 270  func (c *Client) helo() error {
 271  	c.ext = nil
 272  	_, _, err := c.cmd(250, "HELO %s", c.localName)
 273  	return err
 274  }
 275  
 276  // ehlo sends the EHLO (extended hello) greeting to the server. It
 277  // should be the preferred greeting for servers that support it.
 278  func (c *Client) ehlo() error {
 279  	cmd := "EHLO"
 280  	if c.lmtp {
 281  		cmd = "LHLO"
 282  	}
 283  
 284  	_, msg, err := c.cmd(250, "%s %s", cmd, c.localName)
 285  	if err != nil {
 286  		return err
 287  	}
 288  	ext := make(map[string]string)
 289  	extList := strings.Split(msg, "\n")
 290  	if len(extList) > 1 {
 291  		extList = extList[1:]
 292  		for _, line := range extList {
 293  			args := strings.SplitN(line, " ", 2)
 294  			if len(args) > 1 {
 295  				ext[args[0]] = args[1]
 296  			} else {
 297  				ext[args[0]] = ""
 298  			}
 299  		}
 300  	}
 301  	c.ext = ext
 302  	return err
 303  }
 304  
 305  // startTLS sends the STARTTLS command and encrypts all further communication.
 306  // Only servers that advertise the STARTTLS extension support this function.
 307  //
 308  // A nil config is equivalent to a zero tls.Config.
 309  //
 310  // If server returns an error, it will be of type *SMTPError.
 311  func (c *Client) startTLS(config *tls.Config) error {
 312  	if err := c.hello(); err != nil {
 313  		return err
 314  	}
 315  	_, _, err := c.cmd(220, "STARTTLS")
 316  	if err != nil {
 317  		return err
 318  	}
 319  	if config == nil {
 320  		config = &tls.Config{}
 321  	}
 322  	if config.ServerName == "" && c.serverName != "" {
 323  		// Make a copy to avoid polluting argument
 324  		config = config.Clone()
 325  		config.ServerName = c.serverName
 326  	}
 327  	if testHookStartTLS != nil {
 328  		testHookStartTLS(config)
 329  	}
 330  	c.setConn(tls.Client(c.conn, config))
 331  	c.didHello = false
 332  	return nil
 333  }
 334  
 335  // TLSConnectionState returns the client's TLS connection state.
 336  // The return values are their zero values if STARTTLS did
 337  // not succeed.
 338  func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool) {
 339  	tc, ok := c.conn.(*tls.Conn)
 340  	if !ok {
 341  		return
 342  	}
 343  	return tc.ConnectionState(), true
 344  }
 345  
 346  // Verify checks the validity of an email address on the server.
 347  // If Verify returns nil, the address is valid. A non-nil return
 348  // does not necessarily indicate an invalid address. Many servers
 349  // will not verify addresses for security reasons.
 350  //
 351  // If server returns an error, it will be of type *SMTPError.
 352  func (c *Client) Verify(addr string) error {
 353  	if err := validateLine(addr); err != nil {
 354  		return err
 355  	}
 356  	if err := c.hello(); err != nil {
 357  		return err
 358  	}
 359  	_, _, err := c.cmd(250, "VRFY %s", addr)
 360  	return err
 361  }
 362  
 363  // Auth authenticates a client using the provided authentication mechanism.
 364  // Only servers that advertise the AUTH extension support this function.
 365  //
 366  // If server returns an error, it will be of type *SMTPError.
 367  func (c *Client) Auth(a sasl.Client) error {
 368  	if err := c.hello(); err != nil {
 369  		return err
 370  	}
 371  	encoding := base64.StdEncoding
 372  	mech, resp, err := a.Start()
 373  	if err != nil {
 374  		return err
 375  	}
 376  	var resp64 []byte
 377  	if len(resp) > 0 {
 378  		resp64 = make([]byte, encoding.EncodedLen(len(resp)))
 379  		encoding.Encode(resp64, resp)
 380  	} else if resp != nil {
 381  		resp64 = []byte{'='}
 382  	}
 383  	code, msg64, err := c.cmd(0, strings.TrimSpace(fmt.Sprintf("AUTH %s %s", mech, resp64)))
 384  	for err == nil {
 385  		var msg []byte
 386  		switch code {
 387  		case 334:
 388  			msg, err = encoding.DecodeString(msg64)
 389  		case 235:
 390  			// the last message isn't base64 because it isn't a challenge
 391  			msg = []byte(msg64)
 392  		default:
 393  			err = toSMTPErr(&textproto.Error{Code: code, Msg: msg64})
 394  		}
 395  		if err == nil {
 396  			if code == 334 {
 397  				resp, err = a.Next(msg)
 398  			} else {
 399  				resp = nil
 400  			}
 401  		}
 402  		if err != nil {
 403  			// abort the AUTH
 404  			c.cmd(501, "*")
 405  			break
 406  		}
 407  		if resp == nil {
 408  			break
 409  		}
 410  		resp64 = make([]byte, encoding.EncodedLen(len(resp)))
 411  		encoding.Encode(resp64, resp)
 412  		code, msg64, err = c.cmd(0, string(resp64))
 413  	}
 414  	return err
 415  }
 416  
 417  // Mail issues a MAIL command to the server using the provided email address.
 418  // If the server supports the 8BITMIME extension, Mail adds the BODY=8BITMIME
 419  // parameter.
 420  // This initiates a mail transaction and is followed by one or more Rcpt calls.
 421  //
 422  // If opts is not nil, MAIL arguments provided in the structure will be added
 423  // to the command. Handling of unsupported options depends on the extension.
 424  //
 425  // If server returns an error, it will be of type *SMTPError.
 426  func (c *Client) Mail(from string, opts *MailOptions) error {
 427  	if err := validateLine(from); err != nil {
 428  		return err
 429  	}
 430  	if err := c.hello(); err != nil {
 431  		return err
 432  	}
 433  
 434  	var sb strings.Builder
 435  	// A high enough power of 2 than 510+14+26+11+9+9+39+500
 436  	sb.Grow(2048)
 437  	fmt.Fprintf(&sb, "MAIL FROM:<%s>", from)
 438  	if _, ok := c.ext["8BITMIME"]; ok {
 439  		sb.WriteString(" BODY=8BITMIME")
 440  	}
 441  	if _, ok := c.ext["SIZE"]; ok && opts != nil && opts.Size != 0 {
 442  		fmt.Fprintf(&sb, " SIZE=%v", opts.Size)
 443  	}
 444  	if opts != nil && opts.RequireTLS {
 445  		if _, ok := c.ext["REQUIRETLS"]; ok {
 446  			sb.WriteString(" REQUIRETLS")
 447  		} else {
 448  			return errors.New("smtp: server does not support REQUIRETLS")
 449  		}
 450  	}
 451  	if opts != nil && opts.UTF8 {
 452  		if _, ok := c.ext["SMTPUTF8"]; ok {
 453  			sb.WriteString(" SMTPUTF8")
 454  		} else {
 455  			return errors.New("smtp: server does not support SMTPUTF8")
 456  		}
 457  	}
 458  	if _, ok := c.ext["DSN"]; ok && opts != nil {
 459  		switch opts.Return {
 460  		case DSNReturnFull, DSNReturnHeaders:
 461  			fmt.Fprintf(&sb, " RET=%s", string(opts.Return))
 462  		case "":
 463  			// This space is intentionally left blank
 464  		default:
 465  			return errors.New("smtp: Unknown RET parameter value")
 466  		}
 467  		if opts.EnvelopeID != "" {
 468  			if !isPrintableASCII(opts.EnvelopeID) {
 469  				return errors.New("smtp: Malformed ENVID parameter value")
 470  			}
 471  			fmt.Fprintf(&sb, " ENVID=%s", encodeXtext(opts.EnvelopeID))
 472  		}
 473  	}
 474  	if opts != nil && opts.Auth != nil {
 475  		if _, ok := c.ext["AUTH"]; ok {
 476  			fmt.Fprintf(&sb, " AUTH=%s", encodeXtext(*opts.Auth))
 477  		}
 478  		// We can safely discard parameter if server does not support AUTH.
 479  	}
 480  	_, _, err := c.cmd(250, "%s", sb.String())
 481  	return err
 482  }
 483  
 484  // Rcpt issues a RCPT command to the server using the provided email address.
 485  // A call to Rcpt must be preceded by a call to Mail and may be followed by
 486  // a Data call or another Rcpt call.
 487  //
 488  // If opts is not nil, RCPT arguments provided in the structure will be added
 489  // to the command. Handling of unsupported options depends on the extension.
 490  //
 491  // If server returns an error, it will be of type *SMTPError.
 492  func (c *Client) Rcpt(to string, opts *RcptOptions) error {
 493  	if err := validateLine(to); err != nil {
 494  		return err
 495  	}
 496  
 497  	var sb strings.Builder
 498  	// A high enough power of 2 than 510+29+501
 499  	sb.Grow(2048)
 500  	fmt.Fprintf(&sb, "RCPT TO:<%s>", to)
 501  	if _, ok := c.ext["DSN"]; ok && opts != nil {
 502  		if len(opts.Notify) != 0 {
 503  			sb.WriteString(" NOTIFY=")
 504  			if err := checkNotifySet(opts.Notify); err != nil {
 505  				return errors.New("smtp: Malformed NOTIFY parameter value")
 506  			}
 507  			for i, v := range opts.Notify {
 508  				if i != 0 {
 509  					sb.WriteString(",")
 510  				}
 511  				sb.WriteString(string(v))
 512  			}
 513  		}
 514  		if opts.OriginalRecipient != "" {
 515  			var enc string
 516  			switch opts.OriginalRecipientType {
 517  			case DSNAddressTypeRFC822:
 518  				if !isPrintableASCII(opts.OriginalRecipient) {
 519  					return errors.New("smtp: Illegal address")
 520  				}
 521  				enc = encodeXtext(opts.OriginalRecipient)
 522  			case DSNAddressTypeUTF8:
 523  				if _, ok := c.ext["SMTPUTF8"]; ok {
 524  					enc = encodeUTF8AddrUnitext(opts.OriginalRecipient)
 525  				} else {
 526  					enc = encodeUTF8AddrXtext(opts.OriginalRecipient)
 527  				}
 528  			default:
 529  				return errors.New("smtp: Unknown address type")
 530  			}
 531  			fmt.Fprintf(&sb, " ORCPT=%s;%s", string(opts.OriginalRecipientType), enc)
 532  		}
 533  	}
 534  	if _, ok := c.ext["RRVS"]; ok && opts != nil && !opts.RequireRecipientValidSince.IsZero() {
 535  		sb.WriteString(fmt.Sprintf(" RRVS=%s", opts.RequireRecipientValidSince.Format(time.RFC3339)))
 536  	}
 537  	if _, ok := c.ext["DELIVERBY"]; ok && opts != nil && opts.DeliverBy != nil {
 538  		if opts.DeliverBy.Mode == DeliverByReturn && opts.DeliverBy.Time < 1 {
 539  			return errors.New("smtp: DELIVERBY mode must be greater than zero with return mode")
 540  		}
 541  		arg := fmt.Sprintf(" BY=%d;%s", int(opts.DeliverBy.Time.Seconds()), opts.DeliverBy.Mode)
 542  		if opts.DeliverBy.Trace {
 543  			arg += "T"
 544  		}
 545  		sb.WriteString(arg)
 546  	}
 547  	if _, ok := c.ext["MT-PRIORITY"]; ok && opts != nil && opts.MTPriority != nil {
 548  		if *opts.MTPriority < -9 || *opts.MTPriority > 9 {
 549  			return errors.New("smtp: MT-PRIORITY must be between -9 and 9")
 550  		}
 551  		sb.WriteString(fmt.Sprintf(" MT-PRIORITY=%d", *opts.MTPriority))
 552  	}
 553  	if _, _, err := c.cmd(25, "%s", sb.String()); err != nil {
 554  		return err
 555  	}
 556  	c.rcpts = append(c.rcpts, to)
 557  	return nil
 558  }
 559  
 560  // DataCommand is a pending DATA command. DataCommand is an io.WriteCloser.
 561  // See Client.Data.
 562  type DataCommand struct {
 563  	client *Client
 564  	wc     io.WriteCloser
 565  
 566  	closeErr error
 567  }
 568  
 569  var _ io.WriteCloser = (*DataCommand)(nil)
 570  
 571  // Write implements io.Writer.
 572  func (cmd *DataCommand) Write(b []byte) (int, error) {
 573  	return cmd.wc.Write(b)
 574  }
 575  
 576  // Close implements io.Closer.
 577  func (cmd *DataCommand) Close() error {
 578  	var err error
 579  	if cmd.client.lmtp {
 580  		_, err = cmd.CloseWithLMTPResponse()
 581  	} else {
 582  		_, err = cmd.CloseWithResponse()
 583  	}
 584  	return err
 585  }
 586  
 587  // CloseWithResponse is equivalent to Close, but also returns the server
 588  // response. It cannot be called when the LMTP protocol is used.
 589  //
 590  // If server returns an error, it will be of type *SMTPError.
 591  func (cmd *DataCommand) CloseWithResponse() (*DataResponse, error) {
 592  	if cmd.client.lmtp {
 593  		return nil, errors.New("smtp: CloseWithResponse used with an LMTP client")
 594  	}
 595  
 596  	if err := cmd.close(); err != nil {
 597  		return nil, err
 598  	}
 599  
 600  	cmd.client.conn.SetDeadline(time.Now().Add(cmd.client.SubmissionTimeout))
 601  	defer cmd.client.conn.SetDeadline(time.Time{})
 602  
 603  	_, msg, err := cmd.client.readResponse(250)
 604  	if err != nil {
 605  		cmd.closeErr = err
 606  		return nil, err
 607  	}
 608  
 609  	return &DataResponse{StatusText: msg}, nil
 610  }
 611  
 612  // CloseWithLMTPResponse is equivalent to Close, but also returns per-recipient
 613  // server responses. It can only be called when the LMTP protocol is used.
 614  //
 615  // If server returns an error, it will be of type LMTPDataError.
 616  func (cmd *DataCommand) CloseWithLMTPResponse() (map[string]*DataResponse, error) {
 617  	if !cmd.client.lmtp {
 618  		return nil, errors.New("smtp: CloseWithLMTPResponse used without an LMTP client")
 619  	}
 620  
 621  	if err := cmd.close(); err != nil {
 622  		return nil, err
 623  	}
 624  
 625  	cmd.client.conn.SetDeadline(time.Now().Add(cmd.client.SubmissionTimeout))
 626  	defer cmd.client.conn.SetDeadline(time.Time{})
 627  
 628  	resp := make(map[string]*DataResponse, len(cmd.client.rcpts))
 629  	lmtpErr := make(LMTPDataError, len(cmd.client.rcpts))
 630  	for i := 0; i < len(cmd.client.rcpts); i++ {
 631  		rcpt := cmd.client.rcpts[i]
 632  		_, msg, err := cmd.client.readResponse(250)
 633  		if err != nil {
 634  			if smtpErr, ok := err.(*SMTPError); ok {
 635  				lmtpErr[rcpt] = smtpErr
 636  			} else {
 637  				if len(lmtpErr) > 0 {
 638  					return resp, errors.Join(err, lmtpErr)
 639  				}
 640  				return resp, err
 641  			}
 642  		} else {
 643  			resp[rcpt] = &DataResponse{StatusText: msg}
 644  		}
 645  	}
 646  
 647  	if len(lmtpErr) > 0 {
 648  		return resp, lmtpErr
 649  	}
 650  	return resp, nil
 651  }
 652  
 653  func (cmd *DataCommand) close() error {
 654  	if cmd.closeErr != nil {
 655  		return cmd.closeErr
 656  	}
 657  
 658  	if err := cmd.wc.Close(); err != nil {
 659  		cmd.closeErr = err
 660  		return err
 661  	}
 662  
 663  	cmd.closeErr = errors.New("smtp: data writer closed twice")
 664  	return nil
 665  }
 666  
 667  // DataResponse is the response returned by a DATA command. See
 668  // DataCommand.CloseWithResponse.
 669  type DataResponse struct {
 670  	// StatusText is the status text returned by the server. It may contain
 671  	// tracking information.
 672  	StatusText string
 673  }
 674  
 675  // LMTPDataError is a collection of errors returned by an LMTP server for a
 676  // DATA command. It holds per-recipient errors.
 677  type LMTPDataError map[string]*SMTPError
 678  
 679  // Error implements error.
 680  func (lmtpErr LMTPDataError) Error() string {
 681  	return errors.Join(lmtpErr.Unwrap()...).Error()
 682  }
 683  
 684  // Unwrap returns all per-recipient errors returned by the server.
 685  func (lmtpErr LMTPDataError) Unwrap() []error {
 686  	l := make([]error, 0, len(lmtpErr))
 687  	for rcpt, smtpErr := range lmtpErr {
 688  		l = append(l, fmt.Errorf("<%v>: %w", rcpt, smtpErr))
 689  	}
 690  	sort.Slice(l, func(i, j int) bool {
 691  		return l[i].Error() < l[j].Error()
 692  	})
 693  	return l
 694  }
 695  
 696  // Data issues a DATA command to the server and returns a writer that
 697  // can be used to write the mail headers and body. The caller should
 698  // close the writer before calling any more methods on c. A call to
 699  // Data must be preceded by one or more calls to Rcpt.
 700  func (c *Client) Data() (*DataCommand, error) {
 701  	_, _, err := c.cmd(354, "DATA")
 702  	if err != nil {
 703  		return nil, err
 704  	}
 705  	return &DataCommand{client: c, wc: c.text.DotWriter()}, nil
 706  }
 707  
 708  // SendMail will use an existing connection to send an email from
 709  // address from, to addresses to, with message r.
 710  //
 711  // This function does not start TLS, nor does it perform authentication. Use
 712  // DialStartTLS and Auth before-hand if desirable.
 713  //
 714  // The addresses in the to parameter are the SMTP RCPT addresses.
 715  //
 716  // The r parameter should be an RFC 822-style email with headers
 717  // first, a blank line, and then the message body. The lines of r
 718  // should be CRLF terminated. The r headers should usually include
 719  // fields such as "From", "To", "Subject", and "Cc".  Sending "Bcc"
 720  // messages is accomplished by including an email address in the to
 721  // parameter but not including it in the r headers.
 722  func (c *Client) SendMail(from string, to []string, r io.Reader) error {
 723  	var err error
 724  
 725  	if err = c.Mail(from, nil); err != nil {
 726  		return err
 727  	}
 728  	for _, addr := range to {
 729  		if err = c.Rcpt(addr, nil); err != nil {
 730  			return err
 731  		}
 732  	}
 733  	w, err := c.Data()
 734  	if err != nil {
 735  		return err
 736  	}
 737  	_, err = io.Copy(w, r)
 738  	if err != nil {
 739  		return err
 740  	}
 741  	return w.Close()
 742  }
 743  
 744  var testHookStartTLS func(*tls.Config) // nil, except for tests
 745  
 746  func sendMail(addr string, implicitTLS bool, a sasl.Client, from string, to []string, r io.Reader) error {
 747  	var (
 748  		c   *Client
 749  		err error
 750  	)
 751  	if implicitTLS {
 752  		c, err = DialTLS(addr, nil)
 753  	} else {
 754  		c, err = DialStartTLS(addr, nil)
 755  	}
 756  	if err != nil {
 757  		return err
 758  	}
 759  	defer c.Close()
 760  
 761  	if a != nil {
 762  		if ok, _ := c.Extension("AUTH"); !ok {
 763  			return errors.New("smtp: server doesn't support AUTH")
 764  		}
 765  		if err = c.Auth(a); err != nil {
 766  			return err
 767  		}
 768  	}
 769  
 770  	if err := c.SendMail(from, to, r); err != nil {
 771  		return err
 772  	}
 773  
 774  	return c.Quit()
 775  }
 776  
 777  // SendMail connects to the server at addr, switches to TLS, authenticates with
 778  // the optional SASL client, and then sends an email from address from, to
 779  // addresses to, with message r. The addr must include a port, as in
 780  // "mail.example.com:smtp".
 781  //
 782  // The addresses in the to parameter are the SMTP RCPT addresses.
 783  //
 784  // The r parameter should be an RFC 822-style email with headers
 785  // first, a blank line, and then the message body. The lines of r
 786  // should be CRLF terminated. The r headers should usually include
 787  // fields such as "From", "To", "Subject", and "Cc".  Sending "Bcc"
 788  // messages is accomplished by including an email address in the to
 789  // parameter but not including it in the r headers.
 790  //
 791  // SendMail is intended to be used for very simple use-cases. If you want to
 792  // customize SendMail's behavior, use a Client instead.
 793  //
 794  // The SendMail function and the go-smtp package are low-level
 795  // mechanisms and provide no support for DKIM signing (see go-msgauth), MIME
 796  // attachments (see the mime/multipart package or the go-message package), or
 797  // other mail functionality.
 798  func SendMail(addr string, a sasl.Client, from string, to []string, r io.Reader) error {
 799  	return sendMail(addr, false, a, from, to, r)
 800  }
 801  
 802  // SendMailTLS works like SendMail, but with implicit TLS.
 803  func SendMailTLS(addr string, a sasl.Client, from string, to []string, r io.Reader) error {
 804  	return sendMail(addr, true, a, from, to, r)
 805  }
 806  
 807  // Extension reports whether an extension is support by the server.
 808  // The extension name is case-insensitive. If the extension is supported,
 809  // Extension also returns a string that contains any parameters the
 810  // server specifies for the extension.
 811  func (c *Client) Extension(ext string) (bool, string) {
 812  	if err := c.hello(); err != nil {
 813  		return false, ""
 814  	}
 815  	ext = strings.ToUpper(ext)
 816  	param, ok := c.ext[ext]
 817  	return ok, param
 818  }
 819  
 820  // SupportsAuth checks whether an authentication mechanism is supported.
 821  func (c *Client) SupportsAuth(mech string) bool {
 822  	if err := c.hello(); err != nil {
 823  		return false
 824  	}
 825  	mechs, ok := c.ext["AUTH"]
 826  	if !ok {
 827  		return false
 828  	}
 829  	for _, m := range strings.Split(mechs, " ") {
 830  		if strings.EqualFold(m, mech) {
 831  			return true
 832  		}
 833  	}
 834  	return false
 835  }
 836  
 837  // MaxMessageSize returns the maximum message size accepted by the server.
 838  // 0 means unlimited.
 839  //
 840  // If the server doesn't convey this information, ok = false is returned.
 841  func (c *Client) MaxMessageSize() (size int, ok bool) {
 842  	if err := c.hello(); err != nil {
 843  		return 0, false
 844  	}
 845  	v := c.ext["SIZE"]
 846  	if v == "" {
 847  		return 0, false
 848  	}
 849  	size, err := strconv.Atoi(v)
 850  	if err != nil || size < 0 {
 851  		return 0, false
 852  	}
 853  	return size, true
 854  }
 855  
 856  // Reset sends the RSET command to the server, aborting the current mail
 857  // transaction.
 858  func (c *Client) Reset() error {
 859  	if err := c.hello(); err != nil {
 860  		return err
 861  	}
 862  	if _, _, err := c.cmd(250, "RSET"); err != nil {
 863  		return err
 864  	}
 865  
 866  	// allow custom HELLO again
 867  	c.didHello = false
 868  	c.helloError = nil
 869  
 870  	c.rcpts = nil
 871  	return nil
 872  }
 873  
 874  // Noop sends the NOOP command to the server. It does nothing but check
 875  // that the connection to the server is okay.
 876  func (c *Client) Noop() error {
 877  	if err := c.hello(); err != nil {
 878  		return err
 879  	}
 880  	_, _, err := c.cmd(250, "NOOP")
 881  	return err
 882  }
 883  
 884  // Quit sends the QUIT command and closes the connection to the server.
 885  //
 886  // If Quit fails the connection is not closed, Close should be used
 887  // in this case.
 888  func (c *Client) Quit() error {
 889  	if err := c.hello(); err != nil {
 890  		return err
 891  	}
 892  	_, _, err := c.cmd(221, "QUIT")
 893  	if err != nil {
 894  		return err
 895  	}
 896  	return c.Close()
 897  }
 898  
 899  func parseEnhancedCode(s string) (EnhancedCode, error) {
 900  	parts := strings.Split(s, ".")
 901  	if len(parts) != 3 {
 902  		return EnhancedCode{}, fmt.Errorf("wrong amount of enhanced code parts")
 903  	}
 904  
 905  	code := EnhancedCode{}
 906  	for i, part := range parts {
 907  		num, err := strconv.Atoi(part)
 908  		if err != nil {
 909  			return code, err
 910  		}
 911  		code[i] = num
 912  	}
 913  	return code, nil
 914  }
 915  
 916  // toSMTPErr converts textproto.Error into SMTPError, parsing
 917  // enhanced status code if it is present.
 918  func toSMTPErr(protoErr *textproto.Error) *SMTPError {
 919  	smtpErr := &SMTPError{
 920  		Code:    protoErr.Code,
 921  		Message: protoErr.Msg,
 922  	}
 923  
 924  	parts := strings.SplitN(protoErr.Msg, " ", 2)
 925  	if len(parts) != 2 {
 926  		return smtpErr
 927  	}
 928  
 929  	enchCode, err := parseEnhancedCode(parts[0])
 930  	if err != nil {
 931  		return smtpErr
 932  	}
 933  
 934  	msg := parts[1]
 935  
 936  	// Per RFC 2034, enhanced code should be prepended to each line.
 937  	msg = strings.ReplaceAll(msg, "\n"+parts[0]+" ", "\n")
 938  
 939  	smtpErr.EnhancedCode = enchCode
 940  	smtpErr.Message = msg
 941  	return smtpErr
 942  }
 943  
 944  type clientDebugWriter struct {
 945  	c *Client
 946  }
 947  
 948  func (cdw clientDebugWriter) Write(b []byte) (int, error) {
 949  	if cdw.c.DebugWriter == nil {
 950  		return len(b), nil
 951  	}
 952  	return cdw.c.DebugWriter.Write(b)
 953  }
 954  
 955  // validateLine checks to see if a line has CR or LF.
 956  func validateLine(line string) error {
 957  	if strings.ContainsAny(line, "\n\r") {
 958  		return errors.New("smtp: a line must not contain CR or LF")
 959  	}
 960  	return nil
 961  }
 962