diff --git a/mailhog/data/message.go b/mailhog/data/message.go index 1803181..d045a14 100644 --- a/mailhog/data/message.go +++ b/mailhog/data/message.go @@ -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] diff --git a/mailhog/templates/index.go b/mailhog/templates/index.go index eb69b96..a85d21f 100644 --- a/mailhog/templates/index.go +++ b/mailhog/templates/index.go @@ -101,8 +101,8 @@ func Index() string {
  • Source
  • -
    {{ preview.Content.Body }}
    -
    {{ preview.Content.Body }}
    +
    +
    {{ getMessagePlain(preview) }}
    {{ getSource(preview) }}
    diff --git a/mailhog/templates/js/controllers.go b/mailhog/templates/js/controllers.go index 4e84ec4..5558eba 100644 --- a/mailhog/templates/js/controllers.go +++ b/mailhog/templates/js/controllers.go @@ -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; } diff --git a/mime_test.go b/mime_test.go index f028e20..ce86e4a 100644 --- a/mime_test.go +++ b/mime_test.go @@ -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 += "\r\n" @@ -88,7 +88,7 @@ func TestBasicMIMEHappyPath(t *testing.T) { content += "

    \r\n" content += " \r\n" content += "\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{""}, "Return-Path is ") 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\r\n \r\n Example message\r\n \r\n \r\n

    \r\n Hi there :)\r\n

    \r\n \r\n\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\r\n \r\n Example message\r\n \r\n \r\n

    \r\n Hi there :)\r\n

    \r\n \r\n\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 = "\r\n \r\n Example message\r\n \r\n \r\n

    \r\n Hi there :)\r\n

    \r\n \r\n" + assert.Equal(t, html.Body, expected, "html body is correct") + }