git » chasquid » commit 7403dbb

chasquid: Minor fixes to MAIL FROM and RCPT TO handling

author Alberto Bertogli
2016-10-08 09:29:04 UTC
committer Alberto Bertogli
2016-10-09 23:51:05 UTC
parent 3e6dd12d06f51e1c894009ef48c97c00fe31f64e

chasquid: Minor fixes to MAIL FROM and RCPT TO handling

This patch tidies up the MAIL FROM and RCPT TO handling, in particular:

 - Preserve the case on received email. It could be outgoing and we
   should not change it.
 - Accept (but ignore) RCPT TO options, instead of failing.
 - Fix some error codes to make them follow the RFC.

chasquid.go +15 -8
chasquid_test.go +17 -4

diff --git a/chasquid.go b/chasquid.go
index a7c80c1..c0816ff 100644
--- a/chasquid.go
+++ b/chasquid.go
@@ -595,8 +595,8 @@ func (c *Conn) NOOP(params string) (code int, msg string) {
 
 func (c *Conn) MAIL(params string) (code int, msg string) {
 	// params should be: "FROM:<name@host>", and possibly followed by
-	// "BODY=8BITMIME" (which we ignore).
-	// Check that it begins with "FROM:" first, otherwise it's pointless.
+	// options such as "BODY=8BITMIME" (which we ignore).
+	// Check that it begins with "FROM:" first, it's mandatory.
 	if !strings.HasPrefix(strings.ToLower(params), "from:") {
 		return 500, "unknown command"
 	}
@@ -655,23 +655,30 @@ func (c *Conn) MAIL(params string) (code int, msg string) {
 }
 
 func (c *Conn) RCPT(params string) (code int, msg string) {
-	// params should be: "TO:<name@host>"
-	// First, get rid of the "TO:" part (but check it, it's mandatory).
-	sp := strings.SplitN(strings.ToLower(params), ":", 2)
-	if len(sp) != 2 || sp[0] != "to" {
+	// params should be: "TO:<name@host>", and possibly followed by options
+	// such as "NOTIFY=SUCCESS,DELAY" (which we ignore).
+	// Check that it begins with "TO:" first, it's mandatory.
+	if !strings.HasPrefix(strings.ToLower(params), "to:") {
 		return 500, "unknown command"
 	}
 
+	rawAddr := ""
+	_, err := fmt.Sscanf(params[3:], "%s ", &rawAddr)
+	if err != nil {
+		return 500, "malformed command - " + err.Error()
+	}
+
 	// RFC says 100 is the minimum limit for this, but it seems excessive.
+	// https://tools.ietf.org/html/rfc5321#section-4.5.3.1.8
 	if len(c.rcptTo) > 100 {
-		return 503, "too many recipients"
+		return 452, "too many recipients"
 	}
 
 	// TODO: Write our own parser (we have different needs, mail.ParseAddress
 	// is useful for other things).
 	// Allow utf8, but prevent "control" characters.
 
-	e, err := mail.ParseAddress(sp[1])
+	e, err := mail.ParseAddress(rawAddr)
 	if err != nil || e.Address == "" {
 		return 501, "malformed address"
 	}
diff --git a/chasquid_test.go b/chasquid_test.go
index 638f92c..d44344d 100644
--- a/chasquid_test.go
+++ b/chasquid_test.go
@@ -184,11 +184,9 @@ func TestNullMailFrom(t *testing.T) {
 	c := mustDial(t, ModeSMTP, false)
 	defer c.Close()
 
-	addrs := []string{"", "<>", "  <>", " <  > "}
+	addrs := []string{"<>", "  <>", "<> OPTION"}
 	for _, addr := range addrs {
-		if err := c.Text.PrintfLine(addr); err != nil {
-			t.Fatalf("MAIL FROM failed with addr %q: %v", addr, err)
-		}
+		simpleCmd(t, c, fmt.Sprintf("MAIL FROM:%s", addr), 250)
 	}
 }
 
@@ -201,6 +199,21 @@ func TestRcptBeforeMail(t *testing.T) {
 	}
 }
 
+func TestRcptOption(t *testing.T) {
+	c := mustDial(t, ModeSMTP, false)
+	defer c.Close()
+
+	if err := c.Mail("from@localhost"); err != nil {
+		t.Errorf("Mail: %v", err)
+	}
+
+	params := []string{
+		"<to@localhost>", "  <to@localhost>", "<to@localhost> OPTION"}
+	for _, p := range params {
+		simpleCmd(t, c, fmt.Sprintf("RCPT TO:%s", p), 250)
+	}
+}
+
 func TestRelayForbidden(t *testing.T) {
 	c := mustDial(t, ModeSMTP, false)
 	defer c.Close()