From 47dbf5612a259bca7b7495eb9f318d117d5537f4 Mon Sep 17 00:00:00 2001 From: Nikos Kitmeridis Date: Sun, 20 Oct 2019 21:24:02 +0300 Subject: [PATCH] Adds content-type message header to support other content-types such HTML Closes #112 Closes #250 --- .gitattributes | 1 + .../internal/http/handlers/messages.go | 14 ++- internal/mail/headers.go | 26 +++-- internal/mail/headers_test.go | 53 ++++++--- internal/mail/message.go | 20 +++- internal/mail/message_test.go | 35 ++++++ internal/mail/rfc2822/headers.go | 18 ++- internal/mail/rfc2822/headers_test.go | 108 +++++++++++++++++- internal/mail/rfc2822/message.go | 3 +- internal/mail/rfc2822/message_test.go | 61 +++++++--- .../mail/rfc2822/testdata/html.golden.eml | 20 ++++ internal/mailbox/message_test.go | 27 +++-- 12 files changed, 317 insertions(+), 69 deletions(-) create mode 100644 .gitattributes create mode 100644 internal/mail/rfc2822/testdata/html.golden.eml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..aa3929870 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*eml text eol=crlf diff --git a/cmd/mailchain/internal/http/handlers/messages.go b/cmd/mailchain/internal/http/handlers/messages.go index 176f53001..21281b828 100644 --- a/cmd/mailchain/internal/http/handlers/messages.go +++ b/cmd/mailchain/internal/http/handlers/messages.go @@ -87,10 +87,11 @@ func GetMessages(inbox stores.State, receivers map[string]mailbox.Receiver, ks k messages = append(messages, getMessage{ Body: string(message.Body), Headers: &getHeaders{ - To: message.Headers.To.String(), - From: message.Headers.From.String(), - Date: message.Headers.Date, - MessageID: message.ID.HexString(), + To: message.Headers.To.String(), + From: message.Headers.From.String(), + Date: message.Headers.Date, + MessageID: message.ID.HexString(), + ContentType: message.Headers.ContentType, }, Read: readStatus, Subject: message.Headers.Subject, @@ -223,4 +224,9 @@ type getHeaders struct { // example: 47eca011e32b52c71005ad8a8f75e1b44c92c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471@mailchain // readOnly: true MessageID string `json:"message-id"` + // The content type and the encoding of the message body + // readOnly: true + // example: text/plain; charset=\"UTF-8\", + // text/html; charset=\"UTF-8\" + ContentType string `json:"content-type"` } diff --git a/internal/mail/headers.go b/internal/mail/headers.go index 8f3d9eab6..5b0327678 100644 --- a/internal/mail/headers.go +++ b/internal/mail/headers.go @@ -18,22 +18,26 @@ import ( "time" ) +const DefaultContentType = "text/plain; charset=\"UTF-8\"" + // NewHeaders create the headers for sending a new message -func NewHeaders(date time.Time, from, to Address, replyTo *Address, subject string) *Headers { +func NewHeaders(date time.Time, from, to Address, replyTo *Address, subject string, contentType string) *Headers { return &Headers{ - From: from, - To: to, - ReplyTo: replyTo, - Subject: subject, - Date: date, + From: from, + To: to, + ReplyTo: replyTo, + Subject: subject, + Date: date, + ContentType: contentType, } } // Headers for the message type Headers struct { - From Address - To Address - Date time.Time - Subject string - ReplyTo *Address + From Address + To Address + Date time.Time + Subject string + ReplyTo *Address + ContentType string } diff --git a/internal/mail/headers_test.go b/internal/mail/headers_test.go index ba4eb63a9..3c7a5ad94 100644 --- a/internal/mail/headers_test.go +++ b/internal/mail/headers_test.go @@ -24,11 +24,12 @@ import ( func TestNewHeaders(t *testing.T) { assert := assert.New(t) type args struct { - date time.Time - from Address - to Address - replyTo *Address - subject string + date time.Time + from Address + to Address + replyTo *Address + subject string + contentType string } tests := []struct { name string @@ -43,12 +44,32 @@ func TestNewHeaders(t *testing.T) { Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, nil, "Hello World", + "text/plain; charset=\"UTF-8\"", }, &Headers{ - Date: time.Date(2001, 01, 02, 03, 04, 5, 6, time.UTC), - From: Address{ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, - To: Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, - Subject: "Hello World", + Date: time.Date(2001, 01, 02, 03, 04, 5, 6, time.UTC), + From: Address{ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + To: Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, + Subject: "Hello World", + ContentType: "text/plain; charset=\"UTF-8\"", + }, + }, + { + "html", + args{ + time.Date(2001, 01, 02, 03, 04, 5, 6, time.UTC), + Address{ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, + nil, + "Hello World", + "text/html; charset=\"UTF-8\"", + }, + &Headers{ + Date: time.Date(2001, 01, 02, 03, 04, 5, 6, time.UTC), + From: Address{ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + To: Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, + Subject: "Hello World", + ContentType: "text/html; charset=\"UTF-8\"", }, }, { @@ -59,19 +80,21 @@ func TestNewHeaders(t *testing.T) { Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, &Address{ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, "Hello World", + "text/plain; charset=\"UTF-8\"", }, &Headers{ - Date: time.Date(2001, 01, 02, 03, 04, 5, 6, time.UTC), - From: Address{ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, - To: Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, - ReplyTo: &Address{ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, - Subject: "Hello World", + Date: time.Date(2001, 01, 02, 03, 04, 5, 6, time.UTC), + From: Address{ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + To: Address{ChainAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb", DisplayName: "", FullAddress: "0x92d8f10248c6a3953cc3692a894655ad05d61efb@ropsten.ethereum"}, + ReplyTo: &Address{ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + Subject: "Hello World", + ContentType: "text/plain; charset=\"UTF-8\"", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := NewHeaders(tt.args.date, tt.args.from, tt.args.to, tt.args.replyTo, tt.args.subject) + got := NewHeaders(tt.args.date, tt.args.from, tt.args.to, tt.args.replyTo, tt.args.subject, tt.args.contentType) if !assert.Equal(got, tt.want) { t.Errorf("NewHeaders() = %v, want %v", got, tt.want) } diff --git a/internal/mail/message.go b/internal/mail/message.go index 9c6e66d7c..a4f034729 100644 --- a/internal/mail/message.go +++ b/internal/mail/message.go @@ -15,6 +15,9 @@ package mail import ( + "fmt" + "net/http" + "strings" "time" "github.com/pkg/errors" @@ -26,11 +29,26 @@ func NewMessage(date time.Time, from, to Address, replyTo *Address, subject stri return &Message{ ID: id, - Headers: NewHeaders(date, from, to, replyTo, subject), + Headers: NewHeaders(date, from, to, replyTo, subject, detectContentType(body)), Body: body, }, errors.WithMessage(err, "could not create ID") } +func detectContentType(body []byte) string { + contentType := http.DetectContentType(body) + result := strings.Split(contentType, ";") + + if len(result) == 1 { + return result[0] + } else if len(result) == 2 { + encodingParts := strings.Split(result[1], "=") + encoding := fmt.Sprintf("%s=\"%s\"", encodingParts[0], strings.ToUpper(encodingParts[1])) + return fmt.Sprintf("%s;%s", result[0], encoding) + } + + return DefaultContentType +} + // Message Mailchain message. type Message struct { Headers *Headers diff --git a/internal/mail/message_test.go b/internal/mail/message_test.go index 0808f88ac..8919a3a4f 100644 --- a/internal/mail/message_test.go +++ b/internal/mail/message_test.go @@ -68,3 +68,38 @@ func TestNewMessage(t *testing.T) { }) } } + +func Test_detectContentType(t *testing.T) { + type args struct { + body []byte + } + tests := []struct { + name string + args args + want string + }{ + { + "content-type-and-encoding", + args{ + []byte("this is plain text message"), + }, + "text/plain; charset=\"UTF-8\"", + }, + { + "content-type-and-encoding-html", + args{ + []byte("

