summaryrefslogtreecommitdiff
path: root/src/pkg/mail/message.go
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-08-03 16:54:30 +0200
committerOndřej Surý <ondrej@sury.org>2011-08-03 16:54:30 +0200
commit28592ee1ea1f5cdffcf85472f9de0285d928cf12 (patch)
tree32944e18b23f7fe4a0818a694aa2a6dfb1835463 /src/pkg/mail/message.go
parente836bee4716dc0d4d913537ad3ad1925a7ac32d0 (diff)
downloadgolang-upstream/59.tar.gz
Imported Upstream version 59upstream/59
Diffstat (limited to 'src/pkg/mail/message.go')
-rw-r--r--src/pkg/mail/message.go115
1 files changed, 74 insertions, 41 deletions
diff --git a/src/pkg/mail/message.go b/src/pkg/mail/message.go
index 754b779be..e227d17d6 100644
--- a/src/pkg/mail/message.go
+++ b/src/pkg/mail/message.go
@@ -18,8 +18,10 @@ package mail
import (
"bufio"
"bytes"
+ "encoding/base64"
"fmt"
"io"
+ "io/ioutil"
"log"
"net/textproto"
"os"
@@ -94,7 +96,7 @@ func parseDate(date string) (*time.Time, os.Error) {
return t, nil
}
}
- return nil, os.ErrorString("mail: header could not be parsed")
+ return nil, os.NewError("mail: header could not be parsed")
}
// A Header represents the key-value pairs in a mail message header.
@@ -106,7 +108,7 @@ func (h Header) Get(key string) string {
return textproto.MIMEHeader(h).Get(key)
}
-var ErrHeaderNotPresent = os.ErrorString("mail: header not in message")
+var ErrHeaderNotPresent = os.NewError("mail: header not in message")
// Date parses the Date header field.
func (h Header) Date() (*time.Time, os.Error) {
@@ -202,7 +204,7 @@ func (p *addrParser) parseAddressList() ([]*Address, os.Error) {
break
}
if !p.consume(',') {
- return nil, os.ErrorString("mail: expected comma")
+ return nil, os.NewError("mail: expected comma")
}
}
return list, nil
@@ -213,7 +215,7 @@ func (p *addrParser) parseAddress() (addr *Address, err os.Error) {
debug.Printf("parseAddress: %q", *p)
p.skipSpace()
if p.empty() {
- return nil, os.ErrorString("mail: no address")
+ return nil, os.NewError("mail: no address")
}
// address = name-addr / addr-spec
@@ -244,14 +246,14 @@ func (p *addrParser) parseAddress() (addr *Address, err os.Error) {
// angle-addr = "<" addr-spec ">"
p.skipSpace()
if !p.consume('<') {
- return nil, os.ErrorString("mail: no angle-addr")
+ return nil, os.NewError("mail: no angle-addr")
}
spec, err = p.consumeAddrSpec()
if err != nil {
return nil, err
}
if !p.consume('>') {
- return nil, os.ErrorString("mail: unclosed angle-addr")
+ return nil, os.NewError("mail: unclosed angle-addr")
}
debug.Printf("parseAddress: spec=%q", spec)
@@ -276,7 +278,7 @@ func (p *addrParser) consumeAddrSpec() (spec string, err os.Error) {
var localPart string
p.skipSpace()
if p.empty() {
- return "", os.ErrorString("mail: no addr-spec")
+ return "", os.NewError("mail: no addr-spec")
}
if p.peek() == '"' {
// quoted-string
@@ -293,14 +295,14 @@ func (p *addrParser) consumeAddrSpec() (spec string, err os.Error) {
}
if !p.consume('@') {
- return "", os.ErrorString("mail: missing @ in addr-spec")
+ return "", os.NewError("mail: missing @ in addr-spec")
}
// domain = dot-atom / domain-literal
var domain string
p.skipSpace()
if p.empty() {
- return "", os.ErrorString("mail: no domain in addr-spec")
+ return "", os.NewError("mail: no domain in addr-spec")
}
// TODO(dsymonds): Handle domain-literal
domain, err = p.consumeAtom(true)
@@ -321,7 +323,7 @@ func (p *addrParser) consumePhrase() (phrase string, err os.Error) {
var word string
p.skipSpace()
if p.empty() {
- return "", os.ErrorString("mail: missing phrase")
+ return "", os.NewError("mail: missing phrase")
}
if p.peek() == '"' {
// quoted-string
@@ -345,7 +347,7 @@ func (p *addrParser) consumePhrase() (phrase string, err os.Error) {
// Ignore any error if we got at least one word.
if err != nil && len(words) == 0 {
debug.Printf("consumePhrase: hit err: %v", err)
- return "", os.ErrorString("mail: missing word in phrase")
+ return "", os.NewError("mail: missing word in phrase")
}
phrase = strings.Join(words, " ")
return phrase, nil
@@ -359,14 +361,14 @@ func (p *addrParser) consumeQuotedString() (qs string, err os.Error) {
Loop:
for {
if i >= p.len() {
- return "", os.ErrorString("mail: unclosed quoted-string")
+ return "", os.NewError("mail: unclosed quoted-string")
}
switch c := (*p)[i]; {
case c == '"':
break Loop
case c == '\\':
if i+1 == p.len() {
- return "", os.ErrorString("mail: unclosed quoted-string")
+ return "", os.NewError("mail: unclosed quoted-string")
}
qsb = append(qsb, (*p)[i+1])
i += 2
@@ -387,7 +389,7 @@ Loop:
// If dot is true, consumeAtom parses an RFC 5322 dot-atom instead.
func (p *addrParser) consumeAtom(dot bool) (atom string, err os.Error) {
if !isAtext(p.peek(), false) {
- return "", os.ErrorString("mail: invalid string")
+ return "", os.NewError("mail: invalid string")
}
i := 1
for ; i < p.len() && isAtext((*p)[i], dot); i++ {
@@ -423,42 +425,73 @@ func (p *addrParser) len() int {
}
func decodeRFC2047Word(s string) (string, os.Error) {
- fields := strings.Split(s, "?", -1)
+ fields := strings.Split(s, "?")
if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
- return "", os.ErrorString("mail: address not RFC 2047 encoded")
+ return "", os.NewError("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)
+ in := bytes.NewBufferString(fields[3])
+ var r io.Reader
+ switch enc {
+ case "b":
+ r = base64.NewDecoder(base64.StdEncoding, in)
+ case "q":
+ r = qDecoder{r: in}
+ default:
+ return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc)
+ }
+
+ 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 b.String(), nil
+ return 1, nil
}
var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +