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:
patrickandClaude Sonnet 5.5 committed 2026-10-22 00:46:21 +02:00
1 parent 1d8c063a4f
commit 833ff55e1d
23 files changed
+336 -5

No files matched your search

+30
View File
@@ -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
}
+61
View File
@@ -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)
}
}
+3
View File
@@ -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