Clean mailbox preview rendering
This commit is contained in:
parent
647ce4bc90
commit
8823b431f4
2 changed files with 132 additions and 4 deletions
|
|
@ -1167,10 +1167,10 @@ func renderSourceMailboxRead(account Account, folder string, index int, messages
|
||||||
msg, err := mail.ReadMessage(bytes.NewReader(raw))
|
msg, err := mail.ReadMessage(bytes.NewReader(raw))
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
if err != nil {
|
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()
|
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("Subject")),
|
||||||
decodeHeader(msg.Header.Get("From")),
|
decodeHeader(msg.Header.Get("From")),
|
||||||
decodeHeader(msg.Header.Get("Date")),
|
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))
|
msg, err := mail.ReadMessage(bytes.NewReader(raw))
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
if err != nil {
|
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()
|
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("Subject")),
|
||||||
decodeHeader(msg.Header.Get("From")),
|
decodeHeader(msg.Header.Get("From")),
|
||||||
decodeHeader(msg.Header.Get("Date")),
|
decodeHeader(msg.Header.Get("Date")),
|
||||||
|
|
@ -1366,6 +1366,15 @@ func renderTargetMailboxRead(account Account, folder string, index int, messages
|
||||||
return b.String()
|
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, `<div class="pane-head mode-head"><span class="mode-label">%s</span></div>`, html.EscapeString(title))
|
||||||
|
fmt.Fprintf(w, `<div class="read-body"><div class="read-head"><h1 class="read-subject">%s</h1><div class="read-meta">Von: %s<br>Datum: %s<br>Postfach: %s / %s / #%d</div></div><pre class="read-pre">%s</pre></div>`,
|
||||||
|
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 {
|
func accountsByTarget(accounts []Account) []Account {
|
||||||
seen := map[string]bool{}
|
seen := map[string]bool{}
|
||||||
out := make([]Account, 0, len(accounts))
|
out := make([]Account, 0, len(accounts))
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
|
|
@ -205,9 +206,127 @@ func extractTextBody(h headerGetter, body []byte) string {
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(mediaType, "text/html") || looksLikeHTML(decoded) {
|
||||||
|
return htmlToText(string(decoded))
|
||||||
|
}
|
||||||
return string(decoded)
|
return string(decoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func looksLikeHTML(body []byte) bool {
|
||||||
|
s := strings.ToLower(strings.TrimSpace(string(body)))
|
||||||
|
return strings.HasPrefix(s, "<!doctype html") || strings.HasPrefix(s, "<html") || strings.Contains(s, "<body")
|
||||||
|
}
|
||||||
|
|
||||||
|
func htmlToText(s string) string {
|
||||||
|
s = stripHTMLBlock(s, "script")
|
||||||
|
s = stripHTMLBlock(s, "style")
|
||||||
|
s = markHTMLBreaks(s)
|
||||||
|
var b strings.Builder
|
||||||
|
inTag := false
|
||||||
|
lastSpace := false
|
||||||
|
for _, r := range s {
|
||||||
|
switch r {
|
||||||
|
case '<':
|
||||||
|
inTag = true
|
||||||
|
if !lastSpace {
|
||||||
|
b.WriteByte(' ')
|
||||||
|
lastSpace = true
|
||||||
|
}
|
||||||
|
case '>':
|
||||||
|
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
|
||||||
|
}{
|
||||||
|
{"<br>", "\n"},
|
||||||
|
{"<br/>", "\n"},
|
||||||
|
{"<br />", "\n"},
|
||||||
|
{"</p>", "\n"},
|
||||||
|
{"</div>", "\n"},
|
||||||
|
{"</tr>", "\n"},
|
||||||
|
{"</td>", " "},
|
||||||
|
{"</th>", " "},
|
||||||
|
{"</li>", "\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 {
|
func decodeTransfer(body []byte, enc string) []byte {
|
||||||
switch strings.ToLower(strings.TrimSpace(enc)) {
|
switch strings.ToLower(strings.TrimSpace(enc)) {
|
||||||
case "quoted-printable":
|
case "quoted-printable":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue