diff --git a/backend/00-router.go b/backend/00-router.go
index 692c87e..8c716b1 100644
--- a/backend/00-router.go
+++ b/backend/00-router.go
@@ -1167,10 +1167,10 @@ func renderSourceMailboxRead(account Account, folder string, index int, messages
msg, err := mail.ReadMessage(bytes.NewReader(raw))
var b strings.Builder
if err != nil {
- renderReadPane(&b, "Quelle", account.Name, folder, "", index, "(unlesbar)", "", "", string(raw))
+ renderMailboxReadPane(&b, "Quelle", account.Name, folder, index, "(unlesbar)", "", "", string(raw))
return b.String()
}
- renderReadPane(&b, "Quelle", account.Name, folder, "", index,
+ renderMailboxReadPane(&b, "Quelle", account.Name, folder, index,
decodeHeader(msg.Header.Get("Subject")),
decodeHeader(msg.Header.Get("From")),
decodeHeader(msg.Header.Get("Date")),
@@ -1354,10 +1354,10 @@ func renderTargetMailboxRead(account Account, folder string, index int, messages
msg, err := mail.ReadMessage(bytes.NewReader(raw))
var b strings.Builder
if err != nil {
- renderReadPane(&b, "Ziel", account.Name, folder, "", index, "(unlesbar)", "", "", string(raw))
+ renderMailboxReadPane(&b, "Ziel", account.Name, folder, index, "(unlesbar)", "", "", string(raw))
return b.String()
}
- renderReadPane(&b, "Ziel", account.Name, folder, "", index,
+ renderMailboxReadPane(&b, "Ziel", account.Name, folder, index,
decodeHeader(msg.Header.Get("Subject")),
decodeHeader(msg.Header.Get("From")),
decodeHeader(msg.Header.Get("Date")),
@@ -1366,6 +1366,15 @@ func renderTargetMailboxRead(account Account, folder string, index int, messages
return b.String()
}
+func renderMailboxReadPane(w io.Writer, title, account, folder string, index int, subject, from, date, body string) {
+ if subject == "" {
+ subject = "(ohne Betreff)"
+ }
+ fmt.Fprintf(w, `
%s
`, html.EscapeString(title))
+ fmt.Fprintf(w, `%s
Von: %s
Datum: %s
Postfach: %s / %s / #%d
%s
`,
+ html.EscapeString(subject), html.EscapeString(from), html.EscapeString(date), html.EscapeString(account), html.EscapeString(folder), index+1, html.EscapeString(body))
+}
+
func accountsByTarget(accounts []Account) []Account {
seen := map[string]bool{}
out := make([]Account, 0, len(accounts))
diff --git a/backend/06-mbox.go b/backend/06-mbox.go
index 712ed4f..6bbc50e 100644
--- a/backend/06-mbox.go
+++ b/backend/06-mbox.go
@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/base64"
"fmt"
+ "html"
"io"
"mime"
"mime/multipart"
@@ -205,9 +206,127 @@ func extractTextBody(h headerGetter, body []byte) string {
}
return ""
}
+ if strings.HasPrefix(mediaType, "text/html") || looksLikeHTML(decoded) {
+ return htmlToText(string(decoded))
+ }
return string(decoded)
}
+func looksLikeHTML(body []byte) bool {
+ s := strings.ToLower(strings.TrimSpace(string(body)))
+ return strings.HasPrefix(s, "':
+ inTag = false
+ default:
+ if inTag {
+ continue
+ }
+ if r == '\n' {
+ b.WriteByte('\n')
+ lastSpace = true
+ continue
+ }
+ if r == '\r' || r == '\t' || r == ' ' {
+ if !lastSpace {
+ b.WriteByte(' ')
+ lastSpace = true
+ }
+ continue
+ }
+ b.WriteRune(r)
+ lastSpace = false
+ }
+ }
+ text := html.UnescapeString(b.String())
+ lines := strings.Split(text, "\n")
+ out := make([]string, 0, len(lines))
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line != "" {
+ out = append(out, line)
+ }
+ }
+ if len(out) == 0 {
+ return strings.TrimSpace(text)
+ }
+ return strings.Join(out, "\n")
+}
+
+func markHTMLBreaks(s string) string {
+ repls := []struct {
+ Needle string
+ With string
+ }{
+ {"
", "\n"},
+ {"
", "\n"},
+ {"
", "\n"},
+ {"", "\n"},
+ {"", "\n"},
+ {"", "\n"},
+ {"", " "},
+ {"", " "},
+ {"", "\n"},
+ }
+ for _, repl := range repls {
+ s = replaceCaseInsensitive(s, repl.Needle, repl.With)
+ }
+ return s
+}
+
+func replaceCaseInsensitive(s, old, new string) string {
+ var b strings.Builder
+ lower := strings.ToLower(s)
+ needle := strings.ToLower(old)
+ for {
+ i := strings.Index(lower, needle)
+ if i < 0 {
+ b.WriteString(s)
+ return b.String()
+ }
+ b.WriteString(s[:i])
+ b.WriteString(new)
+ cut := i + len(old)
+ s = s[cut:]
+ lower = lower[cut:]
+ }
+}
+
+func stripHTMLBlock(s, tag string) string {
+ lower := strings.ToLower(s)
+ open := "<" + tag
+ close := "" + tag + ">"
+ for {
+ start := strings.Index(lower, open)
+ if start < 0 {
+ return s
+ }
+ end := strings.Index(lower[start:], close)
+ if end < 0 {
+ return s[:start]
+ }
+ end += start + len(close)
+ s = s[:start] + " " + s[end:]
+ lower = strings.ToLower(s)
+ }
+}
+
func decodeTransfer(body []byte, enc string) []byte {
switch strings.ToLower(strings.TrimSpace(enc)) {
case "quoted-printable":