Launcher, app/base, sentry, update service

This commit is contained in:
James Houlahan
2020-11-23 11:56:57 +01:00
parent 6fffb460b8
commit dc3f61acee
164 changed files with 5368 additions and 4039 deletions

View File

@ -21,7 +21,8 @@ package cliie
import (
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/abiosoft/ishell"
@ -35,31 +36,33 @@ var (
type frontendCLI struct {
*ishell.Shell
config *config.Config
locations *locations.Locations
eventListener listener.Listener
updates types.Updater
updater types.Updater
ie types.ImportExporter
appRestart bool
restarter types.Restarter
}
// New returns a new CLI frontend configured with the given options.
func New( //nolint[funlen]
panicHandler types.PanicHandler,
config *config.Config,
locations *locations.Locations,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
ie types.ImportExporter,
restarter types.Restarter,
) *frontendCLI { //nolint[golint]
fe := &frontendCLI{
Shell: ishell.New(),
config: config,
locations: locations,
eventListener: eventListener,
updates: updates,
updater: updater,
ie: ie,
appRestart: false,
restarter: restarter,
}
// Clear commands.
@ -175,13 +178,12 @@ func New( //nolint[funlen]
defer panicHandler.HandlePanic()
fe.watchEvents()
}()
fe.eventListener.RetryEmit(events.TLSCertIssue)
fe.eventListener.RetryEmit(events.ErrorEvent)
return fe
}
func (f *frontendCLI) watchEvents() {
errorCh := f.getEventChannel(events.ErrorEvent)
credentialsErrorCh := f.getEventChannel(events.CredentialsErrorEvent)
internetOffCh := f.getEventChannel(events.InternetOffEvent)
internetOnCh := f.getEventChannel(events.InternetOnEvent)
addressChangedLogoutCh := f.getEventChannel(events.AddressChangedLogoutEvent)
@ -191,6 +193,8 @@ func (f *frontendCLI) watchEvents() {
select {
case errorDetails := <-errorCh:
f.Println("Import-Export failed:", errorDetails)
case <-credentialsErrorCh:
f.notifyCredentialsError()
case <-internetOffCh:
f.notifyInternetOff()
case <-internetOnCh:
@ -212,21 +216,12 @@ func (f *frontendCLI) watchEvents() {
func (f *frontendCLI) getEventChannel(event string) <-chan string {
ch := make(chan string)
f.eventListener.Add(event, ch)
f.eventListener.RetryEmit(event)
return ch
}
// IsAppRestarting returns whether the app is currently set to restart.
func (f *frontendCLI) IsAppRestarting() bool {
return f.appRestart
}
// Loop starts the frontend loop with an interactive shell.
func (f *frontendCLI) Loop(credentialsError error) error {
if credentialsError != nil {
f.notifyCredentialsError()
return credentialsError
}
func (f *frontendCLI) Loop() error {
f.Print(`
Welcome to ProtonMail Import-Export app interactive shell
@ -235,3 +230,8 @@ WARNING: The CLI is an experimental feature and does not yet cover all functiona
f.Run()
return nil
}
func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
}

View File

@ -24,7 +24,7 @@ import (
func (f *frontendCLI) restart(c *ishell.Context) {
if f.yesNoQuestion("Are you sure you want to restart the Import-Export app") {
f.Println("Restarting the Import-Export app...")
f.appRestart = true
f.restarter.SetToRestart()
f.Stop()
}
}
@ -38,7 +38,11 @@ func (f *frontendCLI) checkInternetConnection(c *ishell.Context) {
}
func (f *frontendCLI) printLogDir(c *ishell.Context) {
f.Println("Log files are stored in\n\n ", f.config.GetLogDir())
if path, err := f.locations.ProvideLogsPath(); err != nil {
f.Println("Failed to determine location of log files")
} else {
f.Println("Log files are stored in\n\n ", path)
}
}
func (f *frontendCLI) printManual(c *ishell.Context) {

View File

@ -21,41 +21,15 @@ import (
"strings"
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/updates"
"github.com/abiosoft/ishell"
)
func (f *frontendCLI) checkUpdates(c *ishell.Context) {
isUpToDate, latestVersionInfo, err := f.updates.CheckIsUpToDate()
if err != nil {
f.printAndLogError("Cannot retrieve version info: ", err)
f.checkInternetConnection(c)
return
}
if isUpToDate {
f.Println("Your version is up to date.")
} else {
f.notifyNeedUpgrade()
f.Println("")
f.printReleaseNotes(latestVersionInfo)
}
f.Println("Your version is up to date.")
}
func (f *frontendCLI) printLocalReleaseNotes(c *ishell.Context) {
localVersion := f.updates.GetLocalVersion()
f.printReleaseNotes(localVersion)
}
func (f *frontendCLI) printReleaseNotes(versionInfo updates.VersionInfo) {
f.Println(bold("ProtonMail Import-Export app "+versionInfo.Version), "\n")
if versionInfo.ReleaseNotes != "" {
f.Println(bold("Release Notes"))
f.Println(versionInfo.ReleaseNotes)
}
if versionInfo.ReleaseFixedBugs != "" {
f.Println(bold("Fixed bugs"))
f.Println(versionInfo.ReleaseFixedBugs)
}
f.Println("TODO")
}
func (f *frontendCLI) printCredits(c *ishell.Context) {

View File

@ -93,10 +93,10 @@ func (f *frontendCLI) notifyLogout(address string) {
}
func (f *frontendCLI) notifyNeedUpgrade() {
f.Println("Please download and install the newest version of application from", f.updates.GetDownloadLink())
f.Println("TODO")
}
func (f *frontendCLI) notifyCredentialsError() {
func (f *frontendCLI) notifyCredentialsError() { // nolint[unused]
// Print in 80-column width.
f.Println("ProtonMail Import-Export app is not able to detect a supported password manager")
f.Println("(pass, gnome-keyring). Please install and set up a supported password manager")

View File

@ -21,8 +21,8 @@ import (
"strings"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/preferences"
"github.com/abiosoft/ishell"
)
@ -65,13 +65,13 @@ func (f *frontendCLI) showAccountInfo(c *ishell.Context) {
func (f *frontendCLI) showAccountAddressInfo(user types.User, address string) {
smtpSecurity := "STARTTLS"
if f.preferences.GetBool(preferences.SMTPSSLKey) {
if f.settings.GetBool(settings.SMTPSSLKey) {
smtpSecurity = "SSL"
}
f.Println(bold("Configuration for " + address))
f.Printf("IMAP Settings\nAddress: %s\nIMAP port: %d\nUsername: %s\nPassword: %s\nSecurity: %s\n",
bridge.Host,
f.preferences.GetInt(preferences.IMAPPortKey),
f.settings.GetInt(settings.IMAPPortKey),
address,
user.GetBridgePassword(),
"STARTTLS",
@ -79,7 +79,7 @@ func (f *frontendCLI) showAccountAddressInfo(user types.User, address string) {
f.Println("")
f.Printf("SMTP Settings\nAddress: %s\nSMTP port: %d\nUsername: %s\nPassword: %s\nSecurity: %s\n",
bridge.Host,
f.preferences.GetInt(preferences.SMTPPortKey),
f.settings.GetInt(settings.SMTPPortKey),
address,
user.GetBridgePassword(),
smtpSecurity,

View File

@ -19,9 +19,11 @@
package cli
import (
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/abiosoft/ishell"
@ -35,34 +37,36 @@ var (
type frontendCLI struct {
*ishell.Shell
config *config.Config
preferences *config.Preferences
locations *locations.Locations
settings *settings.Settings
eventListener listener.Listener
updates types.Updater
updater types.Updater
bridge types.Bridger
appRestart bool
restarter types.Restarter
}
// New returns a new CLI frontend configured with the given options.
func New( //nolint[funlen]
panicHandler types.PanicHandler,
config *config.Config,
preferences *config.Preferences,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
bridge types.Bridger,
restarter types.Restarter,
) *frontendCLI { //nolint[golint]
fe := &frontendCLI{
Shell: ishell.New(),
config: config,
preferences: preferences,
locations: locations,
settings: settings,
eventListener: eventListener,
updates: updates,
updater: updater,
bridge: bridge,
appRestart: false,
restarter: restarter,
}
// Clear commands.
@ -185,13 +189,12 @@ func New( //nolint[funlen]
defer panicHandler.HandlePanic()
fe.watchEvents()
}()
fe.eventListener.RetryEmit(events.TLSCertIssue)
fe.eventListener.RetryEmit(events.ErrorEvent)
return fe
}
func (f *frontendCLI) watchEvents() {
errorCh := f.getEventChannel(events.ErrorEvent)
credentialsErrorCh := f.getEventChannel(events.CredentialsErrorEvent)
internetOffCh := f.getEventChannel(events.InternetOffEvent)
internetOnCh := f.getEventChannel(events.InternetOnEvent)
addressChangedCh := f.getEventChannel(events.AddressChangedEvent)
@ -202,6 +205,8 @@ func (f *frontendCLI) watchEvents() {
select {
case errorDetails := <-errorCh:
f.Println("Bridge failed:", errorDetails)
case <-credentialsErrorCh:
f.notifyCredentialsError()
case <-internetOffCh:
f.notifyInternetOff()
case <-internetOnCh:
@ -225,21 +230,12 @@ func (f *frontendCLI) watchEvents() {
func (f *frontendCLI) getEventChannel(event string) <-chan string {
ch := make(chan string)
f.eventListener.Add(event, ch)
f.eventListener.RetryEmit(event)
return ch
}
// IsAppRestarting returns whether the app is currently set to restart.
func (f *frontendCLI) IsAppRestarting() bool {
return f.appRestart
}
// Loop starts the frontend loop with an interactive shell.
func (f *frontendCLI) Loop(credentialsError error) error {
if credentialsError != nil {
f.notifyCredentialsError()
return credentialsError
}
func (f *frontendCLI) Loop() error {
f.Print(`
Welcome to ProtonMail Bridge interactive shell
___....___
@ -260,3 +256,8 @@ func (f *frontendCLI) Loop(credentialsError error) error {
f.Run()
return nil
}
func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
}

View File

@ -22,7 +22,7 @@ import (
"strconv"
"strings"
"github.com/ProtonMail/proton-bridge/internal/preferences"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/pkg/ports"
"github.com/abiosoft/ishell"
)
@ -34,7 +34,7 @@ var (
func (f *frontendCLI) restart(c *ishell.Context) {
if f.yesNoQuestion("Are you sure you want to restart the Bridge") {
f.Println("Restarting Bridge...")
f.appRestart = true
f.restarter.SetToRestart()
f.Stop()
}
}
@ -48,7 +48,11 @@ func (f *frontendCLI) checkInternetConnection(c *ishell.Context) {
}
func (f *frontendCLI) printLogDir(c *ishell.Context) {
f.Println("Log files are stored in\n\n ", f.config.GetLogDir())
if path, err := f.locations.ProvideLogsPath(); err != nil {
f.Println("Failed to determine location of log files")
} else {
f.Println("Log files are stored in\n\n ", path)
}
}
func (f *frontendCLI) printManual(c *ishell.Context) {
@ -69,7 +73,7 @@ func (f *frontendCLI) deleteCache(c *ishell.Context) {
f.Println("Cached cleared, restarting bridge")
// Clearing data removes everything (db, preferences, ...)
// so everything has to be stopped and started again.
f.appRestart = true
f.restarter.SetToRestart()
f.Stop()
}
@ -77,7 +81,7 @@ func (f *frontendCLI) changeSMTPSecurity(c *ishell.Context) {
f.ShowPrompt(false)
defer f.ShowPrompt(true)
isSSL := f.preferences.GetBool(preferences.SMTPSSLKey)
isSSL := f.settings.GetBool(settings.SMTPSSLKey)
newSecurity := "SSL"
if isSSL {
newSecurity = "STARTTLS"
@ -86,9 +90,9 @@ func (f *frontendCLI) changeSMTPSecurity(c *ishell.Context) {
msg := fmt.Sprintf("Are you sure you want to change SMTP setting to %q and restart the Bridge", newSecurity)
if f.yesNoQuestion(msg) {
f.preferences.SetBool(preferences.SMTPSSLKey, !isSSL)
f.settings.SetBool(settings.SMTPSSLKey, !isSSL)
f.Println("Restarting Bridge...")
f.appRestart = true
f.restarter.SetToRestart()
f.Stop()
}
}
@ -97,14 +101,14 @@ func (f *frontendCLI) changePort(c *ishell.Context) {
f.ShowPrompt(false)
defer f.ShowPrompt(true)
currentPort = f.preferences.Get(preferences.IMAPPortKey)
currentPort = f.settings.Get(settings.IMAPPortKey)
newIMAPPort := f.readStringInAttempts("Set IMAP port (current "+currentPort+")", c.ReadLine, f.isPortFree)
if newIMAPPort == "" {
newIMAPPort = currentPort
}
imapPortChanged := newIMAPPort != currentPort
currentPort = f.preferences.Get(preferences.SMTPPortKey)
currentPort = f.settings.Get(settings.SMTPPortKey)
newSMTPPort := f.readStringInAttempts("Set SMTP port (current "+currentPort+")", c.ReadLine, f.isPortFree)
if newSMTPPort == "" {
newSMTPPort = currentPort
@ -118,10 +122,10 @@ func (f *frontendCLI) changePort(c *ishell.Context) {
if imapPortChanged || smtpPortChanged {
f.Println("Saving values IMAP:", newIMAPPort, "SMTP:", newSMTPPort)
f.preferences.Set(preferences.IMAPPortKey, newIMAPPort)
f.preferences.Set(preferences.SMTPPortKey, newSMTPPort)
f.settings.Set(settings.IMAPPortKey, newIMAPPort)
f.settings.Set(settings.SMTPPortKey, newSMTPPort)
f.Println("Restarting Bridge...")
f.appRestart = true
f.restarter.SetToRestart()
f.Stop()
} else {
f.Println("Nothing changed")
@ -129,16 +133,16 @@ func (f *frontendCLI) changePort(c *ishell.Context) {
}
func (f *frontendCLI) toggleAllowProxy(c *ishell.Context) {
if f.preferences.GetBool(preferences.AllowProxyKey) {
if f.settings.GetBool(settings.AllowProxyKey) {
f.Println("Bridge is currently set to use alternative routing to connect to Proton if it is being blocked.")
if f.yesNoQuestion("Are you sure you want to stop bridge from doing this") {
f.preferences.SetBool(preferences.AllowProxyKey, false)
f.settings.SetBool(settings.AllowProxyKey, false)
f.bridge.DisallowProxy()
}
} else {
f.Println("Bridge is currently set to NOT use alternative routing to connect to Proton if it is being blocked.")
if f.yesNoQuestion("Are you sure you want to allow bridge to do this") {
f.preferences.SetBool(preferences.AllowProxyKey, true)
f.settings.SetBool(settings.AllowProxyKey, true)
f.bridge.AllowProxy()
}
}

View File

@ -21,41 +21,15 @@ import (
"strings"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/updates"
"github.com/abiosoft/ishell"
)
func (f *frontendCLI) checkUpdates(c *ishell.Context) {
isUpToDate, latestVersionInfo, err := f.updates.CheckIsUpToDate()
if err != nil {
f.printAndLogError("Cannot retrieve version info: ", err)
f.checkInternetConnection(c)
return
}
if isUpToDate {
f.Println("Your version is up to date.")
} else {
f.notifyNeedUpgrade()
f.Println("")
f.printReleaseNotes(latestVersionInfo)
}
f.Println("Your version is up to date.")
}
func (f *frontendCLI) printLocalReleaseNotes(c *ishell.Context) {
localVersion := f.updates.GetLocalVersion()
f.printReleaseNotes(localVersion)
}
func (f *frontendCLI) printReleaseNotes(versionInfo updates.VersionInfo) {
f.Println(bold("ProtonMail Bridge "+versionInfo.Version), "\n")
if versionInfo.ReleaseNotes != "" {
f.Println(bold("Release Notes"))
f.Println(versionInfo.ReleaseNotes)
}
if versionInfo.ReleaseFixedBugs != "" {
f.Println(bold("Fixed bugs"))
f.Println(versionInfo.ReleaseFixedBugs)
}
f.Println("TODO")
}
func (f *frontendCLI) printCredits(c *ishell.Context) {

View File

@ -93,10 +93,10 @@ func (f *frontendCLI) notifyLogout(address string) {
}
func (f *frontendCLI) notifyNeedUpgrade() {
f.Println("Please download and install the newest version of application from", f.updates.GetDownloadLink())
f.Println("TODO")
}
func (f *frontendCLI) notifyCredentialsError() {
func (f *frontendCLI) notifyCredentialsError() { // nolint[unused]
// Print in 80-column width.
f.Println("ProtonMail Bridge is not able to detect a supported password manager")
f.Println("(pass, gnome-keyring). Please install and set up a supported password manager")

View File

@ -19,15 +19,16 @@
package frontend
import (
"github.com/0xAX/notificator"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/frontend/cli"
cliie "github.com/ProtonMail/proton-bridge/internal/frontend/cli-ie"
"github.com/ProtonMail/proton-bridge/internal/frontend/qt"
qtie "github.com/ProtonMail/proton-bridge/internal/frontend/qt-ie"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/sirupsen/logrus"
)
@ -38,17 +39,8 @@ var (
// Frontend is an interface to be implemented by each frontend type (cli, gui, html).
type Frontend interface {
Loop(credentialsError error) error
IsAppRestarting() bool
}
// HandlePanic handles panics which occur for users with GUI.
func HandlePanic(appName string) {
notify := notificator.New(notificator.Options{
DefaultIcon: "../frontend/ui/icon/icon.png",
AppName: appName,
})
_ = notify.Push("Fatal Error", "The "+appName+" has encountered a fatal error. ", "/frontend/icon/icon.png", notificator.UR_CRITICAL)
Loop() error
NotifyManualUpdate(update updater.VersionInfo) error
}
// New returns initialized frontend based on `frontendType`, which can be `cli` or `qt`.
@ -58,35 +50,70 @@ func New(
frontendType string,
showWindowOnStart bool,
panicHandler types.PanicHandler,
config *config.Config,
preferences *config.Preferences,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
bridge *bridge.Bridge,
noEncConfirmator types.NoEncConfirmator,
restarter types.Restarter,
) Frontend {
bridgeWrap := types.NewBridgeWrap(bridge)
return new(version, buildVersion, frontendType, showWindowOnStart, panicHandler, config, preferences, eventListener, updates, bridgeWrap, noEncConfirmator)
return newBridgeFrontend(
version,
buildVersion,
frontendType,
showWindowOnStart,
panicHandler,
locations,
settings,
eventListener,
updater,
bridgeWrap,
noEncConfirmator,
restarter,
)
}
func new(
func newBridgeFrontend(
version,
buildVersion,
frontendType string,
showWindowOnStart bool,
panicHandler types.PanicHandler,
config *config.Config,
preferences *config.Preferences,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
bridge types.Bridger,
noEncConfirmator types.NoEncConfirmator,
restarter types.Restarter,
) Frontend {
switch frontendType {
case "cli":
return cli.New(panicHandler, config, preferences, eventListener, updates, bridge)
return cli.New(
panicHandler,
locations,
settings,
eventListener,
updater,
bridge,
restarter,
)
default:
return qt.New(version, buildVersion, showWindowOnStart, panicHandler, config, preferences, eventListener, updates, bridge, noEncConfirmator)
return qt.New(
version,
buildVersion,
showWindowOnStart,
panicHandler,
locations,
settings,
eventListener,
updater,
bridge,
noEncConfirmator,
restarter,
)
}
}
@ -96,29 +123,43 @@ func NewImportExport(
buildVersion,
frontendType string,
panicHandler types.PanicHandler,
config *config.Config,
locations *locations.Locations,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
ie *importexport.ImportExport,
restarter types.Restarter,
) Frontend {
ieWrap := types.NewImportExportWrap(ie)
return newImportExport(version, buildVersion, frontendType, panicHandler, config, eventListener, updates, ieWrap)
return newIEFrontend(
version,
buildVersion,
frontendType,
panicHandler,
locations,
eventListener,
updater,
ieWrap,
restarter,
)
}
func newImportExport(
func newIEFrontend(
version,
buildVersion,
frontendType string,
panicHandler types.PanicHandler,
config *config.Config,
locations *locations.Locations,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
ie types.ImportExporter,
restarter types.Restarter,
) Frontend {
switch frontendType {
case "cli":
return cliie.New(panicHandler, config, eventListener, updates, ie)
return cliie.New(panicHandler, locations, eventListener, updater, ie, restarter)
default:
return qtie.New(version, buildVersion, panicHandler, config, eventListener, updates, ie)
return qtie.New(version, buildVersion, panicHandler, locations, eventListener, updater, ie, restarter)
}
}

View File

@ -226,7 +226,7 @@ Dialog {
target: timer
onTriggered: {
go.setPortsAndSecurity(imapPort.text, smtpPort.text, securitySMTPSTARTTLS.checked)
go.isRestarting = true
go.setToRestart()
Qt.quit()
}
}

View File

@ -69,20 +69,9 @@ Item {
Connections {
target: go
onShowWindow : {
winMain.showAndRise()
}
onProcessFinished : {
winMain.dialogAddUser.hide()

View File

@ -566,7 +566,6 @@ Window {
return 0
}
property bool isRestarting: false
function setPortsAndSecurity(portIMAP, portSMTP, secSMTP) {
console.log("Test: ports changed", portIMAP, portSMTP, secSMTP)
}

View File

@ -25,10 +25,9 @@ import (
"sync"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/preferences"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/pkg/keychain"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
)
@ -38,7 +37,6 @@ type QMLer interface {
ProcessFinished()
NotifyHasNoKeychain()
SetConnectionStatus(bool)
SetIsRestarting(bool)
SetAddAccountWarning(string, int)
NotifyBubble(int, string)
EmitEvent(string, string)
@ -50,23 +48,25 @@ type QMLer interface {
// Accounts holds functionality of users
type Accounts struct {
Model *AccountsModel
qml QMLer
um types.UserManager
prefs *config.Preferences
Model *AccountsModel
qml QMLer
um types.UserManager
settings *settings.Settings
authClient pmapi.Client
auth *pmapi.Auth
LatestUserID string
accountMutex sync.Mutex
restarter types.Restarter
}
// SetupAccounts will create Model and set QMLer and UserManager
func (a *Accounts) SetupAccounts(qml QMLer, um types.UserManager) {
func (a *Accounts) SetupAccounts(qml QMLer, um types.UserManager, restarter types.Restarter) {
a.Model = NewAccountsModel(nil)
a.qml = qml
a.um = um
a.restarter = restarter
}
// LoadAccounts refreshes the current account list in GUI
@ -102,9 +102,9 @@ func (a *Accounts) LoadAccounts() {
accInfo.SetUserID(user.ID())
accInfo.SetHostname(bridge.Host)
accInfo.SetPassword(user.GetBridgePassword())
if a.prefs != nil {
accInfo.SetPortIMAP(a.prefs.GetInt(preferences.IMAPPortKey))
accInfo.SetPortSMTP(a.prefs.GetInt(preferences.SMTPPortKey))
if a.settings != nil {
accInfo.SetPortIMAP(a.settings.GetInt(settings.IMAPPortKey))
accInfo.SetPortSMTP(a.settings.GetInt(settings.SMTPPortKey))
}
// Set aliases.
@ -127,7 +127,7 @@ func (a *Accounts) ClearCache() {
}
// Clearing data removes everything (db, preferences, ...)
// so everything has to be stopped and started again.
a.qml.SetIsRestarting(true)
a.restarter.SetToRestart()
a.qml.Quit()
}

View File

@ -111,10 +111,12 @@ func WaitForEnter() {
type Listener interface {
Add(string, chan<- string)
RetryEmit(string)
}
func MakeAndRegisterEvent(eventListener Listener, event string) <-chan string {
ch := make(chan string)
eventListener.Add(event, ch)
eventListener.RetryEmit(event)
return ch
}

View File

@ -22,16 +22,14 @@ package qtie
import (
"errors"
"os"
"strconv"
"time"
"github.com/ProtonMail/proton-bridge/internal/events"
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/ProtonMail/proton-bridge/internal/updates"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/therecipe/qt/core"
@ -51,9 +49,9 @@ var log = logrus.WithField("pkg", "frontend-qt-ie")
// Qt and QML objects. QML signals and slots are connected via methods of GoQMLInterface.
type FrontendQt struct {
panicHandler types.PanicHandler
config *config.Config
locations *locations.Locations
eventListener listener.Listener
updates types.Updater
updater types.Updater
ie types.ImportExporter
App *widgets.QApplication // Main Application pointer
@ -72,44 +70,39 @@ type FrontendQt struct {
transfer *transfer.Transfer
progress *transfer.Progress
notifyHasNoKeychain bool
restarter types.Restarter
}
// New is constructor for Import-Export Qt-Go interface
func New(
version, buildVersion string,
panicHandler types.PanicHandler,
config *config.Config,
locations *locations.Locations,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
ie types.ImportExporter,
restarter types.Restarter,
) *FrontendQt {
f := &FrontendQt{
panicHandler: panicHandler,
config: config,
locations: locations,
programName: "ProtonMail Import-Export",
programVersion: "v" + version,
eventListener: eventListener,
updater: updater,
buildVersion: buildVersion,
updates: updates,
ie: ie,
restarter: restarter,
}
log.Debugf("New Qt frontend: %p", f)
return f
}
// IsAppRestarting for Import-Export is always false i.e never restarts
func (f *FrontendQt) IsAppRestarting() bool {
return false
}
// Loop function for Import-Export interface. It runs QtExecute in main thread
// with no additional function.
func (f *FrontendQt) Loop(setupError error) (err error) {
if setupError != nil {
f.notifyHasNoKeychain = true
}
func (f *FrontendQt) Loop() (err error) {
go func() {
defer f.panicHandler.HandlePanic()
f.watchEvents()
@ -118,9 +111,16 @@ func (f *FrontendQt) Loop(setupError error) (err error) {
return err
}
func (f *FrontendQt) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
}
func (f *FrontendQt) watchEvents() {
credentialsErrorCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.CredentialsErrorEvent)
internetOffCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.InternetOffEvent)
internetOnCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.InternetOnEvent)
secondInstanceCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.SecondInstanceEvent)
restartBridgeCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.RestartBridgeEvent)
addressChangedCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.AddressChangedEvent)
addressChangedLogoutCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.AddressChangedLogoutEvent)
@ -129,12 +129,16 @@ func (f *FrontendQt) watchEvents() {
newUserCh := qtcommon.MakeAndRegisterEvent(f.eventListener, events.UserRefreshEvent)
for {
select {
case <-credentialsErrorCh:
f.Qml.NotifyHasNoKeychain()
case <-internetOffCh:
f.Qml.SetConnectionStatus(false)
case <-internetOnCh:
f.Qml.SetConnectionStatus(true)
case <-secondInstanceCh:
f.Qml.ShowWindow()
case <-restartBridgeCh:
f.Qml.SetIsRestarting(true)
f.restarter.SetToRestart()
f.App.Quit()
case address := <-addressChangedCh:
f.Qml.NotifyAddressChanged(address)
@ -165,7 +169,7 @@ func (f *FrontendQt) qtSetupQmlAndStructures() {
f.View.RootContext().SetContextProperty("go", f.Qml)
// Add AccountsModel
f.Accounts.SetupAccounts(f.Qml, f.ie)
f.Accounts.SetupAccounts(f.Qml, f.ie, f.restarter)
f.View.RootContext().SetContextProperty("accountsModel", f.Accounts.Model)
// Add TransferRules structure
@ -189,11 +193,6 @@ func (f *FrontendQt) qtSetupQmlAndStructures() {
} else {
f.Qml.SetIsFirstStart(false)
}
// Notify user about error during initialization.
if f.notifyHasNoKeychain {
f.Qml.NotifyHasNoKeychain()
}
}
// QtExecute in main for starting Qt application
@ -233,7 +232,12 @@ func (f *FrontendQt) QtExecute(Procedure func(*FrontendQt) error) error {
}
func (f *FrontendQt) openLogs() {
go open.Run(f.config.GetLogDir())
logsPath, err := f.locations.ProvideLogsPath()
if err != nil {
return
}
go open.Run(logsPath)
}
func (f *FrontendQt) openReport() {
@ -241,7 +245,7 @@ func (f *FrontendQt) openReport() {
}
func (f *FrontendQt) openDownloadLink() {
go open.Run(f.updates.GetDownloadLink())
// NOTE: Fix this.
}
// sendImportReport sends an anonymized import or export report file to our customer support
@ -365,34 +369,8 @@ func (f *FrontendQt) setProgressManager(progress *transfer.Progress) {
}()
}
// StartUpdate is identical to bridge
func (f *FrontendQt) StartUpdate() {
progress := make(chan updates.Progress)
go func() { // Update progress in QML.
defer f.panicHandler.HandlePanic()
for current := range progress {
f.Qml.SetProgress(current.Processed)
f.Qml.SetProgressDescription(strconv.Itoa(current.Description))
// Error happend
if current.Err != nil {
log.Error("update progress: ", current.Err)
f.Qml.UpdateFinished(true)
return
}
// Finished everything OK.
if current.Description >= updates.InfoQuitApp {
f.Qml.UpdateFinished(false)
time.Sleep(3 * time.Second) // Just notify.
f.Qml.SetIsRestarting(current.Description == updates.InfoRestartApp)
f.App.Quit()
return
}
}
}()
go func() {
defer f.panicHandler.HandlePanic()
f.updates.StartUpgrade(progress)
}()
// NOTE: Fix this.
}
// isNewVersionAvailable is identical to bridge
@ -401,26 +379,11 @@ func (f *FrontendQt) StartUpdate() {
func (f *FrontendQt) isNewVersionAvailable(showMessage bool) {
go func() {
defer f.Qml.ProcessFinished()
isUpToDate, latestVersionInfo, err := f.updates.CheckIsUpToDate()
if err != nil {
log.Warnln("Cannot retrieve version info: ", err)
f.checkInternet()
return
}
f.Qml.SetConnectionStatus(true) // if we are here connection is ok
if isUpToDate {
f.Qml.SetUpdateState(StatusUpToDate)
if showMessage {
f.Qml.NotifyVersionIsTheLatest()
}
return
f.Qml.SetUpdateState(StatusUpToDate)
if showMessage {
f.Qml.NotifyVersionIsTheLatest()
}
f.Qml.SetNewversion(latestVersionInfo.Version)
f.Qml.SetChangelog(latestVersionInfo.ReleaseNotes)
f.Qml.SetBugfixes(latestVersionInfo.ReleaseFixedBugs)
f.Qml.SetLandingPage(latestVersionInfo.LandingPage)
f.Qml.SetDownloadLink(latestVersionInfo.GetDownloadLink())
f.Qml.SetUpdateState(StatusNewVersionAvailable)
}()
}
@ -434,16 +397,12 @@ func (f *FrontendQt) resetSource() {
}
func (f *FrontendQt) openLicenseFile() {
go open.Run(f.config.GetLicenseFilePath())
go open.Run(f.locations.GetLicenseFilePath())
}
// getLocalVersionInfo is identical to bridge.
func (f *FrontendQt) getLocalVersionInfo() {
defer f.Qml.ProcessFinished()
localVersion := f.updates.GetLocalVersion()
f.Qml.SetNewversion(localVersion.Version)
f.Qml.SetChangelog(localVersion.ReleaseNotes)
f.Qml.SetBugfixes(localVersion.ReleaseFixedBugs)
// NOTE: Fix this.
}
// LeastUsedColor is intended to return color for creating a new inbox or label.

View File

@ -24,7 +24,8 @@ import (
"net/http"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/sirupsen/logrus"
)
@ -33,23 +34,27 @@ var log = logrus.WithField("pkg", "frontend-nogui") //nolint[gochecknoglobals]
type FrontendHeadless struct{}
func (s *FrontendHeadless) Loop(credentialsError error) error {
log.Info("Check status on localhost:8081")
func (s *FrontendHeadless) Loop() error {
log.Info("Check status on localhost:8082")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "IE is running")
})
return http.ListenAndServe(":8081", nil)
return http.ListenAndServe(":8082", nil)
}
func (s *FrontendHeadless) IsAppRestarting() bool { return false }
func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
}
func New(
version, buildVersion string,
panicHandler types.PanicHandler,
config *config.Config,
locations *locations.Locations,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
ie types.ImportExporter,
restarter types.Restarter,
) *FrontendHeadless {
return &FrontendHeadless{}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2021 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// +build !nogui
package qtie
type panicHandler interface {
HandlePanic()
SendReport(interface{})
}

View File

@ -37,7 +37,6 @@ type GoQMLInterface struct {
_ string `property:"goos"`
_ string `property:"credits"`
_ bool `property:"isFirstStart"`
_ bool `property:"isRestarting"`
_ bool `property:"isConnectionOK"`
_ string `property:lastError`
@ -68,6 +67,8 @@ type GoQMLInterface struct {
_ func(updateState string) `signal:"setUpdateState"`
_ func() `slot:"checkInternet"`
_ func() `slot:"setToRestart"`
_ func() `signal:"processFinished"`
_ func(okay bool) `signal:"exportStructureLoadFinished"`
_ func(okay bool) `signal:"importStructuresLoadFinished"`
@ -77,6 +78,8 @@ type GoQMLInterface struct {
_ func() `slot:"getLocalVersionInfo"`
_ func() `slot:"loadImportReports"`
_ func() `signal:"showWindow"`
_ func() `slot:"quit"`
_ func() `slot:"loadAccounts"`
_ func() `slot:"openLogs"`
@ -165,7 +168,6 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectAddAccount(f.Accounts.AddAccount)
s.SetGoos(runtime.GOOS)
s.SetIsRestarting(false)
s.SetProgramTitle(f.programName)
s.ConnectOpenLicenseFile(f.openLicenseFile)
@ -177,6 +179,8 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectCheckInternet(f.checkInternet)
s.ConnectSetToRestart(f.restarter.SetToRestart)
s.ConnectLoadStructureForExport(f.LoadStructureForExport)
s.ConnectSetupAndLoadForImport(f.setupAndLoadForImport)
s.ConnectResetSource(f.resetSource)

View File

@ -24,8 +24,8 @@ import (
"strings"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/preferences"
"github.com/ProtonMail/proton-bridge/pkg/keychain"
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
)
@ -63,8 +63,8 @@ func (s *FrontendQt) loadAccounts() {
acc_info.SetUserID(user.ID())
acc_info.SetHostname(bridge.Host)
acc_info.SetPassword(user.GetBridgePassword())
acc_info.SetPortIMAP(s.preferences.GetInt(preferences.IMAPPortKey))
acc_info.SetPortSMTP(s.preferences.GetInt(preferences.SMTPPortKey))
acc_info.SetPortIMAP(s.settings.GetInt(settings.IMAPPortKey))
acc_info.SetPortSMTP(s.settings.GetInt(settings.SMTPPortKey))
// Set aliases.
acc_info.SetAliases(strings.Join(user.GetAddresses(), ";"))
@ -85,7 +85,7 @@ func (s *FrontendQt) clearCache() {
}
// Clearing data removes everything (db, preferences, ...)
// so everything has to be stopped and started again.
s.Qml.SetIsRestarting(true)
s.restarter.SetToRestart()
s.App.Quit()
}

View File

@ -38,13 +38,13 @@ import (
"github.com/ProtonMail/go-autostart"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/frontend/autoconfig"
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/preferences"
"github.com/ProtonMail/proton-bridge/internal/updates"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/pkg/ports"
@ -70,10 +70,10 @@ type FrontendQt struct {
buildVersion string
showWindowOnStart bool
panicHandler types.PanicHandler
config *config.Config
preferences *config.Preferences
locations *locations.Locations
settings *settings.Settings
eventListener listener.Listener
updates types.Updater
updater types.Updater
bridge types.Bridger
noEncConfirmator types.NoEncConfirmator
@ -94,21 +94,22 @@ type FrontendQt struct {
// expand userID when added
userIDAdded string
notifyHasNoKeychain bool
restarter types.Restarter
}
// New returns a new Qt frontendend for the bridge.
// New returns a new Qt frontend for the bridge.
func New(
version,
buildVersion string,
showWindowOnStart bool,
panicHandler types.PanicHandler,
config *config.Config,
preferences *config.Preferences,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
bridge types.Bridger,
noEncConfirmator types.NoEncConfirmator,
restarter types.Restarter,
) *FrontendQt {
prgName := "ProtonMail Bridge"
tmp := &FrontendQt{
@ -116,10 +117,10 @@ func New(
buildVersion: buildVersion,
showWindowOnStart: showWindowOnStart,
panicHandler: panicHandler,
config: config,
preferences: preferences,
locations: locations,
settings: settings,
eventListener: eventListener,
updates: updates,
updater: updater,
bridge: bridge,
noEncConfirmator: noEncConfirmator,
@ -130,6 +131,8 @@ func New(
DisplayName: prgName,
Exec: []string{"", "--no-window"},
},
restarter: restarter,
}
// Handle autostart if wanted.
@ -161,10 +164,7 @@ func (s *FrontendQt) InstanceExistAlert() {
// Loop function for Bridge interface.
//
// It runs QtExecute in main thread with no additional function.
func (s *FrontendQt) Loop(credentialsError error) (err error) {
if credentialsError != nil {
s.notifyHasNoKeychain = true
}
func (s *FrontendQt) Loop() (err error) {
go func() {
defer s.panicHandler.HandlePanic()
s.watchEvents()
@ -173,8 +173,14 @@ func (s *FrontendQt) Loop(credentialsError error) (err error) {
return err
}
func (s *FrontendQt) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
}
func (s *FrontendQt) watchEvents() {
errorCh := s.getEventChannel(events.ErrorEvent)
credentialsErrorCh := s.getEventChannel(events.CredentialsErrorEvent)
outgoingNoEncCh := s.getEventChannel(events.OutgoingNoEncEvent)
noActiveKeyForRecipientCh := s.getEventChannel(events.NoActiveKeyForRecipientEvent)
internetOffCh := s.getEventChannel(events.InternetOffEvent)
@ -193,6 +199,8 @@ func (s *FrontendQt) watchEvents() {
imapIssue := strings.Contains(errorDetails, "IMAP failed")
smtpIssue := strings.Contains(errorDetails, "SMTP failed")
s.Qml.NotifyPortIssue(imapIssue, smtpIssue)
case <-credentialsErrorCh:
s.Qml.NotifyHasNoKeychain()
case idAndSubject := <-outgoingNoEncCh:
idAndSubjectSlice := strings.SplitN(idAndSubject, ":", 2)
messageID := idAndSubjectSlice[0]
@ -207,7 +215,7 @@ func (s *FrontendQt) watchEvents() {
case <-secondInstanceCh:
s.Qml.ShowWindow()
case <-restartBridgeCh:
s.Qml.SetIsRestarting(true)
s.restarter.SetToRestart()
// watchEvents is started in parallel with the Qt app.
// If the event comes too early, app might not be ready yet.
if s.App != nil {
@ -267,10 +275,6 @@ func (s *FrontendQt) Start() (err error) {
return nil
}
func (s *FrontendQt) IsAppRestarting() bool {
return s.Qml.IsRestarting()
}
// InvMethod runs the function with name `method` defined in RootObject of the QML.
// Used for tests.
func (s *FrontendQt) InvMethod(method string) error {
@ -304,13 +308,13 @@ func (s *FrontendQt) qtExecute(Procedure func(*FrontendQt) error) error {
s.View.RootContext().SetContextProperty("go", s.Qml)
// Set first start flag.
s.Qml.SetIsFirstStart(s.preferences.GetBool(preferences.FirstStartGUIKey))
s.preferences.SetBool(preferences.FirstStartGUIKey, false)
s.Qml.SetIsFirstStart(s.settings.GetBool(settings.FirstStartGUIKey))
s.settings.SetBool(settings.FirstStartGUIKey, false)
// Check if it is first start after update (fresh version).
lastVersion := s.preferences.Get(preferences.LastVersionKey)
lastVersion := s.settings.Get(settings.LastVersionKey)
s.Qml.SetIsFreshVersion(lastVersion != "" && s.version != lastVersion)
s.preferences.Set(preferences.LastVersionKey, s.version)
s.settings.Set(settings.LastVersionKey, s.version)
// Add AccountsModel.
s.Accounts = NewAccountsModel(nil)
@ -339,27 +343,25 @@ func (s *FrontendQt) qtExecute(Procedure func(*FrontendQt) error) error {
s.Qml.SetIsAutoStart(false)
}
if s.preferences.GetBool(preferences.AllowProxyKey) {
if s.settings.GetBool(settings.AllowProxyKey) {
s.Qml.SetIsProxyAllowed(true)
} else {
s.Qml.SetIsProxyAllowed(false)
}
// Notify user about error during initialization.
if s.notifyHasNoKeychain {
s.Qml.NotifyHasNoKeychain()
}
s.eventListener.RetryEmit(events.TLSCertIssue)
s.eventListener.RetryEmit(events.ErrorEvent)
// Set reporting of outgoing email without encryption.
s.Qml.SetIsReportingOutgoingNoEnc(s.preferences.GetBool(preferences.ReportOutgoingNoEncKey))
s.Qml.SetIsReportingOutgoingNoEnc(s.settings.GetBool(settings.ReportOutgoingNoEncKey))
defaultIMAPPort, _ := strconv.Atoi(settings.DefaultIMAPPort)
defaultSMTPPort, _ := strconv.Atoi(settings.DefaultSMTPPort)
// IMAP/SMTP ports.
s.Qml.SetIsDefaultPort(
s.config.GetDefaultIMAPPort() == s.preferences.GetInt(preferences.IMAPPortKey) &&
s.config.GetDefaultSMTPPort() == s.preferences.GetInt(preferences.SMTPPortKey),
defaultIMAPPort == s.settings.GetInt(settings.IMAPPortKey) &&
defaultSMTPPort == s.settings.GetInt(settings.SMTPPortKey),
)
// Check QML is loaded properly.
@ -387,7 +389,12 @@ func (s *FrontendQt) qtExecute(Procedure func(*FrontendQt) error) error {
}
func (s *FrontendQt) openLogs() {
go open.Run(s.config.GetLogDir())
logsPath, err := s.locations.ProvideLogsPath()
if err != nil {
return
}
go open.Run(logsPath)
}
// Check version in separate goroutine to not block the GUI (avoid program not responding message).
@ -395,40 +402,20 @@ func (s *FrontendQt) isNewVersionAvailable(showMessage bool) {
go func() {
defer s.panicHandler.HandlePanic()
defer s.Qml.ProcessFinished()
isUpToDate, latestVersionInfo, err := s.updates.CheckIsUpToDate()
if err != nil {
log.Warn("Can not retrieve version info: ", err)
s.checkInternet()
return
}
s.Qml.SetConnectionStatus(true) // If we are here connection is ok.
if isUpToDate {
s.Qml.SetUpdateState("upToDate")
if showMessage {
s.Qml.NotifyVersionIsTheLatest()
}
return
s.Qml.SetUpdateState("upToDate")
if showMessage {
s.Qml.NotifyVersionIsTheLatest()
}
s.Qml.SetNewversion(latestVersionInfo.Version)
s.Qml.SetChangelog(latestVersionInfo.ReleaseNotes)
s.Qml.SetBugfixes(latestVersionInfo.ReleaseFixedBugs)
s.Qml.SetLandingPage(latestVersionInfo.LandingPage)
s.Qml.SetDownloadLink(latestVersionInfo.GetDownloadLink())
s.Qml.ShowWindow()
s.Qml.SetUpdateState("oldVersion")
}()
}
func (s *FrontendQt) openLicenseFile() {
go open.Run(s.config.GetLicenseFilePath())
go open.Run(s.locations.GetLicenseFilePath())
}
func (s *FrontendQt) getLocalVersionInfo() {
defer s.Qml.ProcessFinished()
localVersion := s.updates.GetLocalVersion()
s.Qml.SetNewversion(localVersion.Version)
s.Qml.SetChangelog(localVersion.ReleaseNotes)
s.Qml.SetBugfixes(localVersion.ReleaseFixedBugs)
// NOTE: Fix this.
}
func (s *FrontendQt) sendBug(description, client, address string) (isOK bool) {
@ -465,16 +452,16 @@ func (s *FrontendQt) configureAppleMail(iAccount, iAddress int) {
return
}
imapPort := s.preferences.GetInt(preferences.IMAPPortKey)
imapPort := s.settings.GetInt(settings.IMAPPortKey)
imapSSL := false
smtpPort := s.preferences.GetInt(preferences.SMTPPortKey)
smtpSSL := s.preferences.GetBool(preferences.SMTPSSLKey)
smtpPort := s.settings.GetInt(settings.SMTPPortKey)
smtpSSL := s.settings.GetBool(settings.SMTPSSLKey)
// If configuring apple mail for Catalina or newer, users should use SSL.
doRestart := false
if !smtpSSL && useragent.IsCatalinaOrNewer() {
smtpSSL = true
s.preferences.SetBool(preferences.SMTPSSLKey, true)
s.settings.SetBool(settings.SMTPSSLKey, true)
log.Warn("Detected Catalina or newer with bad SMTP SSL settings, now using SSL, bridge needs to restart")
doRestart = true
}
@ -489,7 +476,7 @@ func (s *FrontendQt) configureAppleMail(iAccount, iAddress int) {
if doRestart {
time.Sleep(2 * time.Second)
s.Qml.SetIsRestarting(true)
s.restarter.SetToRestart()
s.App.Quit()
}
return
@ -517,23 +504,23 @@ func (s *FrontendQt) toggleAutoStart() {
func (s *FrontendQt) toggleAllowProxy() {
defer s.Qml.ProcessFinished()
if s.preferences.GetBool(preferences.AllowProxyKey) {
s.preferences.SetBool(preferences.AllowProxyKey, false)
if s.settings.GetBool(settings.AllowProxyKey) {
s.settings.SetBool(settings.AllowProxyKey, false)
s.bridge.DisallowProxy()
s.Qml.SetIsProxyAllowed(false)
} else {
s.preferences.SetBool(preferences.AllowProxyKey, true)
s.settings.SetBool(settings.AllowProxyKey, true)
s.bridge.AllowProxy()
s.Qml.SetIsProxyAllowed(true)
}
}
func (s *FrontendQt) getIMAPPort() string {
return s.preferences.Get(preferences.IMAPPortKey)
return s.settings.Get(settings.IMAPPortKey)
}
func (s *FrontendQt) getSMTPPort() string {
return s.preferences.Get(preferences.SMTPPortKey)
return s.settings.Get(settings.SMTPPortKey)
}
// Return 0 -- port is free to use for server.
@ -550,13 +537,13 @@ func (s *FrontendQt) isPortOpen(portStr string) int {
}
func (s *FrontendQt) setPortsAndSecurity(imapPort, smtpPort string, useSTARTTLSforSMTP bool) {
s.preferences.Set(preferences.IMAPPortKey, imapPort)
s.preferences.Set(preferences.SMTPPortKey, smtpPort)
s.preferences.SetBool(preferences.SMTPSSLKey, !useSTARTTLSforSMTP)
s.settings.Set(settings.IMAPPortKey, imapPort)
s.settings.Set(settings.SMTPPortKey, smtpPort)
s.settings.SetBool(settings.SMTPSSLKey, !useSTARTTLSforSMTP)
}
func (s *FrontendQt) isSMTPSTARTTLS() bool {
return !s.preferences.GetBool(preferences.SMTPSSLKey)
return !s.settings.GetBool(settings.SMTPSSLKey)
}
func (s *FrontendQt) checkInternet() {
@ -594,7 +581,7 @@ func (s *FrontendQt) autostartError(err error) {
func (s *FrontendQt) toggleIsReportingOutgoingNoEnc() {
shouldReport := !s.Qml.IsReportingOutgoingNoEnc()
s.preferences.SetBool(preferences.ReportOutgoingNoEncKey, shouldReport)
s.settings.SetBool(settings.ReportOutgoingNoEncKey, shouldReport)
s.Qml.SetIsReportingOutgoingNoEnc(shouldReport)
}
@ -608,30 +595,5 @@ func (s *FrontendQt) saveOutgoingNoEncPopupCoord(x, y float32) {
}
func (s *FrontendQt) StartUpdate() {
progress := make(chan updates.Progress)
go func() { // Update progress in QML.
defer s.panicHandler.HandlePanic()
for current := range progress {
s.Qml.SetProgress(current.Processed)
s.Qml.SetProgressDescription(strconv.Itoa(current.Description))
// Error happend
if current.Err != nil {
log.Error("update progress: ", current.Err)
s.Qml.UpdateFinished(true)
return
}
// Finished everything OK.
if current.Description >= updates.InfoQuitApp {
s.Qml.UpdateFinished(false)
time.Sleep(3 * time.Second) // Just notify.
s.Qml.SetIsRestarting(current.Description == updates.InfoRestartApp)
s.App.Quit()
return
}
}
}()
go func() {
defer s.panicHandler.HandlePanic()
s.updates.StartUpgrade(progress)
}()
// NOTE: Fix this.
}

View File

@ -23,8 +23,10 @@ import (
"fmt"
"net/http"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/sirupsen/logrus"
)
@ -33,7 +35,7 @@ var log = logrus.WithField("pkg", "frontend-nogui") //nolint[gochecknoglobals]
type FrontendHeadless struct{}
func (s *FrontendHeadless) Loop(credentialsError error) error {
func (s *FrontendHeadless) Loop() error {
log.Info("Check status on localhost:8081")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Bridge is running")
@ -41,20 +43,25 @@ func (s *FrontendHeadless) Loop(credentialsError error) error {
return http.ListenAndServe(":8081", nil)
}
func (s *FrontendHeadless) InstanceExistAlert() {}
func (s *FrontendHeadless) IsAppRestarting() bool { return false }
func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
}
func (s *FrontendHeadless) InstanceExistAlert() {}
func New(
version,
buildVersion string,
showWindowOnStart bool,
panicHandler types.PanicHandler,
config *config.Config,
preferences *config.Preferences,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updates types.Updater,
updater types.Updater,
bridge types.Bridger,
noEncConfirmator types.NoEncConfirmator,
restarter types.Restarter,
) *FrontendHeadless {
return &FrontendHeadless{}
}

View File

@ -41,7 +41,6 @@ type GoQMLInterface struct {
_ bool `property:"isShownOnStart"`
_ bool `property:"isFirstStart"`
_ bool `property:"isFreshVersion"`
_ bool `property:"isRestarting"`
_ bool `property:"isConnectionOK"`
_ bool `property:"isDefaultPort"`
@ -70,6 +69,8 @@ type GoQMLInterface struct {
_ func(updateState string) `signal:"setUpdateState"`
_ func() `slot:"checkInternet"`
_ func() `slot:"setToRestart"`
_ func(systX, systY, systW, systH int) `signal:"toggleMainWin"`
_ func() `signal:"processFinished"`
@ -178,7 +179,6 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectSwitchAddressMode(f.switchAddressModeUser)
s.SetGoos(runtime.GOOS)
s.SetIsRestarting(false)
s.SetProgramTitle(f.programName)
s.ConnectGetBackendVersion(func() string {
@ -187,6 +187,8 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectCheckInternet(f.checkInternet)
s.ConnectSetToRestart(f.restarter.SetToRestart)
s.ConnectToggleIsReportingOutgoingNoEnc(f.toggleIsReportingOutgoingNoEnc)
s.ConnectShouldSendAnswer(f.shouldSendAnswer)
s.ConnectSaveOutgoingNoEncPopupCoord(f.saveOutgoingNoEncPopupCoord)

View File

@ -22,7 +22,7 @@ import (
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/ProtonMail/proton-bridge/internal/updates"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
)
@ -31,18 +31,19 @@ type PanicHandler interface {
HandlePanic()
}
// Updater is an interface for handling Bridge upgrades.
type Updater interface {
CheckIsUpToDate() (isUpToDate bool, latestVersion updates.VersionInfo, err error)
GetDownloadLink() string
GetLocalVersion() updates.VersionInfo
StartUpgrade(currentStatus chan<- updates.Progress)
// Restarter allows the app to set itself to restart next time it is closed.
type Restarter interface {
SetToRestart()
}
type NoEncConfirmator interface {
ConfirmNoEncryption(string, bool)
}
type Updater interface {
InstallUpdate(updater.VersionInfo) error
}
// UserManager is an interface of users needed by frontend.
type UserManager interface {
Login(username, password string) (pmapi.Client, *pmapi.Auth, error)