From a7a7d9a3d47c98dc6200d12f074e2af7195d5cd0 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 20 Oct 2022 14:38:42 +0200 Subject: [PATCH] GODT-1742: Implement hide All Mail --- internal/bridge/settings.go | 6 ++- internal/bridge/user.go | 2 +- internal/user/imap.go | 6 ++- internal/user/user.go | 21 ++++++++- internal/user/user_test.go | 2 +- tests/bdd_test.go | 2 + tests/bridge_test.go | 8 ++++ .../imap/mailbox/hide_all_mail.feature | 46 +++++++++++++++++++ 8 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 tests/features/imap/mailbox/hide_all_mail.feature diff --git a/internal/bridge/settings.go b/internal/bridge/settings.go index 991fbc5a..3cebf0e0 100644 --- a/internal/bridge/settings.go +++ b/internal/bridge/settings.go @@ -170,7 +170,11 @@ func (bridge *Bridge) GetShowAllMail() bool { } func (bridge *Bridge) SetShowAllMail(show bool) error { - panic("TODO") + bridge.users.IterValues(func(user *user.User) { + user.SetShowAllMail(show) + }) + + return bridge.vault.SetShowAllMail(show) } func (bridge *Bridge) GetAutostart() bool { diff --git a/internal/bridge/user.go b/internal/bridge/user.go index d41c033c..ba40751c 100644 --- a/internal/bridge/user.go +++ b/internal/bridge/user.go @@ -389,7 +389,7 @@ func (bridge *Bridge) addUserWithVault( apiUser liteapi.User, vaultUser *vault.User, ) error { - user, err := user.New(ctx, vaultUser, client, apiUser) + user, err := user.New(ctx, vaultUser, client, apiUser, bridge.GetShowAllMail()) if err != nil { return fmt.Errorf("failed to create user: %w", err) } diff --git a/internal/user/imap.go b/internal/user/imap.go index e7e3bebb..93e0989b 100644 --- a/internal/user/imap.go +++ b/internal/user/imap.go @@ -336,6 +336,10 @@ func (conn *imapConnector) Close(ctx context.Context) error { return nil } -func (conn *imapConnector) IsMailboxVisible(_ context.Context, _ imap.MailboxID) bool { +func (conn *imapConnector) IsMailboxVisible(_ context.Context, id imap.MailboxID) bool { + if !conn.GetShowAllMail() && id == liteapi.AllMailLabel { + return false + } + return true } diff --git a/internal/user/user.go b/internal/user/user.go index 6344dd1b..9882a9cf 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -22,6 +22,7 @@ import ( "crypto/subtle" "fmt" "strings" + "sync/atomic" "time" "github.com/ProtonMail/gluon/connector" @@ -54,9 +55,11 @@ type User struct { syncStopCh chan struct{} syncLock try.Group + + showAllMail int32 } -func New(ctx context.Context, encVault *vault.User, client *liteapi.Client, apiUser liteapi.User) (*User, error) { //nolint:funlen +func New(ctx context.Context, encVault *vault.User, client *liteapi.Client, apiUser liteapi.User, showAllMail bool) (*User, error) { //nolint:funlen // Get the user's API addresses. apiAddrs, err := client.GetAddresses(ctx) if err != nil { @@ -111,6 +114,8 @@ func New(ctx context.Context, encVault *vault.User, client *liteapi.Client, apiU syncStopCh: make(chan struct{}), } + user.SetShowAllMail(showAllMail) + // When we receive an auth object, we update it in the vault. // This will be used to authorize the user on the next run. user.client.AddAuthHandler(func(auth liteapi.Auth) { @@ -390,6 +395,20 @@ func (user *User) Close() error { return nil } +func (user *User) SetShowAllMail(show bool) { + var value int32 + if show { + value = 1 + } else { + value = 0 + } + atomic.StoreInt32(&user.showAllMail, value) +} + +func (user *User) GetShowAllMail() bool { + return atomic.LoadInt32(&user.showAllMail) == 1 +} + func (user *User) checkAuth(email string, password []byte) (string, error) { dec, err := hexDecode(password) if err != nil { diff --git a/internal/user/user_test.go b/internal/user/user_test.go index 765187c0..81ea56f8 100644 --- a/internal/user/user_test.go +++ b/internal/user/user_test.go @@ -142,7 +142,7 @@ func withUser(t *testing.T, ctx context.Context, _ *server.Server, m *liteapi.Ma vaultUser, err := vault.AddUser(apiUser.ID, username, apiAuth.UID, apiAuth.RefreshToken, saltedKeyPass) require.NoError(t, err) - user, err := user.New(ctx, vaultUser, client, apiUser) + user, err := user.New(ctx, vaultUser, client, apiUser, true) require.NoError(t, err) defer func() { require.NoError(t, user.Close()) }() diff --git a/tests/bdd_test.go b/tests/bdd_test.go index 376d93c8..028cf2d4 100644 --- a/tests/bdd_test.go +++ b/tests/bdd_test.go @@ -122,6 +122,8 @@ func TestFeatures(testingT *testing.T) { ctx.Step(`^bridge sends an update installed event for version "([^"]*)"$`, s.bridgeSendsAnUpdateInstalledEventForVersion) ctx.Step(`^bridge sends an update not available event$`, s.bridgeSendsAnUpdateNotAvailableEvent) ctx.Step(`^bridge sends a forced update event$`, s.bridgeSendsAForcedUpdateEvent) + ctx.Step(`^bridge hides all mail$`, s.bridgeHidesAllMail) + ctx.Step(`^bridge shows all mail$`, s.bridgeShowsAllMail) // ==== USER ==== ctx.Step(`^the user logs in with username "([^"]*)" and password "([^"]*)"$`, s.userLogsInWithUsernameAndPassword) diff --git a/tests/bridge_test.go b/tests/bridge_test.go index 21f4b77a..80c7392f 100644 --- a/tests/bridge_test.go +++ b/tests/bridge_test.go @@ -267,6 +267,14 @@ func (s *scenario) bridgeSendsAForcedUpdateEvent() error { }) } +func (s *scenario) bridgeHidesAllMail() error { + return s.t.bridge.SetShowAllMail(false) +} + +func (s *scenario) bridgeShowsAllMail() error { + return s.t.bridge.SetShowAllMail(true) +} + func try[T any](inCh *queue.QueuedChannel[T], wait time.Duration, fn func(T) error) error { select { case event := <-inCh.GetChannel(): diff --git a/tests/features/imap/mailbox/hide_all_mail.feature b/tests/features/imap/mailbox/hide_all_mail.feature new file mode 100644 index 00000000..838f14e9 --- /dev/null +++ b/tests/features/imap/mailbox/hide_all_mail.feature @@ -0,0 +1,46 @@ +Feature: IMAP Hide All Mail + Background: + Given there exists an account with username "user@pm.me" and password "password" + And bridge starts + And the user logs in with username "user@pm.me" and password "password" + And user "user@pm.me" finishes syncing + And user "user@pm.me" connects and authenticates IMAP client "1" + + Scenario: Hide All Mail Mailbox + Given IMAP client "1" sees the following mailbox info: + | name | total | unread | + | INBOX | 0 | 0 | + | Drafts | 0 | 0 | + | Sent | 0 | 0 | + | Starred | 0 | 0 | + | Archive | 0 | 0 | + | Spam | 0 | 0 | + | Trash | 0 | 0 | + | All Mail | 0 | 0 | + | Folders | 0 | 0 | + | Labels | 0 | 0 | + When bridge hides all mail + Then IMAP client "1" sees the following mailbox info: + | name | total | unread | + | INBOX | 0 | 0 | + | Drafts | 0 | 0 | + | Sent | 0 | 0 | + | Starred | 0 | 0 | + | Archive | 0 | 0 | + | Spam | 0 | 0 | + | Trash | 0 | 0 | + | Folders | 0 | 0 | + | Labels | 0 | 0 | + When bridge shows all mail + Then IMAP client "1" sees the following mailbox info: + | name | total | unread | + | INBOX | 0 | 0 | + | Drafts | 0 | 0 | + | Sent | 0 | 0 | + | Starred | 0 | 0 | + | Archive | 0 | 0 | + | Spam | 0 | 0 | + | Trash | 0 | 0 | + | All Mail | 0 | 0 | + | Folders | 0 | 0 | + | Labels | 0 | 0 |