conn.go raw

   1  package smtp
   2  
   3  import (
   4  	"crypto/tls"
   5  	"encoding/base64"
   6  	"errors"
   7  	"fmt"
   8  	"io"
   9  	"io/ioutil"
  10  	"net"
  11  	"net/textproto"
  12  	"regexp"
  13  	"runtime/debug"
  14  	"strconv"
  15  	"strings"
  16  	"sync"
  17  	"time"
  18  
  19  	"github.com/emersion/go-sasl"
  20  )
  21  
  22  // Number of errors we'll tolerate per connection before closing. Defaults to 3.
  23  const errThreshold = 3
  24  
  25  type Conn struct {
  26  	conn   net.Conn
  27  	text   *textproto.Conn
  28  	server *Server
  29  	helo   string
  30  
  31  	// Number of errors witnessed on this connection
  32  	errCount int
  33  
  34  	session    Session
  35  	locker     sync.Mutex
  36  	binarymime bool
  37  
  38  	lineLimitReader *lineLimitReader
  39  	bdatPipe        *io.PipeWriter
  40  	bdatStatus      *statusCollector // used for BDAT on LMTP
  41  	dataResult      chan error
  42  	bytesReceived   int64 // counts total size of chunks when BDAT is used
  43  
  44  	fromReceived bool
  45  	recipients   []string
  46  	didAuth      bool
  47  }
  48  
  49  func newConn(c net.Conn, s *Server) *Conn {
  50  	sc := &Conn{
  51  		server: s,
  52  		conn:   c,
  53  	}
  54  
  55  	sc.init()
  56  	return sc
  57  }
  58  
  59  func (c *Conn) init() {
  60  	c.lineLimitReader = &lineLimitReader{
  61  		R:         c.conn,
  62  		LineLimit: c.server.MaxLineLength,
  63  	}
  64  	rwc := struct {
  65  		io.Reader
  66  		io.Writer
  67  		io.Closer
  68  	}{
  69  		Reader: c.lineLimitReader,
  70  		Writer: c.conn,
  71  		Closer: c.conn,
  72  	}
  73  
  74  	if c.server.Debug != nil {
  75  		rwc = struct {
  76  			io.Reader
  77  			io.Writer
  78  			io.Closer
  79  		}{
  80  			io.TeeReader(rwc.Reader, c.server.Debug),
  81  			io.MultiWriter(rwc.Writer, c.server.Debug),
  82  			rwc.Closer,
  83  		}
  84  	}
  85  
  86  	c.text = textproto.NewConn(rwc)
  87  }
  88  
  89  // Commands are dispatched to the appropriate handler functions.
  90  func (c *Conn) handle(cmd string, arg string) {
  91  	// If panic happens during command handling - send 421 response
  92  	// and close connection.
  93  	defer func() {
  94  		if err := recover(); err != nil {
  95  			c.writeResponse(421, EnhancedCode{4, 0, 0}, "Internal server error")
  96  			c.Close()
  97  
  98  			stack := debug.Stack()
  99  			c.server.ErrorLog.Printf("panic serving %v: %v\n%s", c.conn.RemoteAddr(), err, stack)
 100  		}
 101  	}()
 102  
 103  	if cmd == "" {
 104  		c.protocolError(500, EnhancedCode{5, 5, 2}, "Error: bad syntax")
 105  		return
 106  	}
 107  
 108  	cmd = strings.ToUpper(cmd)
 109  	switch cmd {
 110  	case "SEND", "SOML", "SAML", "EXPN", "HELP", "TURN":
 111  		// These commands are not implemented in any state
 112  		c.writeResponse(502, EnhancedCode{5, 5, 1}, fmt.Sprintf("%v command not implemented", cmd))
 113  	case "HELO", "EHLO", "LHLO":
 114  		lmtp := cmd == "LHLO"
 115  		enhanced := lmtp || cmd == "EHLO"
 116  		if c.server.LMTP && !lmtp {
 117  			c.writeResponse(500, EnhancedCode{5, 5, 1}, "This is a LMTP server, use LHLO")
 118  			return
 119  		}
 120  		if !c.server.LMTP && lmtp {
 121  			c.writeResponse(500, EnhancedCode{5, 5, 1}, "This is not a LMTP server")
 122  			return
 123  		}
 124  		c.handleGreet(enhanced, arg)
 125  	case "MAIL":
 126  		c.handleMail(arg)
 127  	case "RCPT":
 128  		c.handleRcpt(arg)
 129  	case "VRFY":
 130  		c.writeResponse(252, EnhancedCode{2, 5, 0}, "Cannot VRFY user, but will accept message")
 131  	case "NOOP":
 132  		c.writeResponse(250, EnhancedCode{2, 0, 0}, "I have successfully done nothing")
 133  	case "RSET": // Reset session
 134  		c.reset()
 135  		c.writeResponse(250, EnhancedCode{2, 0, 0}, "Session reset")
 136  	case "BDAT":
 137  		c.handleBdat(arg)
 138  	case "DATA":
 139  		c.handleData(arg)
 140  	case "QUIT":
 141  		c.writeResponse(221, EnhancedCode{2, 0, 0}, "Bye")
 142  		c.Close()
 143  	case "AUTH":
 144  		c.handleAuth(arg)
 145  	case "STARTTLS":
 146  		c.handleStartTLS()
 147  	default:
 148  		msg := fmt.Sprintf("Syntax errors, %v command unrecognized", cmd)
 149  		c.protocolError(500, EnhancedCode{5, 5, 2}, msg)
 150  	}
 151  }
 152  
 153  func (c *Conn) Server() *Server {
 154  	return c.server
 155  }
 156  
 157  func (c *Conn) Session() Session {
 158  	c.locker.Lock()
 159  	defer c.locker.Unlock()
 160  	return c.session
 161  }
 162  
 163  func (c *Conn) setSession(session Session) {
 164  	c.locker.Lock()
 165  	defer c.locker.Unlock()
 166  	c.session = session
 167  }
 168  
 169  func (c *Conn) Close() error {
 170  	c.locker.Lock()
 171  	defer c.locker.Unlock()
 172  
 173  	if c.bdatPipe != nil {
 174  		c.bdatPipe.CloseWithError(ErrDataReset)
 175  		c.bdatPipe = nil
 176  	}
 177  
 178  	if c.session != nil {
 179  		c.session.Logout()
 180  		c.session = nil
 181  	}
 182  
 183  	return c.conn.Close()
 184  }
 185  
 186  // TLSConnectionState returns the connection's TLS connection state.
 187  // Zero values are returned if the connection doesn't use TLS.
 188  func (c *Conn) TLSConnectionState() (state tls.ConnectionState, ok bool) {
 189  	tc, ok := c.conn.(*tls.Conn)
 190  	if !ok {
 191  		return
 192  	}
 193  	return tc.ConnectionState(), true
 194  }
 195  
 196  func (c *Conn) Hostname() string {
 197  	return c.helo
 198  }
 199  
 200  func (c *Conn) Conn() net.Conn {
 201  	return c.conn
 202  }
 203  
 204  func (c *Conn) authAllowed() bool {
 205  	_, isTLS := c.TLSConnectionState()
 206  	return isTLS || c.server.AllowInsecureAuth
 207  }
 208  
 209  // protocolError writes errors responses and closes the connection once too many
 210  // have occurred.
 211  func (c *Conn) protocolError(code int, ec EnhancedCode, msg string) {
 212  	c.writeResponse(code, ec, msg)
 213  
 214  	c.errCount++
 215  	if c.errCount > errThreshold {
 216  		c.writeResponse(500, EnhancedCode{5, 5, 1}, "Too many errors. Quiting now")
 217  		c.Close()
 218  	}
 219  }
 220  
 221  // GREET state -> waiting for HELO
 222  func (c *Conn) handleGreet(enhanced bool, arg string) {
 223  	domain, err := parseHelloArgument(arg)
 224  	if err != nil {
 225  		c.writeResponse(501, EnhancedCode{5, 5, 2}, "Domain/address argument required for HELO")
 226  		return
 227  	}
 228  	// c.helo is populated before NewSession so
 229  	// NewSession can access it via Conn.Hostname.
 230  	c.helo = domain
 231  
 232  	// RFC 5321: "An EHLO command MAY be issued by a client later in the session"
 233  	if c.session != nil {
 234  		// RFC 5321: "... the SMTP server MUST clear all buffers
 235  		// and reset the state exactly as if a RSET command has been issued."
 236  		c.reset()
 237  	} else {
 238  		sess, err := c.server.Backend.NewSession(c)
 239  		if err != nil {
 240  			c.helo = ""
 241  			c.writeError(451, EnhancedCode{4, 0, 0}, err)
 242  			return
 243  		}
 244  
 245  		c.setSession(sess)
 246  	}
 247  
 248  	if !enhanced {
 249  		c.writeResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("Hello %s", domain))
 250  		return
 251  	}
 252  
 253  	caps := []string{
 254  		"PIPELINING",
 255  		"8BITMIME",
 256  		"ENHANCEDSTATUSCODES",
 257  		"CHUNKING",
 258  	}
 259  	if _, isTLS := c.TLSConnectionState(); c.server.TLSConfig != nil && !isTLS {
 260  		caps = append(caps, "STARTTLS")
 261  	}
 262  	if c.authAllowed() {
 263  		mechs := c.authMechanisms()
 264  
 265  		authCap := "AUTH"
 266  		for _, name := range mechs {
 267  			authCap += " " + name
 268  		}
 269  
 270  		if len(mechs) > 0 {
 271  			caps = append(caps, authCap)
 272  		}
 273  	}
 274  	if c.server.EnableSMTPUTF8 {
 275  		caps = append(caps, "SMTPUTF8")
 276  	}
 277  	if _, isTLS := c.TLSConnectionState(); isTLS && c.server.EnableREQUIRETLS {
 278  		caps = append(caps, "REQUIRETLS")
 279  	}
 280  	if c.server.EnableBINARYMIME {
 281  		caps = append(caps, "BINARYMIME")
 282  	}
 283  	if c.server.EnableDSN {
 284  		caps = append(caps, "DSN")
 285  	}
 286  	if c.server.MaxMessageBytes > 0 {
 287  		caps = append(caps, fmt.Sprintf("SIZE %v", c.server.MaxMessageBytes))
 288  	} else {
 289  		caps = append(caps, "SIZE")
 290  	}
 291  	if c.server.MaxRecipients > 0 {
 292  		caps = append(caps, fmt.Sprintf("LIMITS RCPTMAX=%v", c.server.MaxRecipients))
 293  	}
 294  	if c.server.EnableRRVS {
 295  		caps = append(caps, "RRVS")
 296  	}
 297  	if c.server.EnableDELIVERBY {
 298  		if c.server.MinimumDeliverByTime == 0 {
 299  			caps = append(caps, "DELIVERBY")
 300  		} else {
 301  			caps = append(caps, fmt.Sprintf("DELIVERBY %d", int(c.server.MinimumDeliverByTime.Seconds())))
 302  		}
 303  	}
 304  	if c.server.EnableMTPRIORITY {
 305  		if c.server.MtPriorityProfile == PriorityUnspecified {
 306  			caps = append(caps, "MT-PRIORITY")
 307  		} else {
 308  			caps = append(caps, fmt.Sprintf("MT-PRIORITY %s", c.server.MtPriorityProfile))
 309  		}
 310  	}
 311  
 312  	args := []string{"Hello " + domain}
 313  	args = append(args, caps...)
 314  	c.writeResponse(250, NoEnhancedCode, args...)
 315  }
 316  
 317  // READY state -> waiting for MAIL
 318  func (c *Conn) handleMail(arg string) {
 319  	if c.helo == "" {
 320  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "Please introduce yourself first.")
 321  		return
 322  	}
 323  	if c.bdatPipe != nil {
 324  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "MAIL not allowed during message transfer")
 325  		return
 326  	}
 327  
 328  	arg, ok := cutPrefixFold(arg, "FROM:")
 329  	if !ok {
 330  		c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:<address>")
 331  		return
 332  	}
 333  
 334  	p := parser{s: strings.TrimSpace(arg)}
 335  	from, err := p.parseReversePath()
 336  	if err != nil {
 337  		c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:<address>")
 338  		return
 339  	}
 340  	args, err := parseArgs(p.s)
 341  	if err != nil {
 342  		c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse MAIL ESMTP parameters")
 343  		return
 344  	}
 345  
 346  	opts := &MailOptions{}
 347  
 348  	c.binarymime = false
 349  	// This is where the Conn may put BODY=8BITMIME, but we already
 350  	// read the DATA as bytes, so it does not effect our processing.
 351  	for key, value := range args {
 352  		switch key {
 353  		case "SIZE":
 354  			size, err := strconv.ParseUint(value, 10, 32)
 355  			if err != nil {
 356  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse SIZE as an integer")
 357  				return
 358  			}
 359  
 360  			if c.server.MaxMessageBytes > 0 && int64(size) > c.server.MaxMessageBytes {
 361  				c.writeResponse(552, EnhancedCode{5, 3, 4}, "Max message size exceeded")
 362  				return
 363  			}
 364  
 365  			opts.Size = int64(size)
 366  		case "SMTPUTF8":
 367  			if !c.server.EnableSMTPUTF8 {
 368  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "SMTPUTF8 is not implemented")
 369  				return
 370  			}
 371  			opts.UTF8 = true
 372  		case "REQUIRETLS":
 373  			if !c.server.EnableREQUIRETLS {
 374  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "REQUIRETLS is not implemented")
 375  				return
 376  			}
 377  			opts.RequireTLS = true
 378  		case "BODY":
 379  			value = strings.ToUpper(value)
 380  			switch BodyType(value) {
 381  			case BodyBinaryMIME:
 382  				if !c.server.EnableBINARYMIME {
 383  					c.writeResponse(504, EnhancedCode{5, 5, 4}, "BINARYMIME is not implemented")
 384  					return
 385  				}
 386  				c.binarymime = true
 387  			case Body7Bit, Body8BitMIME:
 388  				// This space is intentionally left blank
 389  			default:
 390  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unknown BODY value")
 391  				return
 392  			}
 393  			opts.Body = BodyType(value)
 394  		case "RET":
 395  			if !c.server.EnableDSN {
 396  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "RET is not implemented")
 397  				return
 398  			}
 399  			value = strings.ToUpper(value)
 400  			switch DSNReturn(value) {
 401  			case DSNReturnFull, DSNReturnHeaders:
 402  				// This space is intentionally left blank
 403  			default:
 404  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unknown RET value")
 405  				return
 406  			}
 407  			opts.Return = DSNReturn(value)
 408  		case "ENVID":
 409  			if !c.server.EnableDSN {
 410  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "ENVID is not implemented")
 411  				return
 412  			}
 413  			value, err := decodeXtext(value)
 414  			if err != nil || value == "" || !isPrintableASCII(value) {
 415  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed ENVID parameter value")
 416  				return
 417  			}
 418  			opts.EnvelopeID = value
 419  		case "AUTH":
 420  			value, err := decodeXtext(value)
 421  			if err != nil || value == "" {
 422  				c.writeResponse(500, EnhancedCode{5, 5, 4}, "Malformed AUTH parameter value")
 423  				return
 424  			}
 425  			if value == "<>" {
 426  				value = ""
 427  			} else {
 428  				p := parser{s: value}
 429  				value, err = p.parseMailbox()
 430  				if err != nil || p.s != "" {
 431  					c.writeResponse(500, EnhancedCode{5, 5, 4}, "Malformed AUTH parameter mailbox")
 432  					return
 433  				}
 434  			}
 435  			opts.Auth = &value
 436  		default:
 437  			c.writeResponse(500, EnhancedCode{5, 5, 4}, "Unknown MAIL FROM argument")
 438  			return
 439  		}
 440  	}
 441  
 442  	if err := c.Session().Mail(from, opts); err != nil {
 443  		c.writeError(451, EnhancedCode{4, 0, 0}, err)
 444  		return
 445  	}
 446  
 447  	c.writeResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("Roger, accepting mail from <%v>", from))
 448  	c.fromReceived = true
 449  }
 450  
 451  // This regexp matches 'hexchar' token defined in
 452  // https://tools.ietf.org/html/rfc4954#section-8 however it is intentionally
 453  // relaxed by requiring only '+' to be present.  It allows us to detect
 454  // malformed values such as +A or +HH and report them appropriately.
 455  var hexcharRe = regexp.MustCompile(`\+[0-9A-F]?[0-9A-F]?`)
 456  
 457  func decodeXtext(val string) (string, error) {
 458  	if !strings.Contains(val, "+") {
 459  		return val, nil
 460  	}
 461  
 462  	var replaceErr error
 463  	decoded := hexcharRe.ReplaceAllStringFunc(val, func(match string) string {
 464  		if len(match) != 3 {
 465  			replaceErr = errors.New("incomplete hexchar")
 466  			return ""
 467  		}
 468  		char, err := strconv.ParseInt(match, 16, 8)
 469  		if err != nil {
 470  			replaceErr = err
 471  			return ""
 472  		}
 473  
 474  		return string(rune(char))
 475  	})
 476  	if replaceErr != nil {
 477  		return "", replaceErr
 478  	}
 479  
 480  	return decoded, nil
 481  }
 482  
 483  // This regexp matches 'EmbeddedUnicodeChar' token defined in
 484  // https://datatracker.ietf.org/doc/html/rfc6533.html#section-3
 485  // however it is intentionally relaxed by requiring only '\x{HEX}' to be
 486  // present.  It also matches disallowed characters in QCHAR and QUCHAR defined
 487  // in above.
 488  // So it allows us to detect malformed values and report them appropriately.
 489  var eUOrDCharRe = regexp.MustCompile(`\\x[{][0-9A-F]+[}]|[[:cntrl:] \\+=]`)
 490  
 491  // Decodes the utf-8-addr-xtext or the utf-8-addr-unitext form.
 492  func decodeUTF8AddrXtext(val string) (string, error) {
 493  	var replaceErr error
 494  	decoded := eUOrDCharRe.ReplaceAllStringFunc(val, func(match string) string {
 495  		if len(match) == 1 {
 496  			replaceErr = errors.New("disallowed character:" + match)
 497  			return ""
 498  		}
 499  
 500  		hexpoint := match[3 : len(match)-1]
 501  		char, err := strconv.ParseUint(hexpoint, 16, 21)
 502  		if err != nil {
 503  			replaceErr = err
 504  			return ""
 505  		}
 506  		switch len(hexpoint) {
 507  		case 2:
 508  			switch {
 509  			// all xtext-specials
 510  			case 0x01 <= char && char <= 0x09 ||
 511  				0x11 <= char && char <= 0x19 ||
 512  				char == 0x10 || char == 0x20 ||
 513  				char == 0x2B || char == 0x3D || char == 0x7F:
 514  			// 2-digit forms
 515  			case char == 0x5C || 0x80 <= char && char <= 0xFF:
 516  				// This space is intentionally left blank
 517  			default:
 518  				replaceErr = errors.New("illegal hexpoint:" + hexpoint)
 519  				return ""
 520  			}
 521  		// 3-digit forms
 522  		case 3:
 523  			switch {
 524  			case 0x100 <= char && char <= 0xFFF:
 525  				// This space is intentionally left blank
 526  			default:
 527  				replaceErr = errors.New("illegal hexpoint:" + hexpoint)
 528  				return ""
 529  			}
 530  		// 4-digit forms excluding surrogate
 531  		case 4:
 532  			switch {
 533  			case 0x1000 <= char && char <= 0xD7FF:
 534  			case 0xE000 <= char && char <= 0xFFFF:
 535  				// This space is intentionally left blank
 536  			default:
 537  				replaceErr = errors.New("illegal hexpoint:" + hexpoint)
 538  				return ""
 539  			}
 540  		// 5-digit forms
 541  		case 5:
 542  			switch {
 543  			case 0x1_0000 <= char && char <= 0xF_FFFF:
 544  				// This space is intentionally left blank
 545  			default:
 546  				replaceErr = errors.New("illegal hexpoint:" + hexpoint)
 547  				return ""
 548  			}
 549  		// 6-digit forms
 550  		case 6:
 551  			switch {
 552  			case 0x10_0000 <= char && char <= 0x10_FFFF:
 553  				// This space is intentionally left blank
 554  			default:
 555  				replaceErr = errors.New("illegal hexpoint:" + hexpoint)
 556  				return ""
 557  			}
 558  		// the other invalid forms
 559  		default:
 560  			replaceErr = errors.New("illegal hexpoint:" + hexpoint)
 561  			return ""
 562  		}
 563  
 564  		return string(rune(char))
 565  	})
 566  	if replaceErr != nil {
 567  		return "", replaceErr
 568  	}
 569  
 570  	return decoded, nil
 571  }
 572  
 573  func decodeTypedAddress(val string) (DSNAddressType, string, error) {
 574  	tv := strings.SplitN(val, ";", 2)
 575  	if len(tv) != 2 || tv[0] == "" || tv[1] == "" {
 576  		return "", "", errors.New("bad address")
 577  	}
 578  	aType, aAddr := strings.ToUpper(tv[0]), tv[1]
 579  
 580  	var err error
 581  	switch DSNAddressType(aType) {
 582  	case DSNAddressTypeRFC822:
 583  		aAddr, err = decodeXtext(aAddr)
 584  		if err == nil && !isPrintableASCII(aAddr) {
 585  			err = errors.New("illegal address:" + aAddr)
 586  		}
 587  	case DSNAddressTypeUTF8:
 588  		aAddr, err = decodeUTF8AddrXtext(aAddr)
 589  	default:
 590  		err = errors.New("unknown address type:" + aType)
 591  	}
 592  	if err != nil {
 593  		return "", "", err
 594  	}
 595  
 596  	return DSNAddressType(aType), aAddr, nil
 597  }
 598  
 599  func encodeXtext(raw string) string {
 600  	var out strings.Builder
 601  	out.Grow(len(raw))
 602  
 603  	for _, ch := range raw {
 604  		switch {
 605  		case ch >= '!' && ch <= '~' && ch != '+' && ch != '=':
 606  			// printable non-space US-ASCII except '+' and '='
 607  			out.WriteRune(ch)
 608  		default:
 609  			out.WriteRune('+')
 610  			out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
 611  		}
 612  	}
 613  	return out.String()
 614  }
 615  
 616  // Encodes raw string to the utf-8-addr-xtext form in RFC 6533.
 617  func encodeUTF8AddrXtext(raw string) string {
 618  	var out strings.Builder
 619  	out.Grow(len(raw))
 620  
 621  	for _, ch := range raw {
 622  		switch {
 623  		case ch >= '!' && ch <= '~' && ch != '+' && ch != '=':
 624  			// printable non-space US-ASCII except '+' and '='
 625  			out.WriteRune(ch)
 626  		default:
 627  			out.WriteRune('\\')
 628  			out.WriteRune('x')
 629  			out.WriteRune('{')
 630  			out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
 631  			out.WriteRune('}')
 632  		}
 633  	}
 634  	return out.String()
 635  }
 636  
 637  // Encodes raw string to the utf-8-addr-unitext form in RFC 6533.
 638  func encodeUTF8AddrUnitext(raw string) string {
 639  	var out strings.Builder
 640  	out.Grow(len(raw))
 641  
 642  	for _, ch := range raw {
 643  		switch {
 644  		case ch >= '!' && ch <= '~' && ch != '+' && ch != '=':
 645  			// printable non-space US-ASCII except '+' and '='
 646  			out.WriteRune(ch)
 647  		case ch <= '\x7F':
 648  			// other ASCII: CTLs, space and specials
 649  			out.WriteRune('\\')
 650  			out.WriteRune('x')
 651  			out.WriteRune('{')
 652  			out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
 653  			out.WriteRune('}')
 654  		default:
 655  			// UTF-8 non-ASCII
 656  			out.WriteRune(ch)
 657  		}
 658  	}
 659  	return out.String()
 660  }
 661  
 662  func isPrintableASCII(val string) bool {
 663  	for _, ch := range val {
 664  		if ch < ' ' || '~' < ch {
 665  			return false
 666  		}
 667  	}
 668  	return true
 669  }
 670  
 671  // MAIL state -> waiting for RCPTs followed by DATA
 672  func (c *Conn) handleRcpt(arg string) {
 673  	if !c.fromReceived {
 674  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "Missing MAIL FROM command.")
 675  		return
 676  	}
 677  	if c.bdatPipe != nil {
 678  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "RCPT not allowed during message transfer")
 679  		return
 680  	}
 681  
 682  	arg, ok := cutPrefixFold(arg, "TO:")
 683  	if !ok {
 684  		c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting RCPT arg syntax of TO:<address>")
 685  		return
 686  	}
 687  
 688  	p := parser{s: strings.TrimSpace(arg)}
 689  	recipient, err := p.parsePath()
 690  	if err != nil {
 691  		c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting RCPT arg syntax of TO:<address>")
 692  		return
 693  	}
 694  
 695  	if c.server.MaxRecipients > 0 && len(c.recipients) >= c.server.MaxRecipients {
 696  		c.writeResponse(452, EnhancedCode{4, 5, 3}, fmt.Sprintf("Maximum limit of %v recipients reached", c.server.MaxRecipients))
 697  		return
 698  	}
 699  
 700  	args, err := parseArgs(p.s)
 701  	if err != nil {
 702  		c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse RCPT ESMTP parameters")
 703  		return
 704  	}
 705  
 706  	opts := &RcptOptions{}
 707  
 708  	for key, value := range args {
 709  		switch key {
 710  		case "NOTIFY":
 711  			if !c.server.EnableDSN {
 712  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "NOTIFY is not implemented")
 713  				return
 714  			}
 715  			notify := []DSNNotify{}
 716  			for _, val := range strings.Split(value, ",") {
 717  				notify = append(notify, DSNNotify(strings.ToUpper(val)))
 718  			}
 719  			if err := checkNotifySet(notify); err != nil {
 720  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed NOTIFY parameter value")
 721  				return
 722  			}
 723  			opts.Notify = notify
 724  		case "ORCPT":
 725  			if !c.server.EnableDSN {
 726  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "ORCPT is not implemented")
 727  				return
 728  			}
 729  			aType, aAddr, err := decodeTypedAddress(value)
 730  			if err != nil || aAddr == "" {
 731  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed ORCPT parameter value")
 732  				return
 733  			}
 734  			opts.OriginalRecipientType = aType
 735  			opts.OriginalRecipient = aAddr
 736  		case "RRVS":
 737  			if !c.server.EnableRRVS {
 738  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "RRVS is not implemented")
 739  				return
 740  			}
 741  			value, _, _ = strings.Cut(value, ";") // discard the no-support action
 742  			rrvsTime, err := time.Parse(time.RFC3339, value)
 743  			if err != nil {
 744  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed RRVS parameter value")
 745  				return
 746  			}
 747  			opts.RequireRecipientValidSince = rrvsTime
 748  		case "BY":
 749  			if !c.server.EnableDELIVERBY {
 750  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "DELIVERBY is not implemented")
 751  				return
 752  			}
 753  			deliverBy := parseDeliverByArgument(value)
 754  			if deliverBy == nil {
 755  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed BY parameter value")
 756  				return
 757  			}
 758  			if c.server.MinimumDeliverByTime != 0 &&
 759  				deliverBy.Mode == DeliverByReturn &&
 760  				deliverBy.Time < c.server.MinimumDeliverByTime {
 761  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "BY parameter is below server minimum")
 762  				return
 763  			}
 764  			opts.DeliverBy = deliverBy
 765  		case "MT-PRIORITY":
 766  			if !c.server.EnableMTPRIORITY {
 767  				c.writeResponse(504, EnhancedCode{5, 5, 4}, "MT-PRIORITY is not implemented")
 768  				return
 769  			}
 770  			mtPriority, err := strconv.Atoi(value)
 771  			if err != nil {
 772  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed MT-PRIORITY parameter value")
 773  				return
 774  			}
 775  			if mtPriority < -9 || mtPriority > 9 {
 776  				c.writeResponse(501, EnhancedCode{5, 5, 4}, "MT-PRIORITY is outside valid range")
 777  				return
 778  			}
 779  			opts.MTPriority = &mtPriority
 780  		default:
 781  			c.writeResponse(500, EnhancedCode{5, 5, 4}, "Unknown RCPT TO argument")
 782  			return
 783  		}
 784  	}
 785  
 786  	if err := c.Session().Rcpt(recipient, opts); err != nil {
 787  		c.writeError(451, EnhancedCode{4, 0, 0}, err)
 788  		return
 789  	}
 790  	c.recipients = append(c.recipients, recipient)
 791  	c.writeResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("I'll make sure <%v> gets this", recipient))
 792  }
 793  
 794  func checkNotifySet(values []DSNNotify) error {
 795  	if len(values) == 0 {
 796  		return errors.New("Malformed NOTIFY parameter value")
 797  	}
 798  
 799  	seen := map[DSNNotify]struct{}{}
 800  	for _, val := range values {
 801  		switch val {
 802  		case DSNNotifyNever, DSNNotifyDelayed, DSNNotifyFailure, DSNNotifySuccess:
 803  			if _, ok := seen[val]; ok {
 804  				return errors.New("Malformed NOTIFY parameter value")
 805  			}
 806  		default:
 807  			return errors.New("Malformed NOTIFY parameter value")
 808  		}
 809  		seen[val] = struct{}{}
 810  	}
 811  	if _, ok := seen[DSNNotifyNever]; ok && len(seen) > 1 {
 812  		return errors.New("Malformed NOTIFY parameter value")
 813  	}
 814  
 815  	return nil
 816  }
 817  
 818  func (c *Conn) handleAuth(arg string) {
 819  	if c.helo == "" {
 820  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "Please introduce yourself first.")
 821  		return
 822  	}
 823  	if c.didAuth {
 824  		c.writeResponse(503, EnhancedCode{5, 5, 1}, "Already authenticated")
 825  		return
 826  	}
 827  
 828  	parts := strings.Fields(arg)
 829  	if len(parts) == 0 {
 830  		c.writeResponse(502, EnhancedCode{5, 5, 4}, "Missing parameter")
 831  		return
 832  	}
 833  
 834  	if !c.authAllowed() {
 835  		c.writeResponse(523, EnhancedCode{5, 7, 10}, "TLS is required")
 836  		return
 837  	}
 838  
 839  	mechanism := strings.ToUpper(parts[0])
 840  
 841  	// Parse client initial response if there is one
 842  	var ir []byte
 843  	if len(parts) > 1 {
 844  		var err error
 845  		ir, err = decodeSASLResponse(parts[1])
 846  		if err != nil {
 847  			c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data")
 848  			return
 849  		}
 850  	}
 851  
 852  	sasl, err := c.auth(mechanism)
 853  	if err != nil {
 854  		c.writeError(454, EnhancedCode{4, 7, 0}, err)
 855  		return
 856  	}
 857  
 858  	response := ir
 859  	for {
 860  		challenge, done, err := sasl.Next(response)
 861  		if err != nil {
 862  			c.writeError(454, EnhancedCode{4, 7, 0}, err)
 863  			return
 864  		}
 865  
 866  		if done {
 867  			break
 868  		}
 869  
 870  		encoded := ""
 871  		if len(challenge) > 0 {
 872  			encoded = base64.StdEncoding.EncodeToString(challenge)
 873  		}
 874  		c.writeResponse(334, NoEnhancedCode, encoded)
 875  
 876  		encoded, err = c.readLine()
 877  		if err != nil {
 878  			return // TODO: error handling
 879  		}
 880  
 881  		if encoded == "*" {
 882  			// https://tools.ietf.org/html/rfc4954#page-4
 883  			c.writeResponse(501, EnhancedCode{5, 0, 0}, "Negotiation cancelled")
 884  			return
 885  		}
 886  
 887  		response, err = decodeSASLResponse(encoded)
 888  		if err != nil {
 889  			c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data")
 890  			return
 891  		}
 892  	}
 893  
 894  	c.writeResponse(235, EnhancedCode{2, 0, 0}, "Authentication succeeded")
 895  	c.didAuth = true
 896  }
 897  
 898  func decodeSASLResponse(s string) ([]byte, error) {
 899  	if s == "=" {
 900  		return []byte{}, nil
 901  	}
 902  	return base64.StdEncoding.DecodeString(s)
 903  }
 904  
 905  func (c *Conn) authMechanisms() []string {
 906  	if authSession, ok := c.Session().(AuthSession); ok {
 907  		return authSession.AuthMechanisms()
 908  	}
 909  	return nil
 910  }
 911  
 912  func (c *Conn) auth(mech string) (sasl.Server, error) {
 913  	if authSession, ok := c.Session().(AuthSession); ok {
 914  		return authSession.Auth(mech)
 915  	}
 916  	return nil, ErrAuthUnknownMechanism
 917  }
 918  
 919  func (c *Conn) handleStartTLS() {
 920  	if _, isTLS := c.TLSConnectionState(); isTLS {
 921  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "Already running in TLS")
 922  		return
 923  	}
 924  
 925  	if c.server.TLSConfig == nil {
 926  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "TLS not supported")
 927  		return
 928  	}
 929  
 930  	c.writeResponse(220, EnhancedCode{2, 0, 0}, "Ready to start TLS")
 931  
 932  	// Upgrade to TLS
 933  	tlsConn := tls.Server(c.conn, c.server.TLSConfig)
 934  
 935  	if err := tlsConn.Handshake(); err != nil {
 936  		c.writeResponse(550, EnhancedCode{5, 0, 0}, "Handshake error")
 937  		return
 938  	}
 939  
 940  	c.conn = tlsConn
 941  	c.init()
 942  
 943  	// Reset all state and close the previous Session.
 944  	// This is different from just calling reset() since we want the Backend to
 945  	// be able to see the information about TLS connection in the
 946  	// ConnectionState object passed to it.
 947  	if session := c.Session(); session != nil {
 948  		session.Logout()
 949  		c.setSession(nil)
 950  	}
 951  	c.helo = ""
 952  	c.didAuth = false
 953  	c.reset()
 954  }
 955  
 956  // DATA
 957  func (c *Conn) handleData(arg string) {
 958  	if arg != "" {
 959  		c.writeResponse(501, EnhancedCode{5, 5, 4}, "DATA command should not have any arguments")
 960  		return
 961  	}
 962  	if c.bdatPipe != nil {
 963  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "DATA not allowed during message transfer")
 964  		return
 965  	}
 966  	if c.binarymime {
 967  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "DATA not allowed for BINARYMIME messages")
 968  		return
 969  	}
 970  
 971  	if !c.fromReceived || len(c.recipients) == 0 {
 972  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "Missing RCPT TO command.")
 973  		return
 974  	}
 975  
 976  	// We have recipients, go to accept data
 977  	c.writeResponse(354, NoEnhancedCode, "Go ahead. End your data with <CR><LF>.<CR><LF>")
 978  
 979  	defer c.reset()
 980  
 981  	if c.server.LMTP {
 982  		c.handleDataLMTP()
 983  		return
 984  	}
 985  
 986  	r := newDataReader(c)
 987  	code, enhancedCode, msg := dataErrorToStatus(c.Session().Data(r))
 988  	r.limited = false
 989  	io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed
 990  	c.writeResponse(code, enhancedCode, msg)
 991  }
 992  
 993  func (c *Conn) handleBdat(arg string) {
 994  	args := strings.Fields(arg)
 995  	if len(args) == 0 {
 996  		c.writeResponse(501, EnhancedCode{5, 5, 4}, "Missing chunk size argument")
 997  		return
 998  	}
 999  	if len(args) > 2 {
1000  		c.writeResponse(501, EnhancedCode{5, 5, 4}, "Too many arguments")
1001  		return
1002  	}
1003  
1004  	if !c.fromReceived || len(c.recipients) == 0 {
1005  		c.writeResponse(502, EnhancedCode{5, 5, 1}, "Missing RCPT TO command.")
1006  		return
1007  	}
1008  
1009  	last := false
1010  	if len(args) == 2 {
1011  		if !strings.EqualFold(args[1], "LAST") {
1012  			c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unknown BDAT argument")
1013  			return
1014  		}
1015  		last = true
1016  	}
1017  
1018  	// ParseUint instead of Atoi so we will not accept negative values.
1019  	size, err := strconv.ParseUint(args[0], 10, 32)
1020  	if err != nil {
1021  		c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed size argument")
1022  		return
1023  	}
1024  
1025  	if c.server.MaxMessageBytes != 0 && c.bytesReceived+int64(size) > c.server.MaxMessageBytes {
1026  		c.writeResponse(552, EnhancedCode{5, 3, 4}, "Max message size exceeded")
1027  
1028  		// Discard chunk itself without passing it to backend.
1029  		io.Copy(ioutil.Discard, io.LimitReader(c.text.R, int64(size)))
1030  
1031  		c.reset()
1032  		return
1033  	}
1034  
1035  	if c.bdatStatus == nil && c.server.LMTP {
1036  		c.bdatStatus = c.createStatusCollector()
1037  	}
1038  
1039  	if c.bdatPipe == nil {
1040  		var r *io.PipeReader
1041  		r, c.bdatPipe = io.Pipe()
1042  
1043  		c.dataResult = make(chan error, 1)
1044  
1045  		go func() {
1046  			defer func() {
1047  				if err := recover(); err != nil {
1048  					c.handlePanic(err, c.bdatStatus)
1049  
1050  					c.dataResult <- errPanic
1051  					r.CloseWithError(errPanic)
1052  				}
1053  			}()
1054  
1055  			var err error
1056  			if !c.server.LMTP {
1057  				err = c.Session().Data(r)
1058  			} else {
1059  				lmtpSession, ok := c.Session().(LMTPSession)
1060  				if !ok {
1061  					err = c.Session().Data(r)
1062  					for _, rcpt := range c.recipients {
1063  						c.bdatStatus.SetStatus(rcpt, err)
1064  					}
1065  				} else {
1066  					err = lmtpSession.LMTPData(r, c.bdatStatus)
1067  				}
1068  			}
1069  
1070  			c.dataResult <- err
1071  			r.CloseWithError(err)
1072  		}()
1073  	}
1074  
1075  	c.lineLimitReader.LineLimit = 0
1076  
1077  	chunk := io.LimitReader(c.text.R, int64(size))
1078  	_, err = io.Copy(c.bdatPipe, chunk)
1079  	if err != nil {
1080  		// Backend might return an error early using CloseWithError without consuming
1081  		// the whole chunk.
1082  		io.Copy(ioutil.Discard, chunk)
1083  
1084  		c.writeResponse(dataErrorToStatus(err))
1085  
1086  		if err == errPanic {
1087  			c.Close()
1088  		}
1089  
1090  		c.reset()
1091  		c.lineLimitReader.LineLimit = c.server.MaxLineLength
1092  		return
1093  	}
1094  
1095  	c.bytesReceived += int64(size)
1096  
1097  	if last {
1098  		c.lineLimitReader.LineLimit = c.server.MaxLineLength
1099  
1100  		c.bdatPipe.Close()
1101  
1102  		err := <-c.dataResult
1103  
1104  		if c.server.LMTP {
1105  			c.bdatStatus.fillRemaining(err)
1106  			for i, rcpt := range c.recipients {
1107  				code, enchCode, msg := dataErrorToStatus(<-c.bdatStatus.status[i])
1108  				c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg)
1109  			}
1110  		} else {
1111  			c.writeResponse(dataErrorToStatus(err))
1112  		}
1113  
1114  		if err == errPanic {
1115  			c.Close()
1116  			return
1117  		}
1118  
1119  		c.reset()
1120  	} else {
1121  		c.writeResponse(250, EnhancedCode{2, 0, 0}, "Continue")
1122  	}
1123  }
1124  
1125  // ErrDataReset is returned by Reader pased to Data function if client does not
1126  // send another BDAT command and instead closes connection or issues RSET command.
1127  var ErrDataReset = errors.New("smtp: message transmission aborted")
1128  
1129  var errPanic = &SMTPError{
1130  	Code:         421,
1131  	EnhancedCode: EnhancedCode{4, 0, 0},
1132  	Message:      "Internal server error",
1133  }
1134  
1135  func (c *Conn) handlePanic(err interface{}, status *statusCollector) {
1136  	if status != nil {
1137  		status.fillRemaining(errPanic)
1138  	}
1139  
1140  	stack := debug.Stack()
1141  	c.server.ErrorLog.Printf("panic serving %v: %v\n%s", c.conn.RemoteAddr(), err, stack)
1142  }
1143  
1144  func (c *Conn) createStatusCollector() *statusCollector {
1145  	rcptCounts := make(map[string]int, len(c.recipients))
1146  
1147  	status := &statusCollector{
1148  		statusMap: make(map[string]chan error, len(c.recipients)),
1149  		status:    make([]chan error, 0, len(c.recipients)),
1150  	}
1151  	for _, rcpt := range c.recipients {
1152  		rcptCounts[rcpt]++
1153  	}
1154  	// Create channels with buffer sizes necessary to fit all
1155  	// statuses for a single recipient to avoid deadlocks.
1156  	for rcpt, count := range rcptCounts {
1157  		status.statusMap[rcpt] = make(chan error, count)
1158  	}
1159  	for _, rcpt := range c.recipients {
1160  		status.status = append(status.status, status.statusMap[rcpt])
1161  	}
1162  
1163  	return status
1164  }
1165  
1166  type statusCollector struct {
1167  	// Contains map from recipient to list of channels that are used for that
1168  	// recipient.
1169  	statusMap map[string]chan error
1170  
1171  	// Contains channels from statusMap, in the same
1172  	// order as Conn.recipients.
1173  	status []chan error
1174  }
1175  
1176  // fillRemaining sets status for all recipients SetStatus was not called for before.
1177  func (s *statusCollector) fillRemaining(err error) {
1178  	// Amount of times certain recipient was specified is indicated by the channel
1179  	// buffer size, so once we fill it, we can be confident that we sent
1180  	// at least as much statuses as needed. Extra statuses will be ignored anyway.
1181  chLoop:
1182  	for _, ch := range s.statusMap {
1183  		for {
1184  			select {
1185  			case ch <- err:
1186  			default:
1187  				continue chLoop
1188  			}
1189  		}
1190  	}
1191  }
1192  
1193  func (s *statusCollector) SetStatus(rcptTo string, err error) {
1194  	ch := s.statusMap[rcptTo]
1195  	if ch == nil {
1196  		panic("SetStatus is called for recipient that was not specified before")
1197  	}
1198  
1199  	select {
1200  	case ch <- err:
1201  	default:
1202  		// There enough buffer space to fit all statuses at once, if this is
1203  		// not the case - backend is doing something wrong.
1204  		panic("SetStatus is called more times than particular recipient was specified")
1205  	}
1206  }
1207  
1208  func (c *Conn) handleDataLMTP() {
1209  	r := newDataReader(c)
1210  	status := c.createStatusCollector()
1211  
1212  	done := make(chan bool, 1)
1213  
1214  	lmtpSession, ok := c.Session().(LMTPSession)
1215  	if !ok {
1216  		// Fallback to using a single status for all recipients.
1217  		err := c.Session().Data(r)
1218  		io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed
1219  		for _, rcpt := range c.recipients {
1220  			status.SetStatus(rcpt, err)
1221  		}
1222  		done <- true
1223  	} else {
1224  		go func() {
1225  			defer func() {
1226  				if err := recover(); err != nil {
1227  					status.fillRemaining(&SMTPError{
1228  						Code:         421,
1229  						EnhancedCode: EnhancedCode{4, 0, 0},
1230  						Message:      "Internal server error",
1231  					})
1232  
1233  					stack := debug.Stack()
1234  					c.server.ErrorLog.Printf("panic serving %v: %v\n%s", c.conn.RemoteAddr(), err, stack)
1235  					done <- false
1236  				}
1237  			}()
1238  
1239  			status.fillRemaining(lmtpSession.LMTPData(r, status))
1240  			io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed
1241  			done <- true
1242  		}()
1243  	}
1244  
1245  	for i, rcpt := range c.recipients {
1246  		code, enchCode, msg := dataErrorToStatus(<-status.status[i])
1247  		c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg)
1248  	}
1249  
1250  	// If done gets false, the panic occured in LMTPData and the connection
1251  	// should be closed.
1252  	if !<-done {
1253  		c.Close()
1254  	}
1255  }
1256  
1257  func dataErrorToStatus(err error) (code int, enchCode EnhancedCode, msg string) {
1258  	if err != nil {
1259  		if smtperr, ok := err.(*SMTPError); ok {
1260  			return smtperr.Code, smtperr.EnhancedCode, smtperr.Message
1261  		} else {
1262  			return 554, EnhancedCode{5, 0, 0}, "Error: transaction failed: " + err.Error()
1263  		}
1264  	}
1265  
1266  	return 250, EnhancedCode{2, 0, 0}, "OK: queued"
1267  }
1268  
1269  func (c *Conn) Reject() {
1270  	c.writeResponse(421, EnhancedCode{4, 4, 5}, "Too busy. Try again later.")
1271  	c.Close()
1272  }
1273  
1274  func (c *Conn) greet() {
1275  	protocol := "ESMTP"
1276  	if c.server.LMTP {
1277  		protocol = "LMTP"
1278  	}
1279  	c.writeResponse(220, NoEnhancedCode, fmt.Sprintf("%v %s Service Ready", c.server.Domain, protocol))
1280  }
1281  
1282  func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text ...string) {
1283  	// TODO: error handling
1284  	if c.server.WriteTimeout != 0 {
1285  		c.conn.SetWriteDeadline(time.Now().Add(c.server.WriteTimeout))
1286  	}
1287  
1288  	// All responses must include an enhanced code, if it is missing - use
1289  	// a generic code X.0.0.
1290  	if enhCode == EnhancedCodeNotSet {
1291  		cat := code / 100
1292  		switch cat {
1293  		case 2, 4, 5:
1294  			enhCode = EnhancedCode{cat, 0, 0}
1295  		default:
1296  			enhCode = NoEnhancedCode
1297  		}
1298  	}
1299  
1300  	// transform each single line with \n, into separate lines
1301  	text = strings.Split(strings.Join(text, "\n"), "\n")
1302  
1303  	lastLineIndex := len(text) - 1
1304  	for i := 0; i < lastLineIndex; i++ {
1305  		c.text.PrintfLine("%d-%v", code, text[i])
1306  	}
1307  	if enhCode == NoEnhancedCode {
1308  		c.text.PrintfLine("%d %v", code, text[lastLineIndex])
1309  	} else {
1310  		c.text.PrintfLine("%d %v.%v.%v %v", code, enhCode[0], enhCode[1], enhCode[2], text[lastLineIndex])
1311  	}
1312  }
1313  
1314  func (c *Conn) writeError(code int, enhCode EnhancedCode, err error) {
1315  	if smtpErr, ok := err.(*SMTPError); ok {
1316  		c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message)
1317  	} else {
1318  		c.writeResponse(code, enhCode, err.Error())
1319  	}
1320  }
1321  
1322  // Reads a line of input
1323  func (c *Conn) readLine() (string, error) {
1324  	if c.server.ReadTimeout != 0 {
1325  		if err := c.conn.SetReadDeadline(time.Now().Add(c.server.ReadTimeout)); err != nil {
1326  			return "", err
1327  		}
1328  	}
1329  
1330  	return c.text.ReadLine()
1331  }
1332  
1333  func (c *Conn) reset() {
1334  	c.locker.Lock()
1335  	defer c.locker.Unlock()
1336  
1337  	if c.bdatPipe != nil {
1338  		c.bdatPipe.CloseWithError(ErrDataReset)
1339  		c.bdatPipe = nil
1340  	}
1341  	c.bdatStatus = nil
1342  	c.bytesReceived = 0
1343  
1344  	if c.session != nil {
1345  		c.session.Reset()
1346  	}
1347  
1348  	c.fromReceived = false
1349  	c.recipients = nil
1350  }
1351