// TODO(dsymonds): Is this really correct?
spec, err := p.consumeAddrSpec()
if err == nil {
+ var displayName string
+ p.skipSpace()
+ if !p.empty() && p.peek() == '(' {
+ displayName, err = p.consumeDisplayNameComment()
+ if err != nil {
+ return nil, err
+ }
+ }
+
return []*Address{{
+ Name: displayName,
Address: spec,
}}, err
}
return atom, nil
}
+func (p *addrParser) consumeDisplayNameComment() (string, error) {
+ if !p.consume('(') {
+ return "", errors.New("mail: comment does not start with (")
+ }
+ comment, ok := p.consumeComment()
+ if !ok {
+ return "", errors.New("mail: misformatted parenthetical comment")
+ }
+
+ // TODO(stapelberg): parse quoted-string within comment
+ words := strings.FieldsFunc(comment, func(r rune) bool { return r == ' ' || r == '\t' })
+ for idx, word := range words {
+ decoded, isEncoded, err := p.decodeRFC2047Word(word)
+ if err != nil {
+ return "", err
+ }
+ if isEncoded {
+ words[idx] = decoded
+ }
+ }
+
+ return strings.Join(words, " "), nil
+}
+
func (p *addrParser) consume(c byte) bool {
if p.empty() || p.peek() != c {
return false
break
}
- if !p.skipComment() {
+ if _, ok := p.consumeComment(); !ok {
return false
}
return true
}
-func (p *addrParser) skipComment() bool {
+func (p *addrParser) consumeComment() (string, bool) {
// '(' already consumed.
depth := 1
+ var comment string
for {
if p.empty() || depth == 0 {
break
} else if p.peek() == ')' {
depth--
}
+ if depth > 0 {
+ comment += p.s[:1]
+ }
p.s = p.s[1:]
}
- return depth == 0
+ return comment, depth == 0
}
func (p *addrParser) decodeRFC2047Word(s string) (word string, isEncoded bool, err error) {
},
// CFWS
{
- `cfws@example.com (CFWS (cfws)) (another comment)`,
+ `<cfws@example.com> (CFWS (cfws)) (another comment)`,
[]*Address{
{
Name: "",
},
},
{
- `cfws@example.com () (another comment), cfws2@example.com (another)`,
+ `<cfws@example.com> () (another comment), <cfws2@example.com> (another)`,
[]*Address{
{
Name: "",
},
},
},
+ // Comment as display name
+ {
+ `john@example.com (John Doe)`,
+ []*Address{
+ {
+ Name: "John Doe",
+ Address: "john@example.com",
+ },
+ },
+ },
+ // Comment and display name
+ {
+ `John Doe <john@example.com> (Joey)`,
+ []*Address{
+ {
+ Name: "John Doe",
+ Address: "john@example.com",
+ },
+ },
+ },
+ // Comment as display name, no space
+ {
+ `john@example.com(John Doe)`,
+ []*Address{
+ {
+ Name: "John Doe",
+ Address: "john@example.com",
+ },
+ },
+ },
+ // Comment as display name, Q-encoded
+ {
+ `asjo@example.com (Adam =?utf-8?Q?Sj=C3=B8gren?=)`,
+ []*Address{
+ {
+ Name: "Adam Sjøgren",
+ Address: "asjo@example.com",
+ },
+ },
+ },
+ // Comment as display name, Q-encoded and tab-separated
+ {
+ `asjo@example.com (Adam =?utf-8?Q?Sj=C3=B8gren?=)`,
+ []*Address{
+ {
+ Name: "Adam Sjøgren",
+ Address: "asjo@example.com",
+ },
+ },
+ },
+ // Nested comment as display name, Q-encoded
+ {
+ `asjo@example.com (Adam =?utf-8?Q?Sj=C3=B8gren?= (Debian))`,
+ []*Address{
+ {
+ Name: "Adam Sjøgren (Debian)",
+ Address: "asjo@example.com",
+ },
+ },
+ },
}
for _, test := range tests {
if len(test.exp) == 1 {