Files
archivmail/features/PROJ-85-fix-undeklarierte-8bit-header-charset.md
sysopsandClaude Sonnet 5 7727ad5cdf docs(PROJ-84,PROJ-85): Status auf Deployed - Backfill/Reindex auf 131+132 verifiziert
PROJ-84: 131 hatte 0 Kandidaten, 132 541/541 korrigiert (bereits
vorher bestätigt). PROJ-85: --apply + reindex auf 132 (54/54) und
131 (Produktiv) ohne Fehler durchgelaufen, User-Gegenprobe bestätigt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WapWkrQusDuBMhaN8WyuXB
2026-08-06 22:38:44 +02:00

68 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROJ-85: Fix undeklarierte 8-Bit-Zeichen in Header/Body ohne Encoded-Word
## Status: Deployed
**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
- [x] `RepairUTF8`/`RepairUTF8Bytes` erkennt Bytes außerhalb gültiger UTF-8-Sequenzen und versucht Windows-1252-Fallback-Dekodierung, bytegenau (nicht bufferweise) — gültiges UTF-8 bleibt unverändert
- [x] `decodeMIMEHeader()` wendet die Reparatur auf das Ergebnis an (inkl. Fehlerpfad)
- [x] Body-Decoding (Single-Part und Multipart, alle Content-Type-Fehlerpfade) wendet dieselbe Reparatur an, außer bei binären Attachments (`Attachment.Data` bleibt byte-exakt)
- [x] `fix-subjects`-Kommando erkennt zusätzlich zu Encoded-Word-Fällen (PROJ-84) auch rohe 8-Bit-Subjects ohne Encoded-Word-Marker (`NeedsCharsetRepair`)
- [x] Tests decken ab: gültiges UTF-8 bleibt unangetastet, Windows-1252-Sonderzeichen (ä/ö/ü/ß//®) werden korrekt repariert
- [x] `--apply` auf 132 durchgeführt (54/54 aktualisiert, 0 Fehler), danach `archivmail reindex` für Body-Korrektur (User-Gegenprobe bestätigt, 2026-08-06)
- [x] `--apply` + `reindex` auf 131 (Produktiv) durchgeführt, ohne Fehler (2026-08-06)
## 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. `=3F` statt 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.Data` bleibt 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)
---
<!-- Sections below are added by subsequent skills -->
## 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älle
- `pkg/mailparser/parser.go``decodeMIMEHeader()`-Ergebnis, Single-Part-Body, Multipart-Text-/HTML-Buffer, Content-Type-Fehlerpfad laufen durch die Reparatur; `Attachment.Data` bewusst ausgenommen
- `internal/storage/subject_charset_backfill.go` (neu) — `ListNonASCIISubjects()`
- `cmd/archivmail/cmd_fix_subjects_candidates.go` (neu) — merged/dedupliziert Encoded-Word- und Charset-Repair-Kandidaten
- `cmd/archivmail/cmd_fix_subjects.go` — Filter erweitert auf `HasEncodedWord || 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_