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.
228 lines
6.6 KiB
228 lines
6.6 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 pmmime |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
|
|
"io/ioutil" |
|
"net/mail" |
|
|
|
"net/textproto" |
|
"strings" |
|
"testing" |
|
) |
|
|
|
func minimalParse(mimeBody string) (readBody string, plainContents string, err error) { |
|
mm, err := mail.ReadMessage(strings.NewReader(mimeBody)) |
|
if err != nil { |
|
return |
|
} |
|
|
|
h := textproto.MIMEHeader(mm.Header) |
|
mmBodyData, err := ioutil.ReadAll(mm.Body) |
|
if err != nil { |
|
return |
|
} |
|
|
|
printAccepter := NewMIMEPrinter() |
|
plainTextCollector := NewPlainTextCollector(printAccepter) |
|
visitor := NewMimeVisitor(plainTextCollector) |
|
err = VisitAll(bytes.NewReader(mmBodyData), h, visitor) |
|
|
|
readBody = printAccepter.String() |
|
plainContents = plainTextCollector.GetPlainText() |
|
|
|
return readBody, plainContents, err |
|
} |
|
|
|
func androidParse(mimeBody string) (body, headers string, atts, attHeaders []string, err error) { |
|
mm, err := mail.ReadMessage(strings.NewReader(mimeBody)) |
|
if err != nil { |
|
return |
|
} |
|
|
|
h := textproto.MIMEHeader(mm.Header) |
|
mmBodyData, err := ioutil.ReadAll(mm.Body) |
|
|
|
printAccepter := NewMIMEPrinter() |
|
bodyCollector := NewBodyCollector(printAccepter) |
|
attachmentsCollector := NewAttachmentsCollector(bodyCollector) |
|
mimeVisitor := NewMimeVisitor(attachmentsCollector) |
|
err = VisitAll(bytes.NewReader(mmBodyData), h, mimeVisitor) |
|
|
|
body, _ = bodyCollector.GetBody() |
|
headers = bodyCollector.GetHeaders() |
|
atts = attachmentsCollector.GetAttachments() |
|
attHeaders = attachmentsCollector.GetAttHeaders() |
|
|
|
return |
|
} |
|
|
|
func TestParseBoundaryIsEmpty(t *testing.T) { |
|
testMessage := |
|
`Date: Sun, 10 Mar 2019 11:10:06 -0600 |
|
In-Reply-To: <abcbase64@protonmail.com> |
|
X-Original-To: enterprise@protonmail.com |
|
References: <abc64@unicoderns.com> <abc63@protonmail.com> <abc64@protonmail.com> <abc65@mail.gmail.com> <abc66@protonmail.com> |
|
To: "ProtonMail" <enterprise@protonmail.com> |
|
X-Pm-Origin: external |
|
Delivered-To: enterprise@protonmail.com |
|
Content-Type: multipart/mixed; boundary=ac7e36bd45425e70b4dab2128f34172e4dc3f9ff2eeb47e909267d4252794ec7 |
|
Reply-To: XYZ <xyz@xyz.com> |
|
Mime-Version: 1.0 |
|
Subject: Encrypted Message |
|
Return-Path: <xyz@xyz.com> |
|
From: XYZ <xyz@xyz.com> |
|
X-Pm-Conversationid-Id: gNX9bDPLmBgFZ-C3Tdlb628cas1Xl0m4dql5nsWzQAEI-WQv0ytfwPR4-PWELEK0_87XuFOgetc239Y0pjPYHQ== |
|
X-Pm-Date: Sun, 10 Mar 2019 18:10:06 +0100 |
|
Message-Id: <68c11e46-e611-d9e4-edc1-5ec96bac77cc@unicoderns.com> |
|
X-Pm-Transfer-Encryption: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits) |
|
X-Pm-External-Id: <68c11e46-e611-d9e4-edc1-5ec96bac77cc@unicoderns.com> |
|
X-Pm-Internal-Id: _iJ8ETxcqXTSK8IzCn0qFpMUTwvRf-xJUtldRA1f6yHdmXjXzKleG3F_NLjZL3FvIWVHoItTxOuuVXcukwwW3g== |
|
Openpgp: preference=signencrypt |
|
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.4.0 |
|
X-Pm-Content-Encryption: end-to-end |
|
|
|
--ac7e36bd45425e70b4dab2128f34172e4dc3f9ff2eeb47e909267d4252794ec7 |
|
Content-Disposition: inline |
|
Content-Transfer-Encoding: quoted-printable |
|
Content-Type: multipart/mixed; charset=utf-8 |
|
|
|
Content-Type: multipart/mixed; boundary="xnAIW3Turb9YQZ2rXc2ZGZH45WepHIZyy"; |
|
protected-headers="v1" |
|
From: XYZ <xyz@xyz.com> |
|
To: "ProtonMail" <enterprise@protonmail.com> |
|
Subject: Encrypted Message |
|
Message-ID: <68c11e46-e611-d9e4-edc1-5ec96bac77cc@unicoderns.com> |
|
References: <abc64@unicoderns.com> <abc63@protonmail.com> <abc64@protonmail.com> <abc65@mail.gmail.com> <abc66@protonmail.com> |
|
In-Reply-To: <abcbase64@protonmail.com> |
|
|
|
--xnAIW3Turb9YQZ2rXc2ZGZH45WepHIZyy |
|
Content-Type: text/rfc822-headers; protected-headers="v1" |
|
Content-Disposition: inline |
|
|
|
From: XYZ <xyz@xyz.com> |
|
To: ProtonMail <enterprise@protonmail.com> |
|
Subject: Re: Encrypted Message |
|
|
|
--xnAIW3Turb9YQZ2rXc2ZGZH45WepHIZyy |
|
Content-Type: multipart/alternative; |
|
boundary="------------F9E5AA6D49692F51484075E3" |
|
Content-Language: en-US |
|
|
|
This is a multi-part message in MIME format. |
|
--------------F9E5AA6D49692F51484075E3 |
|
Content-Type: text/plain; charset=utf-8 |
|
Content-Transfer-Encoding: quoted-printable |
|
|
|
Hi ... |
|
|
|
--------------F9E5AA6D49692F51484075E3 |
|
Content-Type: text/html; charset=utf-8 |
|
Content-Transfer-Encoding: quoted-printable |
|
|
|
<html> |
|
<head> |
|
</head> |
|
<body text=3D"#000000" bgcolor=3D"#FFFFFF"> |
|
<p>Hi .. </p> |
|
</body> |
|
</html> |
|
|
|
--------------F9E5AA6D49692F51484075E3-- |
|
|
|
--xnAIW3Turb9YQZ2rXc2ZGZH45WepHIZyy-- |
|
|
|
--ac7e36bd45425e70b4dab2128f34172e4dc3f9ff2eeb47e909267d4252794ec7-- |
|
|
|
|
|
` |
|
|
|
body, content, err := minimalParse(testMessage) |
|
if err == nil { |
|
t.Fatal("should have error but is", err) |
|
} |
|
t.Log("==BODY==") |
|
t.Log(body) |
|
t.Log("==CONTENT==") |
|
t.Log(content) |
|
} |
|
|
|
func TestParse(t *testing.T) { |
|
testMessage := |
|
`From: John Doe <example@example.com> |
|
MIME-Version: 1.0 |
|
Content-Type: multipart/mixed; |
|
boundary="XXXXboundary text" |
|
|
|
This is a multipart message in MIME format. |
|
|
|
--XXXXboundary text |
|
Content-Type: text/plain; charset=utf-8 |
|
|
|
this is the body text |
|
|
|
--XXXXboundary text |
|
Content-Type: text/html; charset=utf-8 |
|
|
|
<html><body>this is the html body text</body></html> |
|
|
|
--XXXXboundary text |
|
Content-Type: text/plain; charset=utf-8 |
|
Content-Disposition: attachment; |
|
filename="test.txt" |
|
|
|
this is the attachment text |
|
|
|
--XXXXboundary text-- |
|
|
|
|
|
` |
|
body, heads, att, attHeads, err := androidParse(testMessage) |
|
if err != nil { |
|
t.Error("parse error", err) |
|
} |
|
|
|
fmt.Println("==BODY:") |
|
fmt.Println(body) |
|
fmt.Println("==BODY HEADERS:") |
|
fmt.Println(heads) |
|
|
|
fmt.Println("==ATTACHMENTS:") |
|
fmt.Println(att) |
|
fmt.Println("==ATTACHMENT HEADERS:") |
|
fmt.Println(attHeads) |
|
} |
|
|
|
func TestParseAddressComment(t *testing.T) { |
|
parsingExamples := map[string]string{ |
|
"": "", |
|
"(Only Comment) here@pm.me": "\"Only Comment\" <here@pm.me>", |
|
"Normal Name (With Comment) <here@pm.me>": "\"Normal Name\" <here@pm.me>", |
|
"<Muhammed.(I am the greatest)Ali@(the)Vegas.WBA>": "\"I am the greatest the\" <Muhammed.Ali@Vegas.WBA>", |
|
} |
|
|
|
for raw, expected := range parsingExamples { |
|
parsed := parseAddressComment(raw) |
|
if expected != parsed { |
|
t.Errorf("When parsing %q expected %q but have %q", raw, expected, parsed) |
|
} |
|
} |
|
}
|
|
|