mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +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:
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package inbucket
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"github.com/stretchrcom/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseMailboxName(t *testing.T) {
|
||||
in, out := "MailBOX", "mailbox"
|
||||
@@ -26,3 +29,16 @@ func TestHashMailboxName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTextToHtml(t *testing.T) {
|
||||
// Identity
|
||||
assert.Equal(t, TextToHtml("html"), "html")
|
||||
|
||||
// Check it escapes
|
||||
assert.Equal(t, TextToHtml("<html>"), "<html>")
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
<table>
|
||||
<div id="emailSubject"><h3>{{.message.Subject}}</h3></div>
|
||||
|
||||
<pre id="emailBody">{{.body}}</pre>
|
||||
<div id="emailBody">{{.body}}</div>
|
||||
|
||||
|
||||
@@ -286,6 +286,7 @@ a:hover {
|
||||
|
||||
#emailBody {
|
||||
color: #000000;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#emailActions {
|
||||
|
||||
79
test-data/quoted-printable-mime.raw
Normal file
79
test-data/quoted-printable-mime.raw
Normal file
@@ -0,0 +1,79 @@
|
||||
Message-ID: <5081A889.3020108@jamehi03lx.noa.com>
|
||||
Date: Fri, 19 Oct 2012 12:22:49 -0700
|
||||
From: James Hillyerd <jamehi03@jamehi03lx.noa.com>
|
||||
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20121010 Thunderbird/16.0.1
|
||||
MIME-Version: 1.0
|
||||
To: greg@inbucket.com
|
||||
Subject: MIME Quoted Printable
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="------------020203040006070307010003"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------020203040006070307010003
|
||||
Content-Type: text/plain; charset=ISO-8859-1
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam =
|
||||
venenatis ante fermentum justo varius hendrerit. Sed adipiscing =
|
||||
adipiscing nisi at placerat. Sed luctus neque pellentesque sem laoreet =
|
||||
dapibus. Class aptent taciti sociosqu ad litora torquent per conubia =
|
||||
nostra, per inceptos himenaeos. In eu scelerisque nibh. Fusce faucibus, =
|
||||
nisl vel tincidunt sodales, quam est condimentum lorem, vitae semper =
|
||||
lacus nisl vel lacus. Vestibulum vitae iaculis urna. Donec pellentesque =
|
||||
pellentesque ipsum sit amet suscipit. Ut aliquam vestibulum justo sit =
|
||||
amet congue. Mauris nisl lacus, varius a ultrices id, suscipit mattis =
|
||||
libero.
|
||||
|
||||
Donec iaculis dapibus purus in accumsan. Cras faucibus, orci dictum =
|
||||
molestie congue, neque magna adipiscing lacus, ut laoreet sapien ante et =
|
||||
mi. Suspendisse potenti. Nullam sed dui magna. Nullam vel purus augue, =
|
||||
imperdiet vehicula purus. Lorem ipsum dolor sit amet, consectetur =
|
||||
adipiscing elit. In aliquet, ante at tincidunt ultricies, arcu dolor =
|
||||
rutrum odio, quis tempus lacus leo a quam. Fusce hendrerit, urna sed =
|
||||
elementum pulvinar, dolor sem imperdiet arcu, ut ornare erat metus sit =
|
||||
amet diam. Duis sagittis libero ut metus vulputate dictum. Etiam eget =
|
||||
augue dolor, in lacinia felis. Nulla facilisi. Proin sollicitudin =
|
||||
laoreet vehicula. Praesent non nibh odio.
|
||||
|
||||
|
||||
--------------020203040006070307010003
|
||||
Content-Type: text/html; charset=ISO-8859-1
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
|
||||
</head>
|
||||
<body bgcolor="#FFFFFF" text="#000000">
|
||||
<div id="lipsum">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
|
||||
venenatis ante fermentum justo varius hendrerit. Sed adipiscing
|
||||
adipiscing nisi at placerat. Sed luctus neque pellentesque sem
|
||||
laoreet dapibus. Class aptent taciti sociosqu ad litora torquent
|
||||
per conubia nostra, per inceptos himenaeos. In eu scelerisque
|
||||
nibh. Fusce faucibus, nisl vel tincidunt sodales, quam est
|
||||
condimentum lorem, vitae semper lacus nisl vel lacus. Vestibulum
|
||||
vitae iaculis urna. Donec pellentesque pellentesque ipsum sit
|
||||
amet suscipit. Ut aliquam vestibulum justo sit amet congue.
|
||||
Mauris nisl lacus, varius a ultrices id, suscipit mattis libero.
|
||||
</p>
|
||||
<p>
|
||||
Donec iaculis dapibus purus in accumsan. Cras faucibus, orci
|
||||
dictum molestie congue, neque magna adipiscing lacus, ut laoreet
|
||||
sapien ante et mi. Suspendisse potenti. Nullam sed dui magna.
|
||||
Nullam vel purus augue, imperdiet vehicula purus. Lorem ipsum
|
||||
dolor sit amet, consectetur adipiscing elit. In aliquet, ante at
|
||||
tincidunt ultricies, arcu dolor rutrum odio, quis tempus lacus
|
||||
leo a quam. Fusce hendrerit, urna sed elementum pulvinar, dolor
|
||||
sem imperdiet arcu, ut ornare erat metus sit amet diam. Duis
|
||||
sagittis libero ut metus vulputate dictum. Etiam eget augue
|
||||
dolor, in lacinia felis. Nulla facilisi. Proin sollicitudin
|
||||
laoreet vehicula. Praesent non nibh odio.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
--------------020203040006070307010003--
|
||||
Reference in New Issue
Block a user