Skip to content

Commit

Permalink
fix parsing message headers with addresses that need double quotes
Browse files Browse the repository at this point in the history
we are using Go's net/mail to parse message headers. it can parse addresses,
and properly decodes email addresses with double quotes (e.g. " "@example.com).
however, it gives us an address without the double quotes in the localpart,
effectively an invalid address. we now have a workaround to parse such
not-quite-addresses.

for issue #199 reported by gene-hightower, thanks for reporting!
  • Loading branch information
mjl- committed Aug 22, 2024
1 parent 79b641c commit 016fde8
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 3 deletions.
4 changes: 2 additions & 2 deletions message/part.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,9 @@ func parseAddressList(log mlog.Log, h mail.Header, k string) []Address {
for _, a := range l {
// todo: parse more fully according to ../rfc/5322:959
var user, host string
addr, err := smtp.ParseAddress(a.Address)
addr, err := smtp.ParseNetMailAddress(a.Address)
if err != nil {
log.Infox("parsing address (continuing)", err, slog.Any("address", a.Address))
log.Infox("parsing address (continuing)", err, slog.Any("netmailaddress", a.Address))
} else {
user = addr.Localpart.String()
host = addr.Domain.ASCII
Expand Down
7 changes: 7 additions & 0 deletions message/part_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,3 +596,10 @@ func TestEmbedded2(t *testing.T) {
_, err = EnsurePart(pkglog.Logger, false, bytes.NewReader(buf), int64(len(buf)))
tfail(t, err, nil)
}

func TestNetMailAddress(t *testing.T) {
const s = "From: \" \"@example.com\r\n\r\nbody\r\n"
p, err := EnsurePart(pkglog.Logger, false, strings.NewReader(s), int64(len(s)))
tcheck(t, err, "parse")
tcompare(t, p.Envelope.From, []Address{{"", `" "`, "example.com"}})
}
6 changes: 5 additions & 1 deletion sendmail.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,11 @@ binary should be setgid that group:
if len(addrs) != 1 {
log.Fatalf("only single address allowed in To header")
}
recipient = addrs[0].Address
addr, err := smtp.ParseNetMailAddress(addrs[0].Address)
if err != nil {
log.Fatalf("parsing address: %v", err)
}
recipient = addr.Pack(false)
}
}
if k == "to" {
Expand Down
15 changes: 15 additions & 0 deletions smtp/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,21 @@ func ParseAddress(s string) (address Address, err error) {
return Address{lp, d}, err
}

// ParseNetMailAddress parses a not-quite-valid address as found in
// net/mail.Address.Address.
//
// net/mail does parse quoted addresses properly, but stores the localpart
// unquoted. So an address `" "@example.com` would be stored as ` @example.com`,
// which we would fail to parse without special attention.
func ParseNetMailAddress(a string) (address Address, err error) {
i := strings.LastIndex(a, "@")
if i < 0 {
return Address{}, fmt.Errorf("%w: missing @", ErrBadAddress)
}
addrStr := Localpart(a[:i]).String() + "@" + a[i+1:]
return ParseAddress(addrStr)
}

var ErrBadLocalpart = errors.New("invalid localpart")

// ParseLocalpart parses the local part.
Expand Down

0 comments on commit 016fde8

Please sign in to comment.