mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-17 23:56:56 +00:00
GODT-1779: Remove go-imap
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -72,7 +72,6 @@ service Bridge {
|
||||
rpc IsAutomaticUpdateOn(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||
|
||||
// cache
|
||||
rpc IsCacheOnDiskEnabled (google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||
rpc DiskCachePath(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||
rpc ChangeLocalCache(ChangeLocalCacheRequest) returns (google.protobuf.Empty);
|
||||
|
||||
@ -160,7 +159,6 @@ message LoginAbortRequest {
|
||||
// Cache on disk related message
|
||||
//**********************************************************
|
||||
message ChangeLocalCacheRequest {
|
||||
bool enableDiskCache = 1;
|
||||
string diskCachePath = 2;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.3
|
||||
// - protoc v3.21.7
|
||||
// source: bridge.proto
|
||||
|
||||
package grpc
|
||||
@ -64,7 +64,6 @@ type BridgeClient interface {
|
||||
SetIsAutomaticUpdateOn(ctx context.Context, in *wrapperspb.BoolValue, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
IsAutomaticUpdateOn(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.BoolValue, error)
|
||||
// cache
|
||||
IsCacheOnDiskEnabled(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.BoolValue, error)
|
||||
DiskCachePath(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.StringValue, error)
|
||||
ChangeLocalCache(ctx context.Context, in *ChangeLocalCacheRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// mail
|
||||
@ -425,15 +424,6 @@ func (c *bridgeClient) IsAutomaticUpdateOn(ctx context.Context, in *emptypb.Empt
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *bridgeClient) IsCacheOnDiskEnabled(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.BoolValue, error) {
|
||||
out := new(wrapperspb.BoolValue)
|
||||
err := c.cc.Invoke(ctx, "/grpc.Bridge/IsCacheOnDiskEnabled", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *bridgeClient) DiskCachePath(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.StringValue, error) {
|
||||
out := new(wrapperspb.StringValue)
|
||||
err := c.cc.Invoke(ctx, "/grpc.Bridge/DiskCachePath", in, out, opts...)
|
||||
@ -699,7 +689,6 @@ type BridgeServer interface {
|
||||
SetIsAutomaticUpdateOn(context.Context, *wrapperspb.BoolValue) (*emptypb.Empty, error)
|
||||
IsAutomaticUpdateOn(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error)
|
||||
// cache
|
||||
IsCacheOnDiskEnabled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error)
|
||||
DiskCachePath(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error)
|
||||
ChangeLocalCache(context.Context, *ChangeLocalCacheRequest) (*emptypb.Empty, error)
|
||||
// mail
|
||||
@ -841,9 +830,6 @@ func (UnimplementedBridgeServer) SetIsAutomaticUpdateOn(context.Context, *wrappe
|
||||
func (UnimplementedBridgeServer) IsAutomaticUpdateOn(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method IsAutomaticUpdateOn not implemented")
|
||||
}
|
||||
func (UnimplementedBridgeServer) IsCacheOnDiskEnabled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method IsCacheOnDiskEnabled not implemented")
|
||||
}
|
||||
func (UnimplementedBridgeServer) DiskCachePath(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DiskCachePath not implemented")
|
||||
}
|
||||
@ -1571,24 +1557,6 @@ func _Bridge_IsAutomaticUpdateOn_Handler(srv interface{}, ctx context.Context, d
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Bridge_IsCacheOnDiskEnabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(emptypb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BridgeServer).IsCacheOnDiskEnabled(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/grpc.Bridge/IsCacheOnDiskEnabled",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BridgeServer).IsCacheOnDiskEnabled(ctx, req.(*emptypb.Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Bridge_DiskCachePath_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(emptypb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
@ -2139,10 +2107,6 @@ var Bridge_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "IsAutomaticUpdateOn",
|
||||
Handler: _Bridge_IsAutomaticUpdateOn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "IsCacheOnDiskEnabled",
|
||||
Handler: _Bridge_IsCacheOnDiskEnabled_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DiskCachePath",
|
||||
Handler: _Bridge_DiskCachePath_Handler,
|
||||
|
||||
32
internal/frontend/grpc/event_utils.go
Normal file
32
internal/frontend/grpc/event_utils.go
Normal file
@ -0,0 +1,32 @@
|
||||
// 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 grpc
|
||||
|
||||
import "github.com/bradenaw/juniper/xslices"
|
||||
|
||||
// isInternetStatus returns true iff the event is InternetStatus.
|
||||
func (x *StreamEvent) isInternetStatus() bool {
|
||||
appEvent := x.GetApp()
|
||||
|
||||
return (appEvent != nil) && (appEvent.GetInternetStatus() != nil)
|
||||
}
|
||||
|
||||
// filterOutInternetStatusEvents return a copy of the events list where all internet connection events have been removed.
|
||||
func filterOutInternetStatusEvents(events []*StreamEvent) []*StreamEvent {
|
||||
return xslices.Filter(events, func(event *StreamEvent) bool { return !event.isInternetStatus() })
|
||||
}
|
||||
@ -21,34 +21,29 @@ package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
cryptotls "crypto/tls"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/tls"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/certs"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/crash"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/restarter"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gitlab.protontech.ch/go/liteapi"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -59,6 +54,7 @@ const (
|
||||
// Service is the RPC service struct.
|
||||
type Service struct { // nolint:structcheck
|
||||
UnimplementedBridgeServer
|
||||
|
||||
grpcServer *grpc.Server // the gGRPC server
|
||||
listener net.Listener
|
||||
eventStreamCh chan *StreamEvent
|
||||
@ -66,99 +62,87 @@ type Service struct { // nolint:structcheck
|
||||
eventQueue []*StreamEvent
|
||||
eventQueueMutex sync.Mutex
|
||||
|
||||
panicHandler types.PanicHandler
|
||||
eventListener listener.Listener
|
||||
updater types.Updater
|
||||
updateCheckMutex sync.Mutex
|
||||
bridge types.Bridger
|
||||
restarter types.Restarter
|
||||
showOnStartup bool
|
||||
authClient pmapi.Client
|
||||
auth *pmapi.Auth
|
||||
password []byte
|
||||
newVersionInfo updater.VersionInfo
|
||||
panicHandler *crash.Handler
|
||||
restarter *restarter.Restarter
|
||||
bridge *bridge.Bridge
|
||||
newVersionInfo updater.VersionInfo
|
||||
|
||||
log *logrus.Entry
|
||||
initializing sync.WaitGroup
|
||||
initializationDone sync.Once
|
||||
firstTimeAutostart sync.Once
|
||||
locations *locations.Locations
|
||||
token string
|
||||
pemCert string
|
||||
|
||||
showOnStartup bool
|
||||
}
|
||||
|
||||
// NewService returns a new instance of the service.
|
||||
func NewService(
|
||||
showOnStartup bool,
|
||||
panicHandler types.PanicHandler,
|
||||
eventListener listener.Listener,
|
||||
updater types.Updater,
|
||||
bridge types.Bridger,
|
||||
restarter types.Restarter,
|
||||
panicHandler *crash.Handler,
|
||||
restarter *restarter.Restarter,
|
||||
locations *locations.Locations,
|
||||
) *Service {
|
||||
s := Service{
|
||||
UnimplementedBridgeServer: UnimplementedBridgeServer{},
|
||||
panicHandler: panicHandler,
|
||||
eventListener: eventListener,
|
||||
updater: updater,
|
||||
bridge: bridge,
|
||||
restarter: restarter,
|
||||
showOnStartup: showOnStartup,
|
||||
bridge *bridge.Bridge,
|
||||
showOnStartup bool,
|
||||
) (*Service, error) {
|
||||
tlsConfig, certPEM, err := newTLSConfig()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panic("Could not generate gRPC TLS config")
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0") // Port should be provided by the OS.
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panic("Could not create gRPC listener")
|
||||
}
|
||||
|
||||
token := uuid.NewString()
|
||||
|
||||
if path, err := saveGRPCServerConfigFile(locations, listener, token, certPEM); err != nil {
|
||||
logrus.WithError(err).WithField("path", path).Panic("Could not write gRPC service config file")
|
||||
} else {
|
||||
logrus.WithField("path", path).Info("Successfully saved gRPC service config file")
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
grpcServer: grpc.NewServer(
|
||||
grpc.Creds(credentials.NewTLS(tlsConfig)),
|
||||
grpc.UnaryInterceptor(newUnaryTokenValidator(token)),
|
||||
grpc.StreamInterceptor(newStreamTokenValidator(token)),
|
||||
),
|
||||
listener: listener,
|
||||
|
||||
panicHandler: panicHandler,
|
||||
restarter: restarter,
|
||||
bridge: bridge,
|
||||
|
||||
log: logrus.WithField("pkg", "grpc"),
|
||||
initializing: sync.WaitGroup{},
|
||||
initializationDone: sync.Once{},
|
||||
firstTimeAutostart: sync.Once{},
|
||||
locations: locations,
|
||||
token: uuid.NewString(),
|
||||
|
||||
showOnStartup: showOnStartup,
|
||||
}
|
||||
|
||||
// Initializing.Done is only called sync.Once. Please keep the increment
|
||||
// set to 1
|
||||
// Initializing.Done is only called sync.Once. Please keep the increment set to 1
|
||||
s.initializing.Add(1)
|
||||
|
||||
tlsConfig, pemCert, err := s.generateTLSConfig()
|
||||
if err != nil {
|
||||
s.log.WithError(err).Panic("Could not generate gRPC TLS config")
|
||||
}
|
||||
|
||||
s.pemCert = string(pemCert)
|
||||
|
||||
// Initialize the autostart.
|
||||
s.initAutostart()
|
||||
s.grpcServer = grpc.NewServer(
|
||||
grpc.Creds(credentials.NewTLS(tlsConfig)),
|
||||
grpc.UnaryInterceptor(s.validateUnaryServerToken),
|
||||
grpc.StreamInterceptor(s.validateStreamServerToken),
|
||||
)
|
||||
|
||||
RegisterBridgeServer(s.grpcServer, &s)
|
||||
|
||||
s.listener, err = net.Listen("tcp", "127.0.0.1:0") // Port 0 means that the port is randomly picked by the system.
|
||||
if err != nil {
|
||||
s.log.WithError(err).Panic("Could not create gRPC listener")
|
||||
}
|
||||
|
||||
if path, err := s.saveGRPCServerConfigFile(); err != nil {
|
||||
s.log.WithError(err).WithField("path", path).Panic("Could not write gRPC service config file")
|
||||
} else {
|
||||
s.log.WithField("path", path).Info("Successfully saved gRPC service config file")
|
||||
}
|
||||
// Register the gRPC service implementation.
|
||||
RegisterBridgeServer(s.grpcServer, s)
|
||||
|
||||
s.log.Info("gRPC server listening on ", s.listener.Addr())
|
||||
|
||||
return &s
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// GODT-1507 Windows: autostart needs to be created after Qt is initialized.
|
||||
// GODT-1206: if preferences file says it should be on enable it here.
|
||||
// TO-DO GODT-1681 Autostart needs to be properly implement for gRPC approach.
|
||||
func (s *Service) initAutostart() {
|
||||
// GODT-1507 Windows: autostart needs to be created after Qt is initialized.
|
||||
// GODT-1206: if preferences file says it should be on enable it here.
|
||||
|
||||
// TO-DO GODT-1681 Autostart needs to be properly implement for gRPC approach.
|
||||
|
||||
s.firstTimeAutostart.Do(func() {
|
||||
shouldAutostartBeOn := s.bridge.GetBool(settings.AutostartKey)
|
||||
if s.bridge.IsFirstStart() || shouldAutostartBeOn {
|
||||
if err := s.bridge.EnableAutostart(); err != nil {
|
||||
shouldAutostartBeOn := s.bridge.GetAutostart()
|
||||
if s.bridge.GetFirstStart() || shouldAutostartBeOn {
|
||||
if err := s.bridge.SetAutostart(true); err != nil {
|
||||
s.log.WithField("prefs", shouldAutostartBeOn).WithError(err).Error("Failed to enable first autostart")
|
||||
}
|
||||
return
|
||||
@ -168,7 +152,7 @@ func (s *Service) initAutostart() {
|
||||
|
||||
func (s *Service) Loop() error {
|
||||
defer func() {
|
||||
s.bridge.SetBool(settings.FirstStartGUIKey, false)
|
||||
_ = s.bridge.SetFirstStartGUI(false)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
@ -179,7 +163,7 @@ func (s *Service) Loop() error {
|
||||
s.log.Info("Starting gRPC server")
|
||||
|
||||
if err := s.grpcServer.Serve(s.listener); err != nil {
|
||||
s.log.WithError(err).Error("Error serving gRPC")
|
||||
s.log.WithError(err).Error("Failed to serve gRPC")
|
||||
return err
|
||||
}
|
||||
|
||||
@ -212,140 +196,59 @@ func (s *Service) WaitUntilFrontendIsReady() {
|
||||
s.initializing.Wait()
|
||||
}
|
||||
|
||||
func (s *Service) watchEvents() { // nolint:funlen
|
||||
if s.bridge.HasError(bridge.ErrLocalCacheUnavailable) {
|
||||
_ = s.SendEvent(NewCacheErrorEvent(CacheErrorType_CACHE_UNAVAILABLE_ERROR))
|
||||
}
|
||||
func (s *Service) watchEvents() {
|
||||
eventCh, done := s.bridge.GetEvents()
|
||||
defer done()
|
||||
|
||||
errorCh := s.eventListener.ProvideChannel(events.ErrorEvent)
|
||||
credentialsErrorCh := s.eventListener.ProvideChannel(events.CredentialsErrorEvent)
|
||||
noActiveKeyForRecipientCh := s.eventListener.ProvideChannel(events.NoActiveKeyForRecipientEvent)
|
||||
internetConnChangedCh := s.eventListener.ProvideChannel(events.InternetConnChangedEvent)
|
||||
secondInstanceCh := s.eventListener.ProvideChannel(events.SecondInstanceEvent)
|
||||
restartBridgeCh := s.eventListener.ProvideChannel(events.RestartBridgeEvent)
|
||||
addressChangedCh := s.eventListener.ProvideChannel(events.AddressChangedEvent)
|
||||
addressChangedLogoutCh := s.eventListener.ProvideChannel(events.AddressChangedLogoutEvent)
|
||||
logoutCh := s.eventListener.ProvideChannel(events.LogoutEvent)
|
||||
updateApplicationCh := s.eventListener.ProvideChannel(events.UpgradeApplicationEvent)
|
||||
userChangedCh := s.eventListener.ProvideChannel(events.UserRefreshEvent)
|
||||
certIssue := s.eventListener.ProvideChannel(events.TLSCertIssue)
|
||||
|
||||
// we forward events to the GUI/frontend via the gRPC event stream.
|
||||
for {
|
||||
select {
|
||||
case errorDetails := <-errorCh:
|
||||
if strings.Contains(errorDetails, "IMAP failed") {
|
||||
_ = s.SendEvent(NewMailSettingsErrorEvent(MailSettingsErrorType_IMAP_PORT_ISSUE))
|
||||
}
|
||||
if strings.Contains(errorDetails, "SMTP failed") {
|
||||
_ = s.SendEvent(NewMailSettingsErrorEvent(MailSettingsErrorType_SMTP_PORT_ISSUE))
|
||||
}
|
||||
case reason := <-credentialsErrorCh:
|
||||
if reason == keychain.ErrMacKeychainRebuild.Error() {
|
||||
_ = s.SendEvent(NewKeychainRebuildKeychainEvent())
|
||||
continue
|
||||
}
|
||||
// TODO: Better error events.
|
||||
for _, err := range s.bridge.GetErrors() {
|
||||
switch {
|
||||
case errors.Is(err, vault.ErrCorrupt):
|
||||
_ = s.SendEvent(NewKeychainHasNoKeychainEvent())
|
||||
case email := <-noActiveKeyForRecipientCh:
|
||||
_ = s.SendEvent(NewMailNoActiveKeyForRecipientEvent(email))
|
||||
case stat := <-internetConnChangedCh:
|
||||
if stat == events.InternetOff {
|
||||
_ = s.SendEvent(NewInternetStatusEvent(false))
|
||||
}
|
||||
if stat == events.InternetOn {
|
||||
_ = s.SendEvent(NewInternetStatusEvent(true))
|
||||
}
|
||||
|
||||
case <-secondInstanceCh:
|
||||
_ = s.SendEvent(NewShowMainWindowEvent())
|
||||
case <-restartBridgeCh:
|
||||
_, _ = s.Restart(
|
||||
metadata.AppendToOutgoingContext(context.Background(), serverTokenMetadataKey, s.token),
|
||||
&emptypb.Empty{},
|
||||
)
|
||||
case address := <-addressChangedCh:
|
||||
_ = s.SendEvent(NewMailAddressChangeEvent(address))
|
||||
case address := <-addressChangedLogoutCh:
|
||||
_ = s.SendEvent(NewMailAddressChangeLogoutEvent(address))
|
||||
case userID := <-logoutCh:
|
||||
user, err := s.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = s.SendEvent(NewUserDisconnectedEvent(user.Username))
|
||||
case <-updateApplicationCh:
|
||||
s.updateForce()
|
||||
case userID := <-userChangedCh:
|
||||
_ = s.SendEvent(NewUserChangedEvent(userID))
|
||||
case <-certIssue:
|
||||
_ = s.SendEvent(NewMailApiCertIssue())
|
||||
case errors.Is(err, vault.ErrInsecure):
|
||||
_ = s.SendEvent(NewKeychainHasNoKeychainEvent())
|
||||
|
||||
case errors.Is(err, bridge.ErrServeIMAP):
|
||||
_ = s.SendEvent(NewMailSettingsErrorEvent(MailSettingsErrorType_IMAP_PORT_ISSUE))
|
||||
|
||||
case errors.Is(err, bridge.ErrServeSMTP):
|
||||
_ = s.SendEvent(NewMailSettingsErrorEvent(MailSettingsErrorType_SMTP_PORT_ISSUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) loginAbort() {
|
||||
s.loginClean()
|
||||
}
|
||||
for event := range eventCh {
|
||||
switch event := event.(type) {
|
||||
case events.ConnStatus:
|
||||
_ = s.SendEvent(NewInternetStatusEvent(event.Status == liteapi.StatusUp))
|
||||
|
||||
func (s *Service) loginClean() {
|
||||
s.auth = nil
|
||||
s.authClient = nil
|
||||
for i := range s.password {
|
||||
s.password[i] = '\x00'
|
||||
}
|
||||
s.password = s.password[0:0]
|
||||
}
|
||||
case events.Raise:
|
||||
_ = s.SendEvent(NewShowMainWindowEvent())
|
||||
|
||||
func (s *Service) finishLogin() {
|
||||
defer s.loginClean()
|
||||
case events.UserAddressCreated:
|
||||
_ = s.SendEvent(NewMailAddressChangeEvent(event.Address))
|
||||
|
||||
if len(s.password) == 0 || s.auth == nil || s.authClient == nil {
|
||||
s.log.
|
||||
WithField("hasPass", len(s.password) != 0).
|
||||
WithField("hasAuth", s.auth != nil).
|
||||
WithField("hasClient", s.authClient != nil).
|
||||
Error("Finish login: authentication incomplete")
|
||||
case events.UserAddressChanged:
|
||||
_ = s.SendEvent(NewMailAddressChangeEvent(event.Address))
|
||||
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TWO_PASSWORDS_ABORT, "Missing authentication, try again."))
|
||||
return
|
||||
}
|
||||
case events.UserAddressDeleted:
|
||||
_ = s.SendEvent(NewMailAddressChangeLogoutEvent(event.Address))
|
||||
|
||||
done := make(chan string)
|
||||
s.eventListener.Add(events.UserChangeDone, done)
|
||||
defer s.eventListener.Remove(events.UserChangeDone, done)
|
||||
case events.UserChanged:
|
||||
_ = s.SendEvent(NewUserChangedEvent(event.UserID))
|
||||
|
||||
userID, err := s.bridge.FinishLogin(s.authClient, s.auth, s.password)
|
||||
|
||||
if err != nil && err != users.ErrUserAlreadyConnected {
|
||||
s.log.WithError(err).Errorf("Finish login failed")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TWO_PASSWORDS_ABORT, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// The user changed should be triggered by FinishLogin, but it is not
|
||||
// guaranteed when this is going to happen. Therefor we should wait
|
||||
// until we receive the signal from userChanged function.
|
||||
s.waitForUserChangeDone(done, userID)
|
||||
|
||||
s.log.WithField("userID", userID).Debug("Login finished")
|
||||
_ = s.SendEvent(NewLoginFinishedEvent(userID))
|
||||
|
||||
if err == users.ErrUserAlreadyConnected {
|
||||
s.log.WithError(err).Error("User already logged in")
|
||||
_ = s.SendEvent(NewLoginAlreadyLoggedInEvent(userID))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) waitForUserChangeDone(done <-chan string, userID string) {
|
||||
for {
|
||||
select {
|
||||
case changedID := <-done:
|
||||
if changedID == userID {
|
||||
return
|
||||
case events.UserDeauth:
|
||||
if user, err := s.bridge.GetUserInfo(event.UserID); err != nil {
|
||||
s.log.WithError(err).Error("Failed to get user info")
|
||||
} else {
|
||||
_ = s.SendEvent(NewUserDisconnectedEvent(user.Username))
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
s.log.WithField("ID", userID).Warning("Login finished but user not added within 2 seconds")
|
||||
return
|
||||
|
||||
case events.TLSIssue:
|
||||
_ = s.SendEvent(NewMailApiCertIssue())
|
||||
|
||||
case events.UpdateForced:
|
||||
panic("TODO")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,103 +257,46 @@ func (s *Service) triggerReset() {
|
||||
defer func() {
|
||||
_ = s.SendEvent(NewResetFinishedEvent())
|
||||
}()
|
||||
s.bridge.FactoryReset()
|
||||
if err := s.bridge.FactoryReset(context.Background()); err != nil {
|
||||
s.log.WithError(err).Error("Failed to reset")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) checkUpdate() {
|
||||
version, err := s.updater.Check()
|
||||
func newTLSConfig() (*tls.Config, []byte, error) {
|
||||
template, err := certs.NewTLSTemplate()
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("An error occurred while checking for updates")
|
||||
s.SetVersion(updater.VersionInfo{})
|
||||
return
|
||||
}
|
||||
s.SetVersion(version)
|
||||
}
|
||||
|
||||
func (s *Service) updateForce() {
|
||||
s.updateCheckMutex.Lock()
|
||||
defer s.updateCheckMutex.Unlock()
|
||||
s.checkUpdate()
|
||||
_ = s.SendEvent(NewUpdateForceEvent(s.newVersionInfo.Version.String()))
|
||||
}
|
||||
|
||||
func (s *Service) checkUpdateAndNotify(isReqFromUser bool) {
|
||||
s.updateCheckMutex.Lock()
|
||||
defer func() {
|
||||
s.updateCheckMutex.Unlock()
|
||||
_ = s.SendEvent(NewUpdateCheckFinishedEvent())
|
||||
}()
|
||||
|
||||
s.checkUpdate()
|
||||
version := s.newVersionInfo
|
||||
if (version.Version == nil) || (version.Version.String() == "") {
|
||||
if isReqFromUser {
|
||||
_ = s.SendEvent(NewUpdateErrorEvent(UpdateErrorType_UPDATE_MANUAL_ERROR))
|
||||
}
|
||||
return
|
||||
}
|
||||
if !s.updater.IsUpdateApplicable(s.newVersionInfo) {
|
||||
s.log.Info("No need to update")
|
||||
if isReqFromUser {
|
||||
_ = s.SendEvent(NewUpdateIsLatestVersionEvent())
|
||||
}
|
||||
} else if isReqFromUser {
|
||||
s.NotifyManualUpdate(s.newVersionInfo, s.updater.CanInstall(s.newVersionInfo))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) installUpdate() {
|
||||
s.updateCheckMutex.Lock()
|
||||
defer s.updateCheckMutex.Unlock()
|
||||
|
||||
if !s.updater.CanInstall(s.newVersionInfo) {
|
||||
s.log.Warning("Skipping update installation, current version too old")
|
||||
_ = s.SendEvent(NewUpdateErrorEvent(UpdateErrorType_UPDATE_MANUAL_ERROR))
|
||||
return
|
||||
return nil, nil, fmt.Errorf("failed to create TLS template: %w", err)
|
||||
}
|
||||
|
||||
if err := s.updater.InstallUpdate(s.newVersionInfo); err != nil {
|
||||
if errors.Cause(err) == updater.ErrDownloadVerify {
|
||||
s.log.WithError(err).Warning("Skipping update installation due to temporary error")
|
||||
} else {
|
||||
s.log.WithError(err).Error("The update couldn't be installed")
|
||||
_ = s.SendEvent(NewUpdateErrorEvent(UpdateErrorType_UPDATE_MANUAL_ERROR))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_ = s.SendEvent(NewUpdateSilentRestartNeededEvent())
|
||||
}
|
||||
|
||||
func (s *Service) generateTLSConfig() (tlsConfig *cryptotls.Config, pemCert []byte, err error) {
|
||||
pemCert, pemKey, err := tls.NewPEMKeyPair()
|
||||
certPEM, keyPEM, err := certs.GenerateCert(template)
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("Could not get TLS config")
|
||||
return nil, nil, fmt.Errorf("failed to generate cert: %w", err)
|
||||
}
|
||||
|
||||
tlsConfig, err = tls.GetConfigFromPEMKeyPair(pemCert, pemKey)
|
||||
cert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("Could not get TLS config")
|
||||
return nil, nil, fmt.Errorf("failed to load cert: %w", err)
|
||||
}
|
||||
|
||||
tlsConfig.ClientAuth = cryptotls.NoClientCert // skip client auth if the certificate allow it.
|
||||
|
||||
return tlsConfig, pemCert, nil
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
}, certPEM, nil
|
||||
}
|
||||
|
||||
func (s *Service) saveGRPCServerConfigFile() (string, error) {
|
||||
address, ok := s.listener.Addr().(*net.TCPAddr)
|
||||
func saveGRPCServerConfigFile(locations *locations.Locations, listener net.Listener, token string, certPEM []byte) (string, error) {
|
||||
address, ok := listener.Addr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("could not retrieve gRPC service listener address")
|
||||
}
|
||||
|
||||
sc := config{
|
||||
Port: address.Port,
|
||||
Cert: s.pemCert,
|
||||
Token: s.token,
|
||||
Cert: string(certPEM),
|
||||
Token: token,
|
||||
}
|
||||
|
||||
settingsPath, err := s.locations.ProvideSettingsPath()
|
||||
settingsPath, err := locations.ProvideSettingsPath()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -461,7 +307,7 @@ func (s *Service) saveGRPCServerConfigFile() (string, error) {
|
||||
}
|
||||
|
||||
// validateServerToken verify that the server token provided by the client is valid.
|
||||
func (s *Service) validateServerToken(ctx context.Context) error {
|
||||
func validateServerToken(ctx context.Context, wantToken string) error {
|
||||
values, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return status.Error(codes.Unauthenticated, "missing server token")
|
||||
@ -476,40 +322,31 @@ func (s *Service) validateServerToken(ctx context.Context) error {
|
||||
return status.Error(codes.Unauthenticated, "more than one server token was provided")
|
||||
}
|
||||
|
||||
if token[0] != s.token {
|
||||
if token[0] != wantToken {
|
||||
return status.Error(codes.Unauthenticated, "invalid server token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateUnaryServerToken check the server token for every unary gRPC call.
|
||||
func (s *Service) validateUnaryServerToken(
|
||||
ctx context.Context,
|
||||
req interface{},
|
||||
info *grpc.UnaryServerInfo,
|
||||
handler grpc.UnaryHandler,
|
||||
) (resp interface{}, err error) {
|
||||
if err := s.validateServerToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// newUnaryTokenValidator checks the server token for every unary gRPC call.
|
||||
func newUnaryTokenValidator(wantToken string) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
if err := validateServerToken(ctx, wantToken); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return handler(ctx, req)
|
||||
return handler(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
// validateStreamServerToken check the server token for every gRPC stream request.
|
||||
func (s *Service) validateStreamServerToken(
|
||||
srv interface{},
|
||||
ss grpc.ServerStream,
|
||||
info *grpc.StreamServerInfo,
|
||||
handler grpc.StreamHandler,
|
||||
) error {
|
||||
logEntry := s.log.WithField("FullMethod", info.FullMethod)
|
||||
// newStreamTokenValidator checks the server token for every gRPC stream request.
|
||||
func newStreamTokenValidator(wantToken string) grpc.StreamServerInterceptor {
|
||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
if err := validateServerToken(stream.Context(), wantToken); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.validateServerToken(ss.Context()); err != nil {
|
||||
logEntry.WithError(err).Error("Stream validator failed")
|
||||
return err
|
||||
return handler(srv, stream)
|
||||
}
|
||||
|
||||
return handler(srv, ss)
|
||||
}
|
||||
|
||||
@ -23,15 +23,13 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/theme"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/ports"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/maps"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
@ -116,7 +114,7 @@ func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empt
|
||||
func (s *Service) Restart(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
s.log.Debug("Restart")
|
||||
|
||||
s.restarter.SetToRestart()
|
||||
s.restarter.Set(true, false)
|
||||
return s.Quit(ctx, empty)
|
||||
}
|
||||
|
||||
@ -129,25 +127,19 @@ func (s *Service) ShowOnStartup(ctx context.Context, _ *emptypb.Empty) (*wrapper
|
||||
func (s *Service) ShowSplashScreen(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
s.log.Debug("ShowSplashScreen")
|
||||
|
||||
if s.bridge.IsFirstStart() {
|
||||
return wrapperspb.Bool(false), nil
|
||||
}
|
||||
|
||||
ver, err := semver.NewVersion(s.bridge.GetLastVersion())
|
||||
if err != nil {
|
||||
s.log.WithError(err).WithField("last", s.bridge.GetLastVersion()).Debug("Cannot parse last version")
|
||||
if s.bridge.GetFirstStart() {
|
||||
return wrapperspb.Bool(false), nil
|
||||
}
|
||||
|
||||
// Current splash screen contains update on rebranding. Therefore, it
|
||||
// should be shown only if the last used version was less than 2.2.0.
|
||||
return wrapperspb.Bool(ver.LessThan(semver.MustParse("2.2.0"))), nil
|
||||
return wrapperspb.Bool(s.bridge.GetLastVersion().LessThan(semver.MustParse("2.2.0"))), nil
|
||||
}
|
||||
|
||||
func (s *Service) IsFirstGuiStart(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
s.log.Debug("IsFirstGuiStart")
|
||||
|
||||
return wrapperspb.Bool(s.bridge.GetBool(settings.FirstStartGUIKey)), nil
|
||||
return wrapperspb.Bool(s.bridge.GetFirstStartGUI()), nil
|
||||
}
|
||||
|
||||
func (s *Service) SetIsAutostartOn(ctx context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||
@ -155,22 +147,16 @@ func (s *Service) SetIsAutostartOn(ctx context.Context, isOn *wrapperspb.BoolVal
|
||||
|
||||
defer func() { _ = s.SendEvent(NewToggleAutostartFinishedEvent()) }()
|
||||
|
||||
if isOn.Value == s.bridge.IsAutostartEnabled() {
|
||||
if isOn.Value == s.bridge.GetAutostart() {
|
||||
s.initAutostart()
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if isOn.Value {
|
||||
err = s.bridge.EnableAutostart()
|
||||
} else {
|
||||
err = s.bridge.DisableAutostart()
|
||||
}
|
||||
|
||||
s.initAutostart()
|
||||
|
||||
if err != nil {
|
||||
if err := s.bridge.SetAutostart(isOn.Value); err != nil {
|
||||
s.log.WithField("makeItEnabled", isOn.Value).WithError(err).Error("Autostart change failed")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set autostart: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
@ -179,7 +165,7 @@ func (s *Service) SetIsAutostartOn(ctx context.Context, isOn *wrapperspb.BoolVal
|
||||
func (s *Service) IsAutostartOn(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
s.log.Debug("IsAutostartOn")
|
||||
|
||||
return wrapperspb.Bool(s.bridge.IsAutostartEnabled()), nil
|
||||
return wrapperspb.Bool(s.bridge.GetAutostart()), nil
|
||||
}
|
||||
|
||||
func (s *Service) SetIsBetaEnabled(ctx context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||
@ -190,8 +176,10 @@ func (s *Service) SetIsBetaEnabled(ctx context.Context, isEnabled *wrapperspb.Bo
|
||||
channel = updater.EarlyChannel
|
||||
}
|
||||
|
||||
s.bridge.SetUpdateChannel(channel)
|
||||
s.checkUpdate()
|
||||
if err := s.bridge.SetUpdateChannel(channel); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set update channel")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set update channel: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@ -205,7 +193,10 @@ func (s *Service) IsBetaEnabled(ctx context.Context, _ *emptypb.Empty) (*wrapper
|
||||
func (s *Service) SetIsAllMailVisible(ctx context.Context, isVisible *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||
s.log.WithField("isVisible", isVisible.Value).Debug("SetIsAllMailVisible")
|
||||
|
||||
s.bridge.SetIsAllMailVisible(isVisible.Value)
|
||||
if err := s.bridge.SetShowAllMail(isVisible.Value); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set show all mail")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set show all mail: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@ -213,7 +204,7 @@ func (s *Service) SetIsAllMailVisible(ctx context.Context, isVisible *wrapperspb
|
||||
func (s *Service) IsAllMailVisible(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
s.log.Debug("IsAllMailVisible")
|
||||
|
||||
return wrapperspb.Bool(s.bridge.IsAllMailVisible()), nil
|
||||
return wrapperspb.Bool(s.bridge.GetShowAllMail()), nil
|
||||
}
|
||||
|
||||
func (s *Service) GoOs(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||
@ -241,7 +232,7 @@ func (s *Service) Version(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.St
|
||||
func (s *Service) LogsPath(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||
s.log.Debug("LogsPath")
|
||||
|
||||
path, err := s.bridge.ProvideLogsPath()
|
||||
path, err := s.bridge.GetLogsPath()
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("Cannot determine logs path")
|
||||
return nil, err
|
||||
@ -275,7 +266,10 @@ func (s *Service) SetColorSchemeName(ctx context.Context, name *wrapperspb.Strin
|
||||
return nil, status.Error(codes.NotFound, "Color scheme not available")
|
||||
}
|
||||
|
||||
s.bridge.Set(settings.ColorScheme, name.Value)
|
||||
if err := s.bridge.SetColorScheme(name.Value); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set color scheme")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set color scheme: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@ -283,10 +277,13 @@ func (s *Service) SetColorSchemeName(ctx context.Context, name *wrapperspb.Strin
|
||||
func (s *Service) ColorSchemeName(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||
s.log.Debug("ColorSchemeName")
|
||||
|
||||
current := s.bridge.Get(settings.ColorScheme)
|
||||
current := s.bridge.GetColorScheme()
|
||||
if !theme.IsAvailable(theme.Theme(current)) {
|
||||
current = string(theme.DefaultTheme())
|
||||
s.bridge.Set(settings.ColorScheme, current)
|
||||
if err := s.bridge.SetColorScheme(current); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set color scheme")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set color scheme: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return wrapperspb.String(current), nil
|
||||
@ -312,6 +309,7 @@ func (s *Service) ReportBug(ctx context.Context, report *ReportBugRequest) (*emp
|
||||
defer func() { _ = s.SendEvent(NewReportBugFinishedEvent()) }()
|
||||
|
||||
if err := s.bridge.ReportBug(
|
||||
context.Background(),
|
||||
report.OsType,
|
||||
report.OsVersion,
|
||||
report.Description,
|
||||
@ -331,6 +329,7 @@ func (s *Service) ReportBug(ctx context.Context, report *ReportBugRequest) (*emp
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (s *Service) ForceLauncher(ctx context.Context, launcher *wrapperspb.StringValue) (*emptypb.Empty, error) {
|
||||
s.log.WithField("launcher", launcher.Value).Debug("ForceLauncher")
|
||||
|
||||
@ -350,6 +349,7 @@ func (s *Service) SetMainExecutable(ctx context.Context, exe *wrapperspb.StringV
|
||||
}()
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (s *Service) Login(ctx context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("username", login.Username).Debug("Login")
|
||||
@ -357,135 +357,44 @@ func (s *Service) Login(ctx context.Context, login *LoginRequest) (*emptypb.Empt
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
|
||||
var err error
|
||||
s.password, err = base64.StdEncoding.DecodeString(login.Password)
|
||||
password, err := base64.StdEncoding.DecodeString(login.Password)
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("Cannot decode password")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode password"))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
|
||||
s.authClient, s.auth, err = s.bridge.Login(login.Username, s.password)
|
||||
// TODO: Handle different error types!
|
||||
// - bad credentials
|
||||
// - bad proton plan
|
||||
// - user already exists
|
||||
userID, err := s.bridge.LoginUser(context.Background(), login.Username, string(password), nil, nil)
|
||||
if err != nil {
|
||||
if err == pmapi.ErrPasswordWrong {
|
||||
// Remove error message since it is hardcoded in QML.
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, ""))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
if err == pmapi.ErrPaidPlanRequired {
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_FREE_USER, ""))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, err.Error()))
|
||||
s.loginClean()
|
||||
s.log.WithError(err).Error("Cannot login user")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot login user"))
|
||||
return
|
||||
}
|
||||
|
||||
if s.auth.HasTwoFactor() {
|
||||
_ = s.SendEvent(NewLoginTfaRequestedEvent(login.Username))
|
||||
return
|
||||
}
|
||||
if s.auth.HasMailboxPassword() {
|
||||
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
||||
return
|
||||
}
|
||||
|
||||
s.finishLogin()
|
||||
}()
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Login2FA(ctx context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("username", login.Username).Debug("Login2FA")
|
||||
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
|
||||
if s.auth == nil || s.authClient == nil {
|
||||
s.log.Errorf("Login 2FA: authethication incomplete %p %p", s.auth, s.authClient)
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, "Missing authentication, try again."))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
|
||||
twoFA, err := base64.StdEncoding.DecodeString(login.Password)
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("Cannot decode 2fa code")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode 2fa code"))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
|
||||
err = s.authClient.Auth2FA(context.Background(), string(twoFA))
|
||||
if err == pmapi.ErrBad2FACodeTryAgain {
|
||||
s.log.Warn("Login 2FA: retry 2fa")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ERROR, ""))
|
||||
return
|
||||
}
|
||||
|
||||
if err == pmapi.ErrBad2FACode {
|
||||
s.log.Warn("Login 2FA: abort 2fa")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, ""))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.log.WithError(err).Warn("Login 2FA: failed.")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, err.Error()))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
|
||||
if s.auth.HasMailboxPassword() {
|
||||
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
||||
return
|
||||
}
|
||||
|
||||
s.finishLogin()
|
||||
_ = s.SendEvent(NewLoginFinishedEvent(userID))
|
||||
}()
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Login2Passwords(ctx context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("username", login.Username).Debug("Login2Passwords")
|
||||
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
|
||||
var err error
|
||||
s.password, err = base64.StdEncoding.DecodeString(login.Password)
|
||||
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("Cannot decode mbox password")
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode mbox password"))
|
||||
s.loginClean()
|
||||
return
|
||||
}
|
||||
|
||||
s.finishLogin()
|
||||
}()
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *Service) Login2FA(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (s *Service) LoginAbort(ctx context.Context, loginAbort *LoginAbortRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("username", loginAbort.Username).Debug("LoginAbort")
|
||||
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
|
||||
s.loginAbort()
|
||||
}()
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
func (s *Service) Login2Passwords(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (s *Service) CheckUpdate(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
func (s *Service) LoginAbort(_ context.Context, loginAbort *LoginAbortRequest) (*emptypb.Empty, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
/*
|
||||
func (s *Service) CheckUpdate(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
s.log.Debug("CheckUpdate")
|
||||
|
||||
go func() {
|
||||
@ -507,21 +416,20 @@ func (s *Service) InstallUpdate(ctx context.Context, _ *emptypb.Empty) (*emptypb
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (s *Service) SetIsAutomaticUpdateOn(ctx context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||
s.log.WithField("isOn", isOn.Value).Debug("SetIsAutomaticUpdateOn")
|
||||
|
||||
currentlyOn := s.bridge.GetBool(settings.AutoUpdateKey)
|
||||
currentlyOn := s.bridge.GetAutoUpdate()
|
||||
if currentlyOn == isOn.Value {
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
s.bridge.SetBool(settings.AutoUpdateKey, isOn.Value)
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
|
||||
s.checkUpdateAndNotify(false)
|
||||
}()
|
||||
if err := s.bridge.SetAutoUpdate(isOn.Value); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set auto update")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set auto update: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@ -529,51 +437,21 @@ func (s *Service) SetIsAutomaticUpdateOn(ctx context.Context, isOn *wrapperspb.B
|
||||
func (s *Service) IsAutomaticUpdateOn(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
s.log.Debug("IsAutomaticUpdateOn")
|
||||
|
||||
return wrapperspb.Bool(s.bridge.GetBool(settings.AutoUpdateKey)), nil
|
||||
}
|
||||
|
||||
func (s *Service) IsCacheOnDiskEnabled(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||
s.log.Debug("IsCacheOnDiskEnabled")
|
||||
|
||||
return wrapperspb.Bool(s.bridge.GetBool(settings.CacheEnabledKey)), nil
|
||||
return wrapperspb.Bool(s.bridge.GetAutoUpdate()), nil
|
||||
}
|
||||
|
||||
func (s *Service) DiskCachePath(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||
s.log.Debug("DiskCachePath")
|
||||
|
||||
return wrapperspb.String(s.bridge.Get(settings.CacheLocationKey)), nil
|
||||
return wrapperspb.String(s.bridge.GetGluonDir()), nil
|
||||
}
|
||||
|
||||
func (s *Service) ChangeLocalCache(ctx context.Context, change *ChangeLocalCacheRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("enableDiskCache", change.EnableDiskCache).
|
||||
WithField("diskCachePath", change.DiskCachePath).
|
||||
Debug("DiskCachePath")
|
||||
s.log.WithField("diskCachePath", change.DiskCachePath).Debug("DiskCachePath")
|
||||
|
||||
restart := false
|
||||
defer func(willRestart *bool) {
|
||||
_ = s.SendEvent(NewCacheChangeLocalCacheFinishedEvent(*willRestart))
|
||||
if *willRestart {
|
||||
_, _ = s.Restart(ctx, &emptypb.Empty{})
|
||||
}
|
||||
}(&restart)
|
||||
|
||||
if change.EnableDiskCache != s.bridge.GetBool(settings.CacheEnabledKey) {
|
||||
if change.EnableDiskCache {
|
||||
if err := s.bridge.EnableCache(); err != nil {
|
||||
s.log.WithError(err).Error("Cannot enable disk cache")
|
||||
} else {
|
||||
restart = true
|
||||
_ = s.SendEvent(NewIsCacheOnDiskEnabledChanged(s.bridge.GetBool(settings.CacheEnabledKey)))
|
||||
}
|
||||
} else {
|
||||
if err := s.bridge.DisableCache(); err != nil {
|
||||
s.log.WithError(err).Error("Cannot disable disk cache")
|
||||
} else {
|
||||
restart = true
|
||||
_ = s.SendEvent(NewIsCacheOnDiskEnabledChanged(s.bridge.GetBool(settings.CacheEnabledKey)))
|
||||
}
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = s.SendEvent(NewCacheChangeLocalCacheFinishedEvent(false))
|
||||
}()
|
||||
|
||||
path := change.DiskCachePath
|
||||
//goland:noinspection GoBoolExpressions
|
||||
@ -581,16 +459,14 @@ func (s *Service) ChangeLocalCache(ctx context.Context, change *ChangeLocalCache
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
if change.EnableDiskCache && path != s.bridge.Get(settings.CacheLocationKey) {
|
||||
if err := s.bridge.MigrateCache(s.bridge.Get(settings.CacheLocationKey), path); err != nil {
|
||||
if path != s.bridge.GetGluonDir() {
|
||||
if err := s.bridge.SetGluonDir(ctx, path); err != nil {
|
||||
s.log.WithError(err).Error("The local cache location could not be changed.")
|
||||
_ = s.SendEvent(NewCacheErrorEvent(CacheErrorType_CACHE_CANT_MOVE_ERROR))
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
s.bridge.Set(settings.CacheLocationKey, path)
|
||||
restart = true
|
||||
_ = s.SendEvent(NewDiskCachePathChanged(s.bridge.Get(settings.CacheLocationKey)))
|
||||
_ = s.SendEvent(NewDiskCachePathChanged(s.bridge.GetGluonDir()))
|
||||
}
|
||||
|
||||
_ = s.SendEvent(NewCacheLocationChangeSuccessEvent())
|
||||
@ -601,7 +477,10 @@ func (s *Service) ChangeLocalCache(ctx context.Context, change *ChangeLocalCache
|
||||
func (s *Service) SetIsDoHEnabled(ctx context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||
s.log.WithField("isEnabled", isEnabled.Value).Debug("SetIsDohEnabled")
|
||||
|
||||
s.bridge.SetProxyAllowed(isEnabled.Value)
|
||||
if err := s.bridge.SetProxyAllowed(isEnabled.Value); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set DoH")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set DoH: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@ -615,13 +494,14 @@ func (s *Service) IsDoHEnabled(ctx context.Context, _ *emptypb.Empty) (*wrappers
|
||||
func (s *Service) SetUseSslForSmtp(ctx context.Context, useSsl *wrapperspb.BoolValue) (*emptypb.Empty, error) { //nolint:revive,stylecheck
|
||||
s.log.WithField("useSsl", useSsl.Value).Debug("SetUseSslForSmtp")
|
||||
|
||||
if s.bridge.GetBool(settings.SMTPSSLKey) == useSsl.Value {
|
||||
if s.bridge.GetSMTPSSL() == useSsl.Value {
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
s.bridge.SetBool(settings.SMTPSSLKey, useSsl.Value)
|
||||
|
||||
defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
|
||||
if err := s.bridge.SetSMTPSSL(useSsl.Value); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set SMTP SSL")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set SMTP SSL: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, s.SendEvent(NewMailSettingsUseSslForSmtpFinishedEvent())
|
||||
}
|
||||
@ -629,34 +509,39 @@ func (s *Service) SetUseSslForSmtp(ctx context.Context, useSsl *wrapperspb.BoolV
|
||||
func (s *Service) UseSslForSmtp(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { //nolint:revive,stylecheck
|
||||
s.log.Debug("UseSslForSmtp")
|
||||
|
||||
return wrapperspb.Bool(s.bridge.GetBool(settings.SMTPSSLKey)), nil
|
||||
return wrapperspb.Bool(s.bridge.GetSMTPSSL()), nil
|
||||
}
|
||||
|
||||
func (s *Service) Hostname(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||
s.log.Debug("Hostname")
|
||||
|
||||
return wrapperspb.String(bridge.Host), nil
|
||||
return wrapperspb.String(constants.Host), nil
|
||||
}
|
||||
|
||||
func (s *Service) ImapPort(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.Int32Value, error) {
|
||||
s.log.Debug("ImapPort")
|
||||
|
||||
return wrapperspb.Int32(int32(s.bridge.GetInt(settings.IMAPPortKey))), nil
|
||||
return wrapperspb.Int32(int32(s.bridge.GetIMAPPort())), nil
|
||||
}
|
||||
|
||||
func (s *Service) SmtpPort(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.Int32Value, error) { //nolint:revive,stylecheck
|
||||
s.log.Debug("SmtpPort")
|
||||
|
||||
return wrapperspb.Int32(int32(s.bridge.GetInt(settings.SMTPPortKey))), nil
|
||||
return wrapperspb.Int32(int32(s.bridge.GetSMTPPort())), nil
|
||||
}
|
||||
|
||||
func (s *Service) ChangePorts(ctx context.Context, ports *ChangePortsRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("imapPort", ports.ImapPort).WithField("smtpPort", ports.SmtpPort).Debug("ChangePorts")
|
||||
|
||||
s.bridge.SetInt(settings.IMAPPortKey, int(ports.ImapPort))
|
||||
s.bridge.SetInt(settings.SMTPPortKey, int(ports.SmtpPort))
|
||||
if err := s.bridge.SetIMAPPort(int(ports.ImapPort)); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set IMAP port")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set IMAP port: %v", err)
|
||||
}
|
||||
|
||||
defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
|
||||
if err := s.bridge.SetSMTPPort(int(ports.SmtpPort)); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set SMTP port")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set SMTP port: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, s.SendEvent(NewMailSettingsChangePortFinishedEvent())
|
||||
}
|
||||
@ -670,12 +555,7 @@ func (s *Service) IsPortFree(ctx context.Context, port *wrapperspb.Int32Value) (
|
||||
func (s *Service) AvailableKeychains(ctx context.Context, _ *emptypb.Empty) (*AvailableKeychainsResponse, error) {
|
||||
s.log.Debug("AvailableKeychains")
|
||||
|
||||
keychains := make([]string, 0, len(keychain.Helpers))
|
||||
for chain := range keychain.Helpers {
|
||||
keychains = append(keychains, chain)
|
||||
}
|
||||
|
||||
return &AvailableKeychainsResponse{Keychains: keychains}, nil
|
||||
return &AvailableKeychainsResponse{Keychains: maps.Keys(keychain.Helpers)}, nil
|
||||
}
|
||||
|
||||
func (s *Service) SetCurrentKeychain(ctx context.Context, keychain *wrapperspb.StringValue) (*emptypb.Empty, error) {
|
||||
@ -684,11 +564,20 @@ func (s *Service) SetCurrentKeychain(ctx context.Context, keychain *wrapperspb.S
|
||||
defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
|
||||
defer func() { _ = s.SendEvent(NewKeychainChangeKeychainFinishedEvent()) }()
|
||||
|
||||
if s.bridge.GetKeychainApp() == keychain.Value {
|
||||
helper, err := s.bridge.GetKeychainApp()
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("Failed to get current keychain")
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current keychain: %v", err)
|
||||
}
|
||||
|
||||
if helper == keychain.Value {
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
s.bridge.SetKeychainApp(keychain.Value)
|
||||
if err := s.bridge.SetKeychainApp(keychain.Value); err != nil {
|
||||
s.log.WithError(err).Error("Failed to set keychain")
|
||||
return nil, status.Errorf(codes.Internal, "failed to set keychain: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
@ -696,5 +585,11 @@ func (s *Service) SetCurrentKeychain(ctx context.Context, keychain *wrapperspb.S
|
||||
func (s *Service) CurrentKeychain(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||
s.log.Debug("CurrentKeychain")
|
||||
|
||||
return wrapperspb.String(s.bridge.GetKeychainApp()), nil
|
||||
helper, err := s.bridge.GetKeychainApp()
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("Failed to get current keychain")
|
||||
return nil, status.Errorf(codes.Internal, "failed to get current keychain: %v", err)
|
||||
}
|
||||
|
||||
return wrapperspb.String(helper), nil
|
||||
}
|
||||
|
||||
@ -87,12 +87,8 @@ func (s *Service) StopEventStream(ctx context.Context, _ *emptypb.Empty) (*empty
|
||||
|
||||
// SendEvent sends an event to the via the gRPC event stream.
|
||||
func (s *Service) SendEvent(event *StreamEvent) error {
|
||||
s.eventQueueMutex.Lock()
|
||||
defer s.eventQueueMutex.Unlock()
|
||||
|
||||
if s.eventStreamCh == nil {
|
||||
// nobody is connected to the event stream, we queue events
|
||||
s.eventQueue = append(s.eventQueue, event)
|
||||
if s.eventStreamCh == nil { // nobody is connected to the event stream, we queue events
|
||||
s.queueEvent(event)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -167,3 +163,14 @@ func (s *Service) StartEventTest() error { //nolint:funlen
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) queueEvent(event *StreamEvent) {
|
||||
s.eventQueueMutex.Lock()
|
||||
defer s.eventQueueMutex.Unlock()
|
||||
|
||||
if event.isInternetStatus() {
|
||||
s.eventQueue = append(filterOutInternetStatusEvents(s.eventQueue), event)
|
||||
} else {
|
||||
s.eventQueue = append(s.eventQueue, event)
|
||||
}
|
||||
}
|
||||
|
||||
68
internal/frontend/grpc/service_updates.go
Normal file
68
internal/frontend/grpc/service_updates.go
Normal file
@ -0,0 +1,68 @@
|
||||
package grpc
|
||||
|
||||
/*
|
||||
func (s *Service) checkUpdate() {
|
||||
version, err := s.updater.Check()
|
||||
if err != nil {
|
||||
s.log.WithError(err).Error("An error occurred while checking for updates")
|
||||
s.SetVersion(updater.VersionInfo{})
|
||||
return
|
||||
}
|
||||
s.SetVersion(version)
|
||||
}
|
||||
|
||||
func (s *Service) updateForce() {
|
||||
s.updateCheckMutex.Lock()
|
||||
defer s.updateCheckMutex.Unlock()
|
||||
s.checkUpdate()
|
||||
_ = s.SendEvent(NewUpdateForceEvent(s.newVersionInfo.Version.String()))
|
||||
}
|
||||
|
||||
func (s *Service) checkUpdateAndNotify(isReqFromUser bool) {
|
||||
s.updateCheckMutex.Lock()
|
||||
defer func() {
|
||||
s.updateCheckMutex.Unlock()
|
||||
_ = s.SendEvent(NewUpdateCheckFinishedEvent())
|
||||
}()
|
||||
|
||||
s.checkUpdate()
|
||||
version := s.newVersionInfo
|
||||
if version.Version.String() == "" {
|
||||
if isReqFromUser {
|
||||
_ = s.SendEvent(NewUpdateErrorEvent(UpdateErrorType_UPDATE_MANUAL_ERROR))
|
||||
}
|
||||
return
|
||||
}
|
||||
if !s.updater.IsUpdateApplicable(s.newVersionInfo) {
|
||||
s.log.Info("No need to update")
|
||||
if isReqFromUser {
|
||||
_ = s.SendEvent(NewUpdateIsLatestVersionEvent())
|
||||
}
|
||||
} else if isReqFromUser {
|
||||
s.NotifyManualUpdate(s.newVersionInfo, s.updater.CanInstall(s.newVersionInfo))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) installUpdate() {
|
||||
s.updateCheckMutex.Lock()
|
||||
defer s.updateCheckMutex.Unlock()
|
||||
|
||||
if !s.updater.CanInstall(s.newVersionInfo) {
|
||||
s.log.Warning("Skipping update installation, current version too old")
|
||||
_ = s.SendEvent(NewUpdateErrorEvent(UpdateErrorType_UPDATE_MANUAL_ERROR))
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.updater.InstallUpdate(s.newVersionInfo); err != nil {
|
||||
if errors.Cause(err) == updater.ErrDownloadVerify {
|
||||
s.log.WithError(err).Warning("Skipping update installation due to temporary error")
|
||||
} else {
|
||||
s.log.WithError(err).Error("The update couldn't be installed")
|
||||
_ = s.SendEvent(NewUpdateErrorEvent(UpdateErrorType_UPDATE_MANUAL_ERROR))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_ = s.SendEvent(NewUpdateSilentRestartNeededEvent())
|
||||
}
|
||||
*/
|
||||
@ -19,9 +19,8 @@ package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@ -75,15 +74,15 @@ func (s *Service) SetUserSplitMode(ctx context.Context, splitMode *UserSplitMode
|
||||
defer s.panicHandler.HandlePanic()
|
||||
defer func() { _ = s.SendEvent(NewUserToggleSplitModeFinishedEvent(splitMode.UserID)) }()
|
||||
|
||||
var targetMode users.AddressMode
|
||||
var targetMode bridge.AddressMode
|
||||
|
||||
if splitMode.Active && user.Mode == users.CombinedMode {
|
||||
targetMode = users.SplitMode
|
||||
} else if !splitMode.Active && user.Mode == users.SplitMode {
|
||||
targetMode = users.CombinedMode
|
||||
if splitMode.Active && user.AddressMode == bridge.CombinedMode {
|
||||
targetMode = bridge.SplitMode
|
||||
} else if !splitMode.Active && user.AddressMode == bridge.SplitMode {
|
||||
targetMode = bridge.CombinedMode
|
||||
}
|
||||
|
||||
if err := s.bridge.SetAddressMode(user.ID, targetMode); err != nil {
|
||||
if err := s.bridge.SetAddressMode(user.UserID, targetMode); err != nil {
|
||||
logrus.WithError(err).Error("Failed to set address mode")
|
||||
}
|
||||
}()
|
||||
@ -101,7 +100,7 @@ func (s *Service) LogoutUser(ctx context.Context, userID *wrapperspb.StringValue
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
|
||||
if err := s.bridge.LogoutUser(userID.Value); err != nil {
|
||||
if err := s.bridge.LogoutUser(context.Background(), userID.Value); err != nil {
|
||||
logrus.WithError(err).Error("Failed to log user out")
|
||||
}
|
||||
}()
|
||||
@ -116,7 +115,7 @@ func (s *Service) RemoveUser(ctx context.Context, userID *wrapperspb.StringValue
|
||||
defer s.panicHandler.HandlePanic()
|
||||
|
||||
// remove preferences
|
||||
if err := s.bridge.DeleteUser(userID.Value, false); err != nil {
|
||||
if err := s.bridge.DeleteUser(context.Background(), userID.Value); err != nil {
|
||||
s.log.WithError(err).Error("Failed to remove user")
|
||||
// notification
|
||||
}
|
||||
@ -127,18 +126,10 @@ func (s *Service) RemoveUser(ctx context.Context, userID *wrapperspb.StringValue
|
||||
func (s *Service) ConfigureUserAppleMail(ctx context.Context, request *ConfigureAppleMailRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("UserID", request.UserID).WithField("Address", request.Address).Debug("ConfigureUserAppleMail")
|
||||
|
||||
restart, err := s.bridge.ConfigureAppleMail(request.UserID, request.Address)
|
||||
if err != nil {
|
||||
if err := s.bridge.ConfigureAppleMail(request.UserID, request.Address); err != nil {
|
||||
s.log.WithField("userID", request.UserID).Error("Cannot configure AppleMail for user")
|
||||
return nil, status.Error(codes.Internal, "Apple Mail config failed")
|
||||
}
|
||||
|
||||
// There is delay needed for external window to open.
|
||||
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)
|
||||
return s.Restart(ctx, &emptypb.Empty{})
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -58,17 +58,17 @@ func getInitials(fullName string) string {
|
||||
}
|
||||
|
||||
// grpcUserFromInfo converts a bridge user to a gRPC user.
|
||||
func grpcUserFromInfo(user users.UserInfo) *User {
|
||||
func grpcUserFromInfo(user bridge.UserInfo) *User {
|
||||
return &User{
|
||||
Id: user.ID,
|
||||
Id: user.UserID,
|
||||
Username: user.Username,
|
||||
AvatarText: getInitials(user.Username),
|
||||
LoggedIn: user.Connected,
|
||||
SplitMode: user.Mode == users.SplitMode,
|
||||
SplitMode: user.AddressMode == bridge.SplitMode,
|
||||
SetupGuideSeen: true, // users listed have already seen the setup guide.
|
||||
UsedBytes: user.UsedBytes,
|
||||
TotalBytes: user.TotalBytes,
|
||||
Password: user.Password,
|
||||
UsedBytes: int64(user.UsedSpace),
|
||||
TotalBytes: int64(user.MaxSpace),
|
||||
Password: user.BridgePass,
|
||||
Addresses: user.Addresses,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user