We build too many walls and not enough bridges
This commit is contained in:
645
internal/frontend/qt/frontend.go
Normal file
645
internal/frontend/qt/frontend.go
Normal file
@ -0,0 +1,645 @@
|
||||
// Copyright (c) 2020 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 qt is the Qt User interface for Desktop bridge.
|
||||
//
|
||||
// The FrontendQt implements Frontend interface: `frontend.go`.
|
||||
// The helper functions are in `helpers.go`.
|
||||
// Notification specific is written in `notification.go`.
|
||||
// The AccountsModel is container providing account info to QML ListView.
|
||||
//
|
||||
// Since we are using QML there is only one Qt loop in `ui.go`.
|
||||
package qt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/autoconfig"
|
||||
"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/ports"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/useragent"
|
||||
"github.com/ProtonMail/go-autostart"
|
||||
|
||||
//"github.com/ProtonMail/proton-bridge/pkg/keychain"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/updates"
|
||||
"github.com/kardianos/osext"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
"github.com/therecipe/qt/core"
|
||||
"github.com/therecipe/qt/gui"
|
||||
"github.com/therecipe/qt/qml"
|
||||
"github.com/therecipe/qt/widgets"
|
||||
)
|
||||
|
||||
var log = config.GetLogEntry("frontend-qt")
|
||||
var accountMutex = &sync.Mutex{}
|
||||
|
||||
// API between Bridge and Qt.
|
||||
//
|
||||
// With this interface it is possible to control Qt-Gui interface using pointers to
|
||||
// Qt and QML objects. QML signals and slots are connected via methods of GoQMLInterface.
|
||||
type FrontendQt struct {
|
||||
version string
|
||||
buildVersion string
|
||||
showWindowOnStart bool
|
||||
panicHandler types.PanicHandler
|
||||
config *config.Config
|
||||
preferences *config.Preferences
|
||||
eventListener listener.Listener
|
||||
updates types.Updater
|
||||
bridge types.Bridger
|
||||
noEncConfirmator types.NoEncConfirmator
|
||||
|
||||
App *widgets.QApplication // Main Application pointer.
|
||||
View *qml.QQmlApplicationEngine // QML engine pointer.
|
||||
MainWin *core.QObject // Pointer to main window inside QML.
|
||||
Qml *GoQMLInterface // Object accessible from both Go and QML for methods and signals.
|
||||
Accounts *AccountsModel // Providing data for accounts ListView.
|
||||
programName string // Program name (shown in taskbar).
|
||||
programVer string // Program version (shown in help).
|
||||
|
||||
authClient bridge.PMAPIProvider
|
||||
|
||||
auth *pmapi.Auth
|
||||
|
||||
AutostartEntry *autostart.App
|
||||
|
||||
// expand userID when added
|
||||
userIDAdded string
|
||||
|
||||
notifyHasNoKeychain bool
|
||||
}
|
||||
|
||||
// New returns a new Qt frontendend for the bridge.
|
||||
func New(
|
||||
version,
|
||||
buildVersion string,
|
||||
showWindowOnStart bool,
|
||||
panicHandler types.PanicHandler,
|
||||
config *config.Config,
|
||||
preferences *config.Preferences,
|
||||
eventListener listener.Listener,
|
||||
updates types.Updater,
|
||||
bridge types.Bridger,
|
||||
noEncConfirmator types.NoEncConfirmator,
|
||||
) *FrontendQt {
|
||||
prgName := "ProtonMail Bridge"
|
||||
tmp := &FrontendQt{
|
||||
version: version,
|
||||
buildVersion: buildVersion,
|
||||
showWindowOnStart: showWindowOnStart,
|
||||
panicHandler: panicHandler,
|
||||
config: config,
|
||||
preferences: preferences,
|
||||
eventListener: eventListener,
|
||||
updates: updates,
|
||||
bridge: bridge,
|
||||
noEncConfirmator: noEncConfirmator,
|
||||
|
||||
programName: prgName,
|
||||
programVer: "v" + version,
|
||||
AutostartEntry: &autostart.App{
|
||||
Name: prgName,
|
||||
DisplayName: prgName,
|
||||
Exec: []string{"", "--no-window"},
|
||||
},
|
||||
}
|
||||
|
||||
// Handle autostart if wanted.
|
||||
if p, err := osext.Executable(); err == nil {
|
||||
tmp.AutostartEntry.Exec[0] = p
|
||||
log.Info("Autostart ", p)
|
||||
} else {
|
||||
log.Error("Cannot get current executable path: ", err)
|
||||
}
|
||||
|
||||
// Nicer string for OS.
|
||||
currentOS := core.QSysInfo_PrettyProductName()
|
||||
bridge.SetCurrentOS(currentOS)
|
||||
|
||||
return tmp
|
||||
}
|
||||
|
||||
// InstanceExistAlert is a global warning window indicating an instance already exists.
|
||||
func (s *FrontendQt) InstanceExistAlert() {
|
||||
log.Warn("Instance already exists")
|
||||
s.QtSetupCoreAndControls()
|
||||
s.App = widgets.NewQApplication(len(os.Args), os.Args)
|
||||
s.View = qml.NewQQmlApplicationEngine(s.App)
|
||||
s.View.AddImportPath("qrc:///")
|
||||
s.View.Load(core.NewQUrl3("qrc:/BridgeUI/InstanceExistsWindow.qml", 0))
|
||||
_ = gui.QGuiApplication_Exec()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
s.watchEvents()
|
||||
}()
|
||||
err = s.qtExecute(func(s *FrontendQt) error { return nil })
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *FrontendQt) watchEvents() {
|
||||
errorCh := s.getEventChannel(events.ErrorEvent)
|
||||
outgoingNoEncCh := s.getEventChannel(events.OutgoingNoEncEvent)
|
||||
noActiveKeyForRecipientCh := s.getEventChannel(events.NoActiveKeyForRecipientEvent)
|
||||
internetOffCh := s.getEventChannel(events.InternetOffEvent)
|
||||
internetOnCh := s.getEventChannel(events.InternetOnEvent)
|
||||
secondInstanceCh := s.getEventChannel(events.SecondInstanceEvent)
|
||||
restartBridgeCh := s.getEventChannel(events.RestartBridgeEvent)
|
||||
addressChangedCh := s.getEventChannel(events.AddressChangedEvent)
|
||||
addressChangedLogoutCh := s.getEventChannel(events.AddressChangedLogoutEvent)
|
||||
logoutCh := s.getEventChannel(events.LogoutEvent)
|
||||
updateApplicationCh := s.getEventChannel(events.UpgradeApplicationEvent)
|
||||
newUserCh := s.getEventChannel(events.UserRefreshEvent)
|
||||
certIssue := s.getEventChannel(events.TLSCertIssue)
|
||||
for {
|
||||
select {
|
||||
case errorDetails := <-errorCh:
|
||||
imapIssue := strings.Contains(errorDetails, "IMAP failed")
|
||||
smtpIssue := strings.Contains(errorDetails, "SMTP failed")
|
||||
s.Qml.NotifyPortIssue(imapIssue, smtpIssue)
|
||||
case idAndSubject := <-outgoingNoEncCh:
|
||||
idAndSubjectSlice := strings.SplitN(idAndSubject, ":", 2)
|
||||
messageID := idAndSubjectSlice[0]
|
||||
subject := idAndSubjectSlice[1]
|
||||
s.Qml.ShowOutgoingNoEncPopup(messageID, subject)
|
||||
case email := <-noActiveKeyForRecipientCh:
|
||||
s.Qml.ShowNoActiveKeyForRecipient(email)
|
||||
case <-internetOffCh:
|
||||
s.Qml.SetConnectionStatus(false)
|
||||
case <-internetOnCh:
|
||||
s.Qml.SetConnectionStatus(true)
|
||||
case <-secondInstanceCh:
|
||||
s.Qml.ShowWindow()
|
||||
case <-restartBridgeCh:
|
||||
s.Qml.SetIsRestarting(true)
|
||||
s.App.Quit()
|
||||
case address := <-addressChangedCh:
|
||||
s.Qml.NotifyAddressChanged(address)
|
||||
case address := <-addressChangedLogoutCh:
|
||||
s.Qml.NotifyAddressChangedLogout(address)
|
||||
case userID := <-logoutCh:
|
||||
user, err := s.bridge.GetUser(userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.Qml.NotifyLogout(user.Username())
|
||||
case <-updateApplicationCh:
|
||||
s.Qml.ProcessFinished()
|
||||
s.Qml.NotifyUpdate()
|
||||
case <-newUserCh:
|
||||
s.Qml.LoadAccounts()
|
||||
case <-certIssue:
|
||||
s.Qml.ShowCertIssue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FrontendQt) getEventChannel(event string) <-chan string {
|
||||
ch := make(chan string)
|
||||
s.eventListener.Add(event, ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
// Loop function for tests.
|
||||
//
|
||||
// It runs QtExecute in new thread with function returning itself after setup.
|
||||
// Therefore it is possible to run tests on background.
|
||||
func (s *FrontendQt) Start() (err error) {
|
||||
uiready := make(chan *FrontendQt)
|
||||
go func() {
|
||||
err := s.qtExecute(func(self *FrontendQt) error {
|
||||
// NOTE: Trick to send back UI by channel to access functionality
|
||||
// inside application thread. Other only uninitialized `ui` is visible.
|
||||
uiready <- self
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
uiready <- nil
|
||||
}()
|
||||
|
||||
// Receive UI pointer and set all pointers.
|
||||
running := <-uiready
|
||||
s.App = running.App
|
||||
s.View = running.View
|
||||
s.MainWin = running.MainWin
|
||||
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 {
|
||||
arg := core.NewQGenericArgument("", nil)
|
||||
PauseLong()
|
||||
isGoodMethod := core.QMetaObject_InvokeMethod4(s.MainWin, method, arg, arg, arg, arg, arg, arg, arg, arg, arg, arg)
|
||||
if isGoodMethod == false {
|
||||
return errors.New("Wrong method " + method)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QtSetupCoreAndControls hanldes global setup of Qt.
|
||||
// Should be called once per program. Probably once per thread is fine.
|
||||
func (s *FrontendQt) QtSetupCoreAndControls() {
|
||||
installMessageHandler()
|
||||
// Core setup.
|
||||
core.QCoreApplication_SetApplicationName(s.programName)
|
||||
core.QCoreApplication_SetApplicationVersion(s.programVer)
|
||||
// High DPI scaling for windows.
|
||||
core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, false)
|
||||
// Software OpenGL: to avoid dedicated GPU.
|
||||
core.QCoreApplication_SetAttribute(core.Qt__AA_UseSoftwareOpenGL, true)
|
||||
// Basic style for QuickControls2 objects.
|
||||
//quickcontrols2.QQuickStyle_SetStyle("material")
|
||||
}
|
||||
|
||||
// qtExecute is the main function for starting the Qt application.
|
||||
//
|
||||
// It is better to have just one Qt application per program (at least per same
|
||||
// thread). This functions reads the main user interface defined in QML files.
|
||||
// The files are appended to library by Qt-QRC.
|
||||
func (s *FrontendQt) qtExecute(Procedure func(*FrontendQt) error) error {
|
||||
s.QtSetupCoreAndControls()
|
||||
s.App = widgets.NewQApplication(len(os.Args), os.Args)
|
||||
if runtime.GOOS == "linux" { // Fix default font.
|
||||
s.App.SetFont(gui.NewQFont2(FcMatchSans(), 12, int(gui.QFont__Normal), false), "")
|
||||
}
|
||||
s.App.SetQuitOnLastWindowClosed(false) // Just to make sure it's not closed.
|
||||
|
||||
s.View = qml.NewQQmlApplicationEngine(s.App)
|
||||
// Add Go-QML bridge.
|
||||
s.Qml = NewGoQMLInterface(nil)
|
||||
s.Qml.SetIsShownOnStart(s.showWindowOnStart)
|
||||
s.Qml.SetFrontend(s) // provides access
|
||||
s.View.RootContext().SetContextProperty("go", s.Qml)
|
||||
|
||||
// Set first start flag.
|
||||
s.Qml.SetIsFirstStart(s.preferences.GetBool(preferences.FirstStartKey))
|
||||
// Don't repeat next start.
|
||||
s.preferences.SetBool(preferences.FirstStartKey, false)
|
||||
|
||||
// Check if it is first start after update (fresh version).
|
||||
lastVersion := s.preferences.Get(preferences.LastVersionKey)
|
||||
s.Qml.SetIsFreshVersion(lastVersion != "" && s.version != lastVersion)
|
||||
s.preferences.Set(preferences.LastVersionKey, s.version)
|
||||
|
||||
// Add AccountsModel.
|
||||
s.Accounts = NewAccountsModel(nil)
|
||||
s.View.RootContext().SetContextProperty("accountsModel", s.Accounts)
|
||||
// Import path and load QML files.
|
||||
s.View.AddImportPath("qrc:///")
|
||||
s.View.Load(core.NewQUrl3("qrc:/ui.qml", 0))
|
||||
|
||||
// List of used packages.
|
||||
s.Qml.SetCredits(bridge.Credits)
|
||||
s.Qml.SetFullversion(s.buildVersion)
|
||||
|
||||
// Autostart.
|
||||
if s.Qml.IsFirstStart() {
|
||||
if s.AutostartEntry.IsEnabled() {
|
||||
if err := s.AutostartEntry.Disable(); err != nil {
|
||||
log.Error("First disable ", err)
|
||||
s.autostartError(err)
|
||||
}
|
||||
}
|
||||
s.toggleAutoStart()
|
||||
}
|
||||
if s.AutostartEntry.IsEnabled() {
|
||||
s.Qml.SetIsAutoStart(true)
|
||||
} else {
|
||||
s.Qml.SetIsAutoStart(false)
|
||||
}
|
||||
|
||||
if s.preferences.GetBool(preferences.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))
|
||||
|
||||
// IMAP/SMTP ports.
|
||||
s.Qml.SetIsDefaultPort(
|
||||
s.config.GetDefaultIMAPPort() == s.preferences.GetInt(preferences.IMAPPortKey) &&
|
||||
s.config.GetDefaultSMTPPort() == s.preferences.GetInt(preferences.SMTPPortKey),
|
||||
)
|
||||
|
||||
// Check QML is loaded properly.
|
||||
if len(s.View.RootObjects()) == 0 {
|
||||
return errors.New("QML not loaded properly")
|
||||
}
|
||||
|
||||
// Obtain main window (need for invoke method).
|
||||
s.MainWin = s.View.RootObjects()[0]
|
||||
SetupSystray(s)
|
||||
|
||||
// Injected procedure for out-of-main-thread applications.
|
||||
if err := Procedure(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Loop
|
||||
if ret := gui.QGuiApplication_Exec(); ret != 0 {
|
||||
err := errors.New("Event loop ended with return value:" + string(ret))
|
||||
log.Warn("QGuiApplication_Exec: ", err)
|
||||
return err
|
||||
}
|
||||
HideSystray()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FrontendQt) openLogs() {
|
||||
go open.Run(s.config.GetLogDir())
|
||||
}
|
||||
|
||||
// Check version in separate goroutine to not block the GUI (avoid program not responding message).
|
||||
func (s *FrontendQt) isNewVersionAvailable(showMessage bool) {
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
defer s.Qml.ProcessFinished()
|
||||
isUpToDate, latestVersionInfo, err := s.updates.CheckIsBridgeUpToDate()
|
||||
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.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) getLocalVersionInfo() {
|
||||
defer s.Qml.ProcessFinished()
|
||||
localVersion := s.updates.GetLocalVersion()
|
||||
s.Qml.SetNewversion(localVersion.Version)
|
||||
s.Qml.SetChangelog(localVersion.ReleaseNotes)
|
||||
s.Qml.SetBugfixes(localVersion.ReleaseFixedBugs)
|
||||
}
|
||||
|
||||
func (s *FrontendQt) sendBug(description, client, address string) (isOK bool) {
|
||||
isOK = true
|
||||
var accname = "No account logged in"
|
||||
if s.Accounts.Count() > 0 {
|
||||
accname = s.Accounts.get(0).Account()
|
||||
}
|
||||
if err := s.bridge.ReportBug(
|
||||
core.QSysInfo_ProductType(),
|
||||
core.QSysInfo_PrettyProductName(),
|
||||
description,
|
||||
accname,
|
||||
address,
|
||||
client,
|
||||
); err != nil {
|
||||
log.Error("while sendBug: ", err)
|
||||
isOK = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *FrontendQt) getLastMailClient() string {
|
||||
return s.bridge.GetCurrentClient()
|
||||
}
|
||||
|
||||
func (s *FrontendQt) configureAppleMail(iAccount, iAddress int) {
|
||||
acc := s.Accounts.get(iAccount)
|
||||
|
||||
user, err := s.bridge.GetUser(acc.UserID())
|
||||
if err != nil {
|
||||
log.Warn("UserConfigFromKeychain failed: ", acc.Account(), err)
|
||||
s.SendNotification(TabAccount, s.Qml.GenericErrSeeLogs())
|
||||
return
|
||||
}
|
||||
|
||||
imapPort := s.preferences.GetInt(preferences.IMAPPortKey)
|
||||
imapSSL := false
|
||||
smtpPort := s.preferences.GetInt(preferences.SMTPPortKey)
|
||||
smtpSSL := s.preferences.GetBool(preferences.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)
|
||||
log.Warn("Detected Catalina or newer with bad SMTP SSL settings, now using SSL, bridge needs to restart")
|
||||
doRestart = true
|
||||
}
|
||||
|
||||
for _, autoConf := range autoconfig.Available() {
|
||||
if err := autoConf.Configure(imapPort, smtpPort, imapSSL, smtpSSL, user, iAddress); err != nil {
|
||||
log.Warn("Autoconfig failed: ", autoConf.Name(), err)
|
||||
s.SendNotification(TabAccount, s.Qml.GenericErrSeeLogs())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if doRestart {
|
||||
time.Sleep(2 * time.Second)
|
||||
s.Qml.SetIsRestarting(true)
|
||||
s.App.Quit()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *FrontendQt) toggleAutoStart() {
|
||||
defer s.Qml.ProcessFinished()
|
||||
var err error
|
||||
if s.AutostartEntry.IsEnabled() {
|
||||
err = s.AutostartEntry.Disable()
|
||||
} else {
|
||||
err = s.AutostartEntry.Enable()
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Enable autostart: ", err)
|
||||
s.autostartError(err)
|
||||
}
|
||||
if s.AutostartEntry.IsEnabled() {
|
||||
s.Qml.SetIsAutoStart(true)
|
||||
} else {
|
||||
s.Qml.SetIsAutoStart(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FrontendQt) toggleAllowProxy() {
|
||||
defer s.Qml.ProcessFinished()
|
||||
|
||||
if s.preferences.GetBool(preferences.AllowProxyKey) {
|
||||
s.preferences.SetBool(preferences.AllowProxyKey, false)
|
||||
bridge.DisallowDoH()
|
||||
s.Qml.SetIsProxyAllowed(false)
|
||||
} else {
|
||||
s.preferences.SetBool(preferences.AllowProxyKey, true)
|
||||
bridge.AllowDoH()
|
||||
s.Qml.SetIsProxyAllowed(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FrontendQt) getIMAPPort() string {
|
||||
return s.preferences.Get(preferences.IMAPPortKey)
|
||||
}
|
||||
|
||||
func (s *FrontendQt) getSMTPPort() string {
|
||||
return s.preferences.Get(preferences.SMTPPortKey)
|
||||
}
|
||||
|
||||
// Return 0 -- port is free to use for server.
|
||||
// Return 1 -- port is occupied.
|
||||
func (s *FrontendQt) isPortOpen(portStr string) int {
|
||||
portInt, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
if !ports.IsPortFree(portInt) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (s *FrontendQt) isSMTPSTARTTLS() bool {
|
||||
return !s.preferences.GetBool(preferences.SMTPSSLKey)
|
||||
}
|
||||
|
||||
func (s *FrontendQt) checkInternet() {
|
||||
s.Qml.SetConnectionStatus(IsInternetAvailable())
|
||||
}
|
||||
|
||||
func (s *FrontendQt) switchAddressModeUser(iAccount int) {
|
||||
defer s.Qml.ProcessFinished()
|
||||
userID := s.Accounts.get(iAccount).UserID()
|
||||
user, err := s.bridge.GetUser(userID)
|
||||
if err != nil {
|
||||
log.Error("Get user for switch address mode failed: ", err)
|
||||
s.SendNotification(TabAccount, s.Qml.GenericErrSeeLogs())
|
||||
return
|
||||
}
|
||||
if err := user.SwitchAddressMode(); err != nil {
|
||||
log.Error("Switch address mode failed: ", err)
|
||||
s.SendNotification(TabAccount, s.Qml.GenericErrSeeLogs())
|
||||
return
|
||||
}
|
||||
s.userIDAdded = userID
|
||||
}
|
||||
|
||||
func (s *FrontendQt) autostartError(err error) {
|
||||
if strings.Contains(err.Error(), "permission denied") {
|
||||
s.Qml.FailedAutostartCode("permission")
|
||||
} else if strings.Contains(err.Error(), "error code: 0x") {
|
||||
errorCode := err.Error()
|
||||
errorCode = errorCode[len(errorCode)-8:]
|
||||
s.Qml.FailedAutostartCode(errorCode)
|
||||
} else {
|
||||
s.Qml.FailedAutostartCode("")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FrontendQt) toggleIsReportingOutgoingNoEnc() {
|
||||
shouldReport := !s.Qml.IsReportingOutgoingNoEnc()
|
||||
s.preferences.SetBool(preferences.ReportOutgoingNoEncKey, shouldReport)
|
||||
s.Qml.SetIsReportingOutgoingNoEnc(shouldReport)
|
||||
}
|
||||
|
||||
func (s *FrontendQt) shouldSendAnswer(messageID string, shouldSend bool) {
|
||||
s.noEncConfirmator.ConfirmNoEncryption(messageID, shouldSend)
|
||||
}
|
||||
|
||||
func (s *FrontendQt) saveOutgoingNoEncPopupCoord(x, y float32) {
|
||||
//prefs.SetFloat(prefs.OutgoingNoEncPopupCoordX, x)
|
||||
//prefs.SetFloat(prefs.OutgoingNoEncPopupCoordY, y)
|
||||
}
|
||||
|
||||
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(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)
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user