]> Cypherpunks repositories - gostls13.git/commitdiff
mail: decode "Q"-encoded mail addresses.
authorDavid Symonds <dsymonds@golang.org>
Thu, 9 Jun 2011 00:18:36 +0000 (10:18 +1000)
committerDavid Symonds <dsymonds@golang.org>
Thu, 9 Jun 2011 00:18:36 +0000 (10:18 +1000)
Supports ISO-8859-1 and UTF-8.

R=rsc, bradfitz
CC=golang-dev
https://golang.org/cl/4568064

src/pkg/mail/message.go
src/pkg/mail/message_test.go

index 377a8d394385a2149370043d3bb1c71b1e21b759..ca818ebde40bfb6e4ba543f0a3d9b5847b1a2e1f 100644 (file)
@@ -23,6 +23,7 @@ import (
        "log"
        "net/textproto"
        "os"
+       "strconv"
        "strings"
        "time"
 )
@@ -340,7 +341,13 @@ func (p *addrParser) consumePhrase() (phrase string, err os.Error) {
                debug.Printf("consumePhrase: hit err: %v", err)
                return "", os.ErrorString("mail: missing word in phrase")
        }
-       return strings.Join(words, " "), nil
+       phrase = strings.Join(words, " ")
+
+       // RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s.
+       if strings.HasPrefix(phrase, "=?") && strings.HasSuffix(phrase, "?=") && strings.Count(phrase, "?") == 4 {
+               return decodeRFC2047Word(phrase)
+       }
+       return phrase, nil
 }
 
 // consumeQuotedString parses the quoted string at the start of p.
@@ -414,6 +421,45 @@ func (p *addrParser) len() int {
        return len(*p)
 }
 
+func decodeRFC2047Word(s string) (string, os.Error) {
+       fields := strings.Split(s, "?", -1)
+       if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
+               return "", os.ErrorString("mail: address not RFC 2047 encoded")
+       }
+       charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
+       // TODO(dsymonds): Support "b" encoding too.
+       if enc != "q" {
+               return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc)
+       }
+       if charset != "iso-8859-1" && charset != "utf-8" {
+               return "", fmt.Errorf("mail: charset not supported: %q", charset)
+       }
+
+       in := fields[3]
+       b := new(bytes.Buffer)
+       for i := 0; i < len(in); i++ {
+               switch c := in[i]; {
+               case c == '=' && i+2 < len(in):
+                       x, err := strconv.Btoi64(in[i+1:i+3], 16)
+                       if err != nil {
+                               return "", fmt.Errorf("mail: invalid RFC 2047 encoding: %q", in[i:i+3])
+                       }
+                       i += 2
+                       switch charset {
+                       case "iso-8859-1":
+                               b.WriteRune(int(x))
+                       case "utf-8":
+                               b.WriteByte(byte(x))
+                       }
+               case c == '_':
+                       b.WriteByte(' ')
+               default:
+                       b.WriteByte(c)
+               }
+       }
+       return b.String(), nil
+}
+
 var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789" +
index 731a748edecb14c1f72ea0c735f54f79be1c1a5e..92e9ef8de768f74ac6d676e27f7e22c4363e5352 100644 (file)
@@ -186,6 +186,27 @@ func TestAddressParsing(t *testing.T) {
                },
                // RFC 5322, Appendix A.1.3
                // TODO(dsymonds): Group addresses.
+
+               // RFC 2047 "Q"-encoded ISO-8859-1 address.
+               {
+                       `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`,
+                       []*Address{
+                               &Address{
+                                       Name:    `Jörg Doe`,
+                                       Address: "joerg@example.com",
+                               },
+                       },
+               },
+               // RFC 2047 "Q"-encoded UTF-8 address.
+               {
+                       `=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`,
+                       []*Address{
+                               &Address{
+                                       Name:    `Jörg Doe`,
+                                       Address: "joerg@example.com",
+                               },
+                       },
+               },
        }
        for _, test := range tests {
                addrs, err := newAddrParser(test.addrsStr).parseAddressList()