diff options
-rw-r--r-- | src/net/mail/message.go | 30 | ||||
-rw-r--r-- | src/net/mail/message_test.go | 40 |
2 files changed, 46 insertions, 24 deletions
diff --git a/src/net/mail/message.go b/src/net/mail/message.go index af516fc30f..fc2a9e46f8 100644 --- a/src/net/mail/message.go +++ b/src/net/mail/message.go @@ -280,7 +280,7 @@ func (a *Address) String() string { // Add quotes if needed quoteLocal := false for i, r := range local { - if isAtext(r, false, false) { + if isAtext(r, false) { continue } if r == '.' { @@ -444,7 +444,7 @@ func (p *addrParser) parseAddress(handleGroup bool) ([]*Address, error) { if !p.consume('<') { atext := true for _, r := range displayName { - if !isAtext(r, true, false) { + if !isAtext(r, true) { atext = false break } @@ -479,7 +479,9 @@ func (p *addrParser) consumeGroupList() ([]*Address, error) { // handle empty group. p.skipSpace() if p.consume(';') { - p.skipCFWS() + if !p.skipCFWS() { + return nil, errors.New("mail: misformatted parenthetical comment") + } return group, nil } @@ -496,7 +498,9 @@ func (p *addrParser) consumeGroupList() ([]*Address, error) { return nil, errors.New("mail: misformatted parenthetical comment") } if p.consume(';') { - p.skipCFWS() + if !p.skipCFWS() { + return nil, errors.New("mail: misformatted parenthetical comment") + } break } if !p.consume(',') { @@ -566,6 +570,12 @@ func (p *addrParser) consumePhrase() (phrase string, err error) { var words []string var isPrevEncoded bool for { + // obs-phrase allows CFWS after one word + if len(words) > 0 { + if !p.skipCFWS() { + return "", errors.New("mail: misformatted parenthetical comment") + } + } // word = atom / quoted-string var word string p.skipSpace() @@ -661,7 +671,6 @@ Loop: // If dot is true, consumeAtom parses an RFC 5322 dot-atom instead. // If permissive is true, consumeAtom will not fail on: // - leading/trailing/double dots in the atom (see golang.org/issue/4938) -// - special characters (RFC 5322 3.2.3) except '<', '>', ':' and '"' (see golang.org/issue/21018) func (p *addrParser) consumeAtom(dot bool, permissive bool) (atom string, err error) { i := 0 @@ -672,7 +681,7 @@ Loop: case size == 1 && r == utf8.RuneError: return "", fmt.Errorf("mail: invalid utf-8 in address: %q", p.s) - case size == 0 || !isAtext(r, dot, permissive): + case size == 0 || !isAtext(r, dot): break Loop default: @@ -850,18 +859,13 @@ func (e charsetError) Error() string { // isAtext reports whether r is an RFC 5322 atext character. // If dot is true, period is included. -// If permissive is true, RFC 5322 3.2.3 specials is included, -// except '<', '>', ':' and '"'. -func isAtext(r rune, dot, permissive bool) bool { +func isAtext(r rune, dot bool) bool { switch r { case '.': return dot // RFC 5322 3.2.3. specials - case '(', ')', '[', ']', ';', '@', '\\', ',': - return permissive - - case '<', '>', '"', ':': + case '(', ')', '<', '>', '[', ']', ':', ';', '@', '\\', ',', '"': // RFC 5322 3.2.3. specials return false } return isVchar(r) diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go index 1e1bb4092f..1f2f62afbf 100644 --- a/src/net/mail/message_test.go +++ b/src/net/mail/message_test.go @@ -385,8 +385,11 @@ func TestAddressParsingError(t *testing.T) { 13: {"group not closed: null@example.com", "expected comma"}, 14: {"group: first@example.com, second@example.com;", "group with multiple addresses"}, 15: {"john.doe", "missing '@' or angle-addr"}, - 16: {"john.doe@", "no angle-addr"}, + 16: {"john.doe@", "missing '@' or angle-addr"}, 17: {"John Doe@foo.bar", "no angle-addr"}, + 18: {" group: null@example.com; (asd", "misformatted parenthetical comment"}, + 19: {" group: ; (asd", "misformatted parenthetical comment"}, + 20: {`(John) Doe <jdoe@machine.example>`, "missing word in phrase:"}, } for i, tc := range mustErrTestCases { @@ -436,24 +439,19 @@ func TestAddressParsing(t *testing.T) { Address: "john.q.public@example.com", }}, }, - { - `"John (middle) Doe" <jdoe@machine.example>`, - []*Address{{ - Name: "John (middle) Doe", - Address: "jdoe@machine.example", - }}, - }, + // Comment in display name { `John (middle) Doe <jdoe@machine.example>`, []*Address{{ - Name: "John (middle) Doe", + Name: "John Doe", Address: "jdoe@machine.example", }}, }, + // Display name is quoted string, so comment is not a comment { - `John !@M@! Doe <jdoe@machine.example>`, + `"John (middle) Doe" <jdoe@machine.example>`, []*Address{{ - Name: "John !@M@! Doe", + Name: "John (middle) Doe", Address: "jdoe@machine.example", }}, }, @@ -788,6 +786,26 @@ func TestAddressParsing(t *testing.T) { }, }, }, + // Comment in group display name + { + `group (comment:): a@example.com, b@example.com;`, + []*Address{ + { + Address: "a@example.com", + }, + { + Address: "b@example.com", + }, + }, + }, + { + `x(:"):"@a.example;("@b.example;`, + []*Address{ + { + Address: `@a.example;(@b.example`, + }, + }, + }, } for _, test := range tests { if len(test.exp) == 1 { |