mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2024-11-30 09:44:03 +00:00
mime support and html email rendering
This commit is contained in:
parent
b2e0ae7f46
commit
07df8aa087
4 changed files with 77 additions and 12 deletions
|
@ -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]
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
23
mime_test.go
23
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 += "<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")
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue