Skip to content

Commit 3bd39b3

Browse files
jameshoulahanJakub Cuth
authored andcommitted
fix(GODT-1804): Only promote content headers if non-empty
When attaching public key, we take the root mime part, create a new root, and put the old root alongside an additional public key mime part. But when moving the root, we would copy all content headers, even empty ones. So we’d be left with Content-Disposition: "" which would fail to parse.
1 parent e89dcb2 commit 3bd39b3

File tree

6 files changed

+213
-7
lines changed

6 files changed

+213
-7
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/Masterminds/semver/v3 v3.1.1
88
github.com/ProtonMail/gluon v0.14.2-0.20230127085305-bc2d818d9d13
99
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
10-
github.com/ProtonMail/go-proton-api v0.3.1-0.20230126112849-3c1ac277855e
10+
github.com/ProtonMail/go-proton-api v0.3.1-0.20230203120457-1849bf7d578b
1111
github.com/ProtonMail/go-rfc5322 v0.11.0
1212
github.com/ProtonMail/gopenpgp/v2 v2.4.10
1313
github.com/PuerkitoBio/goquery v1.8.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753/go.mod h1:NB
4141
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
4242
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f h1:4IWzKjHzZxdrW9k4zl/qCwenOVHDbVDADPPHFLjs0Oc=
4343
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM=
44-
github.com/ProtonMail/go-proton-api v0.3.1-0.20230126112849-3c1ac277855e h1:UkfLQc44UvknNCLoBEZb1qg7zfVWVLMvCE/LtdVEcAw=
45-
github.com/ProtonMail/go-proton-api v0.3.1-0.20230126112849-3c1ac277855e/go.mod h1:JUo5IQG0hNuPRuDpOUsCOvtee6UjTEHHF1QN2i8RSos=
44+
github.com/ProtonMail/go-proton-api v0.3.1-0.20230203120457-1849bf7d578b h1:3UX0j2WA3NsqmrjTK769sCIOX2KnISfo8tZdFl7+mRE=
45+
github.com/ProtonMail/go-proton-api v0.3.1-0.20230203120457-1849bf7d578b/go.mod h1:JUo5IQG0hNuPRuDpOUsCOvtee6UjTEHHF1QN2i8RSos=
4646
github.com/ProtonMail/go-rfc5322 v0.11.0 h1:o5Obrm4DpmQEffvgsVqG6S4BKwC1Wat+hYwjIp2YcCY=
4747
github.com/ProtonMail/go-rfc5322 v0.11.0/go.mod h1:6oOKr0jXvpoE6pwTx/HukigQpX2J9WUf6h0auplrFTw=
4848
github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg=

internal/bridge/send_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
package bridge_test
1919

