1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-20 11:07:01 +00:00

Improved message rendering

Added a decodeSection function to mime.go that uses go-qprintable to
parse quoted-printable emails or MIME parts, fixes #7

Added a very basic TextToHtml converter to provide nicer rending of text
message bodies.
This commit is contained in:
James Hillyerd
2012-10-19 12:50:16 -07:00
parent 9e389e00d3
commit 2b3491fc87
8 changed files with 175 additions and 25 deletions

View File

@@ -69,7 +69,7 @@ func (c Mailbox) Show(name string, id string) rev.Result {
if err != nil {
return c.RenderError(err)
}
body := mime.Text
body := template.HTML(inbucket.TextToHtml(mime.Text))
htmlAvailable := mime.Html != ""
c.Response.Out.Header().Set("Expires", "-1")

View File

@@ -4,11 +4,12 @@ import (
"bytes"
"container/list"
"fmt"
"github.com/sloonz/go-qprintable"
"io"
"io/ioutil"
"mime"
"mime/multipart"
"net/mail"
"strings"
)
type MIMENodeMatcher func(node *MIMENode) bool
@@ -85,7 +86,7 @@ func ParseMIMEBody(mailMsg *mail.Message) (*MIMEBody, error) {
if !IsMIMEMessage(mailMsg) {
// Parse as text only
bodyBytes, err := ioutil.ReadAll(mailMsg.Body)
bodyBytes, err := decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
if err != nil {
return nil, err
}
@@ -136,6 +137,29 @@ func (m *MIMEBody) String() string {
return fmt.Sprintf("----TEXT----\n%v\n----HTML----\n%v\n----END----\n", m.Text, m.Html)
}
// decodeSection attempts to decode the data from reader using the algorithm listed in
// the Content-Transfer-Encoding header, returning the raw data if it does not know
// the encoding type.
func decodeSection(encoding string, reader io.Reader) ([]byte, error) {
switch strings.ToLower(encoding) {
case "quoted-printable":
decoder := qprintable.NewDecoder(qprintable.WindowsTextEncoding, reader)
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(decoder)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Don't know this type, just return bytes
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(reader)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func parseNodes(parent *MIMENode, reader io.Reader, boundary string) error {
var prevSibling *MIMENode
@@ -172,13 +196,12 @@ func parseNodes(parent *MIMENode, reader io.Reader, boundary string) error {
return err
}
} else {
// Content is data, allocate a buffer
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(part)
// Content is text or data, decode it
data, err := decodeSection(part.Header.Get("Content-Transfer-Encoding"), part)
if err != nil {
return err
}
node.Content = buf.Bytes()
node.Content = data
}
}

View File

@@ -42,6 +42,28 @@ func TestParseInlineText(t *testing.T) {
assert.Equal(t, mime.Text, "Test of HTML section")
}
func TestParseQuotedPrintable(t *testing.T) {
msg := readMessage("quoted-printable.raw")
mime, err := ParseMIMEBody(msg)
if err != nil {
t.Fatalf("Failed to parse MIME: %v", err)
}
assert.Contains(t, mime.Text, "Phasellus sit amet arcu")
}
func TestParseQuotedPrintableMime(t *testing.T) {
msg := readMessage("quoted-printable-mime.raw")
mime, err := ParseMIMEBody(msg)
if err != nil {
t.Fatalf("Failed to parse MIME: %v", err)
}
assert.Contains(t, mime.Text, "Nullam venenatis ante")
}
func TestParseInlineHtml(t *testing.T) {
msg := readMessage("html-mime-inline.raw")

View File

@@ -3,6 +3,7 @@ package inbucket
import (
"crypto/sha1"
"fmt"
"html"
"io"
"strings"
)
@@ -26,3 +27,11 @@ func HashMailboxName(mailbox string) string {
return fmt.Sprintf("%x", h.Sum(nil))
}
// TextToHtml takes plain text, escapes it and tries to pretty it up for
// HTML display
func TextToHtml(text string) string {
text = html.EscapeString(text)
replacer := strings.NewReplacer("\r\n", "<br/>\n", "\r", "<br/>\n", "\n", "<br/>\n")
return replacer.Replace(text)
}

View File

@@ -1,28 +1,44 @@
package inbucket
import "testing"
import (
"github.com/stretchrcom/testify/assert"
"testing"
)
func TestParseMailboxName(t *testing.T) {
in, out := "MailBOX", "mailbox"
if x := ParseMailboxName(in); x != out {
t.Errorf("ParseMailboxName(%v) = %v, want %v", in, x, out)
}
in, out := "MailBOX", "mailbox"
if x := ParseMailboxName(in); x != out {
t.Errorf("ParseMailboxName(%v) = %v, want %v", in, x, out)
}
in, out = "MailBox@Host.Com", "mailbox"
if x := ParseMailboxName(in); x != out {
t.Errorf("ParseMailboxName(%v) = %v, want %v", in, x, out)
}
in, out = "MailBox@Host.Com", "mailbox"
if x := ParseMailboxName(in); x != out {
t.Errorf("ParseMailboxName(%v) = %v, want %v", in, x, out)
}
in, out = "Mail+extra@Host.Com", "mail"
if x := ParseMailboxName(in); x != out {
t.Errorf("ParseMailboxName(%v) = %v, want %v", in, x, out)
}
in, out = "Mail+extra@Host.Com", "mail"
if x := ParseMailboxName(in); x != out {
t.Errorf("ParseMailboxName(%v) = %v, want %v", in, x, out)
}
}
func TestHashMailboxName(t *testing.T) {
in, out := "mail", "1d6e1cf70ec6f9ab28d3ea4b27a49a77654d370e"
if x := HashMailboxName(in); x != out {
t.Errorf("HashMailboxName(%v) = %v, want %v", in, x, out)
}
in, out := "mail", "1d6e1cf70ec6f9ab28d3ea4b27a49a77654d370e"
if x := HashMailboxName(in); x != out {
t.Errorf("HashMailboxName(%v) = %v, want %v", in, x, out)
}
}
func TestTextToHtml(t *testing.T) {
// Identity
assert.Equal(t, TextToHtml("html"), "html")
// Check it escapes
assert.Equal(t, TextToHtml("<html>"), "&lt;html&gt;")
// Check for linebreaks
assert.Equal(t, TextToHtml("line\nbreak"), "line<br/>\nbreak")
assert.Equal(t, TextToHtml("line\r\nbreak"), "line<br/>\nbreak")
assert.Equal(t, TextToHtml("line\rbreak"), "line<br/>\nbreak")
}

View File

@@ -17,5 +17,5 @@
<table>
<div id="emailSubject"><h3>{{.message.Subject}}</h3></div>
<pre id="emailBody">{{.body}}</pre>
<div id="emailBody">{{.body}}</div>