mirror of
https://github.com/golang/go
synced 2024-11-22 01:44:40 -07:00
mail: decode RFC 2047 "B" encoding.
R=rsc, r CC=golang-dev https://golang.org/cl/4584056
This commit is contained in:
parent
c6c8dbd646
commit
371aa14e06
@ -18,8 +18,10 @@ package mail
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"os"
|
"os"
|
||||||
@ -57,7 +59,7 @@ func ReadMessage(r io.Reader) (msg *Message, err os.Error) {
|
|||||||
return &Message{
|
return &Message{
|
||||||
Header: Header(hdr),
|
Header: Header(hdr),
|
||||||
Body: tp.R,
|
Body: tp.R,
|
||||||
}, nil
|
},nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layouts suitable for passing to time.Parse.
|
// Layouts suitable for passing to time.Parse.
|
||||||
@ -226,7 +228,7 @@ func (p *addrParser) parseAddress() (addr *Address, err os.Error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return &Address{
|
return &Address{
|
||||||
Address: spec,
|
Address: spec,
|
||||||
}, err
|
},err
|
||||||
}
|
}
|
||||||
debug.Printf("parseAddress: not an addr-spec: %v", err)
|
debug.Printf("parseAddress: not an addr-spec: %v", err)
|
||||||
debug.Printf("parseAddress: state is now %q", *p)
|
debug.Printf("parseAddress: state is now %q", *p)
|
||||||
@ -258,7 +260,7 @@ func (p *addrParser) parseAddress() (addr *Address, err os.Error) {
|
|||||||
return &Address{
|
return &Address{
|
||||||
Name: displayName,
|
Name: displayName,
|
||||||
Address: spec,
|
Address: spec,
|
||||||
}, nil
|
},nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// consumeAddrSpec parses a single RFC 5322 addr-spec at the start of p.
|
// consumeAddrSpec parses a single RFC 5322 addr-spec at the start of p.
|
||||||
@ -428,37 +430,68 @@ func decodeRFC2047Word(s string) (string, os.Error) {
|
|||||||
return "", os.ErrorString("mail: address not RFC 2047 encoded")
|
return "", os.ErrorString("mail: address not RFC 2047 encoded")
|
||||||
}
|
}
|
||||||
charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
|
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" {
|
if charset != "iso-8859-1" && charset != "utf-8" {
|
||||||
return "", fmt.Errorf("mail: charset not supported: %q", charset)
|
return "", fmt.Errorf("mail: charset not supported: %q", charset)
|
||||||
}
|
}
|
||||||
|
|
||||||
in := fields[3]
|
in := bytes.NewBufferString(fields[3])
|
||||||
b := new(bytes.Buffer)
|
var r io.Reader
|
||||||
for i := 0; i < len(in); i++ {
|
switch enc {
|
||||||
switch c := in[i]; {
|
case "b":
|
||||||
case c == '=' && i+2 < len(in):
|
r = base64.NewDecoder(base64.StdEncoding, in)
|
||||||
x, err := strconv.Btoi64(in[i+1:i+3], 16)
|
case "q":
|
||||||
if err != nil {
|
r = qDecoder{r: in}
|
||||||
return "", fmt.Errorf("mail: invalid RFC 2047 encoding: %q", in[i:i+3])
|
default:
|
||||||
}
|
return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc)
|
||||||
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
|
|
||||||
|
dec, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch charset {
|
||||||
|
case "iso-8859-1":
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
for _, c := range dec {
|
||||||
|
b.WriteRune(int(c))
|
||||||
|
}
|
||||||
|
return b.String(), nil
|
||||||
|
case "utf-8":
|
||||||
|
return string(dec), nil
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
type qDecoder struct {
|
||||||
|
r io.Reader
|
||||||
|
scratch [2]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qd qDecoder) Read(p []byte) (n int, err os.Error) {
|
||||||
|
// This method writes at most one byte into p.
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if _, err := qd.r.Read(qd.scratch[:1]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
switch c := qd.scratch[0]; {
|
||||||
|
case c == '=':
|
||||||
|
if _, err := io.ReadFull(qd.r, qd.scratch[:2]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
x, err := strconv.Btoi64(string(qd.scratch[:2]), 16)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("mail: invalid RFC 2047 encoding: %q", qd.scratch[:2])
|
||||||
|
}
|
||||||
|
p[0] = byte(x)
|
||||||
|
case c == '_':
|
||||||
|
p[0] = ' '
|
||||||
|
default:
|
||||||
|
p[0] = c
|
||||||
|
}
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||||
|
@ -217,6 +217,27 @@ func TestAddressParsing(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Custom example of RFC 2047 "B"-encoded ISO-8859-1 address.
|
||||||
|
{
|
||||||
|
`=?ISO-8859-1?B?SvZyZw==?= <joerg@example.com>`,
|
||||||
|
[]*Address{
|
||||||
|
&Address{
|
||||||
|
Name: `Jörg`,
|
||||||
|
Address: "joerg@example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Custom example of RFC 2047 "B"-encoded UTF-8 address.
|
||||||
|
{
|
||||||
|
// XXX: a different example
|
||||||
|
`=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`,
|
||||||
|
[]*Address{
|
||||||
|
&Address{
|
||||||
|
Name: `Jörg`,
|
||||||
|
Address: "joerg@example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
addrs, err := newAddrParser(test.addrsStr).parseAddressList()
|
addrs, err := newAddrParser(test.addrsStr).parseAddressList()
|
||||||
@ -225,7 +246,7 @@ func TestAddressParsing(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(addrs, test.exp) {
|
if !reflect.DeepEqual(addrs, test.exp) {
|
||||||
t.Errorf("Parse of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
|
t.Errorf("Parse of %q: got %+v, want %+v", test.addrsStr, *addrs[0], *test.exp[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user