fix(GODT-3176): assume inline if content id is present.

This commit is contained in:
Romain Le Jeune 2024-01-18 16:45:08 +00:00 committed by Jakub Cuth
parent 76d257af21
commit 773a230d14
3 changed files with 77 additions and 4 deletions

View File

@ -119,7 +119,7 @@ func parse(p *parser.Parser, allowInvalidAddressLists bool) (Message, error) {
}
if err := patchInlineImages(p); err != nil {
return Message{}, err
return Message{}, errors.Wrap(err, "patching inline images failed")
}
m, err := parseMessageHeader(p.Root().Header, allowInvalidAddressLists)
@ -671,11 +671,10 @@ func patchInlineImages(p *parser.Parser) error {
if rfc822.MIMEType(contentType) == rfc822.TextPlain {
result[i] = &inlinePatchBodyOnly{part: curPart, contentTypeMap: contentTypeMap}
} else if strings.HasPrefix(contentType, "image/") {
disposition, _, err := curPart.ContentDisposition()
disposition, err := getImageContentDisposition(curPart)
if err != nil {
return fmt.Errorf("failted to get content disposition for child %v:%w", i, err)
return fmt.Errorf("failed to get content disposition for child %v:%w", i, err)
}
if disposition == "inline" && !curPart.HasContentID() {
if rfc822.MIMEType(prevContentType) == rfc822.TextPlain {
result[i-1] = &inlinePatchBodyWithInlineImage{
@ -707,6 +706,23 @@ func patchInlineImages(p *parser.Parser) error {
return nil
}
func getImageContentDisposition(curPart *parser.Part) (string, error) {
disposition, _, err := curPart.ContentDisposition()
if err == nil {
return disposition, nil
}
if curPart.Header.Get("Content-Disposition") != "" {
return "", err
}
if curPart.HasContentID() {
return "inline", nil
}
return "attachment", nil
}
type inlinePatchJob interface {
Patch()
}

View File

@ -495,6 +495,7 @@ func TestParseTextHTMLWithImageInline(t *testing.T) {
assert.Equal(t, `"Receiver" <receiver@pm.me>`, m.ToList[0].String())
require.Len(t, m.Attachments, 1)
require.Equal(t, m.Attachments[0].Disposition, proton.InlineDisposition)
assert.Equal(t, fmt.Sprintf(`<html><body>This is body of <b>HTML mail</b> with attachment</body></html><html><body><img src="cid:%v"/></body></html>`, m.Attachments[0].ContentID), string(m.RichBody))
assert.Equal(t, "This is body of *HTML mail* with attachment", string(m.PlainBody))
@ -506,6 +507,27 @@ func TestParseTextHTMLWithImageInline(t *testing.T) {
assert.Equal(t, 8, img.Height)
}
func TestParseTextHTMLWithImageInlineNoDisposition(t *testing.T) {
f := getFileReader("text_html_image_inline_no_disposition.eml")
m, err := Parse(f)
require.NoError(t, err)
assert.Equal(t, `"Sender" <sender@pm.me>`, m.Sender.String())
assert.Equal(t, `"Receiver" <receiver@pm.me>`, m.ToList[0].String())
require.Len(t, m.Attachments, 1)
assert.Equal(t, `<html><body>This is body of <b>HTML mail</b> with attachment</body></html>`, string(m.RichBody))
assert.Equal(t, "This is body of *HTML mail* with attachment", string(m.PlainBody))
// The inline image is an 8x8 mic-dropping gopher.
img, err := png.DecodeConfig(bytes.NewReader(m.Attachments[0].Data))
require.NoError(t, err)
assert.Equal(t, 8, img.Width)
assert.Equal(t, 8, img.Height)
}
func TestParseWithAttachedPublicKey(t *testing.T) {
f := getFileReader("text_plain.eml")

View File

@ -0,0 +1,35 @@
From: Sender <sender@pm.me>
To: Receiver <receiver@pm.me>
Content-Type: multipart/mixed; boundary=longrandomstring
--longrandomstring
Content-Type: text/html
<html><body>This is body of <b>HTML mail</b> with attachment</body></html>
--longrandomstring
Content-Type: image/png
Content-ID: <image001.png@01DA2AC1.EF68ABC0>
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAABGdBTUEAALGPC/xhBQAAACBjSFJ
NAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFAR
IAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAA
ABaAAAAAAAAASwAAAABAAABLAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACKADAAQAAAAB
AAAACAAAAAAAXWZ6AAAACXBIWXMAAC4jAAAuIwF4pT92AAACZmlUWHRYTUw6Y29tLmFkb2JlLnh
tcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIE
NvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5O
TkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91
dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4
wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC
8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgI
CAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAg
ICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl
4ZWxYRGltZW5zaW9uPjE2PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UG
l4ZWxZRGltZW5zaW9uPjE2PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY
3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CgZBD4sAAAEISURBVBgZY2CAAO5F
x07Zz96xZ0Pn4lXqIKGGhgYmsFTHvAWdW6/dvnb89Yf/B5+9/r/y9IXzbVPahCH6/jMysfAJygo
JC2r++/T619Mb139J8HIb8Gs5hYMUzJ+/gJ1Jmo9H6c+L5wz3bt5iEeLmYOHn42fQ4vyacqGNQS
0xMfEHc7Cvl6CYho4rh5jUPyYefqafLKyMbH9+/d28/dFfdWtfDaZvTy7Zvv72nYGZkeEvw98/f
5j//2P4yCvxq/nU7zVs//8yM2gzMMitOnnu5cUff/8ff/v5/5Xf///vuHBhJcSRDAws9aEMr38c
W7XjNgvzexZ2rn9vbjx/IXl/M9iLM2fOZAUAKCZv7dU+UgAAAAAASUVORK5CYII=
--longrandomstring--