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
5.9 KiB
PROJ-85: Fix undeklarierte 8-Bit-Zeichen in Header/Body ohne Encoded-Word
Status: In Review
Created: 2026-08-06 Last Updated: 2026-08-06
Kontext
Anlass war eine gemeldete Mojibake-Mail ("Grü?e" statt "Grüße"). Diagnose ergab: diese konkrete Mail ist korrekt archiviert — der Fehler (=3F statt =DC im Original-Encoded-Word) kam bereits so vom Absender (Amazon, 2018, DKIM-signiert) und darf laut GoBD nicht nachträglich verändert werden.
Beim Scan wurde daneben aber ein echter archivmail-Bug gefunden, verschieden von PROJ-84: Header (insbesondere Subject) mit rohen 8-Bit-Bytes ohne RFC-2047-Encoded-Word-Syntax (technisch RFC-5322-widrig, in freier Wildbahn bei Marketing-/Newsletter-Systemen üblich) wurden nicht auf ihr tatsächliches Charset geprüft. decodeCharset() hatte dieselbe Lücke für den Body bei fehlendem/leerem Content-Type. Ergebnis: ungültige UTF-8-Bytes landeten unverändert in emails.subject (Postgres, DB-Encoding SQL_ASCII, akzeptiert das klaglos) und im Manticore-Index.
Dependencies
- Ergänzt PROJ-84 (Fix MIME-Header-Charset-Dekodierung) — andere Ursache (kein Encoded-Word-Marker vorhanden), daher eigenes Ticket statt Erweiterung
- Nutzt dieselbe Backfill-Infrastruktur (
fix-subjects-Kommando, Audit-Log)
User Stories
- Als User will ich, dass Betreffs mit undeklarierten 8-Bit-Sonderzeichen (ä/ö/ü/ß/„–"/„®" etc.) korrekt lesbar sind, wenn der Absender kein valides Encoded-Word genutzt hat.
- Als User will ich, dass auch der Mail-Body korrekt dargestellt/durchsuchbar ist, wenn das deklarierte oder fehlende Charset nicht zu den tatsächlichen Bytes passt.
- Als Auditor will ich, dass eine Reparatur klar von echter Absenderseiten-Korruption unterschieden wird — Mails, die der Absender bereits fehlerhaft verschickt hat (wie das Encoded-Word-Beispiel), werden NICHT verändert.
Acceptance Criteria
RepairUTF8/RepairUTF8Byteserkennt Bytes außerhalb gültiger UTF-8-Sequenzen und versucht Windows-1252-Fallback-Dekodierung, bytegenau (nicht bufferweise) — gültiges UTF-8 bleibt unverändertdecodeMIMEHeader()wendet die Reparatur auf das Ergebnis an (inkl. Fehlerpfad)- Body-Decoding (Single-Part und Multipart, alle Content-Type-Fehlerpfade) wendet dieselbe Reparatur an, außer bei binären Attachments (
Attachment.Datableibt byte-exakt) fix-subjects-Kommando erkennt zusätzlich zu Encoded-Word-Fällen (PROJ-84) auch rohe 8-Bit-Subjects ohne Encoded-Word-Marker (NeedsCharsetRepair)- Tests decken ab: gültiges UTF-8 bleibt unangetastet, Windows-1252-Sonderzeichen (ä/ö/ü/ß/–/®) werden korrekt repariert
--applyauf 132 durchgeführt, danacharchivmail reindexfür Body-Korrektur--apply+reindexauf 131 (Produktiv), Umfang dort separat messen (131 ≠ 132, siehe PROJ-84-Erfahrung: 0 vs. 543 Kandidaten)
Edge Cases
- Encoded-Word mit RFC-5322-widriger Syntax (Leerzeichen im codierten Teil) → bleibt unverändert, als „undecodable" gezählt, nicht Teil dieses Fixes (schon in PROJ-84 als Grenzfall dokumentiert)
- Mail, bei der der Absender selbst bereits fehlerhaft kodiert hat (z.B.
=3Fstatt korrektem Byte im Encoded-Word) → wird NICHT repariert, da das die archivierte Originalnachricht selbst verfälschen würde (GoBD-Grundsatz: Archiv bildet ab, was ankam, nicht was gemeint war) - Binäre Attachments (z.B. CSV mit Windows-1252) →
Attachment.Datableibt byte-exakt, keine Reparatur, sonst wäre der heruntergeladene Anhang verändert - Text, der zufällig wie gültiges UTF-8 aussieht, aber eigentlich Windows-1252 ist → nicht erkennbar/nicht behandelt (Kollisionsfall, in der Praxis selten und nicht zuverlässig unterscheidbar)
Technical Requirements (optional)
- Kein neues Datenbankschema
- Postgres-DB-Encoding bleibt
SQL_ASCII(Bestand, nicht Teil dieses Fixes) — Nebenbefund, der erklärt, warum ungültige Bytes bisher klaglos angenommen wurden - Body-Korrektur wirkt erst nach zusätzlichem
archivmail reindex-Lauf (Body liegt nur in Manticore, nicht in Postgres)
Tech Design (Solution Architect)
Direkt umgesetzt ohne vorgelagerte Architektur-Phase — Bugfix + Backfill-Erweiterung auf bestehender Infrastruktur (PROJ-84), kein neues UI, kein neues Datenmodell.
Implementation Notes (Backend, 2026-08-06)
Neue/geänderte Dateien:
pkg/mailparser/charset_repair.go(neu) —RepairUTF8(),RepairUTF8Bytes(),NeedsCharsetRepair(). Strategie: erst UTF-8-Validität prüfen, nur für Bytes, die nicht Teil einer gültigen UTF-8-Sequenz sein können, Windows-1252-Fallback (golang.org/x/text/encoding/charmap)pkg/mailparser/charset_repair_test.go(neu) — 12 Testfällepkg/mailparser/parser.go—decodeMIMEHeader()-Ergebnis, Single-Part-Body, Multipart-Text-/HTML-Buffer, Content-Type-Fehlerpfad laufen durch die Reparatur;Attachment.Databewusst ausgenommeninternal/storage/subject_charset_backfill.go(neu) —ListNonASCIISubjects()cmd/archivmail/cmd_fix_subjects_candidates.go(neu) — merged/dedupliziert Encoded-Word- und Charset-Repair-Kandidatencmd/archivmail/cmd_fix_subjects.go— Filter erweitert aufHasEncodedWord || NeedsCharsetRepair
Stolperstein auf dem Server: Prefilter octet_length(subject) <> length(subject) liefert in einer SQL_ASCII-DB immer 0 Treffer, weil length() dort Bytes zählt. Ersetzt durch subject ~ '[^[:ascii:]]'.
Messung auf 192.168.1.132 (Dry-Run, read-only): 21.004 geprüfte Kandidaten (Encoded-Word- + Charset-Repair-Muster kombiniert), 54 zusätzliche Betreffs durch Charset-Repair betroffen (alle Tenant 3), 0 Fehlklassifikationen. Body-Verifikation über alle 52.961 Mails mit gepatchtem Parser: parsedSubjectInvalidUTF8: 54 → 0, bodyInvalidUTF8: 219 → 0.
Build, go vet, go test ./pkg/mailparser/ auf 132 grün. --apply noch nicht ausgeführt.
QA Test Results
To be added by /qa
Deployment
To be added by /deploy