mime support and html email rendering

This commit is contained in:
Ian Kent 2014-04-21 22:32:34 +01:00
parent b2e0ae7f46
commit 07df8aa087
4 changed files with 77 additions and 12 deletions

View file

@ -17,6 +17,7 @@ type Message struct {
To []*Path
Content *Content
Created time.Time
MIME *MIMEBody
}
type Path struct {
@ -40,7 +41,7 @@ type SMTPMessage struct {
}
type MIMEBody struct {
Parts []Content
Parts []*Content
}
func ParseSMTPMessage(c *mailhog.Config, m *SMTPMessage) *Message {
@ -55,7 +56,12 @@ func ParseSMTPMessage(c *mailhog.Config, m *SMTPMessage) *Message {
Content: ContentFromString(m.Data),
Created: time.Now(),
}
log.Printf("Is MIME: %t\n", msg.Content.IsMIME());
if msg.Content.IsMIME() {
log.Printf("Parsing MIME body")
msg.MIME = msg.Content.ParseMIMEBody()
}
msg.Content.Headers["Message-ID"] = []string{msg.Id + "@" + c.Hostname}
msg.Content.Headers["Received"] = []string{"from " + m.Helo + " by " + c.Hostname + " (Go-MailHog)\r\n id " + msg.Id + "@" + c.Hostname + "; " + time.Now().Format(time.RFC1123Z)}
msg.Content.Headers["Return-Path"] = []string{"<" + m.From + ">"}
@ -68,9 +74,20 @@ func (content *Content) IsMIME() bool {
func (content *Content) ParseMIMEBody() *MIMEBody {
re := regexp.MustCompile("boundary=\"([^\"]+)\"")
match := re.FindStringSubmatch(content.Body)
match := re.FindStringSubmatch(content.Headers["Content-Type"][0])
log.Printf("Got boundary: %s", match[1])
return nil
p := strings.Split(content.Body, "--" + match[1])
parts := make([]*Content, 0)
for m := range p {
if len(p[m]) > 0 {
parts = append(parts, ContentFromString(strings.Trim(p[m], "\r\n")))
}
}
return &MIMEBody{
Parts: parts,
}
}
func PathFromString(path string) *Path {
@ -99,6 +116,7 @@ func PathFromString(path string) *Path {
}
func ContentFromString(data string) *Content {
log.Printf("Parsing Content from string: '%s'", data)
x := strings.SplitN(data, "\r\n\r\n", 2)
headers, body := x[0], x[1]

View file

@ -101,8 +101,8 @@ func Index() string {
<li><a href="#preview-source" data-toggle="tab">Source</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="preview-html">{{ preview.Content.Body }}</div>
<div class="tab-pane" id="preview-plain"><pre>{{ preview.Content.Body }}</pre></div>
<div class="tab-pane active" id="preview-html" ng-bind-html="previewHTML"></div>
<div class="tab-pane" id="preview-plain"><pre>{{ getMessagePlain(preview) }}</pre></div>
<div class="tab-pane" id="preview-source"><pre>{{ getSource(preview) }}</pre></div>
</div>
</div>

View file

@ -4,7 +4,7 @@ func Controllers() string {
return `
var mailhogApp = angular.module('mailhogApp', []);
mailhogApp.controller('MailCtrl', function ($scope, $http) {
mailhogApp.controller('MailCtrl', function ($scope, $http, $sce) {
$scope.refresh = function() {
$http.get('/api/v1/messages').success(function(data) {
$scope.messages = data;
@ -12,11 +12,45 @@ mailhogApp.controller('MailCtrl', function ($scope, $http) {
}
$scope.refresh();
$scope.getMessagePlain = function(message) {
var part;
if(message.MIME) {
for(var p in message.MIME.Parts) {
if(message.MIME.Parts[p].Headers["Content-Type"][0] == "text/plain") {
part = message.MIME.Parts[p];
break;
}
}
}
if(!part) part = message.Content;
return part.Body;
}
$scope.getMessageHTML = function(message) {
var part;
if(message.MIME) {
for(var p in message.MIME.Parts) {
if(message.MIME.Parts[p].Headers["Content-Type"][0] == "text/html") {
part = message.MIME.Parts[p];
break;
}
}
}
if(!part) part = message.Content;
return part.Body;
}
$scope.date = function(timestamp) {
return (new Date(timestamp)).toString();
};
$scope.selectMessage = function(message) {
$scope.previewHTML = $sce.trustAsHtml($scope.getMessageHTML(message));
$scope.preview = message;
}

View file

@ -71,11 +71,11 @@ func TestBasicMIMEHappyPath(t *testing.T) {
content += "Subject: Example message\r\n"
content += "MIME-Version: 1.0\r\n"
content += "\r\n"
content += "--mailhog-test-boundary\r\n"
content += "----mailhog-test-boundary\r\n"
content += "Content-Type: text/plain\r\n"
content += "\r\n"
content += "Hi there :)\r\n"
content += "--mailhog-test-boundary\r\n"
content += "----mailhog-test-boundary\r\n"
content += "Content-Type: text/html\r\n"
content += "\r\n"
content += "<html>\r\n"
@ -88,7 +88,7 @@ func TestBasicMIMEHappyPath(t *testing.T) {
content += " </p>\r\n"
content += " </body>\r\n"
content += "</html>\r\n"
content += "--mailhog-test-boundary\r\n"
content += "----mailhog-test-boundary\r\n"
content += ".\r\n"
_, err = conn.Write([]byte(content))
assert.Nil(t, err)
@ -136,8 +136,21 @@ func TestBasicMIMEHappyPath(t *testing.T) {
assert.Equal(t, message.Content.Headers["Return-Path"], []string{"<nobody@mailhog.example>"}, "Return-Path is <nobody@mailhog.example>")
assert.Equal(t, message.Content.Headers["Message-ID"], []string{match[1] + "@mailhog.example"}, "Message-ID is "+match[1]+"@mailhog.example")
expected := "--mailhog-test-boundary\r\nContent-Type: text/plain\r\n\r\nHi there :)\r\n--mailhog-test-boundary\r\nContent-Type: text/html\r\n\r\n<html>\r\n <head>\r\n <title>Example message</title>\r\n </head>\r\n <body>\r\n <p style=\"font-weight: bold; color: #ff0000; text-decoration: underline\">\r\n Hi there :)\r\n </p>\r\n </body>\r\n</html>\r\n--mailhog-test-boundary"
expected := "----mailhog-test-boundary\r\nContent-Type: text/plain\r\n\r\nHi there :)\r\n----mailhog-test-boundary\r\nContent-Type: text/html\r\n\r\n<html>\r\n <head>\r\n <title>Example message</title>\r\n </head>\r\n <body>\r\n <p style=\"font-weight: bold; color: #ff0000; text-decoration: underline\">\r\n Hi there :)\r\n </p>\r\n </body>\r\n</html>\r\n----mailhog-test-boundary"
assert.Equal(t, message.Content.Body, expected, "message has correct body")
assert.NotNil(t, message.MIME)
assert.Equal(t, len(message.MIME.Parts), 2)
plain := message.MIME.Parts[0]
assert.NotNil(t, plain)
assert.Equal(t, plain.Headers["Content-Type"], []string{"text/plain"}, "Content-Type header is text/plain")
assert.Equal(t, plain.Body, "Hi there :)", "plain text body is correct")
html := message.MIME.Parts[1]
assert.NotNil(t, html)
assert.Equal(t, html.Headers["Content-Type"], []string{"text/html"}, "Content-Type header is text/html")
expected = "<html>\r\n <head>\r\n <title>Example message</title>\r\n </head>\r\n <body>\r\n <p style=\"font-weight: bold; color: #ff0000; text-decoration: underline\">\r\n Hi there :)\r\n </p>\r\n </body>\r\n</html>"
assert.Equal(t, html.Body, expected, "html body is correct")
}