You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
420 lines
8.8 KiB
420 lines
8.8 KiB
// Copyright (c) 2020 Proton Technologies AG |
|
// |
|
// This file is part of ProtonMail Bridge. |
|
// |
|
// ProtonMail Bridge is free software: you can redistribute it and/or modify |
|
// it under the terms of the GNU General Public License as published by |
|
// the Free Software Foundation, either version 3 of the License, or |
|
// (at your option) any later version. |
|
// |
|
// ProtonMail Bridge is distributed in the hope that it will be useful, |
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
// GNU General Public License for more details. |
|
// |
|
// You should have received a copy of the GNU General Public License |
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>. |
|
|
|
package message |
|
|
|
import ( |
|
"fmt" |
|
"path/filepath" |
|
"runtime" |
|
"sort" |
|
"strings" |
|
"testing" |
|
|
|
"github.com/stretchr/testify/require" |
|
) |
|
|
|
var enableDebug = false // nolint[global] |
|
|
|
func debug(msg string, v ...interface{}) { |
|
if !enableDebug { |
|
return |
|
} |
|
|
|
_, file, line, _ := runtime.Caller(1) |
|
fmt.Printf("%s:%d: \033[2;33m"+msg+"\033[0;39m\n", append([]interface{}{filepath.Base(file), line}, v...)...) |
|
} |
|
|
|
func TestParseBodyStructure(t *testing.T) { |
|
expectedStructure := map[string]string{ |
|
"": "multipart/mixed; boundary=\"0000MAIN\"", |
|
"1": "text/plain", |
|
"2": "application/octet-stream", |
|
"3": "message/rfc822; boundary=\"0003MSG\"", |
|
"3.1": "text/plain", |
|
"3.2": "application/octet-stream", |
|
"4": "multipart/mixed; boundary=\"0004ATTACH\"", |
|
"4.1": "image/gif", |
|
"4.2": "message/rfc822; boundary=\"0042MSG\"", |
|
"4.2.1": "text/plain", |
|
"4.2.2": "multipart/alternative; boundary=\"0422ALTER\"", |
|
"4.2.2.1": "text/plain", |
|
"4.2.2.2": "text/html", |
|
} |
|
mailReader := strings.NewReader(sampleMail) |
|
bs, err := NewBodyStructure(mailReader) |
|
require.NoError(t, err) |
|
|
|
paths := []string{} |
|
for path := range *bs { |
|
paths = append(paths, path) |
|
} |
|
sort.Strings(paths) |
|
|
|
debug("%10s: %-50s %5s %5s %5s %5s", "section", "type", "start", "size", "bsize", "lines") |
|
for _, path := range paths { |
|
sec := (*bs)[path] |
|
contentType := sec.header.Get("Content-Type") |
|
debug("%10s: %-50s %5d %5d %5d %5d", path, contentType, sec.start, sec.size, sec.bsize, sec.lines) |
|
require.Equal(t, expectedStructure[path], contentType) |
|
} |
|
|
|
require.True(t, len(*bs) == len(expectedStructure), "Wrong number of sections expected %d but have %d", len(expectedStructure), len(*bs)) |
|
} |
|
|
|
func TestGetSection(t *testing.T) { |
|
structReader := strings.NewReader(sampleMail) |
|
bs, err := NewBodyStructure(structReader) |
|
require.NoError(t, err) |
|
// Whole section. |
|
for _, try := range testPaths { |
|
mailReader := strings.NewReader(sampleMail) |
|
info, err := bs.getInfo(try.path) |
|
require.NoError(t, err) |
|
section, err := bs.GetSection(mailReader, try.path) |
|
require.NoError(t, err) |
|
|
|
debug("section %v: %d %d\n___\n%s\n‾‾‾\n", try.path, info.start, info.size, string(section)) |
|
|
|
require.True(t, string(section) == try.expectedSection, "not same as expected:\n___\n%s\n‾‾‾", try.expectedSection) |
|
} |
|
// Body content. |
|
for _, try := range testPaths { |
|
mailReader := strings.NewReader(sampleMail) |
|
info, err := bs.getInfo(try.path) |
|
require.NoError(t, err) |
|
section, err := bs.GetSectionContent(mailReader, try.path) |
|
require.NoError(t, err) |
|
|
|
debug("content %v: %d %d\n___\n%s\n‾‾‾\n", try.path, info.start+info.size-info.bsize, info.bsize, string(section)) |
|
|
|
require.True(t, string(section) == try.expectedBody, "not same as expected:\n___\n%s\n‾‾‾", try.expectedBody) |
|
} |
|
} |
|
|
|
/* Structure example: |
|
HEADER ([RFC-2822] header of the message) |
|
TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED |
|
1 TEXT/PLAIN |
|
2 APPLICATION/OCTET-STREAM |
|
3 MESSAGE/RFC822 |
|
3.HEADER ([RFC-2822] header of the message) |
|
3.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED |
|
3.1 TEXT/PLAIN |
|
3.2 APPLICATION/OCTET-STREAM |
|
4 MULTIPART/MIXED |
|
4.1 IMAGE/GIF |
|
4.1.MIME ([MIME-IMB] header for the IMAGE/GIF) |
|
4.2 MESSAGE/RFC822 |
|
4.2.HEADER ([RFC-2822] header of the message) |
|
4.2.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED |
|
4.2.1 TEXT/PLAIN |
|
4.2.2 MULTIPART/ALTERNATIVE |
|
4.2.2.1 TEXT/PLAIN |
|
4.2.2.2 TEXT/RICHTEXT |
|
*/ |
|
|
|
var sampleMail = `Subject: Sample mail |
|
From: John Doe <jdoe@machine.example> |
|
To: Mary Smith <mary@example.net> |
|
Date: Fri, 21 Nov 1997 09:55:06 -0600 |
|
Content-Type: multipart/mixed; boundary="0000MAIN" |
|
|
|
main summary |
|
|
|
--0000MAIN |
|
Content-Type: text/plain |
|
|
|
1. main message |
|
|
|
|
|
--0000MAIN |
|
Content-Type: application/octet-stream |
|
Content-Disposition: inline; filename="main_signature.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
2/MainOctetStream |
|
|
|
--0000MAIN |
|
Subject: Inside mail 3 |
|
From: Mary Smith <mary@example.net> |
|
To: John Doe <jdoe@machine.example> |
|
Date: Fri, 20 Nov 1997 09:55:06 -0600 |
|
Content-Type: message/rfc822; boundary="0003MSG" |
|
|
|
3. message summary |
|
|
|
--0003MSG |
|
Content-Type: text/plain |
|
|
|
3.1 message text |
|
|
|
--0003MSG |
|
Content-Type: application/octet-stream |
|
Content-Disposition: attachment; filename="msg_3_signature.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
3/2/MessageOctestStream/== |
|
|
|
--0003MSG-- |
|
|
|
--0000MAIN |
|
Content-Type: multipart/mixed; boundary="0004ATTACH" |
|
|
|
4 attach summary |
|
|
|
--0004ATTACH |
|
Content-Type: image/gif |
|
Content-Disposition: attachment; filename="att4.1_gif.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
4/1/Gif= |
|
|
|
--0004ATTACH |
|
Subject: Inside mail 4.2 |
|
From: Mary Smith <mary@example.net> |
|
To: John Doe <jdoe@machine.example> |
|
Date: Fri, 10 Nov 1997 09:55:06 -0600 |
|
Content-Type: message/rfc822; boundary="0042MSG" |
|
|
|
4.2 message summary |
|
|
|
--0042MSG |
|
Content-Type: text/plain |
|
|
|
4.2.1 message text |
|
|
|
--0042MSG |
|
Content-Type: multipart/alternative; boundary="0422ALTER" |
|
|
|
4.2.2 alternative summary |
|
|
|
--0422ALTER |
|
Content-Type: text/plain |
|
|
|
4.2.2.1 plain text |
|
|
|
--0422ALTER |
|
Content-Type: text/html |
|
|
|
<h1>4.2.2.2 html text</h1> |
|
|
|
--0422ALTER-- |
|
|
|
--0042MSG-- |
|
|
|
--0004ATTACH-- |
|
|
|
--0000MAIN-- |
|
|
|
|
|
` |
|
|
|
var testPaths = []struct { |
|
path []int |
|
expectedSection, expectedBody string |
|
}{ |
|
{[]int{}, |
|
sampleMail, |
|
`main summary |
|
|
|
--0000MAIN |
|
Content-Type: text/plain |
|
|
|
1. main message |
|
|
|
|
|
--0000MAIN |
|
Content-Type: application/octet-stream |
|
Content-Disposition: inline; filename="main_signature.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
2/MainOctetStream |
|
|
|
--0000MAIN |
|
Subject: Inside mail 3 |
|
From: Mary Smith <mary@example.net> |
|
To: John Doe <jdoe@machine.example> |
|
Date: Fri, 20 Nov 1997 09:55:06 -0600 |
|
Content-Type: message/rfc822; boundary="0003MSG" |
|
|
|
3. message summary |
|
|
|
--0003MSG |
|
Content-Type: text/plain |
|
|
|
3.1 message text |
|
|
|
--0003MSG |
|
Content-Type: application/octet-stream |
|
Content-Disposition: attachment; filename="msg_3_signature.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
3/2/MessageOctestStream/== |
|
|
|
--0003MSG-- |
|
|
|
--0000MAIN |
|
Content-Type: multipart/mixed; boundary="0004ATTACH" |
|
|
|
4 attach summary |
|
|
|
--0004ATTACH |
|
Content-Type: image/gif |
|
Content-Disposition: attachment; filename="att4.1_gif.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
4/1/Gif= |
|
|
|
--0004ATTACH |
|
Subject: Inside mail 4.2 |
|
From: Mary Smith <mary@example.net> |
|
To: John Doe <jdoe@machine.example> |
|
Date: Fri, 10 Nov 1997 09:55:06 -0600 |
|
Content-Type: message/rfc822; boundary="0042MSG" |
|
|
|
4.2 message summary |
|
|
|
--0042MSG |
|
Content-Type: text/plain |
|
|
|
4.2.1 message text |
|
|
|
--0042MSG |
|
Content-Type: multipart/alternative; boundary="0422ALTER" |
|
|
|
4.2.2 alternative summary |
|
|
|
--0422ALTER |
|
Content-Type: text/plain |
|
|
|
4.2.2.1 plain text |
|
|
|
--0422ALTER |
|
Content-Type: text/html |
|
|
|
<h1>4.2.2.2 html text</h1> |
|
|
|
--0422ALTER-- |
|
|
|
--0042MSG-- |
|
|
|
--0004ATTACH-- |
|
|
|
--0000MAIN-- |
|
|
|
|
|
`, |
|
}, |
|
|
|
{[]int{1}, |
|
`Content-Type: text/plain |
|
|
|
1. main message |
|
|
|
|
|
`, |
|
`1. main message |
|
|
|
|
|
`, |
|
}, |
|
{[]int{3}, |
|
`Subject: Inside mail 3 |
|
From: Mary Smith <mary@example.net> |
|
To: John Doe <jdoe@machine.example> |
|
Date: Fri, 20 Nov 1997 09:55:06 -0600 |
|
Content-Type: message/rfc822; boundary="0003MSG" |
|
|
|
3. message summary |
|
|
|
--0003MSG |
|
Content-Type: text/plain |
|
|
|
3.1 message text |
|
|
|
--0003MSG |
|
Content-Type: application/octet-stream |
|
Content-Disposition: attachment; filename="msg_3_signature.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
3/2/MessageOctestStream/== |
|
|
|
--0003MSG-- |
|
|
|
`, |
|
`3. message summary |
|
|
|
--0003MSG |
|
Content-Type: text/plain |
|
|
|
3.1 message text |
|
|
|
--0003MSG |
|
Content-Type: application/octet-stream |
|
Content-Disposition: attachment; filename="msg_3_signature.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
3/2/MessageOctestStream/== |
|
|
|
--0003MSG-- |
|
|
|
`, |
|
}, |
|
{[]int{3, 1}, |
|
`Content-Type: text/plain |
|
|
|
3.1 message text |
|
|
|
`, |
|
`3.1 message text |
|
|
|
`, |
|
}, |
|
{[]int{3, 2}, |
|
`Content-Type: application/octet-stream |
|
Content-Disposition: attachment; filename="msg_3_signature.sig" |
|
Content-Transfer-Encoding: base64 |
|
|
|
3/2/MessageOctestStream/== |
|
|
|
`, |
|
`3/2/MessageOctestStream/== |
|
|
|
`, |
|
}, |
|
{[]int{4, 2, 2, 1}, |
|
`Content-Type: text/plain |
|
|
|
4.2.2.1 plain text |
|
|
|
`, |
|
`4.2.2.1 plain text |
|
|
|
`, |
|
}, |
|
{[]int{4, 2, 2, 2}, |
|
`Content-Type: text/html |
|
|
|
<h1>4.2.2.2 html text</h1> |
|
|
|
`, |
|
`<h1>4.2.2.2 html text</h1> |
|
|
|
`, |
|
}, |
|
}
|
|
|