feat: strip comments from addresses

This commit is contained in:
James Houlahan
2020-09-14 14:41:14 +02:00
parent 5bca6fc3cf
commit 4b7c234e78
3 changed files with 126 additions and 6 deletions

View File

@ -19,7 +19,9 @@ package message
import (
"mime"
"net/mail"
"net/textproto"
"regexp"
"strings"
"time"
@ -139,3 +141,42 @@ func GetAttachmentHeader(att *pmapi.Attachment) textproto.MIMEHeader {
return h
}
var reEmailComment = regexp.MustCompile("[(][^)]*[)]") // nolint[gochecknoglobals]
// parseAddressComment removes the comments completely even though they should be allowed
// http://tools.wordtothewise.com/rfc/822
// NOTE: This should be supported in go>1.10 but it seems it's not ¯\_(ツ)_/¯
func parseAddressComment(raw string) string {
return reEmailComment.ReplaceAllString(raw, "")
}
func parseAddressList(val string) (addrs []*mail.Address, err error) {
addrs, err = mail.ParseAddressList(parseAddressComment(val))
if err == nil {
if addrs == nil {
addrs = []*mail.Address{}
}
return
}
// Probably missing encoding error -- try to at least parse addresses in brackets.
first := strings.Index(val, "<")
last := strings.LastIndex(val, ">")
if first < 0 || last < 0 || first >= last {
return
}
var addrList []string
open := first
for open < last && 0 <= open {
val = val[open:]
close := strings.Index(val, ">")
addrList = append(addrList, val[:close+1])
val = val[close:]
open = strings.Index(val, "<")
last = strings.LastIndex(val, ">")
}
val = strings.Join(addrList, ", ")
return mail.ParseAddressList(val)
}

View File

@ -375,35 +375,37 @@ func parseMessageHeader(m *pmapi.Message, h message.Header) error { // nolint[fu
m.Subject = val
case "from":
sender, err := mail.ParseAddress(val)
sender, err := parseAddressList(val)
if err != nil {
return err
}
m.Sender = sender
if len(sender) > 0 {
m.Sender = sender[0]
}
case "to":
toList, err := mail.ParseAddressList(val)
toList, err := parseAddressList(val)
if err != nil {
return err
}
m.ToList = toList
case "reply-to":
replyTos, err := mail.ParseAddressList(val)
replyTos, err := parseAddressList(val)
if err != nil {
return err
}
m.ReplyTos = replyTos
case "cc":
ccList, err := mail.ParseAddressList(val)
ccList, err := parseAddressList(val)
if err != nil {
return err
}
m.CCList = ccList
case "bcc":
bccList, err := mail.ParseAddressList(val)
bccList, err := parseAddressList(val)
if err != nil {
return err
}

View File

@ -434,3 +434,80 @@ func readerToString(r io.Reader) string {
return string(b)
}
func TestRFC822AddressFormat(t *testing.T) { //nolint[funlen]
tests := []struct {
address string
expected []string
}{
{
" normal name <username@server.com>",
[]string{
"\"normal name\" <username@server.com>",
},
},
{
" \"comma, name\" <username@server.com>",
[]string{
"\"comma, name\" <username@server.com>",
},
},
{
" name <username@server.com> (ignore comment)",
[]string{
"\"name\" <username@server.com>",
},
},
{
" name (ignore comment) <username@server.com>, (Comment as name) username2@server.com",
[]string{
"\"name\" <username@server.com>",
"<username2@server.com>",
},
},
{
" normal name <username@server.com>, (comment)All.(around)address@(the)server.com",
[]string{
"\"normal name\" <username@server.com>",
"<All.address@server.com>",
},
},
{
" normal name <username@server.com>, All.(\"comma, in comment\")address@(the)server.com",
[]string{
"\"normal name\" <username@server.com>",
"<All.address@server.com>",
},
},
{
" \"normal name\" <username@server.com>, \"comma, name\" <address@server.com>",
[]string{
"\"normal name\" <username@server.com>",
"\"comma, name\" <address@server.com>",
},
},
{
" \"comma, one\" <username@server.com>, \"comma, two\" <address@server.com>",
[]string{
"\"comma, one\" <username@server.com>",
"\"comma, two\" <address@server.com>",
},
},
{
" \"comma, name\" <username@server.com>, another, name <address@server.com>",
[]string{
"\"comma, name\" <username@server.com>",
"\"another, name\" <address@server.com>",
},
},
}
for _, data := range tests {
result, err := parseAddressList(data.address)
assert.NoError(t, err)
assert.Len(t, result, len(data.expected))
for i, result := range result {
assert.Equal(t, data.expected[i], result.String())
}
}
}