aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2024-07-02 15:58:41 +0200
committerRobin Jarry <robin@jarry.cc>2024-07-02 22:12:45 +0200
commit44b14ebdff0e3da8df2348eeda27bfc2ec421fbb (patch)
tree8711abffa76e415dde14a67a671603fa27da586f /lib
parentbee2fc62ac7808cf081e58947023cd4c61c0109e (diff)
downloadaerc-44b14ebdff0e3da8df2348eeda27bfc2ec421fbb.tar.gz
rfc822: be liberal with invalid address headers
Some email clients format email addresses with quotes around B encoded names. E.g.: "=?utf-8?B?U21pZXRhbnNraSwgV29qY2llY2ggVGFkZXVzeiBpbiBUZWFtcw==?=" This seems non standard. I tried reading the multiple RFCs that describe the MIME headers encoding but could not find any precise answer to determine if adding quotes around encoded words is valid or not. In any case, ParseAddressList completely ignores any encoding when it encounters a double quote. When that happens, try to detect it and force decoding a second time after the quotes have been removed. Link: https://datatracker.ietf.org/doc/html/rfc2045#section-3 Link: https://datatracker.ietf.org/doc/html/rfc2047#section-2 Link: https://datatracker.ietf.org/doc/html/rfc822#section-3.3 Reported-by: Bence Ferdinandy <bence@ferdinandy.com> Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/rfc822/message.go8
-rw-r--r--lib/rfc822/message_test.go53
2 files changed, 61 insertions, 0 deletions
diff --git a/lib/rfc822/message.go b/lib/rfc822/message.go
index 391ab3e5..26d790ff 100644
--- a/lib/rfc822/message.go
+++ b/lib/rfc822/message.go
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
+ "mime"
"regexp"
"strings"
"time"
@@ -284,6 +285,13 @@ func parseAddressList(h *mail.Header, key string) ([]*mail.Address, error) {
// errors which are not fatal.
return nil, err
}
+ for _, addr := range addrs {
+ // Handle invalid headers with quoted *AND* encoded names
+ if strings.HasPrefix(addr.Name, "=?") && strings.HasSuffix(addr.Name, "?=") {
+ d := mime.WordDecoder{CharsetReader: message.CharsetReader}
+ addr.Name, _ = d.DecodeHeader(addr.Name)
+ }
+ }
// If we got at least one address, ignore any returned error.
return addrs, nil
}
diff --git a/lib/rfc822/message_test.go b/lib/rfc822/message_test.go
index 3ae71cb9..fed3b093 100644
--- a/lib/rfc822/message_test.go
+++ b/lib/rfc822/message_test.go
@@ -9,6 +9,7 @@ import (
"git.sr.ht/~rjarry/aerc/models"
"github.com/emersion/go-message/mail"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -111,6 +112,58 @@ func TestParseMessageDate(t *testing.T) {
}
}
+func TestParseAddressList(t *testing.T) {
+ header := mail.HeaderFromMap(map[string][]string{
+ "From": {`"=?utf-8?B?U21pZXRhbnNraSwgV29qY2llY2ggVGFkZXVzeiBpbiBUZWFtcw==?=" <noreply@email.teams.microsoft.com>`},
+ "To": {`=?UTF-8?q?Oc=C3=A9ane_de_Seazon?= <hello@seazon.fr>`},
+ "Cc": {`=?utf-8?b?0KjQsNCz0L7QsiDQk9C10L7RgNCz0LjQuSB2aWEgZGlzY3Vzcw==?= <ovs-discuss@openvswitch.org>`},
+ "Bcc": {`"Foo, Baz Bar" <~foo/baz@bar.org>`},
+ })
+ type vector struct {
+ kind string
+ header string
+ name string
+ email string
+ }
+
+ vectors := []vector{
+ {
+ kind: "quoted",
+ header: "Bcc",
+ name: "Foo, Baz Bar",
+ email: "~foo/baz@bar.org",
+ },
+ {
+ kind: "Qencoded",
+ header: "To",
+ name: "Océane de Seazon",
+ email: "hello@seazon.fr",
+ },
+ {
+ kind: "Bencoded",
+ header: "Cc",
+ name: "Шагов Георгий via discuss",
+ email: "ovs-discuss@openvswitch.org",
+ },
+ {
+ kind: "quoted+Bencoded",
+ header: "From",
+ name: "Smietanski, Wojciech Tadeusz in Teams",
+ email: "noreply@email.teams.microsoft.com",
+ },
+ }
+
+ for _, vec := range vectors {
+ t.Run(vec.kind, func(t *testing.T) {
+ addrs, err := parseAddressList(&header, vec.header)
+ assert.Nil(t, err)
+ assert.Len(t, addrs, 1)
+ assert.Equal(t, vec.name, addrs[0].Name)
+ assert.Equal(t, vec.email, addrs[0].Address)
+ })
+ }
+}
+
type mockRawMessage struct {
path string
}