2020
import (
21+
"bytes"
2122
"context"
2223
"crypto/tls"
2324
"fmt"
2425
"net"
26+
"os"
2527
"strings"
2628
"testing"
2729
"time"
@@ -217,3 +219,114 @@ func TestBridge_SendDraftFlags(t *testing.T) {
217219
})
218220
})
219221
}
222+
223+
func TestBridge_SendInvite(t *testing.T) {
224+
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
225+
// Create a recipient user.
226+
_, _, err := s.CreateUser("recipient", password)
227+
require.NoError(t, err)
228+
229+
// Set "attach public keys" to true for the user.
230+
withClient(ctx, t, s, username, password, func(ctx context.Context, client *proton.Client) {
231+
settings, err := client.SetAttachPublicKey(ctx, proton.SetAttachPublicKeyReq{AttachPublicKey: true})
232+
require.NoError(t, err)
233+
require.Equal(t, proton.Bool(true), settings.AttachPublicKey)
234+
})
235+
236+
// The sender should be fully synced.
237+
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) {
238+
syncCh, done := chToType[events.Event, events.SyncFinished](bridge.GetEvents(events.SyncFinished{}))
239+
defer done()
240+
241+
userID, err := bridge.LoginFull(ctx, username, password, nil, nil)
242+
require.NoError(t, err)
243+
244+
require.Equal(t, userID, (<-syncCh).UserID)
245+
})
246+
247+
// Start the bridge.
248+
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) {
249+
// Get the sender user info.
250+
userInfo, err := bridge.QueryUserInfo(username)
251+
require.NoError(t, err)
252+
253+
// Connect the sender IMAP client.
254+
imapClient, err := client.Dial(net.JoinHostPort(constants.Host, fmt.Sprint(bridge.GetIMAPPort())))
255+
require.NoError(t, err)
256+
require.NoError(t, imapClient.Login(userInfo.Addresses[0], string(userInfo.BridgePass)))
257+
defer imapClient.Logout() //nolint:errcheck
258+
259+
// The message to send.
260+
b, err := os.ReadFile("testdata/invite.eml")
261+
require.NoError(t, err)
262+
263+
// Save a draft.
264+
require.NoError(t, imapClient.Append("Drafts", []string{imap.DraftFlag}, time.Now(), bytes.NewReader(b)))
265+
266+
// Assert that the draft exists and is marked as a draft.
267+
{
268+
messages, err := clientFetch(imapClient, "Drafts")
269+
require.NoError(t, err)
270+
require.Len(t, messages, 1)
271+
require.Contains(t, messages[0].Flags, imap.DraftFlag)
272+
}
273+
274+
// Connect the SMTP client.
275+
smtpClient, err := smtp.Dial(net.JoinHostPort(constants.Host, fmt.Sprint(bridge.GetSMTPPort())))
276+
require.NoError(t, err)
277+
defer smtpClient.Close() //nolint:errcheck
278+
279+
// Upgrade to TLS.
280+
require.NoError(t, smtpClient.StartTLS(&tls.Config{InsecureSkipVerify: true}))
281+
282+
// Authorize with SASL PLAIN.
283+
require.NoError(t, smtpClient.Auth(sasl.NewPlainClient(
284+
userInfo.Addresses[0],
285+
userInfo.Addresses[0],
286+
string(userInfo.BridgePass)),
287+
))
288+
289+
// Send the message.
290+
require.NoError(t, smtpClient.SendMail(
291+
userInfo.Addresses[0],
292+
[]string{"recipient@" + s.GetDomain()},
293+
bytes.NewReader(b),
294+
))
295+
296+
// Delete the draft: add the \Deleted flag and expunge.
297+
{
298+
status, err := imapClient.Select("Drafts", false)
299+
require.NoError(t, err)
300+
require.Equal(t, uint32(1), status.Messages)
301+
302+
// Add the \Deleted flag.
303+
require.NoError(t, clientStore(imapClient, 1, 1, true, imap.FormatFlagsOp(imap.AddFlags, true), imap.DeletedFlag))
304+
305+
// Expunge.
306+
require.NoError(t, imapClient.Expunge(nil))
307+
}
308+
309+
// Assert that the draft is eventually gone.
310+
require.Eventually(t, func() bool {
311+
status, err := imapClient.Select("Drafts", false)
312+
require.NoError(t, err)
313+
return status.Messages == 0
314+
}, 10*time.Second, 100*time.Millisecond)
315+
316+
// Assert that the message is eventually in the sent folder.
317+
require.Eventually(t, func() bool {
318+
messages, err := clientFetch(imapClient, "Sent")
319+
require.NoError(t, err)
320+
return len(messages) == 1
321+
}, 10*time.Second, 100*time.Millisecond)
322+
323+
// Assert that the message is not marked as a draft.
324+
{
325+
messages, err := clientFetch(imapClient, "Sent")
326+
require.NoError(t, err)
327+
require.Len(t, messages, 1)
328+
require.NotContains(t, messages[0].Flags, imap.DraftFlag)
329+
}
330+
})
331+
})
332+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
From: <username@proton.local>
2+
To: <recipient@proton.local>
3+
Subject: Testing calendar invite
4+
Date: Fri, 3 Feb 2023 01:04:32 +0100
5+
Message-ID: <000001d93763$183b74e0$48b25ea0$@proton.local>
6+
MIME-Version: 1.0
7+
Content-Type: text/calendar; method=REQUEST;
8+
charset="utf-8"
9+
Content-Transfer-Encoding: 7bit
10+
X-Mailer: Microsoft Outlook 16.0
11+
Thread-Index: Adk3Yw5pLdgwsT46RviXb/nfvQlesQAAAmGA
12+
Content-Language: en-gb
13+
14+
BEGIN:VCALENDAR
15+
PRODID:-//Microsoft Corporation//Outlook 16.0 MIMEDIR//EN
16+
VERSION:2.0
17+
METHOD:REQUEST
18+
X-MS-OLK-FORCEINSPECTOROPEN:TRUE
19+
BEGIN:VTIMEZONE
20+
TZID:Central European Standard Time
21+
BEGIN:STANDARD
22+
DTSTART:16011028T030000
23+
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
24+
TZOFFSETFROM:+0200
25+
TZOFFSETTO:+0100
26+
END:STANDARD
27+
BEGIN:DAYLIGHT
28+
DTSTART:16010325T020000
29+
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
30+
TZOFFSETFROM:+0100
31+
TZOFFSETTO:+0200
32+
END:DAYLIGHT
33+
END:VTIMEZONE
34+
BEGIN:VEVENT
35+
ATTENDEE;CN=recipient@proton.local;RSVP=TRUE:mailto:recipient@proton.local
36+
CLASS:PUBLIC
37+
CREATED:20230203T000432Z
38+
DESCRIPTION:qweqweqweqweqweqwe/gn\\n
39+
DTEND;TZID="Central European Standard Time":20230203T020000
40+
DTSTAMP:20230203T000432Z
41+
DTSTART;TZID="Central European Standard Time":20230203T013000
42+
LAST-MODIFIED:20230203T000432Z
43+
LOCATION:qweqwe
44+
ORGANIZER;CN=username@proton.local:mailto:username@proton.local
45+
PRIORITY:5
46+
SEQUENCE:0
47+
SUMMARY;LANGUAGE=en-gb:Testing calendar invite
48+
TRANSP:OPAQUE
49+
UID:040000008200E00074C5B7101A82E008000000003080B2796B37D901000000000000000
50+
0100000001236CD1CD93CA9449C6FF1AC4DEAC44E
51+
X-ALT-DESC;FMTTYPE=text/html:<html xmlns:v="urn:schemas-microsoft-com:vml"
52+
xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-mic
53+
rosoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/
54+
12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv=Co
55+
ntent-Type content="text/html/g; charset=us-ascii"><meta name=Generator con
56+
tent="Microsoft Word 15 (filtered medium)"><style><!--/gn/* Font Definition
57+
s *//gn@font-face\\n {font-family:"Cambria Math"\\;\\n panose-1:2 4 5 3 5 4 6
58+
3 2 4/g;}\\n@font-face\\n {font-family:Calibri\\;\\n panose-1:2 15 5 2 2 2 4 3
59+
2 4/g;}\\n/* Style Definitions */\\np.MsoNormal\\, li.MsoNormal\\, div.MsoNorma
60+
l/gn {margin:0cm\\;\\n font-size:11.0pt\\;\\n font-family:"Calibri"\\,sans-serif
61+
/g;\\n mso-fareast-language:EN-US\\;}\\nspan.EmailStyle18\\n {mso-style-type:pe
62+
rsonal-compose/g;\\n font-family:"Calibri"\\,sans-serif\\;\\n color:windowtext\\
63+
;}/gn.MsoChpDefault\\n {mso-style-type:export-only\\;\\n font-size:10.0pt\\;}\\n
64+
@page WordSection1/gn {size:612.0pt 792.0pt\\;\\n margin:72.0pt 72.0pt 72.0pt
65+
72.0pt/g;}\\ndiv.WordSection1\\n {page:WordSection1\\;}\\n--></style><!--[if g
66+
te mso 9]><xml>/gn<o:shapedefaults v:ext="edit" spidmax="1026" />\\n</xml><!
67+
[endif]--><!--[if gte mso 9]><xml>/gn<o:shapelayout v:ext="edit">\\n<o:idmap
68+
v:ext="edit" data="1" />/gn</o:shapelayout></xml><![endif]--></head><body
69+
lang=EN-GB link="#0563C1" vlink="#954F72" style='word-wrap:break-word'><di
70+
v class=WordSection1><p class=MsoNormal><span lang=EN-US>qweqweqweqweqweqw
71+
e<o:p></o:p></span></p></div></body></html>
72+
X-MICROSOFT-CDO-BUSYSTATUS:TENTATIVE
73+
X-MICROSOFT-CDO-IMPORTANCE:1
74+
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
75+
X-MICROSOFT-DISALLOW-COUNTER:FALSE
76+
X-MS-OLK-AUTOSTARTCHECK:FALSE
77+
X-MS-OLK-CONFTYPE:0
78+
BEGIN:VALARM
79+
TRIGGER:-PT15M
80+
ACTION:DISPLAY
81+
DESCRIPTION:Reminder
82+
END:VALARM
83+
END:VEVENT
84+
END:VCALENDAR
85+

