GODT-1837: Fix restart.

GOTD-1837: added wait flag.
GODT-1837: strip --wait flag from launcher command-line.
GODT-1837: hide main window before restart.
This commit is contained in:
Xavier Michelon
2022-08-30 11:56:13 +02:00
parent 2780dc6a67
commit 8bb2a399cc
15 changed files with 237 additions and 67 deletions

2
.gitignore vendored
View File

@ -31,6 +31,8 @@ vendor-cache
cmd/Desktop-Bridge/deploy cmd/Desktop-Bridge/deploy
cmd/Import-Export/deploy cmd/Import-Export/deploy
proton-bridge proton-bridge
cmd/Desktop-Bridge/*.exe
cmd/launcher/*.exe
# Jetbrains (CLion, Golang) cmake build dirs # Jetbrains (CLion, Golang) cmake build dirs
cmake-build-*/ cmake-build-*/

View File

@ -22,6 +22,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"time"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
@ -33,6 +34,9 @@ import (
"github.com/ProtonMail/proton-bridge/v2/internal/sentry" "github.com/ProtonMail/proton-bridge/v2/internal/sentry"
"github.com/ProtonMail/proton-bridge/v2/internal/updater" "github.com/ProtonMail/proton-bridge/v2/internal/updater"
"github.com/ProtonMail/proton-bridge/v2/internal/versioner" "github.com/ProtonMail/proton-bridge/v2/internal/versioner"
"github.com/bradenaw/juniper/xslices"
"github.com/elastic/go-sysinfo"
"github.com/elastic/go-sysinfo/types"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/sys/execabs" "golang.org/x/sys/execabs"
@ -46,6 +50,7 @@ const (
FlagCLI = "--cli" FlagCLI = "--cli"
FlagLauncher = "--launcher" FlagLauncher = "--launcher"
FlagWait = "--wait"
) )
func main() { //nolint:funlen func main() { //nolint:funlen
@ -92,7 +97,7 @@ func main() { //nolint:funlen
exeToLaunch := guiName exeToLaunch := guiName
args := os.Args[1:] args := os.Args[1:]
if isCliMode(&args) { if inCLIMode(args) {
exeToLaunch = exeName exeToLaunch = exeName
} }
@ -108,6 +113,12 @@ func main() { //nolint:funlen
logrus.WithError(err).Fatal("Failed to determine path to launcher") logrus.WithError(err).Fatal("Failed to determine path to launcher")
} }
var wait bool
args, wait = findAndStrip(args, FlagWait)
if wait {
waitForProcessToFinish(exe)
}
cmd := execabs.Command(exe, appendLauncherPath(launcher, args)...) //nolint:gosec cmd := execabs.Command(exe, appendLauncherPath(launcher, args)...) //nolint:gosec
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
@ -115,7 +126,8 @@ func main() { //nolint:funlen
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
// On windows, if you use Run(), a terminal stays open; we don't want that. // On windows, if you use Run(), a terminal stays open; we don't want that.
if runtime.GOOS == "windows" { if //goland:noinspection GoBoolExpressions
runtime.GOOS == "windows" {
err = cmd.Start() err = cmd.Start()
} else { } else {
err = cmd.Run() err = cmd.Run()
@ -152,14 +164,21 @@ func appendLauncherPath(path string, args []string) []string {
return res return res
} }
func isCliMode(args *[]string) bool { func inCLIMode(args []string) bool {
for _, v := range *args { return sliceContains(args, FlagCLI)
if v == FlagCLI { }
return true
}
}
return false // sliceContains checks if a value is present in a list.
func sliceContains[T comparable](list []T, s T) bool {
return xslices.Any(list, func(arg T) bool { return arg == s })
}
// findAndStrip check if a value is present in s list and remove all occurrences of the value from this list.
func findAndStrip[T comparable](slice []T, v T) (strippedList []T, found bool) {
strippedList = xslices.Filter(slice, func(value T) bool {
return value != v
})
return strippedList, len(strippedList) != len(slice)
} }
func getPathToUpdatedExecutable( func getPathToUpdatedExecutable(
@ -222,3 +241,44 @@ func getFallbackExecutable(name string, versioner *versioner.Versioner) (string,
return versioner.GetExecutableInDirectory(name, filepath.Dir(launcher)) return versioner.GetExecutableInDirectory(name, filepath.Dir(launcher))
} }
// waitForProcessToFinish waits until the process with the given path is finished.
func waitForProcessToFinish(exePath string) {
for {
processes, err := sysinfo.Processes()
if err != nil {
logrus.WithError(err).Error("Could not determine running processes")
return
}
exeInfo, err := os.Stat(exePath)
if err != nil {
logrus.WithError(err).WithField("file", exeInfo).Error("Could not retrieve file info")
return
}
if xslices.Any(processes, func(process types.Process) bool {
info, err := process.Info()
if err != nil {
logrus.WithError(err).Error("Could not retrieve process info")
}
return sameFile(exeInfo, info.Exe)
}) {
logrus.Infof("Waiting for %v to finish.", exeInfo.Name())
time.Sleep(1 * time.Second)
continue
}
return
}
}
func sameFile(info os.FileInfo, path string) bool {
pathInfo, err := os.Stat(path)
if err != nil {
logrus.WithError(err).WithField("file", path).Error("Could not retrieve file info")
}
return os.SameFile(pathInfo, info)
}

58
cmd/launcher/main_test.go Normal file
View File

@ -0,0 +1,58 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail 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.
//
// Proton Mail 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package main
import (
"testing"
"github.com/bradenaw/juniper/xslices"
"github.com/stretchr/testify/assert"
)
func TestSliceContains(t *testing.T) {
assert.True(t, sliceContains([]string{"a", "b", "c"}, "a"))
assert.True(t, sliceContains([]int{1, 2, 3}, 2))
assert.False(t, sliceContains([]string{"a", "b", "c"}, "A"))
assert.False(t, sliceContains([]int{1, 2, 3}, 4))
assert.False(t, sliceContains([]string{}, "a"))
assert.True(t, sliceContains([]string{"a", "a"}, "a"))
}
func TestFindAndStrip(t *testing.T) {
list := []string{"a", "b", "c", "c", "b", "c"}
result, found := findAndStrip(list, "a")
assert.True(t, found)
assert.True(t, xslices.Equal(result, []string{"b", "c", "c", "b", "c"}))
result, found = findAndStrip(list, "c")
assert.True(t, found)
assert.True(t, xslices.Equal(result, []string{"a", "b", "b"}))
result, found = findAndStrip([]string{"c", "c", "c"}, "c")
assert.True(t, found)
assert.True(t, xslices.Equal(result, []string{}))
result, found = findAndStrip(list, "A")
assert.False(t, found)
assert.True(t, xslices.Equal(result, list))
result, found = findAndStrip([]string{}, "a")
assert.False(t, found)
assert.True(t, xslices.Equal(result, []string{}))
}

View File

@ -42,6 +42,8 @@ func (b *Base) restartApp(crash bool) error {
args = forceLauncherFlag(args, b.launcher) args = forceLauncherFlag(args, b.launcher)
} }
args = append(args, "--wait")
logrus. logrus.
WithField("command", b.command). WithField("command", b.command).
WithField("args", args). WithField("args", args).

View File

@ -31,8 +31,8 @@ using namespace bridgepp;
EventStreamReader::EventStreamReader(QObject *parent) EventStreamReader::EventStreamReader(QObject *parent)
: Worker(parent) : Worker(parent)
{ {
connect(this, &EventStreamReader::started, [&]() { app().log().debug("EventStreamReader started"); }); connect(this, &EventStreamReader::started, this, &EventStreamReader::onStarted);
connect(this, &EventStreamReader::finished, [&]() { app().log().debug("EventStreamReader finished"); }); connect(this, &EventStreamReader::finished, this, &EventStreamReader::onFinished);
connect(this, &EventStreamReader::error, &app().log(), &Log::error); connect(this, &EventStreamReader::error, &app().log(), &Log::error);
} }
@ -57,3 +57,27 @@ void EventStreamReader::run()
emit error(e.qwhat()); emit error(e.qwhat());
} }
} }
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void EventStreamReader::onStarted() const
{
app().log().debug("EventStreamReader started");
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void EventStreamReader::onFinished() const
{
app().log().debug("EventStreamReader finished");
if (!app().bridgeMonitor())
{
// no bridge monitor means we are in a debug environment, running in attached mode. Event stream has terminated, so bridge is shutting
// down. Because we're in attached mode, bridge-gui will not get notified that bridge is going down, so we shutdown manually here.
qApp->exit(EXIT_SUCCESS);
}
}

View File

@ -39,6 +39,8 @@ public: // member functions
public slots: public slots:
void run() override; ///< Run the reader. void run() override; ///< Run the reader.
void onStarted() const; ///< Slot for the 'started' signal.
void onFinished() const; ///< Slot for the 'finished' signal.
signals: signals:
void eventReceived(QString eventString); ///< signal for events. void eventReceived(QString eventString); ///< signal for events.

View File

@ -229,6 +229,10 @@ void QMLBackend::restart()
app().grpc().quit(); app().grpc().quit();
} }
//****************************************************************************************************************************************************
/// \param[in] launcher The path to the launcher.
//****************************************************************************************************************************************************
void QMLBackend::forceLauncher(QString launcher) void QMLBackend::forceLauncher(QString launcher)
{ {
app().grpc().forceLauncher(launcher); app().grpc().forceLauncher(launcher);
@ -270,8 +274,9 @@ void QMLBackend::changeColorScheme(QString const &scheme)
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
void QMLBackend::toggleUseSSLforSMTP(bool makeItActive) void QMLBackend::toggleUseSSLforSMTP(bool makeItActive)
{ {
if (app().grpc().setUseSSLForSMTP(makeItActive).ok()) // if call succeed, app will restart. No need to emit a value change signal, because it will trigger a read-back via gRPC that will fail.
emit useSSLforSMTPChanged(makeItActive); emit hideMainWindow();
app().grpc().setUseSSLForSMTP(makeItActive);
} }
@ -281,11 +286,21 @@ void QMLBackend::toggleUseSSLforSMTP(bool makeItActive)
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
void QMLBackend::changePorts(int imapPort, int smtpPort) void QMLBackend::changePorts(int imapPort, int smtpPort)
{ {
if (app().grpc().changePorts(imapPort, smtpPort).ok()) // if call succeed, app will restart. No need to emit a value change signal, because it will trigger a read-back via gRPC that will fail.
{ emit hideMainWindow();
emit portIMAPChanged(imapPort); app().grpc().changePorts(imapPort, smtpPort);
emit portSMTPChanged(smtpPort); }
}
//****************************************************************************************************************************************************
/// \param[in] enable Is cache enabled?
/// \param[in] path The path of the cache.
//****************************************************************************************************************************************************
void QMLBackend::changeLocalCache(bool enable, QUrl const &path)
{
// if call succeed, app will restart. No need to emit a value change signal, because it will trigger a read-back via gRPC that will fail.
emit hideMainWindow();
app().grpc().changeLocalCache(enable, path);
} }
@ -353,3 +368,5 @@ void QMLBackend::onResetFinished()
emit resetFinished(); emit resetFinished();
this->restart(); this->restart();
} }

