fix(GODT-2965): fix multipart/mixed testdata + structure parsing steps related to this.

This commit is contained in:
Romain Le Jeune
2023-09-29 07:08:10 +00:00
parent 56c53e9188
commit 8a6f96f9f2
2 changed files with 74 additions and 45 deletions

View File

@ -322,11 +322,21 @@ Feature: IMAP import messages
Content-Type: multipart/mixed; boundary="boundary" Content-Type: multipart/mixed; boundary="boundary"
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000 Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
--boundary
This is a multi-part message in MIME format. This is a multi-part message in MIME format.
--boundary --boundary
Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit Content-Transfer-Encoding: 7bit
Hello
--boundary
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<h1> HELLO </h1>
--boundary --boundary
Content-Type: message/rfc822; name="embedded.eml" Content-Type: message/rfc822; name="embedded.eml"
@ -345,33 +355,41 @@ Feature: IMAP import messages
""" """
Then it succeeds Then it succeeds
# And IMAP client "1" eventually sees the following message in "INBOX" with this structure: And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
# """ """
# { {
# "from": "Foo <foo@example.com>", "from": "Foo <foo@example.com>",
# "date": "01 Jan 80 00:00 +0000", "date": "01 Jan 80 00:00 +0000",
# "to": "Bridge Test <bridgetest@pm.test>", "to": "Bridge Test <bridgetest@pm.test>",
# "subject": "Embedded message", "subject": "Embedded message",
# "body-contains": "Hello", "body-contains": "Hello",
# "content": { "content": {
# "content-type": "multipart/mixed", "content-type": "multipart/mixed",
# "body-contains": "This is a multi-part message in MIME format.", "sections":[
# "sections":[ {
# { "body-is": "This is a multi-part message in MIME format."
# "content-type": "text/plain", },
# "content-type-charset": "utf-8", {
# "transfer-encoding": "7bit", "content-type": "text/plain",
# "body-is": "" "content-type-charset": "utf-8",
# }, "transfer-encoding": "7bit",
# { "body-is": "Hello"
# "content-type": "message/rfc822", },
# "content-type-name": "embedded.eml", {
# "transfer-encoding": "7bit", "content-type": "text/html",
# "content-disposition": "attachment", "content-type-charset": "utf-8",
# "content-disposition-filename": "embedded.eml", "transfer-encoding": "7bit",
# "body-is": "From: Bar <bar@example.com>\n\rTo: Bridge Test <bridgetest@pm.test>\n\rSubject: (No Subject)\n\rContent-Type: text/plain; charset=utf-8\n\rContent-Transfer-Encoding: quoted-printable\n\r\n\rhello" "body-contains": "HELLO"
# } },
# ] {
# } "content-type": "message/rfc822",
# } "content-type-name": "embedded.eml",
# """ "transfer-encoding": "7bit",
"content-disposition": "attachment",
"content-disposition-filename": "embedded.eml",
"body-is": "From: Bar <bar@example.com>\nTo: Bridge Test <bridgetest@pm.test>\nSubject: (No Subject)\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: quoted-printable\n\nhello"
}
]
}
}
"""

View File

@ -207,9 +207,12 @@ func newMessageStructFromIMAP(msg *imap.Message) MessageStruct {
panic(err) panic(err)
} }
var body string var body string
if m.MIMEType == rfc822.TextPlain { switch {
case m.MIMEType == rfc822.TextPlain:
body = strings.TrimSpace(string(m.PlainBody)) body = strings.TrimSpace(string(m.PlainBody))
} else { case m.MIMEType == rfc822.MultipartMixed:
_, body, _ = strings.Cut(string(m.MIMEBody), "\r\n\r\n")
default:
body = strings.TrimSpace(string(m.RichBody)) body = strings.TrimSpace(string(m.RichBody))
} }
@ -221,7 +224,7 @@ func newMessageStructFromIMAP(msg *imap.Message) MessageStruct {
CC: formatAddressList(msg.Envelope.Cc), CC: formatAddressList(msg.Envelope.Cc),
BCC: formatAddressList(msg.Envelope.Bcc), BCC: formatAddressList(msg.Envelope.Bcc),
Content: parseMessageSection(literal, body), Content: parseMessageSection([]byte(strings.TrimSpace(string(literal))), strings.TrimSpace(body)),
} }
return message return message
} }
@ -262,20 +265,30 @@ func parseMessageSection(literal []byte, body string) MessageSection {
for id, value := range contentDisposition { for id, value := range contentDisposition {
if id == 0 { if id == 0 {
msgSect.ContentDisposition = strings.TrimSpace(string(value)) msgSect.ContentDisposition = strings.TrimSpace(string(value))
continue
} }
param := bytes.Split(value, []byte("=")) param := bytes.Split(value, []byte("="))
if strings.TrimSpace(string(param[0])) == "filename" && len(param) >= 2 { if strings.TrimSpace(string(param[0])) == "filename" && len(param) >= 2 {
filename := strings.TrimPrefix(string(value), "filename=") _, filename, _ := strings.Cut(string(value), "filename=")
filename = strings.Trim(filename, "\"")
msgSect.ContentDispositionFilename = strings.TrimSpace(filename) msgSect.ContentDispositionFilename = strings.TrimSpace(filename)
} }
} }
if msgSect.ContentTypeBoundary != "" { if msgSect.ContentTypeBoundary != "" {
sections := bytes.Split([]byte(msgSect.BodyIs), []byte("--"+msgSect.ContentTypeBoundary)) sections := bytes.Split(literal, []byte("--"+msgSect.ContentTypeBoundary))
// Remove last element that will be the -- from finale boundary // Remove last element that will be the -- from finale boundary
sections = sections[:len(sections)-1] sections = sections[:len(sections)-1]
sections = sections[1:]
for _, v := range sections { for _, v := range sections {
msgSect.Sections = append(msgSect.Sections, parseMessageSection(v, string(v))) str := strings.TrimSpace(string(v))
_, sectionBody, found := strings.Cut(str, "\r\n\r\n")
if !found {
if _, sectionBody, found = strings.Cut(str, "\n\n"); !found {
sectionBody = str
}
}
msgSect.Sections = append(msgSect.Sections, parseMessageSection([]byte(str), strings.TrimSpace(sectionBody)))
} }
} }
return msgSect return msgSect
@ -367,18 +380,20 @@ func matchContent(have MessageSection, want MessageSection) bool {
if want.TransferEncoding != "" && want.TransferEncoding != have.TransferEncoding { if want.TransferEncoding != "" && want.TransferEncoding != have.TransferEncoding {
return false return false
} }
if want.BodyContains != "" && strings.Contains(have.BodyIs, want.BodyContains) { if want.BodyContains != "" && !strings.Contains(strings.TrimSpace(have.BodyIs), strings.TrimSpace(want.BodyContains)) {
return false return false
} }
if want.BodyIs != "" && want.BodyIs != have.BodyIs { if want.BodyIs != "" && strings.TrimSpace(have.BodyIs) != strings.TrimSpace(want.BodyIs) {
return false return false
} }
for _, section := range want.Sections { if len(have.Sections) != len(want.Sections) {
if !matchContent(have, section) { return false
}
for i, section := range want.Sections {
if !matchContent(have.Sections[i], section) {
return false return false
} }
} }
return true return true
} }
@ -536,7 +551,3 @@ type Contact struct {
Sign string `bdd:"signature"` Sign string `bdd:"signature"`
Encrypt string `bdd:"encryption"` Encrypt string `bdd:"encryption"`
} }
func FullAddress(addr *imap.Address) string {
return addr.PersonalName + " <" + addr.MailboxName + "@" + addr.HostName + ">"
}