mirror of
https://github.com/mjl-/mox.git
synced 2024-12-25 16:03:48 +03:00
recognize more charsets than utf-8/iso-8859-1/us-ascii when parsing message headers with address
as they occur in From/To headers, for example: "From: =?iso-8859-2?Q?Krist=FDna?= <k@example.com>". we are using net/mail to parse such headers. most address-parsing functions in that package will only decode charsets utf-8, iso-8859-1 and us-ascii. we have to be careful to always use net/mail.AddressParser with a WordDecoder that understands more that the basics. for issue #204 by morki, thanks for reporting!
This commit is contained in:
parent
0bb4501472
commit
5678b03324
6 changed files with 64 additions and 15 deletions
29
message/addr.go
Normal file
29
message/addr.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/mail"
|
||||
|
||||
"github.com/mjl-/mox/smtp"
|
||||
)
|
||||
|
||||
// ParseAddressList parses a string as an address list header value
|
||||
// (potentially multiple addresses, comma-separated, with optional display
|
||||
// name).
|
||||
func ParseAddressList(s string) ([]Address, error) {
|
||||
parser := mail.AddressParser{WordDecoder: &wordDecoder}
|
||||
addrs, err := parser.ParseList(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing address list: %v", err)
|
||||
}
|
||||
r := make([]Address, len(addrs))
|
||||
for i, a := range addrs {
|
||||
addr, err := smtp.ParseNetMailAddress(a.Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing adjusted address %q: %v", a.Address, err)
|
||||
}
|
||||
r[i] = Address{a.Name, addr.Localpart.String(), addr.Domain.ASCII}
|
||||
|
||||
}
|
||||
return r, nil
|
||||
}
|
11
message/addr_test.go
Normal file
11
message/addr_test.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package message
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseAddressList(t *testing.T) {
|
||||
l, err := ParseAddressList("=?iso-8859-2?Q?Krist=FDna?= <k@example.com>, mjl@mox.example")
|
||||
tcheck(t, err, "parsing address list")
|
||||
tcompare(t, l, []Address{{"Kristýna", "k", "example.com"}, {"", "mjl", "mox.example"}})
|
||||
}
|
|
@ -489,7 +489,12 @@ func parseEnvelope(log mlog.Log, h mail.Header) (*Envelope, error) {
|
|||
|
||||
func parseAddressList(log mlog.Log, h mail.Header, k string) []Address {
|
||||
// todo: possibly work around ios mail generating incorrect q-encoded "phrases" with unencoded double quotes? ../rfc/2047:382
|
||||
l, err := h.AddressList(k)
|
||||
v := h.Get(k)
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
parser := mail.AddressParser{WordDecoder: &wordDecoder}
|
||||
l, err := parser.ParseList(v)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -603,3 +603,10 @@ func TestNetMailAddress(t *testing.T) {
|
|||
tcheck(t, err, "parse")
|
||||
tcompare(t, p.Envelope.From, []Address{{"", `" "`, "example.com"}})
|
||||
}
|
||||
|
||||
func TestParseQuotedCharset(t *testing.T) {
|
||||
const s = "From: =?iso-8859-2?Q?Krist=FDna?= <k@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{{"Kristýna", "k", "example.com"}})
|
||||
}
|
||||
|
|
10
sendmail.go
10
sendmail.go
|
@ -9,7 +9,6 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/mail"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
@ -19,6 +18,7 @@ import (
|
|||
"github.com/mjl-/sconf"
|
||||
|
||||
"github.com/mjl-/mox/dns"
|
||||
"github.com/mjl-/mox/message"
|
||||
"github.com/mjl-/mox/mox-"
|
||||
"github.com/mjl-/mox/sasl"
|
||||
"github.com/mjl-/mox/smtp"
|
||||
|
@ -196,16 +196,12 @@ binary should be setgid that group:
|
|||
}
|
||||
recipient = submitconf.DefaultDestination
|
||||
} else {
|
||||
addrs, err := mail.ParseAddressList(s)
|
||||
addrs, err := message.ParseAddressList(s)
|
||||
xcheckf(err, "parsing To address list")
|
||||
if len(addrs) != 1 {
|
||||
log.Fatalf("only single address allowed in To header")
|
||||
}
|
||||
addr, err := smtp.ParseNetMailAddress(addrs[0].Address)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing address: %v", err)
|
||||
}
|
||||
recipient = addr.Pack(false)
|
||||
recipient = addrs[0].User + "@" + addrs[0].Host
|
||||
}
|
||||
}
|
||||
if k == "to" {
|
||||
|
|
|
@ -517,13 +517,14 @@ type File struct {
|
|||
// parseAddress expects either a plain email address like "user@domain", or a
|
||||
// single address as used in a message header, like "name <user@domain>".
|
||||
func parseAddress(msghdr string) (message.NameAddress, error) {
|
||||
a, err := mail.ParseAddress(msghdr)
|
||||
// todo: parse more fully according to ../rfc/5322:959
|
||||
parser := mail.AddressParser{WordDecoder: &wordDecoder}
|
||||
a, err := parser.Parse(msghdr)
|
||||
if err != nil {
|
||||
return message.NameAddress{}, err
|
||||
}
|
||||
|
||||
// todo: parse more fully according to ../rfc/5322:959
|
||||
path, err := smtp.ParseAddress(a.Address)
|
||||
path, err := smtp.ParseNetMailAddress(a.Address)
|
||||
if err != nil {
|
||||
return message.NameAddress{}, err
|
||||
}
|
||||
|
@ -1658,12 +1659,12 @@ func recipientSecurity(ctx context.Context, log mlog.Log, resolver dns.Resolver,
|
|||
SecurityResultUnknown,
|
||||
}
|
||||
|
||||
msgAddr, err := mail.ParseAddress(messageAddressee)
|
||||
parser := mail.AddressParser{WordDecoder: &wordDecoder}
|
||||
msgAddr, err := parser.Parse(messageAddressee)
|
||||
if err != nil {
|
||||
return rs, fmt.Errorf("parsing message addressee: %v", err)
|
||||
return rs, fmt.Errorf("parsing addressee: %v", err)
|
||||
}
|
||||
|
||||
addr, err := smtp.ParseAddress(msgAddr.Address)
|
||||
addr, err := smtp.ParseNetMailAddress(msgAddr.Address)
|
||||
if err != nil {
|
||||
return rs, fmt.Errorf("parsing address: %v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue