diff --git a/.gitignore b/.gitignore
index 897ac864..a7185b5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,8 @@ vendor-cache
cmd/Desktop-Bridge/deploy
cmd/Import-Export/deploy
proton-bridge
+cmd/Desktop-Bridge/*.exe
+cmd/launcher/*.exe
# Jetbrains (CLion, Golang) cmake build dirs
cmake-build-*/
diff --git a/cmd/launcher/main.go b/cmd/launcher/main.go
index 07eb55ef..89b3bcd3 100644
--- a/cmd/launcher/main.go
+++ b/cmd/launcher/main.go
@@ -22,6 +22,7 @@ import (
"os"
"path/filepath"
"runtime"
+ "time"
"github.com/Masterminds/semver/v3"
"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/updater"
"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/sirupsen/logrus"
"golang.org/x/sys/execabs"
@@ -46,6 +50,7 @@ const (
FlagCLI = "--cli"
FlagLauncher = "--launcher"
+ FlagWait = "--wait"
)
func main() { //nolint:funlen
@@ -92,7 +97,7 @@ func main() { //nolint:funlen
exeToLaunch := guiName
args := os.Args[1:]
- if isCliMode(&args) {
+ if inCLIMode(args) {
exeToLaunch = exeName
}
@@ -108,6 +113,12 @@ func main() { //nolint:funlen
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.Stdin = os.Stdin
@@ -115,7 +126,8 @@ func main() { //nolint:funlen
cmd.Stderr = os.Stderr
// 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()
} else {
err = cmd.Run()
@@ -152,14 +164,21 @@ func appendLauncherPath(path string, args []string) []string {
return res
}
-func isCliMode(args *[]string) bool {
- for _, v := range *args {
- if v == FlagCLI {
- return true
- }
- }
+func inCLIMode(args []string) bool {
+ return sliceContains(args, FlagCLI)
+}
- 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(
@@ -222,3 +241,44 @@ func getFallbackExecutable(name string, versioner *versioner.Versioner) (string,
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)
+}
diff --git a/cmd/launcher/main_test.go b/cmd/launcher/main_test.go
new file mode 100644
index 00000000..4d0e823a
--- /dev/null
+++ b/cmd/launcher/main_test.go
@@ -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 .
+
+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{}))
+}
diff --git a/internal/app/base/restart.go b/internal/app/base/restart.go
index a7014d60..efd9ef54 100644
--- a/internal/app/base/restart.go
+++ b/internal/app/base/restart.go
@@ -42,6 +42,8 @@ func (b *Base) restartApp(crash bool) error {
args = forceLauncherFlag(args, b.launcher)
}
+ args = append(args, "--wait")
+
logrus.
WithField("command", b.command).
WithField("args", args).
diff --git a/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.cpp b/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.cpp
index fe9223a0..a0cddb68 100644
--- a/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.cpp
+++ b/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.cpp
@@ -31,8 +31,8 @@ using namespace bridgepp;
EventStreamReader::EventStreamReader(QObject *parent)
: Worker(parent)
{
- connect(this, &EventStreamReader::started, [&]() { app().log().debug("EventStreamReader started"); });
- connect(this, &EventStreamReader::finished, [&]() { app().log().debug("EventStreamReader finished"); });
+ connect(this, &EventStreamReader::started, this, &EventStreamReader::onStarted);
+ connect(this, &EventStreamReader::finished, this, &EventStreamReader::onFinished);
connect(this, &EventStreamReader::error, &app().log(), &Log::error);
}
@@ -57,3 +57,27 @@ void EventStreamReader::run()
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);
+ }
+}
diff --git a/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.h b/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.h
index e9976b19..b9aa9ba8 100644
--- a/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.h
+++ b/internal/frontend/bridge-gui/bridge-gui/EventStreamWorker.h
@@ -39,6 +39,8 @@ public: // member functions
public slots:
void run() override; ///< Run the reader.
+ void onStarted() const; ///< Slot for the 'started' signal.
+ void onFinished() const; ///< Slot for the 'finished' signal.
signals:
void eventReceived(QString eventString); ///< signal for events.
diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp
index ec200c59..24f5e326 100644
--- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp
+++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp
@@ -229,6 +229,10 @@ void QMLBackend::restart()
app().grpc().quit();
}
+
+//****************************************************************************************************************************************************
+/// \param[in] launcher The path to the launcher.
+//****************************************************************************************************************************************************
void QMLBackend::forceLauncher(QString launcher)
{
app().grpc().forceLauncher(launcher);
@@ -270,8 +274,9 @@ void QMLBackend::changeColorScheme(QString const &scheme)
//****************************************************************************************************************************************************
void QMLBackend::toggleUseSSLforSMTP(bool makeItActive)
{
- if (app().grpc().setUseSSLForSMTP(makeItActive).ok())
- emit useSSLforSMTPChanged(makeItActive);
+ // 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().setUseSSLForSMTP(makeItActive);
}
@@ -281,11 +286,21 @@ void QMLBackend::toggleUseSSLforSMTP(bool makeItActive)
//****************************************************************************************************************************************************
void QMLBackend::changePorts(int imapPort, int smtpPort)
{
- if (app().grpc().changePorts(imapPort, smtpPort).ok())
- {
- emit portIMAPChanged(imapPort);
- emit portSMTPChanged(smtpPort);
- }
+ // 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().changePorts(imapPort, 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();
this->restart();
}
+
+
diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h
index ab7f6830..6ae1fa83 100644
--- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h
+++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h
@@ -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 toggleBeta(bool active); // _ func(makeItActive bool) `slot:"toggleBeta"`
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 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"`
@@ -211,6 +211,7 @@ signals: // Signals received from the Go backend, to be forwarded to QML
void bugReportSendSuccess(); // _ func() `signal:"bugReportSendSuccess"`
void bugReportSendError(); // _ func() `signal:"bugReportSendError"`
void showMainWindow(); // _ func() `signal:showMainWindow`
+ void hideMainWindow();
private: // member functions
void retrieveUserList(); ///< Retrieve the list of users via gRPC.
diff --git a/internal/frontend/bridge-gui/bridge-gui/main.cpp b/internal/frontend/bridge-gui/bridge-gui/main.cpp
index 2256f33c..ede55b78 100644
--- a/internal/frontend/bridge-gui/bridge-gui/main.cpp
+++ b/internal/frontend/bridge-gui/bridge-gui/main.cpp
@@ -224,12 +224,14 @@ void closeBridgeApp()
//****************************************************************************************************************************************************
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
{
- if (QSysInfo::productType() != "windows")
- QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
-
- QApplication guiApp(argc, argv);
initQtApplication();
Log &log = initLog();
@@ -302,6 +304,7 @@ int main(int argc, char *argv[])
}
catch (Exception const &e)
{
+ QMessageBox::critical(nullptr, "Error", e.qwhat());
QTextStream(stderr) << e.qwhat() << "\n";
return EXIT_FAILURE;
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml
index 75cc2a6f..80127650 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml
@@ -59,6 +59,10 @@ QtObject {
mainWindow.showAndRise()
}
function onColorSchemeNameChanged(scheme) { root.setColorScheme() }
+
+ function onHideMainWindow() {
+ mainWindow.hide();
+ }
}
}
diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp
index 89e3a3a8..a0cd87aa 100644
--- a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp
+++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp
@@ -288,7 +288,9 @@ grpc::Status GRPCClient::reportBug(QString const &description, QString const &ad
//****************************************************************************************************************************************************
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::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::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] 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 allowedErrors)
{
if (log_)
{
- if (status.ok())
+ if (status.ok() || allowedErrors.contains(status.error_code()))
log_->debug(QString("%1()").arg(callName));
else
log_->error(QString("%1() FAILED").arg(callName));
diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h
index 15ea4e67..d1c645f3 100644
--- a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h
+++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h
@@ -201,7 +201,7 @@ private slots:
private:
void logDebug(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 allowedErrors = {}); ///< Log the status of a gRPC code.
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 getBool(BoolGetter getter, bool &outValue); ///< perform a gRPC call to a bool getter.
diff --git a/internal/frontend/grpc/service.go b/internal/frontend/grpc/service.go
index 579349c6..bc927d14 100644
--- a/internal/frontend/grpc/service.go
+++ b/internal/frontend/grpc/service.go
@@ -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
import (
+ "context"
cryptotls "crypto/tls"
"net"
"strings"
@@ -39,6 +40,7 @@ import (
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
+ "google.golang.org/protobuf/types/known/emptypb"
)
// Service is the RPC service struct.
@@ -223,7 +225,7 @@ func (s *Service) watchEvents() { // nolint:funlen
case <-secondInstanceCh:
_ = s.SendEvent(NewShowMainWindowEvent())
case <-restartBridgeCh:
- s.restart()
+ _, _ = s.Restart(context.Background(), &emptypb.Empty{})
case address := <-addressChangedCh:
_ = s.SendEvent(NewMailAddressChangeEvent(address))
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() {
defer func() {
_ = s.SendEvent(NewResetFinishedEvent())
diff --git a/internal/frontend/grpc/service_methods.go b/internal/frontend/grpc/service_methods.go
index aa22174d..c0b49661 100644
--- a/internal/frontend/grpc/service_methods.go
+++ b/internal/frontend/grpc/service_methods.go
@@ -38,8 +38,6 @@ import (
"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) {
entry := s.log
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.
func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
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.
- 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")
+ // Windows is notably slow at Quitting. We do it in a goroutine to speed things up a bit.
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
}
+// 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) {
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
}
-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).
WithField("diskCachePath", change.DiskCachePath).
Info("DiskCachePath")
+ defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
defer func() { _ = s.SendEvent(NewCacheChangeLocalCacheFinishedEvent()) }()
defer func() { _ = s.SendEvent(NewIsCacheOnDiskEnabledChanged(s.bridge.GetBool(settings.CacheEnabledKey))) }()
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.restart()
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
}
-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")
if s.bridge.GetBool(settings.SMTPSSLKey) == useSsl.Value {
return &emptypb.Empty{}, nil
}
- defer func() { _ = s.SendEvent(NewMailSettingsUseSslForSmtpFinishedEvent()) }()
-
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
@@ -581,16 +577,15 @@ func (s *Service) SmtpPort(context.Context, *emptypb.Empty) (*wrapperspb.Int32Va
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")
- defer func() { _ = s.SendEvent(NewMailSettingsChangePortFinishedEvent()) }()
-
s.bridge.SetInt(settings.IMAPPortKey, int(ports.ImapPort))
s.bridge.SetInt(settings.SMTPPortKey, int(ports.SmtpPort))
- s.restart()
- return &emptypb.Empty{}, nil
+ defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
+
+ return &emptypb.Empty{}, s.SendEvent(NewMailSettingsChangePortFinishedEvent())
}
func (s *Service) IsPortFree(_ context.Context, port *wrapperspb.Int32Value) (*wrapperspb.BoolValue, error) {
diff --git a/internal/frontend/grpc/service_user.go b/internal/frontend/grpc/service_user.go
index 14c8e390..9a6af477 100644
--- a/internal/frontend/grpc/service_user.go
+++ b/internal/frontend/grpc/service_user.go
@@ -124,7 +124,7 @@ func (s *Service) RemoveUser(_ context.Context, userID *wrapperspb.StringValue)
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")
restart, err := s.bridge.ConfigureAppleMail(request.UserID, request.Address)
@@ -137,7 +137,7 @@ func (s *Service) ConfigureUserAppleMail(_ context.Context, request *ConfigureAp
if 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)
- s.restart()
+ return s.Restart(ctx, &emptypb.Empty{})
}
return &emptypb.Empty{}, nil