package backend import "strings" // Dieses Modul löst zwei Umzugs-Fallen: // // 1. Standardordner NICHT doppeln. Quelle "Gesendete Objekte" darf am Ziel // nicht neben "Gesendete Elemente" als Zweitordner landen. Wir bilden jeden // Ordner auf eine kanonische ROLLE ab und schreiben in den vorhandenen // Rollen-Ordner des Ziels — statt einen neuen anzulegen. // 2. Hierarchie-Trenner übersetzen. Quelle nutzt evtl. "." (INBOX.Sent.2023), // Ziel "/" — Pfad muss umgehängt werden. Sonderzeichen (Umlaute) sind auf // der Leitung modified-UTF-7; go-imap dekodiert/kodiert das — wir arbeiten // hier IMMER mit dem dekodierten Klartext-Namen, nie doppelt kodieren. type FolderRole string const ( RoleNone FolderRole = "" RoleInbox FolderRole = "inbox" RoleSent FolderRole = "sent" RoleDrafts FolderRole = "drafts" RoleJunk FolderRole = "junk" RoleTrash FolderRole = "trash" RoleArchive FolderRole = "archive" RoleOutbox FolderRole = "outbox" ) // specialUseAttr = IMAP SPECIAL-USE (RFC 6154) -> Rolle. Erste Wahl, weil // sprachunabhängig. Outbox/Postausgang hat kein Standard-Flag (Client-lokal). var specialUseAttr = map[string]FolderRole{ "\\Sent": RoleSent, "\\Drafts": RoleDrafts, "\\Junk": RoleJunk, "\\Trash": RoleTrash, "\\Archive": RoleArchive, } // nameAliases = Fallback, wenn der Alt-Server kein SPECIAL-USE meldet. Vergleich // case-insensitiv gegen das LETZTE Pfadsegment (ohne "INBOX."-Präfix). // Mehrsprachig, weil Provider lokalisiert benennen. var nameAliases = map[FolderRole][]string{ RoleInbox: {"inbox", "posteingang"}, RoleSent: {"sent", "sent items", "sent messages", "gesendet", "gesendete", "gesendete objekte", "gesendete elemente", "gesendete e-mails"}, RoleDrafts: {"drafts", "draft", "entwurf", "entwürfe", "entwuerfe"}, RoleJunk: {"junk", "junk e-mail", "junk-e-mail", "spam", "bulk mail", "werbung"}, RoleTrash: {"trash", "deleted", "deleted items", "deleted messages", "papierkorb", "gelöscht", "gelöschte", "gelöschte objekte", "gelöschte elemente", "müll"}, RoleArchive: {"archive", "archiv", "archived", "archiviert"}, RoleOutbox: {"outbox", "postausgang"}, } // RoleFromAttrs bestimmt die Rolle aus den SPECIAL-USE-Attributen (bevorzugt). func RoleFromAttrs(attrs []string) FolderRole { for _, a := range attrs { if role, ok := specialUseAttr[a]; ok { return role } } return RoleNone } // RoleFromName ist der Fallback über den (dekodierten) Ordnernamen. delim ist // der Hierarchie-Trenner des jeweiligen Servers. func RoleFromName(name, delim string) FolderRole { leaf := name if delim != "" { parts := strings.Split(name, delim) leaf = parts[len(parts)-1] } if strings.EqualFold(strings.TrimPrefix(name, "INBOX"+delim), "") || strings.EqualFold(name, "INBOX") { return RoleInbox } lc := strings.ToLower(strings.TrimSpace(leaf)) for role, aliases := range nameAliases { for _, alias := range aliases { if lc == alias { return role } } } return RoleNone } // Role kombiniert beide: erst SPECIAL-USE, dann Namensliste. func Role(attrs []string, name, delim string) FolderRole { if r := RoleFromAttrs(attrs); r != RoleNone { return r } return RoleFromName(name, delim) } // TargetFolder beschreibt einen am Ziel existierenden Ordner (für die Rollen- // Zuordnung beim Umzug). type TargetFolder struct { Name string Attrs []string } // MapSourceToTarget entscheidet, in welchen ZIEL-Ordner eine Quell-Mailbox // wandert: // - manuelles folder_map hat Vorrang (übergibt override != ""); // - hat die Quelle eine Rolle und existiert am Ziel ein Ordner gleicher Rolle // -> dessen Name (kein Neuanlegen, keine Dublette); // - sonst Pfad vom Quell- auf den Ziel-Trenner umhängen und (ggf. neu) anlegen. // // TODO Codex: Implementieren; srcDelim/dstDelim aus dem jeweiligen Server (LIST- // Antwort). override kommt aus folder_map. func MapSourceToTarget(srcName string, srcAttrs []string, srcDelim, dstDelim string, targets []TargetFolder, override string) string { // TODO Codex. return "" }