`))
}
func forwardHandler(w http.ResponseWriter, r *http.Request) {
// TODO Codex: GET = Weiterleiten-Formular (An/Betreff/Text), POST =
// ForwardMessage() aufrufen. NUR hier kommt SMTP zum Einsatz -- der Umzug
// selbst nutzt IMAP APPEND.
}
func renderViewerTree(w http.ResponseWriter, selected string) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
accounts := viewerAccounts()
if len(accounts) == 0 {
fmt.Fprint(w, `
Archiv-Mailbox
Noch keine mbox-Backups.
`)
return
}
selected = resolveViewerAccount(selected, selected)
if selected == "" {
selected = accounts[0].Name
} else {
found := false
for _, account := range accounts {
if account.Name == selected {
found = true
break
}
}
if !found {
selected = accounts[0].Name
}
}
fmt.Fprint(w, `
`, html.EscapeString(sourceDisplayLabel(account)), html.EscapeString(folder), len(filtered))
fmt.Fprintf(w, ``, html.EscapeString(sourceDisplayLabel(account)), html.EscapeString(folder), html.EscapeString(q))
for _, entry := range filtered {
subject := entry.Subject
if subject == "" {
subject = "(ohne Betreff)"
}
from := entry.From
if from == "" {
from = "(ohne Absender)"
}
fmt.Fprintf(w, `%s%s%s`,
html.EscapeString(sourceDisplayLabel(account)), html.EscapeString(folder), entry.Index,
urlEsc(sourceDisplayLabel(account)), urlEsc(folder), entry.Index,
html.EscapeString(from), html.EscapeString(subject), html.EscapeString(entry.Date))
}
if len(entries) == 0 {
fmt.Fprint(w, `
Keine Nachrichten in dieser mbox.
`)
} else if len(filtered) == 0 {
fmt.Fprint(w, `
Keine Treffer.
`)
}
}
type archiveFolderInfo struct {
Name string
Label string
Count int
}
func archiveFolderInfos(paths []string) []archiveFolderInfo {
out := make([]archiveFolderInfo, 0, len(paths))
for _, path := range paths {
name := folderNameFromMboxPath(path)
entries, err := ReadMboxList(path)
count := 0
if err == nil {
count = len(entries)
}
out = append(out, archiveFolderInfo{
Name: name,
Label: archiveFolderLabel(name),
Count: count,
})
}
sort.SliceStable(out, func(i, j int) bool {
return archiveFolderSortKey(out[i].Name) < archiveFolderSortKey(out[j].Name)
})
return out
}
func archiveFolderLabel(name string) string {
name = strings.TrimSpace(name)
if label := folderRoleLabel(RoleFromName(name, ".")); label != "" {
return label
}
if label := folderRoleLabel(RoleFromName(name, "/")); label != "" {
return label
}
switch strings.ToLower(name) {
case "", "inbox":
return "Posteingang"
case "drafts", "entw&apw-rfe", "entwürfe", "entwuerfe":
return "Entwürfe"
case "sent", "gesendet", "gesendete", "gesendete objekte", "gesendete elemente":
return "Gesendet"
case "trash", "papierkorb", "gelöscht", "geloescht", "gelöschte objekte", "geloeschte objekte":
return "Papierkorb"
case "archive", "archiv":
return "Archiv"
case "spam", "junk":
return "Spam"
}
parts := strings.FieldsFunc(name, func(r rune) bool {
return r == '/' || r == '\\' || r == '.'
})
if len(parts) == 0 {
parts = strings.Split(name, "_")
}
label := strings.TrimSpace(parts[len(parts)-1])
if label == "" {
return name
}
return label
}
func folderRoleLabel(role FolderRole) string {
switch role {
case RoleInbox:
return "Posteingang"
case RoleSent:
return "Gesendet"
case RoleDrafts:
return "Entwürfe"
case RoleTrash:
return "Papierkorb"
case RoleJunk:
return "Spam"
case RoleArchive:
return "Archiv"
case RoleOutbox:
return "Postausgang"
default:
return ""
}
}
func archiveFolderSortKey(name string) string {
lc := strings.ToLower(strings.TrimSpace(name))
switch RoleFromName(name, ".") {
case RoleInbox:
return "00:" + lc
case RoleDrafts:
return "01:" + lc
case RoleSent:
return "02:" + lc
case RoleJunk:
return "03:" + lc
case RoleTrash:
return "04:" + lc
case RoleArchive:
return "05:" + lc
}
switch lc {
case "inbox":
return "00:" + lc
case "drafts", "entw&apw-rfe", "entwürfe", "entwuerfe":
return "01:" + lc
case "sent", "gesendet", "gesendete", "gesendete objekte", "gesendete elemente":
return "02:" + lc
case "spam", "junk":
return "03:" + lc
case "trash", "papierkorb", "gelöscht", "geloescht", "gelöschte objekte", "geloeschte objekte":
return "04:" + lc
case "archive", "archiv":
return "05:" + lc
default:
return "10:" + lc
}
}
func manualCopyHandler(w http.ResponseWriter, r *http.Request) {
if !requireManager(w, r) {
return
}
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
account := resolveViewerAccount(r.FormValue("account"), r.FormValue("source"))
folder := r.FormValue("folder")
targetAccount := r.FormValue("target_account")
index, err := strconv.Atoi(r.FormValue("index"))
if err != nil {
http.Error(w, "bad index", http.StatusBadRequest)
return
}
path, err := mboxPath(account, folder)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
raw, err := ReadMboxMessage(path, index)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
note, err := appendManualTargetCopy(account, folder, targetAccount, raw)
if err != nil {
note = "Fehler beim manuellen Umzug: " + err.Error()
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, renderTargetPane(account, folder, targetAccount, index, raw, note))
}
func renderReadPaneWithSource(w http.ResponseWriter, account, folder, targetAccount string, index int, subject, from, date, body string) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
renderReadPane(w, "Quelle", account, folder, targetAccount, index, subject, from, date, body)
}
func renderReadPane(w io.Writer, title, account, folder, targetAccount string, index int, subject, from, date, body string) {
renderMailboxReadPane(w, title, account, folder, index, subject, from, date, body)
}
func renderTargetPane(account, folder, targetAccount string, index int, raw []byte, note string) string {
var b strings.Builder
b.WriteString(renderTargetAccountSelect(account, folder, targetAccount, index))
if note != "" {
class := "ok"
if strings.HasPrefix(note, "Fehler") {
class = "bad"
}
fmt.Fprintf(&b, `
%s
`, class, html.EscapeString(note))
}
if match, ok, err := findTargetIMAPCopy(account, targetAccount, raw); ok {
renderTargetMatch(&b, match)
return b.String()
} else if err != nil {
fmt.Fprintf(&b, `
Zielpostfach-Pruefung fehlgeschlagen: %s
`, html.EscapeString(err.Error()))
}
match, ok := findForwardedCopy(account, targetAccount, raw)
if !ok {
id := messageIDHeader(raw)
if id == "" {
id = "(ohne Message-ID)"
}
fmt.Fprintf(&b, `