View File

@ -143,7 +143,7 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
void toggleAutostart(bool active); // _ func(makeItActive bool) `slot:"toggleAutostart"` void toggleAutostart(bool active); // _ func(makeItActive bool) `slot:"toggleAutostart"`
void toggleBeta(bool active); // _ func(makeItActive bool) `slot:"toggleBeta"` void toggleBeta(bool active); // _ func(makeItActive bool) `slot:"toggleBeta"`
void changeColorScheme(QString const &scheme); // _ func(string) `slot:"changeColorScheme"` void changeColorScheme(QString const &scheme); // _ func(string) `slot:"changeColorScheme"`
void changeLocalCache(bool enable, QUrl const& path) { app().grpc().changeLocalCache(enable, path); } // _ func(enableDiskCache bool, diskCachePath core.QUrl) `slot:"changeLocalCache"` void changeLocalCache(bool enable, QUrl const& path); // _ func(enableDiskCache bool, diskCachePath core.QUrl) `slot:"changeLocalCache"`
void login(QString const& username, QString const& password) { app().grpc().login(username, password);} // _ func(username, password string) `slot:"login"` void login(QString const& username, QString const& password) { app().grpc().login(username, password);} // _ func(username, password string) `slot:"login"`
void login2FA(QString const& username, QString const& code) { app().grpc().login2FA(username, code);} // _ func(username, code string) `slot:"login2FA"` void login2FA(QString const& username, QString const& code) { app().grpc().login2FA(username, code);} // _ func(username, code string) `slot:"login2FA"`
void login2Password(QString const& username, QString const& password) { app().grpc().login2Passwords(username, password);} // _ func(username, password string) `slot:"login2Password"` void login2Password(QString const& username, QString const& password) { app().grpc().login2Passwords(username, password);} // _ func(username, password string) `slot:"login2Password"`
@ -211,6 +211,7 @@ signals: // Signals received from the Go backend, to be forwarded to QML
void bugReportSendSuccess(); // _ func() `signal:"bugReportSendSuccess"` void bugReportSendSuccess(); // _ func() `signal:"bugReportSendSuccess"`
void bugReportSendError(); // _ func() `signal:"bugReportSendError"` void bugReportSendError(); // _ func() `signal:"bugReportSendError"`
void showMainWindow(); // _ func() `signal:showMainWindow` void showMainWindow(); // _ func() `signal:showMainWindow`
void hideMainWindow();
private: // member functions private: // member functions
void retrieveUserList(); ///< Retrieve the list of users via gRPC. void retrieveUserList(); ///< Retrieve the list of users via gRPC.

