diff --git a/pkg/message/build_rfc822.go b/pkg/message/build_rfc822.go index 67010640..026381f2 100644 --- a/pkg/message/build_rfc822.go +++ b/pkg/message/build_rfc822.go @@ -480,7 +480,7 @@ func getAttachmentPartHeader(att *pmapi.Attachment) message.Header { hdr.SetContentDisposition(att.Disposition, map[string]string{"filename": mime.QEncoding.Encode("utf-8", att.Name)}) // Use base64 for all attachments except embedded RFC822 messages. - if att.MIMEType != "message/rfc822" { + if att.MIMEType != rfc822Message { hdr.Set("Content-Transfer-Encoding", "base64") } else { hdr.Del("Content-Transfer-Encoding") diff --git a/pkg/message/parser.go b/pkg/message/parser.go index 052d7d12..553e900b 100644 --- a/pkg/message/parser.go +++ b/pkg/message/parser.go @@ -528,6 +528,9 @@ func parseAttachment(h message.Header) (*pmapi.Attachment, error) { if att.Name == "" { att.Name = mimeTypeParams["name"] } + if att.Name == "" && mimeType == rfc822Message { + att.Name = "message.eml" + } if att.Name == "" { att.Name = "attachment.bin" } diff --git a/pkg/message/parser_test.go b/pkg/message/parser_test.go index a5794317..46f4bb63 100644 --- a/pkg/message/parser_test.go +++ b/pkg/message/parser_test.go @@ -222,6 +222,22 @@ func TestParseTextPlainWithOctetAttachmentGoodFilename(t *testing.T) { assert.Equal(t, "😁😂.txt", m.Attachments[0].Name) } +func TestParseTextPlainWithRFC822Attachment(t *testing.T) { + f := getFileReader("text_plain_rfc822_attachment.eml") + + m, _, plainBody, attReaders, err := Parse(f) + require.NoError(t, err) + + assert.Equal(t, `"Sender" `, m.Sender.String()) + assert.Equal(t, `"Receiver" `, m.ToList[0].String()) + + assert.Equal(t, "body", m.Body) + assert.Equal(t, "body", plainBody) + + assert.Len(t, attReaders, 1) + assert.Equal(t, "message.eml", m.Attachments[0].Name) +} + func TestParseTextPlainWithOctetAttachmentBadFilename(t *testing.T) { f := getFileReader("text_plain_octet_attachment_bad_2231_filename.eml") diff --git a/pkg/message/section.go b/pkg/message/section.go index 23b592c5..9154fa4c 100644 --- a/pkg/message/section.go +++ b/pkg/message/section.go @@ -103,7 +103,7 @@ func (bs *BodyStructure) parseAllChildSections(r io.Reader, currentPath []int, s mediaType, params, _ := pmmime.ParseMediaType(info.Header.Get("Content-Type")) // If multipart, call getAllParts, else read to count lines. - if (strings.HasPrefix(mediaType, "multipart/") || mediaType == "message/rfc822") && params["boundary"] != "" { + if (strings.HasPrefix(mediaType, "multipart/") || mediaType == rfc822Message) && params["boundary"] != "" { nextPath := getChildPath(currentPath) var br *boundaryReader diff --git a/pkg/message/testdata/text_plain_rfc822_attachment.eml b/pkg/message/testdata/text_plain_rfc822_attachment.eml new file mode 100644 index 00000000..67c8b7e2 --- /dev/null +++ b/pkg/message/testdata/text_plain_rfc822_attachment.eml @@ -0,0 +1,16 @@ +From: Sender +To: Receiver +Content-Type: multipart/mixed; boundary=longrandomstring + +--longrandomstring + +body +--longrandomstring +Content-Type: message/rfc822 +Content-Disposition: attachment + +From: Sender +To: Receiver + +inner body +--longrandomstring-- diff --git a/test/fakeapi/attachments.go b/test/fakeapi/attachments.go index 349f82de..d2f654a2 100644 --- a/test/fakeapi/attachments.go +++ b/test/fakeapi/attachments.go @@ -75,5 +75,10 @@ func (api *FakePMAPI) CreateAttachment(_ context.Context, attachment *pmapi.Atta return nil, err } attachment.KeyPackets = base64.StdEncoding.EncodeToString(bytes) + msg := api.getMessage(attachment.MessageID) + if msg == nil { + return nil, fmt.Errorf("no such message ID %q", attachment.MessageID) + } + msg.Attachments = append(msg.Attachments, attachment) return attachment, nil } diff --git a/test/fakeapi/messages.go b/test/fakeapi/messages.go index f3e9b5c8..eba3f524 100644 --- a/test/fakeapi/messages.go +++ b/test/fakeapi/messages.go @@ -34,10 +34,8 @@ func (api *FakePMAPI) GetMessage(_ context.Context, apiID string) (*pmapi.Messag if err := api.checkAndRecordCall(GET, "/mail/v4/messages/"+apiID, nil); err != nil { return nil, err } - for _, message := range api.messages { - if message.ID == apiID { - return message, nil - } + if msg := api.getMessage(apiID); msg != nil { + return msg, nil } return nil, fmt.Errorf("message %s not found", apiID) } @@ -175,8 +173,8 @@ func (api *FakePMAPI) SendMessage(ctx context.Context, messageID string, sendMes if err := api.checkAndRecordCall(POST, "/mail/v4/messages/"+messageID, sendMessageRequest); err != nil { return nil, nil, err } - message, err := api.GetMessage(ctx, messageID) - if err != nil { + message := api.getMessage(messageID) + if message == nil { return nil, nil, errors.Wrap(err, "draft does not exist") } message.Time = time.Now().Unix() @@ -276,6 +274,15 @@ func (api *FakePMAPI) findMessage(newMsg *pmapi.Message) *pmapi.Message { return nil } +func (api *FakePMAPI) getMessage(msgID string) *pmapi.Message { + for _, msg := range api.messages { + if msg.ID == msgID { + return msg + } + } + return nil +} + func (api *FakePMAPI) addMessage(message *pmapi.Message) { if api.findMessage(message) != nil { return diff --git a/test/features/bridge/smtp/send/html_att.feature b/test/features/bridge/smtp/send/html_att.feature index 4beb0119..be140b0e 100644 --- a/test/features/bridge/smtp/send/html_att.feature +++ b/test/features/bridge/smtp/send/html_att.feature @@ -120,3 +120,91 @@ Feature: SMTP sending of HTML messages with attachments } } """ + + Scenario: Alternative plain and HTML message with rfc822 attachment + When SMTP client sends message + """ + From: Bridge Test <[userAddress]> + To: External Bridge + Subject: Alternative plain and HTML with rfc822 attachment + Content-Type: multipart/mixed; boundary=main-parts + + This is a multipart message in MIME format + + --main-parts + Content-Type: multipart/alternative; boundary=alternatives + + --alternatives + Content-Type: text/plain + + There is an attachment + + + --alternatives + Content-Type: text/html + + There is an attachment + + + --alternatives-- + + --main-parts + Content-Type: message/rfc822 + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment + + Received: from mx1.opensuse.org (mx1.infra.opensuse.org [192.168.47.95]) by + mailman3.infra.opensuse.org (Postfix) with ESMTP id 38BE2AC3 for + ; Sun, 11 Jul 2021 19:50:34 +0000 (UTC) + From: "Bob " + Sender: "Bob" + To: "opensuse-factory" + Cc: "Bob" + References: + Subject: VirtualBox problems with kernel 5.13 + Date: Sun, 11 Jul 2021 21:50:25 +0200 + Message-ID: <71672e5f-24a2-c79f-03cc-4c923eb1790b@lwfinger.net> + MIME-Version: 1.0 + Content-Type: text/plain; charset="utf-8" + Content-Transfer-Encoding: quoted-printable + X-Mailer: Microsoft Outlook 16.0 + List-Unsubscribe: + Content-Language: en-us + List-Help: + List-Subscribe: + Thread-Index: AQFWvbNSAqFOch49YPlLU4eJWPObaQK2iKDq + + I am writing this message as openSUSE's maintainer of VirtualBox. + + Nearly every update of the Linux kernel to a new 5.X version breaks = + VirtualBox. + + Bob + + --main-parts-- + + """ + Then SMTP response is "OK" + And mailbox "Sent" for "user" has messages + | from | to | subject | + | [userAddress] | pm.bridge.qa@gmail.com | Alternative plain and HTML with rfc822 attachment | + And message is sent with API call + """ + { + "Message": { + "Subject": "Alternative plain and HTML with rfc822 attachment", + "Sender": { + "Name": "Bridge Test" + }, + "ToList": [ + { + "Address": "pm.bridge.qa@gmail.com", + "Name": "External Bridge" + } + ], + "CCList": [], + "BCCList": [], + "MIMEType": "text/html" + } + } + """