From: Daniel Cormier Date: Tue, 1 Sep 2020 21:13:01 +0000 (+0000) Subject: net/smtp: adds support for the SMTPUTF8 extension X-Git-Tag: go1.16beta1~1144 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=717266b4c2bd178dcbd49f31048f216d8799e697;p=gostls13.git net/smtp: adds support for the SMTPUTF8 extension If the SMTP server supports the SMTPUTF8 extension, the SMTPUTF8 parameter is added to the MAIL FROM command by the (*Client).Mail method. Fixes #19860 Change-Id: I3287faf114ee514e5faa815a6bbc1bf04cf60b0f GitHub-Last-Rev: d6338bb802da7537223f1ec6eda960606febefb8 GitHub-Pull-Request: golang/go#40627 Reviewed-on: https://go-review.googlesource.com/c/go/+/247257 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- diff --git a/src/net/smtp/smtp.go b/src/net/smtp/smtp.go index e4e12ae5ee..1a6864a0f2 100644 --- a/src/net/smtp/smtp.go +++ b/src/net/smtp/smtp.go @@ -241,7 +241,8 @@ func (c *Client) Auth(a Auth) error { // 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. +// 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 Rcpt calls. func (c *Client) Mail(from string) error { if err := validateLine(from); err != nil { @@ -255,6 +256,9 @@ func (c *Client) Mail(from string) error { if _, ok := c.ext["8BITMIME"]; ok { cmdStr += " BODY=8BITMIME" } + if _, ok := c.ext["SMTPUTF8"]; ok { + cmdStr += " SMTPUTF8" + } } _, _, err := c.cmd(250, cmdStr, from) return err diff --git a/src/net/smtp/smtp_test.go b/src/net/smtp/smtp_test.go index cfda0790e9..55219372d2 100644 --- a/src/net/smtp/smtp_test.go +++ b/src/net/smtp/smtp_test.go @@ -288,6 +288,219 @@ Goodbye. QUIT ` +func TestExtensions(t *testing.T) { + fake := func(server string) (c *Client, bcmdbuf *bufio.Writer, cmdbuf *strings.Builder) { + server = strings.Join(strings.Split(server, "\n"), "\r\n") + + cmdbuf = &strings.Builder{} + bcmdbuf = bufio.NewWriter(cmdbuf) + var fake faker + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) + c = &Client{Text: textproto.NewConn(fake), localName: "localhost"} + + return c, bcmdbuf, cmdbuf + } + + t.Run("helo", func(t *testing.T) { + const ( + basicServer = `250 mx.google.com at your service +250 Sender OK +221 Goodbye +` + + basicClient = `HELO localhost +MAIL FROM: +QUIT +` + ) + + c, bcmdbuf, cmdbuf := fake(basicServer) + + if err := c.helo(); err != nil { + t.Fatalf("HELO failed: %s", err) + } + c.didHello = true + if err := c.Mail("user@gmail.com"); err != nil { + t.Fatalf("MAIL FROM failed: %s", err) + } + if err := c.Quit(); err != nil { + t.Fatalf("QUIT failed: %s", err) + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } + }) + + t.Run("ehlo", func(t *testing.T) { + const ( + basicServer = `250-mx.google.com at your service +250 SIZE 35651584 +250 Sender OK +221 Goodbye +` + + basicClient = `EHLO localhost +MAIL FROM: +QUIT +` + ) + + c, bcmdbuf, cmdbuf := fake(basicServer) + + if err := c.Hello("localhost"); err != nil { + t.Fatalf("EHLO failed: %s", err) + } + if ok, _ := c.Extension("8BITMIME"); ok { + t.Fatalf("Shouldn't support 8BITMIME") + } + if ok, _ := c.Extension("SMTPUTF8"); ok { + t.Fatalf("Shouldn't support SMTPUTF8") + } + if err := c.Mail("user@gmail.com"); err != nil { + t.Fatalf("MAIL FROM failed: %s", err) + } + if err := c.Quit(); err != nil { + t.Fatalf("QUIT failed: %s", err) + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } + }) + + t.Run("ehlo 8bitmime", func(t *testing.T) { + const ( + basicServer = `250-mx.google.com at your service +250-SIZE 35651584 +250 8BITMIME +250 Sender OK +221 Goodbye +` + + basicClient = `EHLO localhost +MAIL FROM: BODY=8BITMIME +QUIT +` + ) + + c, bcmdbuf, cmdbuf := fake(basicServer) + + if err := c.Hello("localhost"); err != nil { + t.Fatalf("EHLO failed: %s", err) + } + if ok, _ := c.Extension("8BITMIME"); !ok { + t.Fatalf("Should support 8BITMIME") + } + if ok, _ := c.Extension("SMTPUTF8"); ok { + t.Fatalf("Shouldn't support SMTPUTF8") + } + if err := c.Mail("user@gmail.com"); err != nil { + t.Fatalf("MAIL FROM failed: %s", err) + } + if err := c.Quit(); err != nil { + t.Fatalf("QUIT failed: %s", err) + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } + }) + + t.Run("ehlo smtputf8", func(t *testing.T) { + const ( + basicServer = `250-mx.google.com at your service +250-SIZE 35651584 +250 SMTPUTF8 +250 Sender OK +221 Goodbye +` + + basicClient = `EHLO localhost +MAIL FROM: SMTPUTF8 +QUIT +` + ) + + c, bcmdbuf, cmdbuf := fake(basicServer) + + if err := c.Hello("localhost"); err != nil { + t.Fatalf("EHLO failed: %s", err) + } + if ok, _ := c.Extension("8BITMIME"); ok { + t.Fatalf("Shouldn't support 8BITMIME") + } + if ok, _ := c.Extension("SMTPUTF8"); !ok { + t.Fatalf("Should support SMTPUTF8") + } + if err := c.Mail("user+📧@gmail.com"); err != nil { + t.Fatalf("MAIL FROM failed: %s", err) + } + if err := c.Quit(); err != nil { + t.Fatalf("QUIT failed: %s", err) + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } + }) + + t.Run("ehlo 8bitmime smtputf8", func(t *testing.T) { + const ( + basicServer = `250-mx.google.com at your service +250-SIZE 35651584 +250-8BITMIME +250 SMTPUTF8 +250 Sender OK +221 Goodbye + ` + + basicClient = `EHLO localhost +MAIL FROM: BODY=8BITMIME SMTPUTF8 +QUIT +` + ) + + c, bcmdbuf, cmdbuf := fake(basicServer) + + if err := c.Hello("localhost"); err != nil { + t.Fatalf("EHLO failed: %s", err) + } + c.didHello = true + if ok, _ := c.Extension("8BITMIME"); !ok { + t.Fatalf("Should support 8BITMIME") + } + if ok, _ := c.Extension("SMTPUTF8"); !ok { + t.Fatalf("Should support SMTPUTF8") + } + if err := c.Mail("user+📧@gmail.com"); err != nil { + t.Fatalf("MAIL FROM failed: %s", err) + } + if err := c.Quit(); err != nil { + t.Fatalf("QUIT failed: %s", err) + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } + }) +} + func TestNewClient(t *testing.T) { server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n") client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n")