diff --git a/cmd/Desktop-Bridge/main.go b/cmd/Desktop-Bridge/main.go index 04decf45..7673f382 100644 --- a/cmd/Desktop-Bridge/main.go +++ b/cmd/Desktop-Bridge/main.go @@ -35,9 +35,7 @@ package main */ import ( - "math/rand" "os" - "time" "github.com/ProtonMail/proton-bridge/internal/app/base" "github.com/ProtonMail/proton-bridge/internal/app/bridge" @@ -54,14 +52,6 @@ const ( ) func main() { - rand.Seed(time.Now().UnixNano()) - - if err := base.MigrateFiles(configName); err != nil { - logrus.WithError(err).Warn("Old config files could not be migrated") - } - - os.Args = base.StripProcessSerialNumber(os.Args) - base, err := base.New( appName, appUsage, diff --git a/cmd/Import-Export/main.go b/cmd/Import-Export/main.go index 324ed53c..868c55b1 100644 --- a/cmd/Import-Export/main.go +++ b/cmd/Import-Export/main.go @@ -18,9 +18,7 @@ package main import ( - "math/rand" "os" - "time" "github.com/ProtonMail/proton-bridge/internal/app/base" "github.com/ProtonMail/proton-bridge/internal/app/ie" @@ -37,14 +35,6 @@ const ( ) func main() { - rand.Seed(time.Now().UnixNano()) - - if err := base.MigrateFiles(configName); err != nil { - logrus.WithError(err).Warn("Old config files could not be migrated") - } - - os.Args = base.StripProcessSerialNumber(os.Args) - base, err := base.New( appName, appUsage, diff --git a/go.sum b/go.sum index 0a8ebfe7..4edeb92d 100644 --- a/go.sum +++ b/go.sum @@ -344,6 +344,7 @@ golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/app/base/base.go b/internal/app/base/base.go index 53ec56bb..09724d51 100644 --- a/internal/app/base/base.go +++ b/internal/app/base/base.go @@ -29,10 +29,12 @@ package base import ( + "math/rand" "os" "path/filepath" "runtime" "runtime/pprof" + "time" "github.com/Masterminds/semver/v3" "github.com/ProtonMail/go-autostart" @@ -91,22 +93,21 @@ func New( // nolint[funlen] cacheVersion string, ) (*Base, error) { sentryReporter := sentry.NewReporter(appName, constants.Version) - crashHandler := crash.NewHandler( sentryReporter.ReportException, crash.ShowErrorNotification(appName), ) defer crashHandler.HandlePanic() + rand.Seed(time.Now().UnixNano()) + os.Args = StripProcessSerialNumber(os.Args) + locationsProvider, err := locations.NewDefaultProvider(filepath.Join(constants.VendorName, configName)) if err != nil { return nil, err } locations := locations.New(locationsProvider, configName) - if err := locations.Clean(); err != nil { - return nil, err - } logsPath, err := locations.ProvideLogsPath() if err != nil { @@ -117,6 +118,14 @@ func New( // nolint[funlen] } crashHandler.AddRecoveryAction(logging.DumpStackTrace(logsPath)) + if err := migrateFiles(configName); err != nil { + logrus.WithError(err).Warn("Old config files could not be migrated") + } + + if err := locations.Clean(); err != nil { + return nil, err + } + settingsPath, err := locations.ProvideSettingsPath() if err != nil { return nil, err diff --git a/internal/app/base/migration.go b/internal/app/base/migration.go index e507781e..7280d594 100644 --- a/internal/app/base/migration.go +++ b/internal/app/base/migration.go @@ -26,14 +26,14 @@ import ( "github.com/sirupsen/logrus" ) -// MigrateFiles migrates files from their old (pre-refactor) locations to their new locations. +// migrateFiles migrates files from their old (pre-refactor) locations to their new locations. // We can remove this eventually. // // | entity | old location | new location | // |--------|-------------------------------------------|----------------------------------------| // | prefs | ~/.cache/protonmail//c11/prefs.json | ~/.config/protonmail//prefs.json | // | c11 | ~/.cache/protonmail//c11 | ~/.cache/protonmail//cache/c11 | -func MigrateFiles(configName string) error { +func migrateFiles(configName string) error { locationsProvider, err := locations.NewDefaultProvider(filepath.Join(constants.VendorName, configName)) if err != nil { return err diff --git a/internal/app/bridge/bridge.go b/internal/app/bridge/bridge.go index c3b23acc..0d0ed855 100644 --- a/internal/app/bridge/bridge.go +++ b/internal/app/bridge/bridge.go @@ -188,6 +188,8 @@ func checkAndHandleUpdate(u types.Updater, f frontend.Frontend, autoUpdate bool) return } + f.WaitUntilFrontendIsReady() + // Update links in UI f.SetVersion(version) diff --git a/internal/app/ie/ie.go b/internal/app/ie/ie.go index 171f6629..47494d27 100644 --- a/internal/app/ie/ie.go +++ b/internal/app/ie/ie.go @@ -95,6 +95,8 @@ func checkAndHandleUpdate(u types.Updater, f frontend.Frontend, autoUpdate bool) return } + f.WaitUntilFrontendIsReady() + // Update links in UI f.SetVersion(version) diff --git a/internal/frontend/cli-ie/frontend.go b/internal/frontend/cli-ie/frontend.go index 3ee50e40..90e13c37 100644 --- a/internal/frontend/cli-ie/frontend.go +++ b/internal/frontend/cli-ie/frontend.go @@ -230,6 +230,7 @@ func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo, canInstall // NOTE: Save the update somewhere so that it can be installed when user chooses "install now". } +func (f *frontendCLI) WaitUntilFrontendIsReady() {} func (f *frontendCLI) SetVersion(version updater.VersionInfo) {} func (f *frontendCLI) NotifySilentUpdateInstalled() {} func (f *frontendCLI) NotifySilentUpdateError(err error) {} diff --git a/internal/frontend/cli/frontend.go b/internal/frontend/cli/frontend.go index 5e5cf2e5..a0b782ee 100644 --- a/internal/frontend/cli/frontend.go +++ b/internal/frontend/cli/frontend.go @@ -257,6 +257,7 @@ func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo, canInstall // NOTE: Save the update somewhere so that it can be installed when user chooses "install now". } +func (f *frontendCLI) WaitUntilFrontendIsReady() {} func (f *frontendCLI) SetVersion(version updater.VersionInfo) {} func (f *frontendCLI) NotifySilentUpdateInstalled() {} func (f *frontendCLI) NotifySilentUpdateError(err error) {} diff --git a/internal/frontend/frontend.go b/internal/frontend/frontend.go index 23511ea7..85c21aa3 100644 --- a/internal/frontend/frontend.go +++ b/internal/frontend/frontend.go @@ -45,6 +45,7 @@ type Frontend interface { SetVersion(update updater.VersionInfo) NotifySilentUpdateInstalled() NotifySilentUpdateError(error) + WaitUntilFrontendIsReady() } // New returns initialized frontend based on `frontendType`, which can be `cli` or `qt`. diff --git a/internal/frontend/qml/Gui.qml b/internal/frontend/qml/Gui.qml index 17f47752..64f9675c 100644 --- a/internal/frontend/qml/Gui.qml +++ b/internal/frontend/qml/Gui.qml @@ -327,6 +327,8 @@ Item { go.failedAutostart = qsTr("Unable to configure automatic start." , "notification", -1) go.genericErrSeeLogs = qsTr("An error happened during procedure. See logs for more details." , "notification", -1) + go.guiIsReady() + // start window gui.openMainWindow(false) if (go.isShownOnStart) { diff --git a/internal/frontend/qml/GuiIE.qml b/internal/frontend/qml/GuiIE.qml index a35cceb2..dee84052 100644 --- a/internal/frontend/qml/GuiIE.qml +++ b/internal/frontend/qml/GuiIE.qml @@ -432,6 +432,9 @@ Item { go.bugNotSent = qsTr("Unable to submit bug report." , "notification", -1) go.bugReportSent = qsTr("Bug report successfully sent." , "notification", -1) + + go.guiIsReady() + gui.allMonths = getMonthList(1,12) gui.allMonthsChanged() } diff --git a/internal/frontend/qml/tst_Gui.qml b/internal/frontend/qml/tst_Gui.qml index b1435989..0eee3997 100644 --- a/internal/frontend/qml/tst_Gui.qml +++ b/internal/frontend/qml/tst_Gui.qml @@ -360,6 +360,7 @@ Window { signal updateFinished(bool hasError) + signal guiIsReady() signal showOutgoingNoEncPopup(string subject) signal setOutgoingNoEncPopupCoord(real x, real y) diff --git a/internal/frontend/qml/tst_GuiIE.qml b/internal/frontend/qml/tst_GuiIE.qml index e7bb8494..854084f1 100644 --- a/internal/frontend/qml/tst_GuiIE.qml +++ b/internal/frontend/qml/tst_GuiIE.qml @@ -918,6 +918,8 @@ Window { signal notifyUpdate() signal updateFinished(bool hasError) + signal guiIsReady() + signal openReleaseNotesExternally() signal notifyLogout(string accname) diff --git a/internal/frontend/qt-ie/frontend.go b/internal/frontend/qt-ie/frontend.go index e84a9c18..39b9ed8b 100644 --- a/internal/frontend/qt-ie/frontend.go +++ b/internal/frontend/qt-ie/frontend.go @@ -22,6 +22,7 @@ package qtie import ( "errors" "os" + "sync" "github.com/ProtonMail/proton-bridge/internal/config/settings" "github.com/ProtonMail/proton-bridge/internal/events" @@ -76,6 +77,9 @@ type FrontendQt struct { // saving most up-to-date update info to install it manually updateInfo updater.VersionInfo + + initializing sync.WaitGroup + initializationDone sync.Once } // New is constructor for Import-Export Qt-Go interface @@ -102,6 +106,10 @@ func New( restarter: restarter, } + // Initializing.Done is only called sync.Once. Please keep the increment + // set to 1 + f.initializing.Add(1) + log.Debugf("New Qt frontend: %p", f) return f } @@ -541,3 +549,14 @@ func (f *FrontendQt) createLabelOrFolder(email, name, color string, isLabel bool } return true } + +func (f *FrontendQt) WaitUntilFrontendIsReady() { + f.initializing.Wait() +} + +// setGUIIsReady unlocks the WaitUntilFrontendIsReady. +func (f *FrontendQt) setGUIIsReady() { + f.initializationDone.Do(func() { + f.initializing.Done() + }) +} diff --git a/internal/frontend/qt-ie/frontend_nogui.go b/internal/frontend/qt-ie/frontend_nogui.go index 0397deba..b144f8ce 100644 --- a/internal/frontend/qt-ie/frontend_nogui.go +++ b/internal/frontend/qt-ie/frontend_nogui.go @@ -50,6 +50,9 @@ func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo, canIns func (s *FrontendHeadless) SetVersion(update updater.VersionInfo) { } +func (s *FrontendHeadless) WaitUntilFrontendIsReady() { +} + func (s *FrontendHeadless) NotifySilentUpdateInstalled() { } diff --git a/internal/frontend/qt-ie/ui.go b/internal/frontend/qt-ie/ui.go index 50d5d655..b5c42d01 100644 --- a/internal/frontend/qt-ie/ui.go +++ b/internal/frontend/qt-ie/ui.go @@ -67,6 +67,7 @@ type GoQMLInterface struct { _ func() `slot:"checkAndOpenReleaseNotes"` _ func() `signal:"openReleaseNotesExternally"` _ func() `slot:"startManualUpdate"` + _ func() `slot:"guiIsReady"` // translations _ string `property:"wrongCredentials"` @@ -201,6 +202,8 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) { s.ConnectStartExport(f.StartExport) s.ConnectStartImport(f.StartImport) + s.ConnectGuiIsReady(f.setGUIIsReady) + s.ConnectCheckPathStatus(CheckPathStatus) s.ConnectEmitEvent(f.emitEvent) diff --git a/internal/frontend/qt/frontend.go b/internal/frontend/qt/frontend.go index ca9d8767..4eff24be 100644 --- a/internal/frontend/qt/frontend.go +++ b/internal/frontend/qt/frontend.go @@ -97,6 +97,9 @@ type FrontendQt struct { // saving most up-to-date update info to install it manually updateInfo updater.VersionInfo + + initializing sync.WaitGroup + initializationDone sync.Once } // New returns a new Qt frontend for the bridge. @@ -132,6 +135,10 @@ func New( restarter: restarter, } + // Initializing.Done is only called sync.Once. Please keep the increment + // set to 1 + tmp.initializing.Add(1) + // Nicer string for OS. currentOS := core.QSysInfo_PrettyProductName() bridge.SetCurrentOS(currentOS) @@ -180,6 +187,8 @@ func (s *FrontendQt) NotifySilentUpdateError(err error) { } func (s *FrontendQt) watchEvents() { + s.WaitUntilFrontendIsReady() + errorCh := s.getEventChannel(events.ErrorEvent) credentialsErrorCh := s.getEventChannel(events.CredentialsErrorEvent) outgoingNoEncCh := s.getEventChannel(events.OutgoingNoEncEvent) @@ -683,3 +692,14 @@ func (s *FrontendQt) startManualUpdate() { } }() } + +func (s *FrontendQt) WaitUntilFrontendIsReady() { + s.initializing.Wait() +} + +// setGUIIsReady unlocks the WaitFrontendIsReady. +func (s *FrontendQt) setGUIIsReady() { + s.initializationDone.Do(func() { + s.initializing.Done() + }) +} diff --git a/internal/frontend/qt/frontend_nogui.go b/internal/frontend/qt/frontend_nogui.go index 93ea4549..c3125e21 100644 --- a/internal/frontend/qt/frontend_nogui.go +++ b/internal/frontend/qt/frontend_nogui.go @@ -48,6 +48,9 @@ func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo, canIns // NOTE: Save the update somewhere so that it can be installed when user chooses "install now". } +func (s *FrontendHeadless) WaitUntilFrontendIsReady() { +} + func (s *FrontendHeadless) SetVersion(update updater.VersionInfo) { } diff --git a/internal/frontend/qt/ui.go b/internal/frontend/qt/ui.go index a71ee8a8..80fe657a 100644 --- a/internal/frontend/qt/ui.go +++ b/internal/frontend/qt/ui.go @@ -25,7 +25,7 @@ import ( "github.com/therecipe/qt/core" ) -// Interface between go and qml. +// GoQMLInterface between go and qml. // // Here we implement all the signals / methods. type GoQMLInterface struct { @@ -64,6 +64,7 @@ type GoQMLInterface struct { _ func() `slot:"checkAndOpenReleaseNotes"` _ func() `signal:"openReleaseNotesExternally"` _ func() `slot:"startManualUpdate"` + _ func() `slot:"guiIsReady"` // Translations. _ string `property:"wrongCredentials"` @@ -170,6 +171,7 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) { s.ConnectClearKeychain(f.clearKeychain) s.ConnectOpenLicenseFile(f.openLicenseFile) s.ConnectStartManualUpdate(f.startManualUpdate) + s.ConnectGuiIsReady(f.setGUIIsReady) s.ConnectGetLocalVersionInfo(f.getLocalVersionInfo) s.ConnectCheckForUpdates(f.checkForUpdates) s.ConnectGetIMAPPort(f.getIMAPPort) diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 27f97324..96796d5e 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -45,6 +45,14 @@ func Init(logsPath string) error { FullTimestamp: true, TimestampFormat: time.StampMilli, }) + logrus.AddHook(&writer.Hook{ + Writer: os.Stderr, + LogLevels: []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + }, + }) rotator, err := NewRotator(MaxLogSize, func() (io.WriteCloser, error) { if err := clearLogs(logsPath, MaxLogs); err != nil { @@ -58,16 +66,6 @@ func Init(logsPath string) error { } logrus.SetOutput(rotator) - - logrus.AddHook(&writer.Hook{ - Writer: os.Stderr, - LogLevels: []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - }, - }) - return nil } @@ -77,6 +75,9 @@ func SetLevel(level string) { } if logrus.GetLevel() == logrus.DebugLevel || logrus.GetLevel() == logrus.TraceLevel { + // The hook to print panic, fatal and error to stderr is always + // added. We want to avoid log duplicates by replacing all hooks + _ = logrus.StandardLogger().ReplaceHooks(logrus.LevelHooks{}) logrus.SetOutput(os.Stderr) } }