# Codex-Briefing — Email-Forwarder Du baust ein Go-Single-Binary im Hausstil (wie WatchDove): `main.go` als Orchestrator, nummerierte `backend/NN-*.go`, Server-gerendertes HTML + HTMX, `config.json`, SQLite via `modernc.org/sqlite` (kein cgo). Das Gerüst steht; fülle die `// TODO Codex`-Stellen. **Lies zuerst `Projektbeschreibung.md` – die 5 harten Regeln sind bindend.** ## Bibliotheken (lean, keine Lawine) - IMAP: `github.com/emersion/go-imap/v2` (+ `imapclient`) - Parsen (Message-ID, Header, Text/HTML für Viewer): `github.com/emersion/go-message` - SQLite: `modernc.org/sqlite` - POP3-Fallback: `github.com/knadh/go-pop3` - SMTP-Weiterleiten: `net/smtp` (stdlib) — keine Extra-Dependency `go mod tidy` + `go mod vendor` (Offline-Bunker, wie im Template). ## Modul-Landkarte | Datei | Inhalt | |---|---| | `main.go` | Flags (`--run`, `--watch`), LoadConfig→ConnectDB→InitAuth, Web oder CLI | | `backend/00-router.go` | Routen + `renderShell` (Outlook-2013-Dreispalter) — **da steht schon das Layout** | | `backend/01-config.go` | `config.json` (App + Forward-SMTP). Konten NICHT hier, sondern DB | | `backend/02-database.go` | Schema + Account-CRUD + `copied`-Cache (Idempotenz) | | `backend/03-auth.go` | Login + `AuthMiddleware` | | `backend/04-imap-source.go` | Quelle: Ordnerbaum + Fetch `BODY[] FLAGS INTERNALDATE` | | `backend/05-imap-target.go` | Ziel: `EnsureFolder` + `Append` (Flags + INTERNALDATE) | | `backend/06-mbox.go` | mbox schreiben (Umzug) **und** lesen (Viewer) | | `backend/07-migrate.go` | Motor: pro Konto/Ordner/Mail, Dedup, Doppel-Write, Fortschritt | | `backend/08-viewer.go` | mbox im Browser: Liste, Lesen, Weiterleiten | | `backend/09-smtp.go` | SMTP nur fürs Weiterleiten | | `backend/10-pop3.go` | POP3-Fallback-Quelle (nur INBOX, nie DELE) | ## DB-Schema (SQLite) ``` accounts(id, name, src_host, src_port, src_security, src_insecure, src_user, src_pass, src_proto, dst_host, dst_port, dst_security, dst_insecure, dst_user, dst_pass, mbox_dir, active) folder_map(id, account_id, src_folder, dst_folder) -- optional, Vorrang vor Rollen-Automatik copied(account_id, folder, message_id UNIQUE) -- Delta-Cache jobs(id, account_id, started, finished, total, done, errors, state) ``` `src_security`/`dst_security` = `"tls"` | `"starttls"` | `"none"`. `src_insecure`/`dst_insecure` = ungültige Zerts akzeptieren. ## Der Umzugs-Kern (07-migrate.go), Schritt für Schritt 1. Quelle öffnen: `src_proto=imap` → `OpenIMAPSource`, sonst `OpenPOP3Source`. 2. `Folders()` → rekursiver Ordnerbaum. 3. Pro Ordner: Zielordner via `MapSourceToTarget` (11-folders.go) bestimmen — Rollen-Zuordnung gegen die vorhandenen Ziel-Ordner (keine Dubletten), Trenner umhängen, `folder_map` als Override. Dann `EnsureFolder`. 4. `Fetch(folder)` streamend. Pro Mail: `AlreadyCopied(id,folder,msgID)`? → ja: überspringen. Nein: **beide** schreiben — `TargetMailbox.Append` (IMAP, mit Flags+INTERNALDATE) **und** `MboxWriter.Append` (lokal), dann `MarkCopied`. Fehler pro Mail zählen, nicht den ganzen Lauf abbrechen. 5. `jobs` fortschreiben (Browser pollt `/migrate/status` alle 3 s). 6. `--watch`: Schleife mit Pause; dank `copied` kopiert jeder Durchlauf nur Neues. ## UI — Outlook 2013 (Vorlage) Look sitzt schon in `frontend-js/dist/style.css` + `renderShell`: **Ribbon** (Outlook-Blau `#0072c6`) mit Reitern *Umzug · Postfächer · Betrachten · Einstellungen*, darunter **Dreispalter**: links Ordnerbaum (Konto → Ordner), Mitte Nachrichtenliste (ungelesen = fett blau), rechts Lesebereich; unten Statusleiste. Flach, Segoe UI, blaue Auswahlzeile `#cde6f7`. Panes per HTMX nachladen (`#tree` / `#list` / `#read`). Klassen stehen im CSS: `.tree-node`, `.msg-row(.unread/.active)`, `.read-*`, `.grid/.input/.btn`. Reiter **Postfächer** = Konten-CRUD (Formular wie WatchDove `targetForm`, Quelle- und Ziel-Block, „Test"-Button prüft beide Logins). Reiter **Umzug** = Konten starten + Fortschritt. Reiter **Betrachten** = mbox-Viewer, im Lesebereich ein „Weiterleiten"-Button (öffnet Formular → `09-smtp.go`). `frontend-js/dist/htmx.min.js` per Bun holen (wie Template) und mit einchecken. ## Sonderfälle — die drei Umzugs-Fallen (zwingend) 1. **Unverschlüsselte Alt-Provider.** Verbinden nach `src_security`: `tls`=DialTLS (993), `starttls`=DialStartTLS (143), `none`=Dial (reiner Klartext). `src_insecure` → `tls.Config{InsecureSkipVerify:true}` für kaputte/selbstsignierte Zerts. Muss ohne TLS funktionieren — alte Hoster können oft nichts anderes. (04/05-imap-*.go, POP3 analog 110/995.) 2. **Sonderzeichen in Ordnernamen.** IMAP-Ordner sind auf der Leitung modified-UTF-7 (`Gelöschte`→`Gel&APY-schte`). go-imap dekodiert/kodiert das — intern IMMER mit dem Klartext-Namen arbeiten, **nie doppelt kodieren**. Hierarchie-Trenner (`.` vs `/`) je Server aus LIST lesen und beim Umhängen übersetzen (`11-folders.go`). 3. **Standardordner nicht doppeln.** `11-folders.go` bildet jeden Ordner auf eine Rolle ab (SPECIAL-USE `\Sent \Drafts \Junk \Trash \Archive`, sonst mehrsprachige Namensliste). `MapSourceToTarget` sortiert Gesendet/Entwürfe/ Spam/Papierkorb/Archiv/Posteingang in den **vorhandenen** Rollen-Ordner des Ziels statt „Gesendete Objekte" **neben** „Gesendete Elemente" zu legen. Manuelles `folder_map` schlägt die Automatik. ## Sicherheit Tool hält fremde IMAP-Passwörter und kann Mails senden. Default `bind=127.0.0.1` + Login. Falls je remote (hinter Caddy): `/vadmin`-Mail-2FA-Muster aus dem Web-Deploy-Kit vorschalten. `config.json`/DB nie committen (`.gitignore` steht). ## Fertig-Kriterien (Durchstich zuerst) 1. **Durchstich:** ein Konto, nur `INBOX`, Quelle→Ziel-`APPEND` + mbox, Message-ID-Dedup, zweimal laufbar ohne Dubletten. 2. Alle Ordner rekursiv + `EnsureFolder` im Ziel. 3. Konten-CRUD + Test-Button im Browser. 4. Viewer (Liste/Lesen) + Weiterleiten. 5. `--watch`-Delta-Schleife. 6. Optional: POP3-Fallback.