Commit 738acbc2 authored by Guilherme Rezende's avatar Guilherme Rezende Committed by Ian Lance Taylor

net/mail: parse cases when phrase has special chars without quotes

Updates #21018

Change-Id: I00b6667fb5fee78559a391815f58760a2baea90e
Reviewed-on: https://go-review.googlesource.com/50911Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
Reviewed-by: default avatarAvelino <t@avelino.xxx>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent b0392159
...@@ -14,6 +14,7 @@ Notable divergences: ...@@ -14,6 +14,7 @@ Notable divergences:
* The full range of spacing (the CFWS syntax element) is not supported, * The full range of spacing (the CFWS syntax element) is not supported,
such as breaking addresses across lines. such as breaking addresses across lines.
* No unicode normalization is performed. * No unicode normalization is performed.
* Address with some RFC 5322 3.2.3 specials without quotes are parsed.
*/ */
package mail package mail
...@@ -190,7 +191,7 @@ func (a *Address) String() string { ...@@ -190,7 +191,7 @@ func (a *Address) String() string {
// Add quotes if needed // Add quotes if needed
quoteLocal := false quoteLocal := false
for i, r := range local { for i, r := range local {
if isAtext(r, false) { if isAtext(r, false, false) {
continue continue
} }
if r == '.' { if r == '.' {
...@@ -482,20 +483,20 @@ Loop: ...@@ -482,20 +483,20 @@ Loop:
// consumeAtom parses an RFC 5322 atom at the start of p. // consumeAtom parses an RFC 5322 atom at the start of p.
// If dot is true, consumeAtom parses an RFC 5322 dot-atom instead. // If dot is true, consumeAtom parses an RFC 5322 dot-atom instead.
// If permissive is true, consumeAtom will not fail on // If permissive is true, consumeAtom will not fail on:
// leading/trailing/double dots in the atom (see golang.org/issue/4938). // - 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) { func (p *addrParser) consumeAtom(dot bool, permissive bool) (atom string, err error) {
i := 0 i := 0
Loop: Loop:
for { for {
r, size := utf8.DecodeRuneInString(p.s[i:]) r, size := utf8.DecodeRuneInString(p.s[i:])
switch { switch {
case size == 1 && r == utf8.RuneError: case size == 1 && r == utf8.RuneError:
return "", fmt.Errorf("mail: invalid utf-8 in address: %q", p.s) return "", fmt.Errorf("mail: invalid utf-8 in address: %q", p.s)
case size == 0 || !isAtext(r, dot): case size == 0 || !isAtext(r, dot, permissive):
break Loop break Loop
default: default:
...@@ -580,12 +581,18 @@ func (e charsetError) Error() string { ...@@ -580,12 +581,18 @@ func (e charsetError) Error() string {
// isAtext reports whether r is an RFC 5322 atext character. // isAtext reports whether r is an RFC 5322 atext character.
// If dot is true, period is included. // If dot is true, period is included.
func isAtext(r rune, dot bool) bool { // If permissive is true, RFC 5322 3.2.3 specials is included,
// except '<', '>' and '"'.
func isAtext(r rune, dot, permissive bool) bool {
switch r { switch r {
case '.': case '.':
return dot return dot
case '(', ')', '<', '>', '[', ']', ':', ';', '@', '\\', ',', '"': // RFC 5322 3.2.3. specials // RFC 5322 3.2.3. specials
case '(', ')', '[', ']', ':', ';', '@', '\\', ',':
return permissive
case '<', '>', '"':
return false return false
} }
return isVchar(r) return isVchar(r)
......
...@@ -137,6 +137,8 @@ func TestAddressParsingError(t *testing.T) { ...@@ -137,6 +137,8 @@ func TestAddressParsingError(t *testing.T) {
5: {"\"\x00\" <null@example.net>", "bad character in quoted-string"}, 5: {"\"\x00\" <null@example.net>", "bad character in quoted-string"},
6: {"\"\\\x00\" <escaped-null@example.net>", "bad character in quoted-string"}, 6: {"\"\\\x00\" <escaped-null@example.net>", "bad character in quoted-string"},
7: {"John Doe", "no angle-addr"}, 7: {"John Doe", "no angle-addr"},
8: {`<jdoe#machine.example>`, "missing @ in addr-spec"},
9: {`John <middle> Doe <jdoe@machine.example>`, "missing @ in addr-spec"},
} }
for i, tc := range mustErrTestCases { for i, tc := range mustErrTestCases {
...@@ -175,6 +177,34 @@ func TestAddressParsing(t *testing.T) { ...@@ -175,6 +177,34 @@ func TestAddressParsing(t *testing.T) {
Address: "john.q.public@example.com", Address: "john.q.public@example.com",
}}, }},
}, },
{
`"John (middle) Doe" <jdoe@machine.example>`,
[]*Address{{
Name: "John (middle) Doe",
Address: "jdoe@machine.example",
}},
},
{
`John (middle) Doe <jdoe@machine.example>`,
[]*Address{{
Name: "John (middle) Doe",
Address: "jdoe@machine.example",
}},
},
{
`John !@M@! Doe <jdoe@machine.example>`,
[]*Address{{
Name: "John !@M@! Doe",
Address: "jdoe@machine.example",
}},
},
{
`"John <middle> Doe" <jdoe@machine.example>`,
[]*Address{{
Name: "John <middle> Doe",
Address: "jdoe@machine.example",
}},
},
{ {
`Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`, `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`,
[]*Address{ []*Address{
...@@ -203,6 +233,14 @@ func TestAddressParsing(t *testing.T) { ...@@ -203,6 +233,14 @@ func TestAddressParsing(t *testing.T) {
}, },
}, },
}, },
// RFC 5322, Appendix A.6.1
{
`Joe Q. Public <john.q.public@example.com>`,
[]*Address{{
Name: "Joe Q. Public",
Address: "john.q.public@example.com",
}},
},
// RFC 5322, Appendix A.1.3 // RFC 5322, Appendix A.1.3
// TODO(dsymonds): Group addresses. // TODO(dsymonds): Group addresses.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment