]> Cypherpunks repositories - gostls13.git/commitdiff
net/smtp: adds support for the SMTPUTF8 extension
authorDaniel Cormier <dcormier@users.noreply.github.com>
Tue, 1 Sep 2020 21:13:01 +0000 (21:13 +0000)
committerIan Lance Taylor <iant@golang.org>
Tue, 1 Sep 2020 23:10:59 +0000 (23:10 +0000)
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 <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/net/smtp/smtp.go
src/net/smtp/smtp_test.go

index e4e12ae5ee92821633851d09867ec96239eb74d5..1a6864a0f2436caf5ad74a5aa05350d1f99c2f48 100644 (file)
@@ -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
index cfda0790e96398dae52961e0fdb0008c355c07c8..55219372d278c7f4a38f0434534303947ab45e35 100644 (file)
@@ -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:<user@gmail.com>
+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:<user@gmail.com>
+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:<user@gmail.com> 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:<user+📧@gmail.com> 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:<user+📧@gmail.com> 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")