View File

@ -224,12 +224,14 @@ void closeBridgeApp()
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// The application instance is needed to display system message boxes. As we may have to do it in the exception handler,
// application instance is create outside the try/catch clause.
if (QSysInfo::productType() != "windows")
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
QApplication guiApp(argc, argv);
try try
{ {
if (QSysInfo::productType() != "windows")
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
QApplication guiApp(argc, argv);
initQtApplication(); initQtApplication();
Log &log = initLog(); Log &log = initLog();
@ -302,6 +304,7 @@ int main(int argc, char *argv[])
} }
catch (Exception const &e) catch (Exception const &e)
{ {
QMessageBox::critical(nullptr, "Error", e.qwhat());
QTextStream(stderr) << e.qwhat() << "\n"; QTextStream(stderr) << e.qwhat() << "\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@ -59,6 +59,10 @@ QtObject {
mainWindow.showAndRise() mainWindow.showAndRise()
} }
function onColorSchemeNameChanged(scheme) { root.setColorScheme() } function onColorSchemeNameChanged(scheme) { root.setColorScheme() }
function onHideMainWindow() {
mainWindow.hide();
}
} }
} }

View File

@ -288,7 +288,9 @@ grpc::Status GRPCClient::reportBug(QString const &description, QString const &ad
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
grpc::Status GRPCClient::useSSLForSMTP(bool &outUseSSL) grpc::Status GRPCClient::useSSLForSMTP(bool &outUseSSL)
{ {
return this->logGRPCCallStatus(this->getBool(&Bridge::Stub::UseSslForSmtp, outUseSSL), __FUNCTION__); Status status = this->getBool(&Bridge::Stub::UseSslForSmtp, outUseSSL);
return this->logGRPCCallStatus(status, __FUNCTION__);
} }
@ -363,7 +365,8 @@ grpc::Status GRPCClient::setIsDoHEnabled(bool enabled)
grpc::Status GRPCClient::quit() grpc::Status GRPCClient::quit()
{ {
grpc::ClientContext ctx; grpc::ClientContext ctx;
return this->logGRPCCallStatus(stub_->Quit(&ctx, empty, &empty), __FUNCTION__); // quitting will shut down the gRPC service, to we may get an 'Unavailable' response for the call
return this->logGRPCCallStatus(stub_->Quit(&ctx, empty, &empty), __FUNCTION__, { StatusCode::UNAVAILABLE });
} }
@ -373,7 +376,8 @@ grpc::Status GRPCClient::quit()
grpc::Status GRPCClient::restart() grpc::Status GRPCClient::restart()
{ {
grpc::ClientContext ctx; grpc::ClientContext ctx;
return this->logGRPCCallStatus(stub_->Restart(&ctx, empty, &empty), __FUNCTION__); // restarting will shut down the gRPC service, to we may get an 'Unavailable' response for the call
return this->logGRPCCallStatus(stub_->Restart(&ctx, empty, &empty), __FUNCTION__, { StatusCode::UNAVAILABLE });
} }
@ -882,11 +886,11 @@ void GRPCClient::logError(QString const &message)
/// \param[in] status The status /// \param[in] status The status
/// \param[in] callName The call name. /// \param[in] callName The call name.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
grpc::Status GRPCClient::logGRPCCallStatus(Status const &status, QString const &callName) grpc::Status GRPCClient::logGRPCCallStatus(Status const &status, QString const &callName, QList<grpc::StatusCode> allowedErrors)
{ {
if (log_) if (log_)
{ {
if (status.ok()) if (status.ok() || allowedErrors.contains(status.error_code()))
log_->debug(QString("%1()").arg(callName)); log_->debug(QString("%1()").arg(callName));
else else
log_->error(QString("%1() FAILED").arg(callName)); log_->error(QString("%1() FAILED").arg(callName));

View File

@ -201,7 +201,7 @@ private slots:
private: private:
void logDebug(QString const &message); ///< Log an event. void logDebug(QString const &message); ///< Log an event.
void logError(QString const &message); ///< Log an event. void logError(QString const &message); ///< Log an event.
grpc::Status logGRPCCallStatus(grpc::Status const &status, QString const &callName); ///< Log the status of a gRPC code. grpc::Status logGRPCCallStatus(grpc::Status const &status, QString const &callName, QList<grpc::StatusCode> allowedErrors = {}); ///< Log the status of a gRPC code.
grpc::Status simpleMethod(SimpleMethod method); ///< perform a gRPC call to a bool setter. grpc::Status simpleMethod(SimpleMethod method); ///< perform a gRPC call to a bool setter.
grpc::Status setBool(BoolSetter setter, bool value); ///< perform a gRPC call to a bool setter. grpc::Status setBool(BoolSetter setter, bool value); ///< perform a gRPC call to a bool setter.
grpc::Status getBool(BoolGetter getter, bool &outValue); ///< perform a gRPC call to a bool getter. grpc::Status getBool(BoolGetter getter, bool &outValue); ///< perform a gRPC call to a bool getter.

View File

@ -20,6 +20,7 @@ package grpc
//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative bridge.proto //go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative bridge.proto
import ( import (
"context"
cryptotls "crypto/tls" cryptotls "crypto/tls"
"net" "net"
"strings" "strings"
@ -39,6 +40,7 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/protobuf/types/known/emptypb"
) )
// Service is the RPC service struct. // Service is the RPC service struct.
@ -223,7 +225,7 @@ func (s *Service) watchEvents() { // nolint:funlen
case <-secondInstanceCh: case <-secondInstanceCh:
_ = s.SendEvent(NewShowMainWindowEvent()) _ = s.SendEvent(NewShowMainWindowEvent())
case <-restartBridgeCh: case <-restartBridgeCh:
s.restart() _, _ = s.Restart(context.Background(), &emptypb.Empty{})
case address := <-addressChangedCh: case address := <-addressChangedCh:
_ = s.SendEvent(NewMailAddressChangeEvent(address)) _ = s.SendEvent(NewMailAddressChangeEvent(address))
case address := <-addressChangedLogoutCh: case address := <-addressChangedLogoutCh:
@ -311,10 +313,6 @@ func (s *Service) waitForUserChangeDone(done <-chan string, userID string) {
} }
} }
func (s *Service) restart() {
s.restarter.SetToRestart()
}
func (s *Service) triggerReset() { func (s *Service) triggerReset() {
defer func() { defer func() {
_ = s.SendEvent(NewResetFinishedEvent()) _ = s.SendEvent(NewResetFinishedEvent())

View File

@ -38,8 +38,6 @@ import (
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
) )
var ErrNotImplemented = status.Errorf(codes.Unimplemented, "Not implemented")
func (s *Service) AddLogEntry(_ context.Context, request *AddLogEntryRequest) (*emptypb.Empty, error) { func (s *Service) AddLogEntry(_ context.Context, request *AddLogEntryRequest) (*emptypb.Empty, error) {
entry := s.log entry := s.log
if len(request.Package) > 0 { if len(request.Package) > 0 {
@ -77,31 +75,30 @@ func (s *Service) GuiReady(context.Context, *emptypb.Empty) (*emptypb.Empty, err
// Quit implement the Quit gRPC service call. // Quit implement the Quit gRPC service call.
func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
s.log.Info("Quit") s.log.Info("Quit")
var err error
if s.eventStreamCh != nil {
if _, err = s.StopEventStream(ctx, empty); err != nil {
s.log.WithError(err).Error("Quit failed.")
}
}
// The following call is launched as a goroutine, as it will wait for current calls to end, including this one. // Windows is notably slow at Quitting. We do it in a goroutine to speed things up a bit.
go func() { s.grpcServer.GracefulStop() }()
return &emptypb.Empty{}, err
}
// Restart implement the Restart gRPC service call.
func (s *Service) Restart(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
s.log.Info("Restart")
go func() { go func() {
defer s.panicHandler.HandlePanic() var err error
if s.eventStreamCh != nil {
if _, err = s.StopEventStream(ctx, empty); err != nil {
s.log.WithError(err).Error("Quit failed.")
}
}
s.restart() // The following call is launched as a goroutine, as it will wait for current calls to end, including this one.
s.grpcServer.GracefulStop()
}() }()
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
// Restart implement the Restart gRPC service call.
func (s *Service) Restart(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
s.log.Info("Restart")
s.restarter.SetToRestart()
return s.Quit(ctx, empty)
}
func (s *Service) ShowOnStartup(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) ShowOnStartup(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
s.log.Info("ShowOnStartup") s.log.Info("ShowOnStartup")
@ -486,11 +483,12 @@ func (s *Service) DiskCachePath(context.Context, *emptypb.Empty) (*wrapperspb.St
return wrapperspb.String(s.bridge.Get(settings.CacheLocationKey)), nil return wrapperspb.String(s.bridge.Get(settings.CacheLocationKey)), nil
} }
func (s *Service) ChangeLocalCache(_ context.Context, change *ChangeLocalCacheRequest) (*emptypb.Empty, error) { func (s *Service) ChangeLocalCache(ctx context.Context, change *ChangeLocalCacheRequest) (*emptypb.Empty, error) {
s.log.WithField("enableDiskCache", change.EnableDiskCache). s.log.WithField("enableDiskCache", change.EnableDiskCache).
WithField("diskCachePath", change.DiskCachePath). WithField("diskCachePath", change.DiskCachePath).
Info("DiskCachePath") Info("DiskCachePath")
defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
defer func() { _ = s.SendEvent(NewCacheChangeLocalCacheFinishedEvent()) }() defer func() { _ = s.SendEvent(NewCacheChangeLocalCacheFinishedEvent()) }()
defer func() { _ = s.SendEvent(NewIsCacheOnDiskEnabledChanged(s.bridge.GetBool(settings.CacheEnabledKey))) }() defer func() { _ = s.SendEvent(NewIsCacheOnDiskEnabledChanged(s.bridge.GetBool(settings.CacheEnabledKey))) }()
defer func() { _ = s.SendEvent(NewDiskCachePathChanged(s.bridge.Get(settings.CacheCompressionKey))) }() defer func() { _ = s.SendEvent(NewDiskCachePathChanged(s.bridge.Get(settings.CacheCompressionKey))) }()
@ -523,7 +521,6 @@ func (s *Service) ChangeLocalCache(_ context.Context, change *ChangeLocalCacheRe
} }
_ = s.SendEvent(NewCacheLocationChangeSuccessEvent()) _ = s.SendEvent(NewCacheLocationChangeSuccessEvent())
s.restart()
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
@ -542,19 +539,18 @@ func (s *Service) IsDoHEnabled(context.Context, *emptypb.Empty) (*wrapperspb.Boo
return wrapperspb.Bool(s.bridge.GetProxyAllowed()), nil return wrapperspb.Bool(s.bridge.GetProxyAllowed()), nil
} }
func (s *Service) SetUseSslForSmtp(_ context.Context, useSsl *wrapperspb.BoolValue) (*emptypb.Empty, error) { //nolint:revive,stylecheck func (s *Service) SetUseSslForSmtp(ctx context.Context, useSsl *wrapperspb.BoolValue) (*emptypb.Empty, error) { //nolint:revive,stylecheck
s.log.WithField("useSsl", useSsl.Value).Info("SetUseSslForSmtp") s.log.WithField("useSsl", useSsl.Value).Info("SetUseSslForSmtp")
if s.bridge.GetBool(settings.SMTPSSLKey) == useSsl.Value { if s.bridge.GetBool(settings.SMTPSSLKey) == useSsl.Value {
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
defer func() { _ = s.SendEvent(NewMailSettingsUseSslForSmtpFinishedEvent()) }()
s.bridge.SetBool(settings.SMTPSSLKey, useSsl.Value) s.bridge.SetBool(settings.SMTPSSLKey, useSsl.Value)
s.restart()
return &emptypb.Empty{}, nil defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
return &emptypb.Empty{}, s.SendEvent(NewMailSettingsUseSslForSmtpFinishedEvent())
} }
func (s *Service) UseSslForSmtp(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) { //nolint:revive,stylecheck func (s *Service) UseSslForSmtp(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) { //nolint:revive,stylecheck
@ -581,16 +577,15 @@ func (s *Service) SmtpPort(context.Context, *emptypb.Empty) (*wrapperspb.Int32Va
return wrapperspb.Int32(int32(s.bridge.GetInt(settings.SMTPPortKey))), nil return wrapperspb.Int32(int32(s.bridge.GetInt(settings.SMTPPortKey))), nil
} }
func (s *Service) ChangePorts(_ context.Context, ports *ChangePortsRequest) (*emptypb.Empty, error) { func (s *Service) ChangePorts(ctx context.Context, ports *ChangePortsRequest) (*emptypb.Empty, error) {
s.log.WithField("imapPort", ports.ImapPort).WithField("smtpPort", ports.SmtpPort).Info("ChangePorts") s.log.WithField("imapPort", ports.ImapPort).WithField("smtpPort", ports.SmtpPort).Info("ChangePorts")
defer func() { _ = s.SendEvent(NewMailSettingsChangePortFinishedEvent()) }()
s.bridge.SetInt(settings.IMAPPortKey, int(ports.ImapPort)) s.bridge.SetInt(settings.IMAPPortKey, int(ports.ImapPort))
s.bridge.SetInt(settings.SMTPPortKey, int(ports.SmtpPort)) s.bridge.SetInt(settings.SMTPPortKey, int(ports.SmtpPort))
s.restart() defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
return &emptypb.Empty{}, nil
return &emptypb.Empty{}, s.SendEvent(NewMailSettingsChangePortFinishedEvent())
} }
func (s *Service) IsPortFree(_ context.Context, port *wrapperspb.Int32Value) (*wrapperspb.BoolValue, error) { func (s *Service) IsPortFree(_ context.Context, port *wrapperspb.Int32Value) (*wrapperspb.BoolValue, error) {

View File

@ -124,7 +124,7 @@ func (s *Service) RemoveUser(_ context.Context, userID *wrapperspb.StringValue)
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
func (s *Service) ConfigureUserAppleMail(_ context.Context, request *ConfigureAppleMailRequest) (*emptypb.Empty, error) { func (s *Service) ConfigureUserAppleMail(ctx context.Context, request *ConfigureAppleMailRequest) (*emptypb.Empty, error) {
s.log.WithField("UserID", request.UserID).WithField("Address", request.Address).Info("ConfigureUserAppleMail") s.log.WithField("UserID", request.UserID).WithField("Address", request.Address).Info("ConfigureUserAppleMail")
restart, err := s.bridge.ConfigureAppleMail(request.UserID, request.Address) restart, err := s.bridge.ConfigureAppleMail(request.UserID, request.Address)
@ -137,7 +137,7 @@ func (s *Service) ConfigureUserAppleMail(_ context.Context, request *ConfigureAp
if restart { if restart {
s.log.Warn("Detected Catalina or newer with bad SMTP SSL settings, now using SSL, bridge needs to restart") s.log.Warn("Detected Catalina or newer with bad SMTP SSL settings, now using SSL, bridge needs to restart")
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
s.restart() return s.Restart(ctx, &emptypb.Empty{})
} }
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil