From 5f19744c4d28a5c6cdbf398c34348f8b7d464817 Mon Sep 17 00:00:00 2001 From: Gary Devenay Date: Tue, 27 Oct 2020 10:16:58 +0000 Subject: [PATCH 1/3] Added ParsedAttachment struct for email attachment header mapping --- helpers/inbound/inbound.go | 21 ++++++++++++++++++--- helpers/inbound/inbound_test.go | 14 +++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/helpers/inbound/inbound.go b/helpers/inbound/inbound.go index 09d748ee..3fa248c7 100644 --- a/helpers/inbound/inbound.go +++ b/helpers/inbound/inbound.go @@ -10,11 +10,16 @@ import ( "strings" ) +type ParsedAttachment struct { + Headers map[string]string + Filename string + Content []byte +} type ParsedEmail struct { Headers map[string]string Body map[string]string - Attachments map[string][]byte + Attachments []ParsedAttachment rawRequest *http.Request } @@ -22,7 +27,7 @@ func Parse(request *http.Request) *ParsedEmail { result := ParsedEmail{ Headers: make(map[string]string), Body: make(map[string]string), - Attachments: make(map[string][]byte), + Attachments: []ParsedAttachment{}, rawRequest: request, } result.parse() @@ -65,7 +70,17 @@ func (email *ParsedEmail) parseRawEmail(rawEmail string) { } } else if emailPart.FileName() != "" { - email.Attachments[emailPart.FileName()] = readBody(emailPart) + headers := make(map[string]string) + content := readBody(emailPart) + + headers["Content-Type"] = emailPart.Header.Get("Content-Type") + attachment := ParsedAttachment{ + Filename: emailPart.FileName(), + Content: content, + Headers: headers, + } + + email.Attachments = append(email.Attachments, attachment) } else { header := emailPart.Header.Get("Content-Type") email.Body[header] = string(readBody(emailPart)) diff --git a/helpers/inbound/inbound_test.go b/helpers/inbound/inbound_test.go index f05b2768..4fc46418 100644 --- a/helpers/inbound/inbound_test.go +++ b/helpers/inbound/inbound_test.go @@ -49,6 +49,14 @@ func TestParse(t *testing.T) { } } +func TestAttachments(t *testing.T) { + req := createRequest("./sample_data/raw_data_with_attachments.txt") + email := Parse(req) + contentType := "image/jpeg; name=\"TwilioSendGrid.jpg\"" + + assert.Equalf(t, contentType, email.Attachments[0].Headers["Content-Type"],"Expected From: %s, Got: %s", contentType, email.Attachments[0].Headers["Content-Type"]) +} + func ExampleParsedEmail_parseHeaders() { headers := ` Foo: foo @@ -57,7 +65,7 @@ Bar: baz email := ParsedEmail{ Headers: make(map[string]string), Body: make(map[string]string), - Attachments: make(map[string][]byte), + Attachments: []ParsedAttachment{}, rawRequest: nil, } email.parseHeaders(headers) @@ -89,7 +97,7 @@ Content-Transfer-Encoding: quoted-printable email := ParsedEmail{ Headers: make(map[string]string), Body: make(map[string]string), - Attachments: make(map[string][]byte), + Attachments: []ParsedAttachment{}, rawRequest: nil, } email.parseRawEmail(rawEmail) @@ -103,4 +111,4 @@ Content-Transfer-Encoding: quoted-printable // Subject Test Email // Content-Type multipart/mixed; boundary=TwiLIo // Hello Twilio SendGrid! -} +} \ No newline at end of file From be0a053b08a71be015e9773e45f6809a44856ea3 Mon Sep 17 00:00:00 2001 From: Gary Devenay Date: Tue, 27 Oct 2020 10:22:23 +0000 Subject: [PATCH 2/3] Updated readme with example --- helpers/inbound/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers/inbound/README.md b/helpers/inbound/README.md index 901c216c..5b07624d 100644 --- a/helpers/inbound/README.md +++ b/helpers/inbound/README.md @@ -24,9 +24,9 @@ func inboundHandler(response http.ResponseWriter, request *http.Request) { fmt.Print(parsedEmail.Headers["From"]) - for filename, contents := range parsedEmail.Attachments { + for _, file := range parsedEmail.Attachments { // Do something with an attachment - handleAttachment(filename, contents) + handleAttachment(file.Filename, file.Content) } for section, body := range parsedEmail.Body { From 6b7c3b868d6d05361ab5299d545e202c164b2fe0 Mon Sep 17 00:00:00 2001 From: Gary Devenay Date: Wed, 28 Oct 2020 10:49:06 +0000 Subject: [PATCH 3/3] Updated ParsedAttachment struct to include a ContentType property, and parsed all attachment headers in the Headers property --- helpers/inbound/inbound.go | 9 +++++---- helpers/inbound/inbound_test.go | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/helpers/inbound/inbound.go b/helpers/inbound/inbound.go index 3fa248c7..820f70f9 100644 --- a/helpers/inbound/inbound.go +++ b/helpers/inbound/inbound.go @@ -7,11 +7,13 @@ import ( "mime" "mime/multipart" "net/http" + "net/textproto" "strings" ) type ParsedAttachment struct { - Headers map[string]string + Headers textproto.MIMEHeader + ContentType string Filename string Content []byte } @@ -70,14 +72,13 @@ func (email *ParsedEmail) parseRawEmail(rawEmail string) { } } else if emailPart.FileName() != "" { - headers := make(map[string]string) content := readBody(emailPart) - headers["Content-Type"] = emailPart.Header.Get("Content-Type") attachment := ParsedAttachment{ Filename: emailPart.FileName(), + ContentType: strings.Split(emailPart.Header.Get("Content-Type"), ";")[0], Content: content, - Headers: headers, + Headers: emailPart.Header, } email.Attachments = append(email.Attachments, attachment) diff --git a/helpers/inbound/inbound_test.go b/helpers/inbound/inbound_test.go index 4fc46418..26af4ab3 100644 --- a/helpers/inbound/inbound_test.go +++ b/helpers/inbound/inbound_test.go @@ -52,9 +52,9 @@ func TestParse(t *testing.T) { func TestAttachments(t *testing.T) { req := createRequest("./sample_data/raw_data_with_attachments.txt") email := Parse(req) - contentType := "image/jpeg; name=\"TwilioSendGrid.jpg\"" + contentType := "image/jpeg" - assert.Equalf(t, contentType, email.Attachments[0].Headers["Content-Type"],"Expected From: %s, Got: %s", contentType, email.Attachments[0].Headers["Content-Type"]) + assert.Equalf(t, contentType, email.Attachments[0].ContentType,"Expected From: %s, Got: %s", contentType, email.Attachments[0].ContentType) } func ExampleParsedEmail_parseHeaders() {