# PROJ-84: Fix MIME-Header-Charset-Dekodierung + Backfill für Bestandsmails ## Status: Deployed **Created:** 2026-08-06 **Last Updated:** 2026-08-06 ## Kontext Betreffzeilen mit MIME-Encoded-Words (RFC 2047) in Charsets außerhalb UTF-8/US-ASCII/ISO-8859-1 (z.B. Windows-1252, ISO-8859-15) wurden nicht dekodiert und roh angezeigt (`=?Windows-1252?Q?ARAG_4_...?=`). Ursache: `decodeMIMEHeader()` in `pkg/mailparser/parser.go` nutzte `mime.WordDecoder` ohne `CharsetReader` — Go unterstützt dort nativ nur die drei genannten Charsets. Der Body-Decoder (`decodeCharset`, genutzt seit PROJ-57) konnte das bereits über `htmlindex`, der Header-Decoder nicht. Fix (Commit 31d7113) behebt das für künftige Importe. Bestandsmails behalten den kaputten Betreff, bis sie per Backfill korrigiert werden — dafür dieses Ticket. ## Dependencies - Baut auf PROJ-57 (UTF-8-Encoding-Fix) auf — nutzt dieselbe `htmlindex`-Charset-Logik - Betrifft PROJ-11/PROJ-48 (Audit-Log) — Backfill-Lauf wird auditiert ## User Stories - Als User will ich, dass Mail-Betreffs unabhängig vom ursprünglichen Absender-Charset korrekt lesbar sind, sowohl bei neuen als auch bei bereits archivierten Mails. - Als Admin will ich einen nachvollziehbaren, wiederholbaren Weg haben, bestehende Mails mit kaputtem Betreff zu korrigieren, ohne das archivierte Original anzufassen. ## Acceptance Criteria - [x] `decodeMIMEHeader()` dekodiert Encoded-Words in Windows-1252, ISO-8859-15 und weiteren von `htmlindex` unterstützten Charsets korrekt - [x] CLI-Kommando `archivmail fix-subjects` identifiziert Bestandsmails mit undekodiertem Encoded-Word im Betreff - [x] Dry-Run ist Default, echte Änderung nur mit `--apply` - [x] Korrektur ändert ausschließlich die Postgres-Metadaten-Spalte `emails.subject` — die archivierte Original-EML im Storage-Layer bleibt unverändert - [x] Nach Korrektur wird der Manticore-Suchindex für die betroffene Mail nachgezogen (inkl. Erhalt von vorhandenem OCR-Text) - [x] Jeder Lauf (auch Dry-Run) erzeugt einen Audit-Log-Eintrag (`event_type=metadata_backfill`) mit Zählern - [x] `--apply`-Lauf auf 132 durchgeführt und stichprobenartig verifiziert (541/541 aktualisiert, 0 Fehler, User-Gegenprobe an ARAG-Mail bestätigt 2026-08-06) - [x] `--apply`-Lauf auf 131 (Produktiv) durchgeführt (0 Kandidaten dort, nichts anzuwenden) ## Edge Cases - Encoded-Word verletzt RFC 2047 selbst (z.B. Leerzeichen im codierten Teil, an Vortext geklebt) → wird übersprungen, mit WARN geloggt, bleibt unverändert (2 von 543 Fällen auf 132) - Mail-Original nicht mehr ladbar/parsebar (z.B. DSGVO-gelöscht) → Index wird nicht angefasst, als `index_skipped` gezählt, DB-Subject bleibt beim Fallback (`DecodeMIMEHeader` auf vorhandenem String) - Mail bereits mit OCR-Text indexiert → Reindex darf `attachment_text` nicht löschen, wird aus bestehendem Index-Dokument übernommen ## Technical Requirements (optional) - Kein neues Datenbankschema, keine neue Tabelle - CLI-Subkommando statt Wegwerf-Skript, für künftige ähnliche Encoding-Bugs wiederverwendbar - Exit-Code 1 bei Fehlern (cron-/scripttauglich) --- ## Tech Design (Solution Architect) Direkt umgesetzt ohne vorgelagerte Architektur-Phase — kleiner, klar umrissener Bugfix + Backfill-Tool, kein neues UI, kein neues Datenmodell. Quelle der Wahrheit ist das archivierte Original: pro Mail wird die verschlüsselte EML lesend geladen und mit `mailparser.Parse()` neu geparst, der neue Betreff kommt aus `pm.Subject`. Fallback auf direktes Redekodieren des gespeicherten Strings, falls Original nicht ladbar. Geschrieben wird ausschließlich `UPDATE emails SET subject=...`. ## Implementation Notes (Backend, 2026-08-06) Neue/geänderte Dateien: - `pkg/mailparser/header_decode.go` (neu) — `DecodeMIMEHeader()` exportierter Wrapper, `HasEncodedWord()` Erkennung - `internal/storage/subject_backfill.go` (neu) — `ListRawEncodedSubjects()`, `UpdateSubjectMetadata()` - `cmd/archivmail/cmd_fix_subjects.go` (neu) — CLI-Kommando inkl. Reindex + Audit - `internal/audit/audit.go` — neue Konstante `EventMetadataBackfill = "metadata_backfill"` - `cmd/archivmail/main.go`, `cmd/archivmail/cmd_import.go` — Dispatch + Hilfetext Kommando: ```bash archivmail fix-subjects # Dry-Run (Default), alle Mandanten archivmail fix-subjects --tenant 3 --limit 50 archivmail fix-subjects --apply archivmail fix-subjects --apply --verbose # + jede Änderung alt->neu loggen ``` Messung auf 192.168.1.132 (Dry-Run, read-only): 543 Kandidaten, 541 reparierbar, 2 nicht dekodierbar (RFC-2047-Verletzung im Original, korrekt übersprungen). Charset-Verteilung: ISO-8859-15 (219), windows-1252 (185), Cp1252 (69), Windows-1252 (38), iso-8859-15 (11), utf8-Varianten (14), windows-1258 (5), ASCII (1). Build auf 132 verifiziert (`CGO_ENABLED=0 go build -buildvcs=false ./cmd/archivmail/` → OK), Dry-Run zweimal gegen Live-DB gelaufen, Stichproben korrekt (u.a. mehrteilige Windows-1252-Encoded-Words richtig zusammengesetzt). `--apply` noch nicht ausgeführt. ## QA Test Results _To be added by /qa_ ## Deployment _To be added by /deploy_