this is HTML text message

"), + }, + "text/html; charset=\"UTF-8\"", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := detectContentType(tt.args.body) + if got != tt.want { + t.Errorf("detectContentType() got = %v, want %v", got, tt.want) + return + } + }) + } +} diff --git a/internal/mail/rfc2822/headers.go b/internal/mail/rfc2822/headers.go index 6dc99b5ec..b06571183 100644 --- a/internal/mail/rfc2822/headers.go +++ b/internal/mail/rfc2822/headers.go @@ -40,10 +40,11 @@ func parseHeaders(h nm.Header) (*mail.Headers, error) { return nil, errors.WithMessage(err, "failed to parse `from`") } return &mail.Headers{ - Date: *date, - Subject: subject, - To: *to, - From: *from, + Date: *date, + Subject: subject, + To: *to, + From: *from, + ContentType: parseContentType(h), }, nil } @@ -96,3 +97,12 @@ func parseSubject(h nm.Header) (string, error) { return sources[0], nil } + +func parseContentType(h nm.Header) string { + sources, ok := h["Content-Type"] + if !ok || len(sources) == 0 || len(sources[0]) == 0 { + return mail.DefaultContentType + } + + return sources[0] +} diff --git a/internal/mail/rfc2822/headers_test.go b/internal/mail/rfc2822/headers_test.go index 6b13067a8..a46cdae96 100644 --- a/internal/mail/rfc2822/headers_test.go +++ b/internal/mail/rfc2822/headers_test.go @@ -251,6 +251,61 @@ func Test_parseTo(t *testing.T) { } } +func Test_parseContentType(t *testing.T) { + assert := assert.New(t) + type args struct { + h nm.Header + } + tests := []struct { + name string + args args + want string + }{ + { + "success-plain-text", + args{ + nm.Header{ + "Content-Type": []string{"text/plain; charset=\"UTF-8\""}, + }, + }, + "text/plain; charset=\"UTF-8\"", + }, + { + "success-empty-header-to-default", + args{ + nm.Header{}, + }, + "text/plain; charset=\"UTF-8\"", + }, + { + "success-empty-header-value-to-default", + args{ + nm.Header{ + "Content-Type": []string{""}, + }, + }, + "text/plain; charset=\"UTF-8\"", + }, + { + "success-html-text", + args{ + nm.Header{ + "Content-Type": []string{"text/html; charset=\"UTF-8\""}, + }, + }, + "text/html; charset=\"UTF-8\"", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := parseContentType(tt.args.h) + if !assert.Equal(tt.want, got) { + t.Errorf("parseTo() = %v, want %v", got, tt.want) + } + }) + } +} + func Test_parseHeaders(t *testing.T) { assert := assert.New(t) type args struct { @@ -263,7 +318,47 @@ func Test_parseHeaders(t *testing.T) { wantErr bool }{ { - "success", + "success-plain-text", + args{ + nm.Header{ + "Subject": []string{"test subject"}, + "To": []string{"<5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum>"}, + "From": []string{"<4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum>"}, + "Date": []string{"Tue, 12 Mar 2019 20:23:13 UTC"}, + "Content-Type": []string{"text/plain; charset=\"UTF-8\""}, + }, + }, + &mail.Headers{ + From: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + To: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + Date: time.Date(2019, 03, 12, 20, 23, 13, 0, time.UTC), + Subject: "test subject", + ReplyTo: nil, + ContentType: "text/plain; charset=\"UTF-8\""}, + false, + }, + { + "success-plain-html", + args{ + nm.Header{ + "Subject": []string{"test subject"}, + "To": []string{"<5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum>"}, + "From": []string{"<4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum>"}, + "Date": []string{"Tue, 12 Mar 2019 20:23:13 UTC"}, + "Content-Type": []string{"text/html; charset=\"UTF-8\""}, + }, + }, + &mail.Headers{ + From: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + To: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + Date: time.Date(2019, 03, 12, 20, 23, 13, 0, time.UTC), + Subject: "test subject", + ReplyTo: nil, + ContentType: "text/html; charset=\"UTF-8\""}, + false, + }, + { + "success-defaultContentType", args{ nm.Header{ "Subject": []string{"test subject"}, @@ -273,11 +368,12 @@ func Test_parseHeaders(t *testing.T) { }, }, &mail.Headers{ - From: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, - To: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, - Date: time.Date(2019, 03, 12, 20, 23, 13, 0, time.UTC), - Subject: "test subject", - ReplyTo: nil}, + From: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + To: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + Date: time.Date(2019, 03, 12, 20, 23, 13, 0, time.UTC), + Subject: "test subject", + ReplyTo: nil, + ContentType: "text/plain; charset=\"UTF-8\""}, false, }, { diff --git a/internal/mail/rfc2822/message.go b/internal/mail/rfc2822/message.go index e2b238bd5..5ffd913ab 100644 --- a/internal/mail/rfc2822/message.go +++ b/internal/mail/rfc2822/message.go @@ -31,7 +31,6 @@ func messageIDHeaderValue(id mail.ID) string { } func EncodeNewMessage(message *mail.Message) ([]byte, error) { - // headers["Content-Type"] = "text/html; charset=\"UTF-8\"" // TODO: implement base64 encoding too // headers["Content-Transfer-Encoding"] = "base64" @@ -49,7 +48,7 @@ func EncodeNewMessage(message *mail.Message) ([]byte, error) { if message.Headers.ReplyTo != nil { headers += fmt.Sprintf("Reply-To: %s\r\n", message.Headers.ReplyTo.String()) } - headers += "Content-Type: text/plain; charset=\"UTF-8\"\r\n" + headers += fmt.Sprintf("Content-Type: %s\r\n", message.Headers.ContentType) headers += "Content-Transfer-Encoding: quoted-printable\r\n" // // Thread-Topic TODO: var ac bytes.Buffer diff --git a/internal/mail/rfc2822/message_test.go b/internal/mail/rfc2822/message_test.go index 60ab4c3b7..c21bbb84e 100644 --- a/internal/mail/rfc2822/message_test.go +++ b/internal/mail/rfc2822/message_test.go @@ -42,24 +42,40 @@ func TestEncodeNewMessage(t *testing.T) { {"simple", args{&mail.Message{ Headers: &mail.Headers{ - Date: time.Date(2019, 3, 12, 20, 23, 13, 45, time.UTC), - From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: ""}, - To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: ""}, - Subject: "Hello world", + Date: time.Date(2019, 3, 12, 20, 23, 13, 45, time.UTC), + From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: ""}, + To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: ""}, + Subject: "Hello world", + ContentType: "text/plain; charset=\"UTF-8\"", }, ID: mail.ID(testutil.MustHexDecodeString("47eca011e32b52c71005ad8a8f75e1b44c92c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471")), Body: []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus eget euismod nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer bibendum venenatis sem sed auctor. Ut aliquam eu diam nec fermentum. Sed turpis nulla, viverra ac efficitur ac, fermentum vel sapien. Curabitur vehicula risus id odio congue tempor. Mauris tincidunt feugiat risus, eget auctor magna blandit sit amet. Curabitur consectetur, dolor eu imperdiet varius, dui neque mattis neque, vel fringilla magna tortor ut risus. Cras cursus sem et nisl interdum molestie. Aliquam auctor sodales blandit."), }}, false, }, + {"html", + args{&mail.Message{ + Headers: &mail.Headers{ + Date: time.Date(2019, 3, 12, 20, 23, 13, 45, time.UTC), + From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: ""}, + To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: ""}, + Subject: "Hello world", + ContentType: "text/html; charset=\"UTF-8\"", + }, + ID: mail.ID(testutil.MustHexDecodeString("47eca011e32b52c71005ad8a8f75e1b44c92c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471")), + Body: []byte("

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus eget euismod nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer bibendum venenatis sem sed auctor. Ut aliquam eu diam nec fermentum. Sed turpis nulla, viverra ac efficitur ac, fermentum vel sapien. Curabitur vehicula risus id odio congue tempor. Mauris tincidunt feugiat risus, eget auctor magna blandit sit amet. Curabitur consectetur, dolor eu imperdiet varius, dui neque mattis neque, vel fringilla magna tortor ut risus. Cras cursus sem et nisl interdum molestie. Aliquam auctor sodales blandit.

"), + }}, + false, + }, {"reply-to", args{&mail.Message{ Headers: &mail.Headers{ - Date: time.Date(2019, 3, 12, 20, 23, 13, 45, time.UTC), - From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: ""}, - To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: ""}, - ReplyTo: &mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@mainnet.ethereum", ChainAddress: ""}, - Subject: "Hello world", + Date: time.Date(2019, 3, 12, 20, 23, 13, 45, time.UTC), + From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: ""}, + To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: ""}, + ReplyTo: &mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@mainnet.ethereum", ChainAddress: ""}, + Subject: "Hello world", + ContentType: "text/plain; charset=\"UTF-8\"", }, ID: mail.ID(testutil.MustHexDecodeString("47eca011e32b52c71005ad8a8f75e1b44c92c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471")), Body: []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus eget euismod nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer bibendum venenatis sem sed auctor. Ut aliquam eu diam nec fermentum. Sed turpis nulla, viverra ac efficitur ac, fermentum vel sapien. Curabitur vehicula risus id odio congue tempor. Mauris tincidunt feugiat risus, eget auctor magna blandit sit amet. Curabitur consectetur, dolor eu imperdiet varius, dui neque mattis neque, vel fringilla magna tortor ut risus. Cras cursus sem et nisl interdum molestie. Aliquam auctor sodales blandit."), @@ -108,16 +124,33 @@ func TestDecodeNewMessage(t *testing.T) { Body: []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus eget euismod nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer bibendum venenatis sem sed auctor. Ut aliquam eu diam nec fermentum. Sed turpis nulla, viverra ac efficitur ac, fermentum vel sapien. Curabitur vehicula risus id odio congue tempor. Mauris tincidunt feugiat risus, eget auctor magna blandit sit amet. Curabitur consectetur, dolor eu imperdiet varius, dui neque mattis neque, vel fringilla magna tortor ut risus. Cras cursus sem et nisl interdum molestie. Aliquam auctor sodales blandit.\r\n"), ID: mail.ID(testutil.MustHexDecodeString("47eca011e32b52c71005ad8a8f75e1b44c92c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471")), Headers: &mail.Headers{ - Date: time.Date(2019, 3, 12, 20, 23, 13, 0, time.UTC), - From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, - To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, - ReplyTo: nil, - Subject: "Hello world", + Date: time.Date(2019, 3, 12, 20, 23, 13, 0, time.UTC), + From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + ReplyTo: nil, + Subject: "Hello world", + ContentType: "text/plain; charset=\"UTF-8\"", }, // TODO: publicKey? }, false, }, + { + "html", + &mail.Message{ + Body: []byte("

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus eget euismod nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer bibendum venenatis sem sed auctor. Ut aliquam eu diam nec fermentum. Sed turpis nulla, viverra ac efficitur ac, fermentum vel sapien. Curabitur vehicula risus id odio congue tempor. Mauris tincidunt feugiat risus, eget auctor magna blandit sit amet. Curabitur consectetur, dolor eu imperdiet varius, dui neque mattis neque, vel fringilla magna tortor ut risus. Cras cursus sem et nisl interdum molestie. Aliquam auctor sodales blandit.

