diff options
Diffstat (limited to 'llgo/third_party/gofrontend/libgo/go/net/mail/message.go')
| -rw-r--r-- | llgo/third_party/gofrontend/libgo/go/net/mail/message.go | 260 |
1 files changed, 140 insertions, 120 deletions
diff --git a/llgo/third_party/gofrontend/libgo/go/net/mail/message.go b/llgo/third_party/gofrontend/libgo/go/net/mail/message.go index 19aa888d872..266ac50a38d 100644 --- a/llgo/third_party/gofrontend/libgo/go/net/mail/message.go +++ b/llgo/third_party/gofrontend/libgo/go/net/mail/message.go @@ -18,17 +18,14 @@ package mail import ( "bufio" "bytes" - "encoding/base64" "errors" "fmt" "io" - "io/ioutil" "log" + "mime" "net/textproto" - "strconv" "strings" "time" - "unicode" ) var debug = debugT(false) @@ -141,22 +138,79 @@ type Address struct { // Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>" func ParseAddress(address string) (*Address, error) { - return newAddrParser(address).parseAddress() + return (&addrParser{s: address}).parseAddress() } // ParseAddressList parses the given string as a list of addresses. func ParseAddressList(list string) ([]*Address, error) { - return newAddrParser(list).parseAddressList() + return (&addrParser{s: list}).parseAddressList() +} + +// An AddressParser is an RFC 5322 address parser. +type AddressParser struct { + // WordDecoder optionally specifies a decoder for RFC 2047 encoded-words. + WordDecoder *mime.WordDecoder +} + +// Parse parses a single RFC 5322 address of the +// form "Gogh Fir <gf@example.com>" or "foo@example.com". +func (p *AddressParser) Parse(address string) (*Address, error) { + return (&addrParser{s: address, dec: p.WordDecoder}).parseAddress() +} + +// ParseList parses the given string as a list of comma-separated addresses +// of the form "Gogh Fir <gf@example.com>" or "foo@example.com". +func (p *AddressParser) ParseList(list string) ([]*Address, error) { + return (&addrParser{s: list, dec: p.WordDecoder}).parseAddressList() } // String formats the address as a valid RFC 5322 address. // If the address's name contains non-ASCII characters // the name will be rendered according to RFC 2047. func (a *Address) String() string { - s := "<" + a.Address + ">" + + // Format address local@domain + at := strings.LastIndex(a.Address, "@") + var local, domain string + if at < 0 { + // This is a malformed address ("@" is required in addr-spec); + // treat the whole address as local-part. + local = a.Address + } else { + local, domain = a.Address[:at], a.Address[at+1:] + } + + // Add quotes if needed + // TODO: rendering quoted local part and rendering printable name + // should be merged in helper function. + quoteLocal := false + for i := 0; i < len(local); i++ { + ch := local[i] + if isAtext(ch, false) { + continue + } + if ch == '.' { + // Dots are okay if they are surrounded by atext. + // We only need to check that the previous byte is + // not a dot, and this isn't the end of the string. + if i > 0 && local[i-1] != '.' && i < len(local)-1 { + continue + } + } + quoteLocal = true + break + } + if quoteLocal { + local = quoteString(local) + + } + + s := "<" + local + "@" + domain + ">" + if a.Name == "" { return s } + // If every character is printable ASCII, quoting is simple. allPrintable := true for i := 0; i < len(a.Name); i++ { @@ -180,28 +234,12 @@ func (a *Address) String() string { return b.String() } - // UTF-8 "Q" encoding - b := bytes.NewBufferString("=?utf-8?q?") - for i := 0; i < len(a.Name); i++ { - switch c := a.Name[i]; { - case c == ' ': - b.WriteByte('_') - case isVchar(c) && c != '=' && c != '?' && c != '_': - b.WriteByte(c) - default: - fmt.Fprintf(b, "=%02X", c) - } - } - b.WriteString("?= ") - b.WriteString(s) - return b.String() + return mime.QEncoding.Encode("utf-8", a.Name) + " " + s } -type addrParser []byte - -func newAddrParser(s string) *addrParser { - p := addrParser(s) - return &p +type addrParser struct { + s string + dec *mime.WordDecoder // may be nil } func (p *addrParser) parseAddressList() ([]*Address, error) { @@ -227,7 +265,7 @@ func (p *addrParser) parseAddressList() ([]*Address, error) { // parseAddress parses a single RFC 5322 address at the start of p. func (p *addrParser) parseAddress() (addr *Address, err error) { - debug.Printf("parseAddress: %q", *p) + debug.Printf("parseAddress: %q", p.s) p.skipSpace() if p.empty() { return nil, errors.New("mail: no address") @@ -246,7 +284,7 @@ func (p *addrParser) parseAddress() (addr *Address, err error) { }, 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.s) // display-name var displayName string @@ -280,7 +318,7 @@ func (p *addrParser) parseAddress() (addr *Address, err error) { // consumeAddrSpec parses a single RFC 5322 addr-spec at the start of p. func (p *addrParser) consumeAddrSpec() (spec string, err error) { - debug.Printf("consumeAddrSpec: %q", *p) + debug.Printf("consumeAddrSpec: %q", p.s) orig := *p defer func() { @@ -302,7 +340,7 @@ func (p *addrParser) consumeAddrSpec() (spec string, err error) { } else { // dot-atom debug.Printf("consumeAddrSpec: parsing dot-atom") - localPart, err = p.consumeAtom(true) + localPart, err = p.consumeAtom(true, false) } if err != nil { debug.Printf("consumeAddrSpec: failed: %v", err) @@ -320,7 +358,7 @@ func (p *addrParser) consumeAddrSpec() (spec string, err error) { return "", errors.New("mail: no domain in addr-spec") } // TODO(dsymonds): Handle domain-literal - domain, err = p.consumeAtom(true) + domain, err = p.consumeAtom(true, false) if err != nil { return "", err } @@ -330,7 +368,7 @@ func (p *addrParser) consumeAddrSpec() (spec string, err error) { // consumePhrase parses the RFC 5322 phrase at the start of p. func (p *addrParser) consumePhrase() (phrase string, err error) { - debug.Printf("consumePhrase: [%s]", *p) + debug.Printf("consumePhrase: [%s]", p.s) // phrase = 1*word var words []string for { @@ -347,12 +385,11 @@ func (p *addrParser) consumePhrase() (phrase string, err error) { // atom // We actually parse dot-atom here to be more permissive // than what RFC 5322 specifies. - word, err = p.consumeAtom(true) + word, err = p.consumeAtom(true, true) } - // RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s. - if err == nil && strings.HasPrefix(word, "=?") && strings.HasSuffix(word, "?=") && strings.Count(word, "?") == 4 { - word, err = decodeRFC2047Word(word) + if err == nil { + word, err = p.decodeRFC2047Word(word) } if err != nil { @@ -380,16 +417,16 @@ Loop: if i >= p.len() { return "", errors.New("mail: unclosed quoted-string") } - switch c := (*p)[i]; { + switch c := p.s[i]; { case c == '"': break Loop case c == '\\': if i+1 == p.len() { return "", errors.New("mail: unclosed quoted-string") } - qsb = append(qsb, (*p)[i+1]) + qsb = append(qsb, p.s[i+1]) i += 2 - case isQtext(c), c == ' ' || c == '\t': + case isQtext(c), c == ' ': // qtext (printable US-ASCII excluding " and \), or // FWS (almost; we're ignoring CRLF) qsb = append(qsb, c) @@ -398,20 +435,36 @@ Loop: return "", fmt.Errorf("mail: bad character in quoted-string: %q", c) } } - *p = (*p)[i+1:] + p.s = p.s[i+1:] + if len(qsb) == 0 { + return "", errors.New("mail: empty quoted-string") + } return string(qsb), nil } // consumeAtom parses an RFC 5322 atom at the start of p. // If dot is true, consumeAtom parses an RFC 5322 dot-atom instead. -func (p *addrParser) consumeAtom(dot bool) (atom string, err error) { +// If permissive is true, consumeAtom will not fail on +// leading/trailing/double dots in the atom (see golang.org/issue/4938). +func (p *addrParser) consumeAtom(dot bool, permissive bool) (atom string, err error) { if !isAtext(p.peek(), false) { return "", errors.New("mail: invalid string") } i := 1 - for ; i < p.len() && isAtext((*p)[i], dot); i++ { + for ; i < p.len() && isAtext(p.s[i], dot); i++ { + } + atom, p.s = string(p.s[:i]), p.s[i:] + if !permissive { + if strings.HasPrefix(atom, ".") { + return "", errors.New("mail: leading dot in atom") + } + if strings.Contains(atom, "..") { + return "", errors.New("mail: double dot in atom") + } + if strings.HasSuffix(atom, ".") { + return "", errors.New("mail: trailing dot in atom") + } } - atom, *p = string((*p)[:i]), (*p)[i:] return atom, nil } @@ -419,17 +472,17 @@ func (p *addrParser) consume(c byte) bool { if p.empty() || p.peek() != c { return false } - *p = (*p)[1:] + p.s = p.s[1:] return true } // skipSpace skips the leading space and tab characters. func (p *addrParser) skipSpace() { - *p = bytes.TrimLeft(*p, " \t") + p.s = strings.TrimLeft(p.s, " \t") } func (p *addrParser) peek() byte { - return (*p)[0] + return p.s[0] } func (p *addrParser) empty() bool { @@ -437,87 +490,37 @@ func (p *addrParser) empty() bool { } func (p *addrParser) len() int { - return len(*p) + return len(p.s) } -func decodeRFC2047Word(s string) (string, error) { - fields := strings.Split(s, "?") - if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" { - return "", errors.New("address not RFC 2047 encoded") - } - charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2]) - if charset != "us-ascii" && charset != "iso-8859-1" && charset != "utf-8" { - return "", fmt.Errorf("charset not supported: %q", charset) +func (p *addrParser) decodeRFC2047Word(s string) (string, error) { + if p.dec != nil { + return p.dec.DecodeHeader(s) } - 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("RFC 2047 encoding not supported: %q", enc) + dec, err := rfc2047Decoder.Decode(s) + if err == nil { + return dec, nil } - dec, err := ioutil.ReadAll(r) - if err != nil { - return "", err + if _, ok := err.(charsetError); ok { + return s, err } - switch charset { - case "us-ascii": - b := new(bytes.Buffer) - for _, c := range dec { - if c >= 0x80 { - b.WriteRune(unicode.ReplacementChar) - } else { - b.WriteRune(rune(c)) - } - } - return b.String(), nil - case "iso-8859-1": - b := new(bytes.Buffer) - for _, c := range dec { - b.WriteRune(rune(c)) - } - return b.String(), nil - case "utf-8": - return string(dec), nil - } - panic("unreachable") + // Ignore invalid RFC 2047 encoded-word errors. + return s, nil } -type qDecoder struct { - r io.Reader - scratch [2]byte +var rfc2047Decoder = mime.WordDecoder{ + CharsetReader: func(charset string, input io.Reader) (io.Reader, error) { + return nil, charsetError(charset) + }, } -func (qd qDecoder) Read(p []byte) (n int, err 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.ParseInt(string(qd.scratch[:2]), 16, 64) - 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 +type charsetError string + +func (e charsetError) Error() string { + return fmt.Sprintf("charset not supported: %q", string(e)) } var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + @@ -525,7 +528,7 @@ var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "!#$%&'*+-/=?^_`{|}~") -// isAtext returns true if c is an RFC 5322 atext character. +// isAtext reports whether c is an RFC 5322 atext character. // If dot is true, period is included. func isAtext(c byte, dot bool) bool { if dot && c == '.' { @@ -534,7 +537,7 @@ func isAtext(c byte, dot bool) bool { return bytes.IndexByte(atextChars, c) >= 0 } -// isQtext returns true if c is an RFC 5322 qtext character. +// isQtext reports whether c is an RFC 5322 qtext character. func isQtext(c byte) bool { // Printable US-ASCII, excluding backslash or quote. if c == '\\' || c == '"' { @@ -543,13 +546,30 @@ func isQtext(c byte) bool { return '!' <= c && c <= '~' } -// isVchar returns true if c is an RFC 5322 VCHAR character. +// quoteString renders a string as a RFC5322 quoted-string. +func quoteString(s string) string { + var buf bytes.Buffer + buf.WriteByte('"') + for _, c := range s { + ch := byte(c) + if isQtext(ch) || isWSP(ch) { + buf.WriteByte(ch) + } else if isVchar(ch) { + buf.WriteByte('\\') + buf.WriteByte(ch) + } + } + buf.WriteByte('"') + return buf.String() +} + +// isVchar reports whether c is an RFC 5322 VCHAR character. func isVchar(c byte) bool { // Visible (printing) characters. return '!' <= c && c <= '~' } -// isWSP returns true if c is a WSP (white space). +// isWSP reports whether c is a WSP (white space). // WSP is a space or horizontal tab (RFC5234 Appendix B). func isWSP(c byte) bool { return c == ' ' || c == '\t' |

