diff --git a/conn.go b/conn.go index 587af432..508c9aa4 100644 --- a/conn.go +++ b/conn.go @@ -61,11 +61,11 @@ func (c *Conn) init() { } func (c *Conn) unrecognizedCommand(cmd string) { - c.WriteResponse(500, fmt.Sprintf("Syntax error, %v command unrecognized", cmd)) + c.WriteResponse(500, EnhancedCode{5, 5, 2}, fmt.Sprintf("Syntax error, %v command unrecognized", cmd)) c.nbrErrors++ if c.nbrErrors > 3 { - c.WriteResponse(500, "Too many unrecognized commands") + c.WriteResponse(500, EnhancedCode{5, 5, 2}, "Too many unrecognized commands") c.Close() } } @@ -73,7 +73,7 @@ func (c *Conn) unrecognizedCommand(cmd string) { // Commands are dispatched to the appropriate handler functions. func (c *Conn) handle(cmd string, arg string) { if cmd == "" { - c.WriteResponse(500, "Speak up") + c.WriteResponse(500, EnhancedCode{5, 5, 2}, "Speak up") return } @@ -81,15 +81,15 @@ func (c *Conn) handle(cmd string, arg string) { switch cmd { case "SEND", "SOML", "SAML", "EXPN", "HELP", "TURN": // These commands are not implemented in any state - c.WriteResponse(502, fmt.Sprintf("%v command not implemented", cmd)) + c.WriteResponse(502, EnhancedCode{5, 5, 1}, fmt.Sprintf("%v command not implemented", cmd)) case "HELO", "EHLO", "LHLO": lmtp := cmd == "LHLO" enhanced := lmtp || cmd == "EHLO" if c.server.LMTP && !lmtp { - c.WriteResponse(500, "This is a LMTP server, use LHLO") + c.WriteResponse(500, EnhancedCode{5, 5, 1}, "This is a LMTP server, use LHLO") } if !c.server.LMTP && lmtp { - c.WriteResponse(500, "This is not a LMTP server") + c.WriteResponse(500, EnhancedCode{5, 5, 1}, "This is not a LMTP server") } c.handleGreet(enhanced, arg) case "MAIL": @@ -97,16 +97,16 @@ func (c *Conn) handle(cmd string, arg string) { case "RCPT": c.handleRcpt(arg) case "VRFY": - c.WriteResponse(252, "Cannot VRFY user, but will accept message") + c.WriteResponse(252, EnhancedCode{2, 5, 0}, "Cannot VRFY user, but will accept message") case "NOOP": - c.WriteResponse(250, "I have sucessfully done nothing") + c.WriteResponse(250, EnhancedCode{2, 0, 0}, "I have sucessfully done nothing") case "RSET": // Reset session c.reset() - c.WriteResponse(250, "Session reset") + c.WriteResponse(250, EnhancedCode{2, 0, 0}, "Session reset") case "DATA": c.handleData(arg) case "QUIT": - c.WriteResponse(221, "Goodnight and good luck") + c.WriteResponse(221, EnhancedCode{2, 0, 0}, "Goodnight and good luck") c.Close() case "AUTH": if c.server.AuthDisabled { @@ -179,16 +179,16 @@ func (c *Conn) handleGreet(enhanced bool, arg string) { if !enhanced { domain, err := parseHelloArgument(arg) if err != nil { - c.WriteResponse(501, "Domain/address argument required for HELO") + c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Domain/address argument required for HELO") return } c.helo = domain - c.WriteResponse(250, fmt.Sprintf("Hello %s", domain)) + c.WriteResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("Hello %s", domain)) } else { domain, err := parseHelloArgument(arg) if err != nil { - c.WriteResponse(501, "Domain/address argument required for EHLO") + c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Domain/address argument required for EHLO") return } @@ -213,14 +213,14 @@ func (c *Conn) handleGreet(enhanced bool, arg string) { args := []string{"Hello " + domain} args = append(args, caps...) - c.WriteResponse(250, args...) + c.WriteResponse(250, NoEnhancedCode, args...) } } // READY state -> waiting for MAIL func (c *Conn) handleMail(arg string) { if c.helo == "" { - c.WriteResponse(502, "Please introduce yourself first.") + c.WriteResponse(502, EnhancedCode{2, 5, 1}, "Please introduce yourself first.") return } @@ -228,7 +228,11 @@ func (c *Conn) handleMail(arg string) { state := c.State() session, err := c.server.Backend.AnonymousLogin(&state) if err != nil { - c.WriteResponse(502, err.Error()) + if smtpErr, ok := err.(*SMTPError); ok { + c.WriteResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) + } else { + c.WriteResponse(502, EnhancedCode{5, 7, 0}, err.Error()) + } return } @@ -236,19 +240,19 @@ func (c *Conn) handleMail(arg string) { } if len(arg) < 6 || strings.ToUpper(arg[0:5]) != "FROM:" { - c.WriteResponse(501, "Was expecting MAIL arg syntax of FROM:
") + c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:") return } fromArgs := strings.Split(strings.Trim(arg[5:], " "), " ") if c.server.Strict { if !strings.HasPrefix(fromArgs[0], "<") || !strings.HasSuffix(fromArgs[0], ">") { - c.WriteResponse(501, "Was expecting MAIL arg syntax of FROM:") + c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:") return } } from := strings.Trim(fromArgs[0], "<> ") if from == "" { - c.WriteResponse(501, "Was expecting MAIL arg syntax of FROM:") + c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:") return } @@ -257,19 +261,19 @@ func (c *Conn) handleMail(arg string) { if len(fromArgs) > 1 { args, err := parseArgs(fromArgs[1:]) if err != nil { - c.WriteResponse(501, "Unable to parse MAIL ESMTP parameters") + c.WriteResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse MAIL ESMTP parameters") return } if args["SIZE"] != "" { size, err := strconv.ParseInt(args["SIZE"], 10, 32) if err != nil { - c.WriteResponse(501, "Unable to parse SIZE as an integer") + c.WriteResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse SIZE as an integer") return } if c.server.MaxMessageBytes > 0 && int(size) > c.server.MaxMessageBytes { - c.WriteResponse(552, "Max message size exceeded") + c.WriteResponse(552, EnhancedCode{5, 3, 4}, "Max message size exceeded") return } } @@ -277,26 +281,26 @@ func (c *Conn) handleMail(arg string) { if err := c.Session().Mail(from); err != nil { if smtpErr, ok := err.(*SMTPError); ok { - c.WriteResponse(smtpErr.Code, smtpErr.Message) + c.WriteResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) return } - c.WriteResponse(451, err.Error()) + c.WriteResponse(451, EnhancedCode{4, 0, 0}, err.Error()) return } - c.WriteResponse(250, fmt.Sprintf("Roger, accepting mail from <%v>", from)) + c.WriteResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("Roger, accepting mail from <%v>", from)) c.fromReceived = true } // MAIL state -> waiting for RCPTs followed by DATA func (c *Conn) handleRcpt(arg string) { if !c.fromReceived { - c.WriteResponse(502, "Missing MAIL FROM command.") + c.WriteResponse(502, EnhancedCode{5, 5, 1}, "Missing MAIL FROM command.") return } if (len(arg) < 4) || (strings.ToUpper(arg[0:3]) != "TO:") { - c.WriteResponse(501, "Was expecting RCPT arg syntax of TO:") + c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Was expecting RCPT arg syntax of TO:") return } @@ -304,30 +308,30 @@ func (c *Conn) handleRcpt(arg string) { recipient := strings.Trim(arg[3:], "<> ") if c.server.MaxRecipients > 0 && len(c.recipients) >= c.server.MaxRecipients { - c.WriteResponse(552, fmt.Sprintf("Maximum limit of %v recipients reached", c.server.MaxRecipients)) + c.WriteResponse(552, EnhancedCode{5, 5, 3}, fmt.Sprintf("Maximum limit of %v recipients reached", c.server.MaxRecipients)) return } if err := c.Session().Rcpt(recipient); err != nil { if smtpErr, ok := err.(*SMTPError); ok { - c.WriteResponse(smtpErr.Code, smtpErr.Message) + c.WriteResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message) return } - c.WriteResponse(451, err.Error()) + c.WriteResponse(451, EnhancedCode{4, 0, 0}, err.Error()) return } c.recipients = append(c.recipients, recipient) - c.WriteResponse(250, fmt.Sprintf("I'll make sure <%v> gets this", recipient)) + c.WriteResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("I'll make sure <%v> gets this", recipient)) } func (c *Conn) handleAuth(arg string) { if c.helo == "" { - c.WriteResponse(502, "Please introduce yourself first.") + c.WriteResponse(502, EnhancedCode{5, 5, 1}, "Please introduce yourself first.") return } if arg == "" { - c.WriteResponse(502, "Missing parameter") + c.WriteResponse(502, EnhancedCode{5, 5, 4}, "Missing parameter") return } @@ -346,7 +350,7 @@ func (c *Conn) handleAuth(arg string) { newSasl, ok := c.server.auths[mechanism] if !ok { - c.WriteResponse(504, "Unsupported authentication mechanism") + c.WriteResponse(504, EnhancedCode{5, 7, 4}, "Unsupported authentication mechanism") return } @@ -356,7 +360,7 @@ func (c *Conn) handleAuth(arg string) { for { challenge, done, err := sasl.Next(response) if err != nil { - c.WriteResponse(454, err.Error()) + c.WriteResponse(454, EnhancedCode{4, 7, 0}, err.Error()) return } @@ -368,7 +372,7 @@ func (c *Conn) handleAuth(arg string) { if len(challenge) > 0 { encoded = base64.StdEncoding.EncodeToString(challenge) } - c.WriteResponse(334, encoded) + c.WriteResponse(334, NoEnhancedCode, encoded) encoded, err = c.ReadLine() if err != nil { @@ -377,35 +381,35 @@ func (c *Conn) handleAuth(arg string) { response, err = base64.StdEncoding.DecodeString(encoded) if err != nil { - c.WriteResponse(454, "Invalid base64 data") + c.WriteResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data") return } } if c.Session() != nil { - c.WriteResponse(235, "Authentication succeeded") + c.WriteResponse(235, EnhancedCode{2, 0, 0}, "Authentication succeeded") } } func (c *Conn) handleStartTLS() { if _, isTLS := c.TLSConnectionState(); isTLS { - c.WriteResponse(502, "Already running in TLS") + c.WriteResponse(502, EnhancedCode{5, 5, 1}, "Already running in TLS") return } if c.server.TLSConfig == nil { - c.WriteResponse(502, "TLS not supported") + c.WriteResponse(502, EnhancedCode{5, 5, 1}, "TLS not supported") return } - c.WriteResponse(220, "Ready to start TLS") + c.WriteResponse(220, EnhancedCode{2, 0, 0}, "Ready to start TLS") // Upgrade to TLS var tlsConn *tls.Conn tlsConn = tls.Server(c.conn, c.server.TLSConfig) if err := tlsConn.Handshake(); err != nil { - c.WriteResponse(550, "Handshake error") + c.WriteResponse(550, EnhancedCode{5, 0, 0}, "Handshake error") } c.conn = tlsConn @@ -418,21 +422,22 @@ func (c *Conn) handleStartTLS() { // DATA func (c *Conn) handleData(arg string) { if arg != "" { - c.WriteResponse(501, "DATA command should not have any arguments") + c.WriteResponse(501, EnhancedCode{5, 5, 4}, "DATA command should not have any arguments") return } if !c.fromReceived || len(c.recipients) == 0 { - c.WriteResponse(502, "Missing RCPT TO command.") + c.WriteResponse(502, EnhancedCode{5, 5, 1}, "Missing RCPT TO command.") return } // We have recipients, go to accept data - c.WriteResponse(354, "Go ahead. End your data with