feat(PROJ-89): BCC auslesen, indexieren, in Sichtbarkeitsprüfung und Portal anzeigen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1d8c063a4f
commit
833ff55e1d
23 files changed
+336
-5
No files matched your search
@@ -0,0 +1,30 @@
|
||||
package mailparser
|
||||
|
||||
import (
|
||||
"net/mail"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func bccAddresses(header string) []string {
|
||||
if strings.TrimSpace(header) == "" {
|
||||
return nil
|
||||
}
|
||||
addrs, err := mail.ParseAddressList(header)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var out []string
|
||||
seen := make(map[string]struct{})
|
||||
for _, a := range addrs {
|
||||
addr := strings.ToLower(strings.TrimSpace(a.Address))
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
if _, dup := seen[addr]; dup {
|
||||
continue
|
||||
}
|
||||
seen[addr] = struct{}{}
|
||||
out = append(out, addr)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package mailparser_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBCCAddresses(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
headers string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "missing",
|
||||
headers: "",
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "single",
|
||||
headers: "Bcc: alice@example.com\r\n",
|
||||
want: []string{"alice@example.com"},
|
||||
},
|
||||
{
|
||||
name: "multiple with display names, lowercased and deduplicated",
|
||||
headers: "Bcc: Alice <Alice@Example.com>, bob@example.com, ALICE@example.com\r\n",
|
||||
want: []string{"alice@example.com", "bob@example.com"},
|
||||
},
|
||||
{
|
||||
name: "unparseable yields nothing",
|
||||
headers: "Bcc: not an address <<<\r\n",
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "empty header",
|
||||
headers: "Bcc: \r\n",
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pm := parseHeaders(t, tt.headers)
|
||||
if !reflect.DeepEqual(pm.BCC, tt.want) {
|
||||
t.Errorf("BCC = %#v, want %#v", pm.BCC, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBCCDoesNotAffectToCC(t *testing.T) {
|
||||
pm := parseHeaders(t, "Cc: erin@example.com\r\nBcc: frank@example.com\r\n")
|
||||
if !reflect.DeepEqual(pm.To, []string{"dave@example.org"}) {
|
||||
t.Errorf("To = %#v", pm.To)
|
||||
}
|
||||
if !reflect.DeepEqual(pm.CC, []string{"erin@example.com"}) {
|
||||
t.Errorf("CC = %#v", pm.CC)
|
||||
}
|
||||
if !reflect.DeepEqual(pm.BCC, []string{"frank@example.com"}) {
|
||||
t.Errorf("BCC = %#v", pm.BCC)
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ type ParsedMail struct {
|
||||
From string
|
||||
To []string
|
||||
CC []string
|
||||
BCC []string
|
||||
Subject string
|
||||
MessageID string
|
||||
InReplyTo string // In-Reply-To header (single message-id, no angle brackets)
|
||||
@@ -167,6 +168,8 @@ func Parse(raw []byte) (pmOut *ParsedMail, errOut error) {
|
||||
}
|
||||
}
|
||||
|
||||
pm.BCC = bccAddresses(msg.Header.Get("Bcc"))
|
||||
|
||||
pm.DeliveryAddresses = deliveryAddresses(msg.Header)
|
||||
|
||||
// Subject - decode MIME encoded-words
|
||||
|
||||
Reference in new issue
Block a user