diff --git a/internal/bridge/bridge.go b/internal/bridge/bridge.go index 4a66774a..ee282dcc 100644 --- a/internal/bridge/bridge.go +++ b/internal/bridge/bridge.go @@ -156,7 +156,7 @@ func (b *Bridge) SetUpdateChannel(channel updater.UpdateChannel) (needRestart bo // We have to deal right away only with downgrade - that action needs to // clear data and updates, and install bridge right away. But regular - // upgrade can be leaved out for periodic check. + // upgrade can be left out for periodic check. if !b.updater.IsDowngrade(version) { return false, nil } @@ -164,6 +164,7 @@ func (b *Bridge) SetUpdateChannel(channel updater.UpdateChannel) (needRestart bo if err := b.Users.ClearData(); err != nil { log.WithError(err).Error("Failed to clear data while downgrading channel") } + if err := b.locations.ClearUpdates(); err != nil { log.WithError(err).Error("Failed to clear updates while downgrading channel") } @@ -175,6 +176,23 @@ func (b *Bridge) SetUpdateChannel(channel updater.UpdateChannel) (needRestart bo return true, b.versioner.RemoveOtherVersions(version.Version) } +// FactoryReset will remove all local cache and settings. +// We want to downgrade to latest stable version if user is early higher than stable. +// Setting the channel back to stable will do this for us. +func (b *Bridge) FactoryReset() { + if _, err := b.SetUpdateChannel(updater.StableChannel); err != nil { + log.WithError(err).Error("Failed to revert to stable update channel") + } + + if err := b.Users.ClearUsers(); err != nil { + log.WithError(err).Error("Failed to remove bridge users") + } + + if err := b.Users.ClearData(); err != nil { + log.WithError(err).Error("Failed to remove bridge data") + } +} + // GetKeychainApp returns current keychain helper. func (b *Bridge) GetKeychainApp() string { return b.settings.Get(settings.PreferredKeychainKey) diff --git a/internal/frontend/cli/accounts.go b/internal/frontend/cli/accounts.go index a1b7152a..02a01d88 100644 --- a/internal/frontend/cli/accounts.go +++ b/internal/frontend/cli/accounts.go @@ -192,14 +192,34 @@ func (f *frontendCLI) deleteAccounts(c *ishell.Context) { if !f.yesNoQuestion("Do you really want remove all accounts") { return } + for _, user := range f.bridge.GetUsers() { if err := f.bridge.DeleteUser(user.ID(), false); err != nil { f.printAndLogError("Cannot delete account ", user.Username(), ": ", err) } } + c.Println("Keychain cleared") } +func (f *frontendCLI) deleteEverything(c *ishell.Context) { + f.ShowPrompt(false) + defer f.ShowPrompt(true) + + if !f.yesNoQuestion("Do you really want remove everything") { + return + } + + f.bridge.FactoryReset() + + c.Println("Everything cleared") + + // Clearing data removes everything (db, preferences, ...) so everything has to be stopped and started again. + f.restarter.SetToRestart() + + f.Stop() +} + func (f *frontendCLI) changeMode(c *ishell.Context) { user := f.askUserByIndexOrName(c) if user == nil { diff --git a/internal/frontend/cli/frontend.go b/internal/frontend/cli/frontend.go index a3e7ff01..ea7899ef 100644 --- a/internal/frontend/cli/frontend.go +++ b/internal/frontend/cli/frontend.go @@ -84,6 +84,11 @@ func New( //nolint[funlen] Aliases: []string{"a", "k", "keychain"}, Func: fe.deleteAccounts, }) + clearCmd.AddCmd(&ishell.Cmd{Name: "everything", + Help: "remove everything", + Aliases: []string{"a", "k", "keychain"}, + Func: fe.deleteEverything, + }) fe.AddCmd(clearCmd) // Change commands. diff --git a/internal/frontend/cli/system.go b/internal/frontend/cli/system.go index f601d1a5..0ec95db3 100644 --- a/internal/frontend/cli/system.go +++ b/internal/frontend/cli/system.go @@ -58,14 +58,17 @@ func (f *frontendCLI) deleteCache(c *ishell.Context) { if !f.yesNoQuestion("Do you really want to remove all stored preferences") { return } + if err := f.bridge.ClearData(); err != nil { f.printAndLogError("Cache clear failed: ", err.Error()) return } + f.Println("Cached cleared, restarting bridge") - // Clearing data removes everything (db, preferences, ...) - // so everything has to be stopped and started again. + + // Clearing data removes everything (db, preferences, ...) so everything has to be stopped and started again. f.restarter.SetToRestart() + f.Stop() } diff --git a/internal/frontend/types/types.go b/internal/frontend/types/types.go index 6b546f53..0552bcee 100644 --- a/internal/frontend/types/types.go +++ b/internal/frontend/types/types.go @@ -53,6 +53,8 @@ type UserManager interface { GetUser(query string) (User, error) DeleteUser(userID string, clearCache bool) error ClearData() error + ClearUsers() error + FactoryReset() } // User is an interface of user needed by frontend. diff --git a/internal/locations/locations.go b/internal/locations/locations.go index f9c37cbf..e8f3b6b0 100644 --- a/internal/locations/locations.go +++ b/internal/locations/locations.go @@ -73,28 +73,20 @@ func (l *Locations) getLicenseFilePath() string { switch runtime.GOOS { case "linux": - appName := l.configName - if l.configName == "importExport" { - appName = "import-export" - } // Most Linux distributions. - path := "/usr/share/doc/protonmail/" + appName + "/LICENSE" + path := "/usr/share/doc/protonmail/" + l.configName + "/LICENSE" if _, err := os.Stat(path); err == nil { return path } // Arch distributions. - return "/usr/share/licenses/protonmail-" + appName + "/LICENSE" + return "/usr/share/licenses/protonmail-" + l.configName + "/LICENSE" case "darwin": //nolint[goconst] path := filepath.Join(filepath.Dir(os.Args[0]), "..", "Resources", "LICENSE") if _, err := os.Stat(path); err == nil { return path } - appName := "ProtonMail Bridge.app" - if l.configName == "importExport" { - appName = "ProtonMail Import-Export.app" - } - return "/Applications/" + appName + "/Contents/Resources/LICENSE" + return "/Applications/ProtonMail Bridge.app/Contents/Resources/LICENSE" case "windows": path := filepath.Join(filepath.Dir(os.Args[0]), "LICENSE.txt") if _, err := os.Stat(path); err == nil { @@ -205,10 +197,10 @@ func (l *Locations) getUpdatesPath() string { // Clear removes everything except the lock and update files. func (l *Locations) Clear() error { return files.Remove( - l.getSettingsPath(), - l.getLogsPath(), - l.getCachePath(), + l.userConfig, + l.userCache, ).Except( + l.GetLockFile(), l.getUpdatesPath(), ).Do() } diff --git a/internal/users/users.go b/internal/users/users.go index f7e211fd..87702a1a 100644 --- a/internal/users/users.go +++ b/internal/users/users.go @@ -331,6 +331,7 @@ func (u *Users) ClearData() error { if err := user.Logout(); err != nil { result = multierror.Append(result, err) } + if err := user.closeStore(); err != nil { result = multierror.Append(result, err) } @@ -340,8 +341,7 @@ func (u *Users) ClearData() error { result = multierror.Append(result, err) } - // Need to clear imap cache otherwise fetch response will be remembered - // from previous test + // Need to clear imap cache otherwise fetch response will be remembered from previous test. imapcache.Clear() return result @@ -385,6 +385,19 @@ func (u *Users) DeleteUser(userID string, clearStore bool) error { return errors.New("user " + userID + " not found") } +// ClearUsers deletes all users. +func (u *Users) ClearUsers() error { + var result error + + for _, user := range u.GetUsers() { + if err := u.DeleteUser(user.ID(), false); err != nil { + result = multierror.Append(result, err) + } + } + + return result +} + // SendMetric sends a metric. We don't want to return any errors, only log them. func (u *Users) SendMetric(m metrics.Metric) error { cat, act, lab := m.Get() diff --git a/test/features/no_internet.feature b/test/features/no_internet.feature deleted file mode 100644 index 5abb1841..00000000 --- a/test/features/no_internet.feature +++ /dev/null @@ -1,30 +0,0 @@ -Feature: Servers are closed when no internet - - Scenario: All connection are closed and then restored multiple times - Given there is connected user "user" - And there is IMAP client "i1" logged in as "user" - And there is SMTP client "s1" logged in as "user" - When there is no internet connection - And 1 second pass - Then IMAP client "i1" is logged out - And SMTP client "s1" is logged out - Given the internet connection is restored - And 1 second pass - And there is IMAP client "i2" logged in as "user" - And there is SMTP client "s2" logged in as "user" - When IMAP client "i2" gets info of "INBOX" - When SMTP client "s2" sends "HELO example.com" - Then IMAP response to "i2" is "OK" - Then SMTP response to "s2" is "OK" - When there is no internet connection - And 1 second pass - Then IMAP client "i2" is logged out - And SMTP client "s2" is logged out - Given the internet connection is restored - And 1 second pass - And there is IMAP client "i3" logged in as "user" - And there is SMTP client "s3" logged in as "user" - When IMAP client "i3" gets info of "INBOX" - When SMTP client "s3" sends "HELO example.com" - Then IMAP response to "i3" is "OK" - Then SMTP response to "s3" is "OK" diff --git a/test/liveapi/persistent_clients.go b/test/liveapi/persistent_clients.go index bb6e4350..0efa9ff4 100644 --- a/test/liveapi/persistent_clients.go +++ b/test/liveapi/persistent_clients.go @@ -86,20 +86,13 @@ func (pc *persistentClient) GetEvent(ctx context.Context, eventID string) (*pmap func SetupPersistentClients() { app := os.Getenv("TEST_APP") - persistentClients.manager = pmapi.New(pmapi.NewConfig(getAppVersionName(app), constants.Version)) + persistentClients.manager = pmapi.New(pmapi.NewConfig(app, constants.Version)) persistentClients.manager.SetLogging(logrus.WithField("pkg", "liveapi"), logrus.GetLevel() == logrus.TraceLevel) persistentClients.byName = map[string]clientAuthGetter{} persistentClients.saltByName = map[string]string{} } -func getAppVersionName(app string) string { - if app == "ie" { - return "importExport" - } - return app -} - func CleanupPersistentClients() { for username, client := range persistentClients.byName { if err := client.AuthDelete(context.Background()); err != nil {