From 9dfdd07f7acc2ab0f7c028566c48345e9f319bf4 Mon Sep 17 00:00:00 2001 From: James Houlahan Date: Mon, 20 Feb 2023 15:35:56 +0100 Subject: [PATCH] fix(GODT-2381): Unset draft flag on sent messages --- go.mod | 2 +- go.sum | 4 +- internal/bridge/user_event_test.go | 100 +++++++++++++++++++++++++++++ internal/user/events.go | 1 + 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 60a41da3..018fab24 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.1.1 - github.com/ProtonMail/gluon v0.14.2-0.20230217111735-c6aa35929f2e + github.com/ProtonMail/gluon v0.14.2-0.20230220142004-09ab7050bc08 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-proton-api v0.4.1-0.20230217130533-0af5d2f08497 github.com/ProtonMail/go-rfc5322 v0.11.0 diff --git a/go.sum b/go.sum index 8b1b5f23..b7c249e8 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo= github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk= github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= -github.com/ProtonMail/gluon v0.14.2-0.20230217111735-c6aa35929f2e h1:WWFhucLz1VeNLggqD/Jw14FwQ47TK8rgboSo+Ttu5qk= -github.com/ProtonMail/gluon v0.14.2-0.20230217111735-c6aa35929f2e/go.mod h1:HYHr7hG7LPWI1S50M8NfHRb1kYi5B+Yu4/N/H+y+JUY= +github.com/ProtonMail/gluon v0.14.2-0.20230220142004-09ab7050bc08 h1:SSXME1MQg1In5zalVi6vxJKnnfYy92mwi6hhugDIxzA= +github.com/ProtonMail/gluon v0.14.2-0.20230220142004-09ab7050bc08/go.mod h1:HYHr7hG7LPWI1S50M8NfHRb1kYi5B+Yu4/N/H+y+JUY= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= diff --git a/internal/bridge/user_event_test.go b/internal/bridge/user_event_test.go index 5b463a7a..87d5cfc2 100644 --- a/internal/bridge/user_event_test.go +++ b/internal/bridge/user_event_test.go @@ -31,6 +31,7 @@ import ( "github.com/ProtonMail/gluon/rfc822" "github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/go-proton-api/server" + "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/proton-bridge/v3/internal/bridge" "github.com/ProtonMail/proton-bridge/v3/internal/constants" "github.com/ProtonMail/proton-bridge/v3/internal/events" @@ -434,6 +435,105 @@ func TestBridge_User_UpdateDraftAndCreateOtherMessage(t *testing.T) { }) } +func TestBridge_User_SendDraftRemoveDraftFlag(t *testing.T) { + withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) { + // Create a bridge user. + _, _, err := s.CreateUser("user", password) + require.NoError(t, err) + + // Initially sync the user. + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) { + userLoginAndSync(ctx, t, bridge, "user", password) + }) + + withClient(ctx, t, s, "user", password, func(ctx context.Context, c *proton.Client) { + user, err := c.GetUser(ctx) + require.NoError(t, err) + + addrs, err := c.GetAddresses(ctx) + require.NoError(t, err) + + salts, err := c.GetSalts(ctx) + require.NoError(t, err) + + keyPass, err := salts.SaltForKey(password, user.Keys.Primary().ID) + require.NoError(t, err) + + _, addrKRs, err := proton.Unlock(user, addrs, keyPass) + require.NoError(t, err) + + // Create a draft (generating a "create draft message" event). + draft, err := c.CreateDraft(ctx, addrKRs[addrs[0].ID], proton.CreateDraftReq{ + Message: proton.DraftTemplate{ + Subject: "subject", + ToList: []*mail.Address{{Address: addrs[0].Email}}, + Sender: &mail.Address{Name: "sender", Address: addrs[0].Email}, + Body: "body", + MIMEType: rfc822.TextPlain, + }, + }) + require.NoError(t, err) + + // Process those events + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) { + userContinueEventProcess(ctx, t, s, bridge) + + info, err := bridge.QueryUserInfo("user") + require.NoError(t, err) + + client, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort())) + require.NoError(t, err) + require.NoError(t, client.Login(info.Addresses[0], string(info.BridgePass))) + defer func() { _ = client.Logout() }() + + messages, err := clientFetch(client, "Drafts") + require.NoError(t, err) + require.Len(t, messages, 1) + require.Contains(t, messages[0].Flags, imap.DraftFlag) + }) + + // Send the draft (generating an "update message" event). + { + pubKeys, recType, err := c.GetPublicKeys(ctx, addrs[0].Email) + require.NoError(t, err) + require.Equal(t, recType, proton.RecipientTypeInternal) + + var req proton.SendDraftReq + + require.NoError(t, req.AddTextPackage(addrKRs[addrs[0].ID], "body", rfc822.TextPlain, map[string]proton.SendPreferences{ + addrs[0].Email: { + Encrypt: true, + PubKey: must(crypto.NewKeyRing(must(crypto.NewKeyFromArmored(pubKeys[0].PublicKey)))), + SignatureType: proton.DetachedSignature, + EncryptionScheme: proton.InternalScheme, + MIMEType: rfc822.TextPlain, + }, + }, nil)) + + require.NoError(t, getErr(c.SendDraft(ctx, draft.ID, req))) + } + + // Process those events; the draft will move to the sent folder and lose the draft flag. + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) { + userContinueEventProcess(ctx, t, s, bridge) + + info, err := bridge.QueryUserInfo("user") + require.NoError(t, err) + + client, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort())) + require.NoError(t, err) + require.NoError(t, client.Login(info.Addresses[0], string(info.BridgePass))) + defer func() { _ = client.Logout() }() + + messages, err := clientFetch(client, "Sent") + require.NoError(t, err) + require.Len(t, messages, 1) + require.NotContains(t, messages[0].Flags, imap.DraftFlag) + }) + }) + }) +} + // userLoginAndSync logs in user and waits until user is fully synced. func userLoginAndSync( ctx context.Context, diff --git a/internal/user/events.go b/internal/user/events.go index 37e80352..e36c25ab 100644 --- a/internal/user/events.go +++ b/internal/user/events.go @@ -570,6 +570,7 @@ func (user *User) handleUpdateMessageEvent(ctx context.Context, event proton.Mes mapTo[string, imap.MailboxID](wantLabels(user.apiLabels, event.Message.LabelIDs)), event.Message.Seen(), event.Message.Starred(), + event.Message.IsDraft(), ) user.updateCh[event.Message.AddressID].Enqueue(update)