internal/user/smtp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func (user *User) sendMail(authID string, from string, to []string, r io.Reader)
120120
}
121121

122122
// If we have to attach the public key, do it now.
123-
if settings.AttachPublicKey == proton.AttachPublicKeyEnabled {
123+
if settings.AttachPublicKey {
124124
key, err := addrKR.GetKey(0)
125125
if err != nil {
126126
return fmt.Errorf("failed to get sending key: %w", err)

pkg/message/parser/part.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,17 @@ func (p *Part) isMultipartMixed() bool {
186186
func getContentHeaders(header message.Header) message.Header {
187187
var res message.Header
188188

189-
res.Set("Content-Type", header.Get("Content-Type"))
190-
res.Set("Content-Disposition", header.Get("Content-Disposition"))
191-
res.Set("Content-Transfer-Encoding", header.Get("Content-Transfer-Encoding"))
189+
if contentType := header.Get("Content-Type"); contentType != "" {
190+
res.Set("Content-Type", contentType)
191+
}
192+
193+
if contentDisposition := header.Get("Content-Disposition"); contentDisposition != "" {
194+
res.Set("Content-Disposition", contentDisposition)
195+
}
196+
197+
if contentTransferEncoding := header.Get("Content-Transfer-Encoding"); contentTransferEncoding != "" {
198+
res.Set("Content-Transfer-Encoding", contentTransferEncoding)
199+
}
192200

193201
return res
194202
}

0 commit comments

Comments
 (0)