\r\n"), + ID: mail.ID(testutil.MustHexDecodeString("47eca011e32b52c71005ad8a8f75e1b44c92c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471")), + Headers: &mail.Headers{ + Date: time.Date(2019, 3, 12, 20, 23, 13, 0, time.UTC), + From: mail.Address{DisplayName: "", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + To: mail.Address{DisplayName: "", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + ReplyTo: nil, + Subject: "Hello world", + ContentType: "text/html; charset=\"UTF-8\"", + }, + }, + false, + }, { "err-header", nil, diff --git a/internal/mail/rfc2822/testdata/html.golden.eml b/internal/mail/rfc2822/testdata/html.golden.eml new file mode 100644 index 000000000..7c5503fcc --- /dev/null +++ b/internal/mail/rfc2822/testdata/html.golden.eml @@ -0,0 +1,20 @@ +Date: Tue, 12 Mar 2019 20:23:13 UTC +Message-ID: 47eca011e32b52c71005ad8a8f75e1b44c92c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471@mailchain +Subject: Hello world +From: <5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum> +To: <4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum> +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

C= +urabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed ves= +tibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut s= +em rutrum aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et = +ultrices posuere cubilia Curae; Phasellus eget euismod nibh. Lorem ipsum do= +lor sit amet, consectetur adipiscing elit. Integer bibendum venenatis sem s= +ed auctor. Ut aliquam eu diam nec fermentum. Sed turpis nulla, viverra ac e= +fficitur ac, fermentum vel sapien. Curabitur vehicula risus id odio congue = +tempor. Mauris tincidunt feugiat risus, eget auctor magna blandit sit amet.= + Curabitur consectetur, dolor eu imperdiet varius, dui neque mattis neque, = +vel fringilla magna tortor ut risus. Cras cursus sem et nisl interdum moles= +tie. Aliquam auctor sodales blandit.

diff --git a/internal/mailbox/message_test.go b/internal/mailbox/message_test.go index a5cdda43b..9abae0847 100644 --- a/internal/mailbox/message_test.go +++ b/internal/mailbox/message_test.go @@ -43,10 +43,11 @@ func TestSendMessage(t *testing.T) { defer mockCtrl.Finish() msg := &mail.Message{ Headers: &mail.Headers{ - From: mail.Address{DisplayName: "From Display Name", FullAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, - To: mail.Address{DisplayName: "To Display Name", FullAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, - Date: time.Date(2018, 01, 02, 03, 04, 05, 06, time.UTC), - Subject: "test", + From: mail.Address{DisplayName: "From Display Name", FullAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + To: mail.Address{DisplayName: "To Display Name", FullAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + Date: time.Date(2018, 01, 02, 03, 04, 05, 06, time.UTC), + Subject: "test", + ContentType: "text/plain; charset=\"UTF-8\"", }, ID: []byte("2c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471002c47eca011e32b52c71005ad8a8f75e1b44c9@mailchain"), Body: []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet."), @@ -110,10 +111,11 @@ func TestSendMessage(t *testing.T) { ethereum.Mainnet, &mail.Message{ Headers: &mail.Headers{ - From: mail.Address{DisplayName: "From Display Name", FullAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, - To: mail.Address{DisplayName: "To Display Name", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, - Date: time.Date(2018, 01, 02, 03, 04, 05, 06, time.UTC), - Subject: "test", + From: mail.Address{DisplayName: "From Display Name", FullAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "0x4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + To: mail.Address{DisplayName: "To Display Name", FullAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + Date: time.Date(2018, 01, 02, 03, 04, 05, 06, time.UTC), + Subject: "test", + ContentType: "text/plain; charset=\"UTF-8\"", }, ID: []byte("2c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471002c47eca011e32b52c71005ad8a8f75e1b44c9@mailchain"), Body: []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet."), @@ -151,10 +153,11 @@ func TestSendMessage(t *testing.T) { ethereum.Mainnet, &mail.Message{ Headers: &mail.Headers{ - From: mail.Address{DisplayName: "From Display Name", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, - To: mail.Address{DisplayName: "To Display Name", FullAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, - Date: time.Date(2018, 01, 02, 03, 04, 05, 06, time.UTC), - Subject: "test", + From: mail.Address{DisplayName: "From Display Name", FullAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2@ropsten.ethereum", ChainAddress: "4cb0a77b76667dac586c40cc9523ace73b5d772bd503c63ed0ca596eae1658b2"}, + To: mail.Address{DisplayName: "To Display Name", FullAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761@ropsten.ethereum", ChainAddress: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761"}, + Date: time.Date(2018, 01, 02, 03, 04, 05, 06, time.UTC), + Subject: "test", + ContentType: "text/plain; charset=\"UTF-8\"", }, ID: []byte("2c99fd12e43bccfe571e3c2d13d2e9a826a550f5ff63b247af471002c47eca011e32b52c71005ad8a8f75e1b44c9@mailchain"), Body: []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur maximus metus ante, sit amet ullamcorper dui hendrerit ac. Sed vestibulum dui lectus, quis eleifend urna mollis eu. Integer dictum metus ut sem rutrum aliquet."),