fix: decode RFC 2047 encoded words in mail sender display name

This commit is contained in:
killerprojecte
2026-06-19 21:27:05 +08:00
parent 25f5933792
commit f0c175e7f6
3 changed files with 106 additions and 9 deletions
+47 -1
View File
@@ -485,12 +485,58 @@ func partFilename(header textproto.MIMEHeader) string {
func firstAddressParts(value string) (string, string) {
items, err := netmail.ParseAddressList(value)
if err != nil || len(items) == 0 {
return strings.TrimSpace(value), ""
// ParseAddressList failed — attempt RFC 2047 decode on the raw header,
// then retry parsing. This handles non-standard From headers where
// encoded words (e.g. =?UTF-8?B?…?=) cause the initial parse to fail.
decoded := decodeMIMEHeader(value)
items, err = netmail.ParseAddressList(decoded)
if err != nil || len(items) == 0 {
// Still unparseable: return the decoded value as the email and
// try to extract a display name from the decoded string.
email, name := splitNameAndEmail(decoded)
return normalizeEmail(email), strings.TrimSpace(name)
}
}
item := items[0]
return normalizeEmail(item.Address), strings.TrimSpace(item.Name)
}
// decodeMIMEHeader decodes all RFC 2047 encoded words (=?charset?encoding?data?=)
// in the given header value. Falls back to the original value on any error.
func decodeMIMEHeader(value string) string {
if !strings.Contains(value, "=?") {
return value
}
decoder := new(mime.WordDecoder)
decoded, err := decoder.DecodeHeader(value)
if err != nil {
return value
}
return decoded
}
// splitNameAndEmail attempts to extract a display name and email address from
// a string like "Display Name <user@example.com>" or plain "user@example.com".
func splitNameAndEmail(value string) (string, string) {
value = strings.TrimSpace(value)
if value == "" {
return "", ""
}
// Try "Name <email>" pattern
if idx := strings.LastIndex(value, "<"); idx > 0 {
email := strings.TrimRight(value[idx+1:], ">")
name := strings.TrimSpace(strings.Trim(value[:idx], `" `))
if strings.Contains(email, "@") {
return email, name
}
}
// Plain email or unknown format
if strings.Contains(value, "@") {
return value, ""
}
return value, ""
}
func addressList(value string) []string {
items, err := netmail.ParseAddressList(value)
if err != nil {