Getrennt von PROJ-84: Header (v.a. Subject) mit rohen 8-Bit-Bytes ohne RFC-2047-Encoded-Word-Syntax wurden nicht auf tatsächliches Charset geprüft, landeten als ungültiges UTF-8 in emails.subject und Manticore. Gleiche Lücke bei decodeCharset() für den Body ohne verwertbaren Content-Type. RepairUTF8/RepairUTF8Bytes (charset_repair.go): bytegenaue Reparatur, gültiges UTF-8 bleibt Identität, nur ungültige Byte-Sequenzen fallen auf Windows-1252 zurück. Attachment.Data bewusst ausgenommen (bleibt byte-exakt für Downloads). fix-subjects-Kommando erkennt jetzt beide Fälle (HasEncodedWord || NeedsCharsetRepair). Verifiziert auf 192.168.1.132: 54 zusätzliche Subject-Fälle, 219 Body-Fälle behoben (bodyInvalidUTF8 219 -> 0). --apply noch nicht ausgeführt, Body-Korrektur braucht zusätzlich reindex. Die ursprünglich gemeldete Amazon-Mail bleibt bewusst unverändert: Encoding-Fehler kam bereits so vom Absender (=3F statt =DC im Original-Encoded-Word), GoBD verbietet nachträgliche Korrektur archivierter Originalinhalte. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WapWkrQusDuBMhaN8WyuXB
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
package mailparser
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestRepairUTF8(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
in string
|
||
want string
|
||
}{
|
||
{"pure ascii untouched", "Passwort geaendert", "Passwort geaendert"},
|
||
{"valid utf8 untouched", "Passwort geändert – ok", "Passwort geändert – ok"},
|
||
{"windows1252 umlauts", "Passwort ge\xe4ndert", "Passwort geändert"},
|
||
{"windows1252 sharp s", "Mini-Fu\xdfball", "Mini-Fußball"},
|
||
{"cp1252 en dash 0x96", "Netbook f\xfcr 207 Euro \x96 jetzt", "Netbook für 207 Euro – jetzt"},
|
||
{"cp1252 registered 0xae", "NVIDIA\xae Karten", "NVIDIA® Karten"},
|
||
{"mixed valid utf8 and latin1", "Gr\xfc\xdfe – ünd", "Grüße – ünd"},
|
||
{"empty", "", ""},
|
||
}
|
||
for _, tc := range tests {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
if got := RepairUTF8(tc.in); got != tc.want {
|
||
t.Errorf("RepairUTF8(%q) = %q, want %q", tc.in, got, tc.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestNeedsCharsetRepair(t *testing.T) {
|
||
if NeedsCharsetRepair("Passwort geändert") {
|
||
t.Error("valid UTF-8 must not be flagged for repair")
|
||
}
|
||
if !NeedsCharsetRepair("Passwort ge\xe4ndert") {
|
||
t.Error("raw Latin-1 byte must be flagged for repair")
|
||
}
|
||
}
|
||
|
||
// A Subject header with raw 8-bit bytes and no RFC 2047 encoded-word must be
|
||
// decoded via the Windows-1252 fallback instead of ending up as invalid UTF-8.
|
||
func TestParseRawEightBitSubject(t *testing.T) {
|
||
raw := "From: a@example.com\r\n" +
|
||
"Subject: Passwort f\xfcr WoltLab ge\xe4ndert\r\n" +
|
||
"Content-Type: text/plain\r\n\r\n" +
|
||
"Gr\xfc\xdfe\r\n"
|
||
|
||
pm, err := Parse([]byte(raw))
|
||
if err != nil {
|
||
t.Fatalf("Parse: %v", err)
|
||
}
|
||
if pm.Subject != "Passwort für WoltLab geändert" {
|
||
t.Errorf("Subject = %q", pm.Subject)
|
||
}
|
||
if !strings.Contains(pm.TextBody, "Grüße") {
|
||
t.Errorf("TextBody = %q", pm.TextBody)
|
||
}
|
||
}
|
||
|
||
// A correctly encoded UTF-8 mail must survive the repair unchanged.
|
||
func TestParseValidUTF8NotMangled(t *testing.T) {
|
||
raw := "From: a@example.com\r\n" +
|
||
"Subject: =?UTF-8?Q?Gr=C3=BC=C3=9Fe?=\r\n" +
|
||
"Content-Type: text/plain; charset=UTF-8\r\n\r\n" +
|
||
"Schöne Grüße – äöüß\r\n"
|
||
|
||
pm, err := Parse([]byte(raw))
|
||
if err != nil {
|
||
t.Fatalf("Parse: %v", err)
|
||
}
|
||
if pm.Subject != "Grüße" {
|
||
t.Errorf("Subject = %q", pm.Subject)
|
||
}
|
||
if !strings.Contains(pm.TextBody, "Schöne Grüße – äöüß") {
|
||
t.Errorf("TextBody = %q", pm.TextBody)
|
||
}
|
||
}
|