mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 12:46:46 +00:00
Other: Add more extensive logging
This commit is contained in:
2
go.mod
2
go.mod
@ -39,7 +39,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
github.com/urfave/cli/v2 v2.20.3
|
github.com/urfave/cli/v2 v2.20.3
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5
|
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||||
gitlab.protontech.ch/go/liteapi v0.39.2
|
gitlab.protontech.ch/go/liteapi v0.41.1-0.20221103233913-df73097358ad
|
||||||
go.uber.org/goleak v1.2.0
|
go.uber.org/goleak v1.2.0
|
||||||
golang.org/x/exp v0.0.0-20221023144134-a1e5550cf13e
|
golang.org/x/exp v0.0.0-20221023144134-a1e5550cf13e
|
||||||
golang.org/x/net v0.1.0
|
golang.org/x/net v0.1.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -403,8 +403,8 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr
|
|||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
|
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
|
||||||
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
|
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
|
||||||
gitlab.protontech.ch/go/liteapi v0.39.2 h1:HWxuO6c9cnRAzpLaj2SrOD1jJEWVa4nAUH1TkVPA4t4=
|
gitlab.protontech.ch/go/liteapi v0.41.1-0.20221103233913-df73097358ad h1:TDEX2GTorFS7slKuLndtmHVLpMEtnk7pIBNk/va9IL8=
|
||||||
gitlab.protontech.ch/go/liteapi v0.39.2/go.mod h1:IM7ADWjgIL2hXopzx0WNamizEuMgM2QZl7QH12FNflk=
|
gitlab.protontech.ch/go/liteapi v0.41.1-0.20221103233913-df73097358ad/go.mod h1:IM7ADWjgIL2hXopzx0WNamizEuMgM2QZl7QH12FNflk=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
|||||||
@ -269,6 +269,8 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||||||
|
|
||||||
// Handle connection up/down events.
|
// Handle connection up/down events.
|
||||||
bridge.api.AddStatusObserver(func(status liteapi.Status) {
|
bridge.api.AddStatusObserver(func(status liteapi.Status) {
|
||||||
|
logrus.Info("API status changed: ", status)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case status == liteapi.StatusUp:
|
case status == liteapi.StatusUp:
|
||||||
bridge.publish(events.ConnStatusUp{})
|
bridge.publish(events.ConnStatusUp{})
|
||||||
@ -282,6 +284,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||||||
|
|
||||||
// If any call returns a bad version code, we need to update.
|
// If any call returns a bad version code, we need to update.
|
||||||
bridge.api.AddErrorHandler(liteapi.AppVersionBadCode, func() {
|
bridge.api.AddErrorHandler(liteapi.AppVersionBadCode, func() {
|
||||||
|
logrus.Warn("App version is bad")
|
||||||
bridge.publish(events.UpdateForced{})
|
bridge.publish(events.UpdateForced{})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -291,9 +294,19 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Log all manager API requests (client requests are logged separately).
|
||||||
|
bridge.api.AddPostRequestHook(func(_ *resty.Client, r *resty.Response) error {
|
||||||
|
if _, ok := liteapi.ClientIDFromContext(r.Request.Context()); !ok {
|
||||||
|
logrus.Infof("[MANAGER] %v: %v %v", r.Status(), r.Request.Method, r.Request.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Publish a TLS issue event if a TLS issue is encountered.
|
// Publish a TLS issue event if a TLS issue is encountered.
|
||||||
bridge.tasks.Once(func(ctx context.Context) {
|
bridge.tasks.Once(func(ctx context.Context) {
|
||||||
async.RangeContext(ctx, tlsReporter.GetTLSIssueCh(), func(struct{}) {
|
async.RangeContext(ctx, tlsReporter.GetTLSIssueCh(), func(struct{}) {
|
||||||
|
logrus.Warn("TLS issue encountered")
|
||||||
bridge.publish(events.TLSIssue{})
|
bridge.publish(events.TLSIssue{})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -301,6 +314,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||||||
// Publish a raise event if the focus service is called.
|
// Publish a raise event if the focus service is called.
|
||||||
bridge.tasks.Once(func(ctx context.Context) {
|
bridge.tasks.Once(func(ctx context.Context) {
|
||||||
async.RangeContext(ctx, bridge.focusService.GetRaiseCh(), func(struct{}) {
|
async.RangeContext(ctx, bridge.focusService.GetRaiseCh(), func(struct{}) {
|
||||||
|
logrus.Info("Focus service requested raise")
|
||||||
bridge.publish(events.Raise{})
|
bridge.publish(events.Raise{})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -308,12 +322,15 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||||||
// Handle any IMAP events that are forwarded to the bridge from gluon.
|
// Handle any IMAP events that are forwarded to the bridge from gluon.
|
||||||
bridge.tasks.Once(func(ctx context.Context) {
|
bridge.tasks.Once(func(ctx context.Context) {
|
||||||
async.RangeContext(ctx, bridge.imapEventCh, func(event imapEvents.Event) {
|
async.RangeContext(ctx, bridge.imapEventCh, func(event imapEvents.Event) {
|
||||||
|
logrus.WithField("event", fmt.Sprintf("%T", event)).Debug("Received IMAP event")
|
||||||
bridge.handleIMAPEvent(event)
|
bridge.handleIMAPEvent(event)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Attempt to lazy load users when triggered.
|
// Attempt to lazy load users when triggered.
|
||||||
bridge.goLoad = bridge.tasks.Trigger(func(ctx context.Context) {
|
bridge.goLoad = bridge.tasks.Trigger(func(ctx context.Context) {
|
||||||
|
logrus.Info("Loading users")
|
||||||
|
|
||||||
if err := bridge.loadUsers(ctx); err != nil {
|
if err := bridge.loadUsers(ctx); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to load users")
|
logrus.WithError(err).Error("Failed to load users")
|
||||||
} else {
|
} else {
|
||||||
@ -323,7 +340,9 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||||||
defer bridge.goLoad()
|
defer bridge.goLoad()
|
||||||
|
|
||||||
// Check for updates when triggered.
|
// Check for updates when triggered.
|
||||||
bridge.goUpdate = bridge.tasks.PeriodicOrTrigger(constants.UpdateCheckInterval, 0, func(ctx context.Context) {
|
bridge.goUpdate = bridge.tasks.PeriodicOrTrigger(constants.UpdateCheckInterval, 0, func(context.Context) {
|
||||||
|
logrus.Info("Checking for updates")
|
||||||
|
|
||||||
version, err := bridge.updater.GetVersionInfo(bridge.api, bridge.vault.GetUpdateChannel())
|
version, err := bridge.updater.GetVersionInfo(bridge.api, bridge.vault.GetUpdateChannel())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Error("Failed to get version info")
|
logrus.WithError(err).Error("Failed to get version info")
|
||||||
@ -353,6 +372,8 @@ func (bridge *Bridge) GetErrors() []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) Close(ctx context.Context) {
|
func (bridge *Bridge) Close(ctx context.Context) {
|
||||||
|
logrus.Info("Closing bridge")
|
||||||
|
|
||||||
// Close the IMAP server.
|
// Close the IMAP server.
|
||||||
if err := bridge.closeIMAP(ctx); err != nil {
|
if err := bridge.closeIMAP(ctx); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to close IMAP server")
|
logrus.WithError(err).Error("Failed to close IMAP server")
|
||||||
@ -396,6 +417,8 @@ func (bridge *Bridge) publish(event events.Event) {
|
|||||||
bridge.watchersLock.RLock()
|
bridge.watchersLock.RLock()
|
||||||
defer bridge.watchersLock.RUnlock()
|
defer bridge.watchersLock.RUnlock()
|
||||||
|
|
||||||
|
logrus.WithField("event", event).Debug("Publishing event")
|
||||||
|
|
||||||
for _, watcher := range bridge.watchers {
|
for _, watcher := range bridge.watchers {
|
||||||
if watcher.IsWatching(event) {
|
if watcher.IsWatching(event) {
|
||||||
if ok := watcher.Send(event); !ok {
|
if ok := watcher.Send(event); !ok {
|
||||||
@ -432,6 +455,8 @@ func (bridge *Bridge) remWatcher(watcher *watcher.Watcher[events.Event]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) onStatusUp(ctx context.Context) {
|
func (bridge *Bridge) onStatusUp(ctx context.Context) {
|
||||||
|
logrus.Info("Handling API status up")
|
||||||
|
|
||||||
safe.RLock(func() {
|
safe.RLock(func() {
|
||||||
for _, user := range bridge.users {
|
for _, user := range bridge.users {
|
||||||
user.OnStatusUp(ctx)
|
user.OnStatusUp(ctx)
|
||||||
@ -442,6 +467,8 @@ func (bridge *Bridge) onStatusUp(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) onStatusDown(ctx context.Context) {
|
func (bridge *Bridge) onStatusDown(ctx context.Context) {
|
||||||
|
logrus.Info("Handling API status down")
|
||||||
|
|
||||||
safe.RLock(func() {
|
safe.RLock(func() {
|
||||||
for _, user := range bridge.users {
|
for _, user := range bridge.users {
|
||||||
user.OnStatusDown(ctx)
|
user.OnStatusDown(ctx)
|
||||||
@ -455,7 +482,7 @@ func (bridge *Bridge) onStatusDown(ctx context.Context) {
|
|||||||
|
|
||||||
case <-time.After(backoff):
|
case <-time.After(backoff):
|
||||||
if err := bridge.api.Ping(ctx); err != nil {
|
if err := bridge.api.Ping(ctx); err != nil {
|
||||||
logrus.WithError(err).Debug("Failed to ping API, will retry")
|
logrus.WithError(err).Warn("Failed to ping API, will retry")
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,14 +22,21 @@ import (
|
|||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/clientconfig"
|
"github.com/ProtonMail/proton-bridge/v2/internal/clientconfig"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/safe"
|
"github.com/ProtonMail/proton-bridge/v2/internal/safe"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/useragent"
|
"github.com/ProtonMail/proton-bridge/v2/internal/useragent"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigureAppleMail configures apple mail for the given userID and address.
|
// ConfigureAppleMail configures apple mail for the given userID and address.
|
||||||
// If configuring apple mail for Catalina or newer, it ensures Bridge is using SSL.
|
// If configuring apple mail for Catalina or newer, it ensures Bridge is using SSL.
|
||||||
func (bridge *Bridge) ConfigureAppleMail(userID, address string) error {
|
func (bridge *Bridge) ConfigureAppleMail(userID, address string) error {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"userID": userID,
|
||||||
|
"address": logging.Sensitive(address),
|
||||||
|
}).Info("Configuring Apple Mail")
|
||||||
|
|
||||||
return safe.RLockRet(func() error {
|
return safe.RLockRet(func() error {
|
||||||
user, ok := bridge.users[userID]
|
user, ok := bridge.users[userID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@ -44,6 +44,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (bridge *Bridge) serveIMAP() error {
|
func (bridge *Bridge) serveIMAP() error {
|
||||||
|
logrus.Info("Starting IMAP server")
|
||||||
|
|
||||||
imapListener, err := newListener(bridge.vault.GetIMAPPort(), bridge.vault.GetIMAPSSL(), bridge.tlsConfig)
|
imapListener, err := newListener(bridge.vault.GetIMAPPort(), bridge.vault.GetIMAPSSL(), bridge.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create IMAP listener: %w", err)
|
return fmt.Errorf("failed to create IMAP listener: %w", err)
|
||||||
@ -63,6 +65,8 @@ func (bridge *Bridge) serveIMAP() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) restartIMAP() error {
|
func (bridge *Bridge) restartIMAP() error {
|
||||||
|
logrus.Info("Restarting IMAP server")
|
||||||
|
|
||||||
if err := bridge.imapListener.Close(); err != nil {
|
if err := bridge.imapListener.Close(); err != nil {
|
||||||
return fmt.Errorf("failed to close IMAP listener: %w", err)
|
return fmt.Errorf("failed to close IMAP listener: %w", err)
|
||||||
}
|
}
|
||||||
@ -71,6 +75,8 @@ func (bridge *Bridge) restartIMAP() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) closeIMAP(ctx context.Context) error {
|
func (bridge *Bridge) closeIMAP(ctx context.Context) error {
|
||||||
|
logrus.Info("Closing IMAP server")
|
||||||
|
|
||||||
if err := bridge.imapServer.Close(ctx); err != nil {
|
if err := bridge.imapServer.Close(ctx); err != nil {
|
||||||
return fmt.Errorf("failed to close IMAP server: %w", err)
|
return fmt.Errorf("failed to close IMAP server: %w", err)
|
||||||
}
|
}
|
||||||
@ -86,6 +92,8 @@ func (bridge *Bridge) closeIMAP(ctx context.Context) error {
|
|||||||
|
|
||||||
// addIMAPUser connects the given user to gluon.
|
// addIMAPUser connects the given user to gluon.
|
||||||
func (bridge *Bridge) addIMAPUser(ctx context.Context, user *user.User) error {
|
func (bridge *Bridge) addIMAPUser(ctx context.Context, user *user.User) error {
|
||||||
|
logrus.WithField("userID", user.ID()).Info("Adding IMAP user")
|
||||||
|
|
||||||
imapConn, err := user.NewIMAPConnectors()
|
imapConn, err := user.NewIMAPConnectors()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create IMAP connectors: %w", err)
|
return fmt.Errorf("failed to create IMAP connectors: %w", err)
|
||||||
@ -172,6 +180,13 @@ func newIMAPServer(
|
|||||||
eventCh chan<- imapEvents.Event,
|
eventCh chan<- imapEvents.Event,
|
||||||
tasks *xsync.Group,
|
tasks *xsync.Group,
|
||||||
) (*gluon.Server, error) {
|
) (*gluon.Server, error) {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"gluonDir": gluonDir,
|
||||||
|
"version": version,
|
||||||
|
"logClient": logClient,
|
||||||
|
"logServer": logServer,
|
||||||
|
}).Info("Creating IMAP server")
|
||||||
|
|
||||||
if logClient || logServer {
|
if logClient || logServer {
|
||||||
log := logrus.WithField("protocol", "IMAP")
|
log := logrus.WithField("protocol", "IMAP")
|
||||||
log.Warning("================================================")
|
log.Warning("================================================")
|
||||||
|
|||||||
@ -30,6 +30,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (bridge *Bridge) serveSMTP() error {
|
func (bridge *Bridge) serveSMTP() error {
|
||||||
|
logrus.Info("Starting SMTP server")
|
||||||
|
|
||||||
smtpListener, err := newListener(bridge.vault.GetSMTPPort(), bridge.vault.GetSMTPSSL(), bridge.tlsConfig)
|
smtpListener, err := newListener(bridge.vault.GetSMTPPort(), bridge.vault.GetSMTPSSL(), bridge.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create SMTP listener: %w", err)
|
return fmt.Errorf("failed to create SMTP listener: %w", err)
|
||||||
@ -37,9 +39,9 @@ func (bridge *Bridge) serveSMTP() error {
|
|||||||
|
|
||||||
bridge.smtpListener = smtpListener
|
bridge.smtpListener = smtpListener
|
||||||
|
|
||||||
bridge.tasks.Once(func(ctx context.Context) {
|
bridge.tasks.Once(func(context.Context) {
|
||||||
if err := bridge.smtpServer.Serve(smtpListener); err != nil {
|
if err := bridge.smtpServer.Serve(smtpListener); err != nil {
|
||||||
logrus.WithError(err).Debug("SMTP server stopped")
|
logrus.WithError(err).Info("SMTP server stopped")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -51,6 +53,8 @@ func (bridge *Bridge) serveSMTP() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) restartSMTP() error {
|
func (bridge *Bridge) restartSMTP() error {
|
||||||
|
logrus.Info("Restarting SMTP server")
|
||||||
|
|
||||||
if err := bridge.closeSMTP(); err != nil {
|
if err := bridge.closeSMTP(); err != nil {
|
||||||
return fmt.Errorf("failed to close SMTP: %w", err)
|
return fmt.Errorf("failed to close SMTP: %w", err)
|
||||||
}
|
}
|
||||||
@ -65,6 +69,8 @@ func (bridge *Bridge) restartSMTP() error {
|
|||||||
// after we've already closed the server. However, go-smtp has a bug; it blocks on the listener
|
// after we've already closed the server. However, go-smtp has a bug; it blocks on the listener
|
||||||
// even after the server has been closed. So we close the listener ourselves to unblock it.
|
// even after the server has been closed. So we close the listener ourselves to unblock it.
|
||||||
func (bridge *Bridge) closeSMTP() error {
|
func (bridge *Bridge) closeSMTP() error {
|
||||||
|
logrus.Info("Closing SMTP server")
|
||||||
|
|
||||||
if bridge.smtpListener != nil {
|
if bridge.smtpListener != nil {
|
||||||
if err := bridge.smtpListener.Close(); err != nil {
|
if err := bridge.smtpListener.Close(); err != nil {
|
||||||
return fmt.Errorf("failed to close SMTP listener: %w", err)
|
return fmt.Errorf("failed to close SMTP listener: %w", err)
|
||||||
@ -72,13 +78,15 @@ func (bridge *Bridge) closeSMTP() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := bridge.smtpServer.Close(); err != nil {
|
if err := bridge.smtpServer.Close(); err != nil {
|
||||||
logrus.WithError(err).Debug("Failed to close SMTP server")
|
logrus.WithError(err).Debug("Failed to close SMTP server (expected -- we close the listener ourselves)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSMTPServer(bridge *Bridge, tlsConfig *tls.Config, shouldLog bool) *smtp.Server {
|
func newSMTPServer(bridge *Bridge, tlsConfig *tls.Config, logSMTP bool) *smtp.Server {
|
||||||
|
logrus.WithField("logSMTP", logSMTP).Info("Creating SMTP server")
|
||||||
|
|
||||||
smtpServer := smtp.NewServer(&smtpBackend{Bridge: bridge})
|
smtpServer := smtp.NewServer(&smtpBackend{Bridge: bridge})
|
||||||
|
|
||||||
smtpServer.TLSConfig = tlsConfig
|
smtpServer.TLSConfig = tlsConfig
|
||||||
@ -87,7 +95,7 @@ func newSMTPServer(bridge *Bridge, tlsConfig *tls.Config, shouldLog bool) *smtp.
|
|||||||
smtpServer.MaxLineLength = 1 << 16
|
smtpServer.MaxLineLength = 1 << 16
|
||||||
smtpServer.ErrorLog = logging.NewSMTPLogger()
|
smtpServer.ErrorLog = logging.NewSMTPLogger()
|
||||||
|
|
||||||
if shouldLog {
|
if logSMTP {
|
||||||
log := logrus.WithField("protocol", "SMTP")
|
log := logrus.WithField("protocol", "SMTP")
|
||||||
log.Warning("================================================")
|
log.Warning("================================================")
|
||||||
log.Warning("THIS LOG WILL CONTAIN **DECRYPTED** MESSAGE DATA")
|
log.Warning("THIS LOG WILL CONTAIN **DECRYPTED** MESSAGE DATA")
|
||||||
|
|||||||
@ -19,13 +19,12 @@ package bridge
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ProtonMail/gluon/imap"
|
"github.com/ProtonMail/gluon/imap"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/async"
|
"github.com/ProtonMail/proton-bridge/v2/internal/async"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/safe"
|
"github.com/ProtonMail/proton-bridge/v2/internal/safe"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/try"
|
"github.com/ProtonMail/proton-bridge/v2/internal/try"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/user"
|
"github.com/ProtonMail/proton-bridge/v2/internal/user"
|
||||||
@ -100,7 +99,7 @@ func (bridge *Bridge) QueryUserInfo(query string) (UserInfo, error) {
|
|||||||
|
|
||||||
// LoginAuth begins the login process. It returns an authorized client that might need 2FA.
|
// LoginAuth begins the login process. It returns an authorized client that might need 2FA.
|
||||||
func (bridge *Bridge) LoginAuth(ctx context.Context, username string, password []byte) (*liteapi.Client, liteapi.Auth, error) {
|
func (bridge *Bridge) LoginAuth(ctx context.Context, username string, password []byte) (*liteapi.Client, liteapi.Auth, error) {
|
||||||
logrus.WithField("username", hash(username)).Debug("Authorizing user for login")
|
logrus.WithField("username", logging.Sensitive(username)).Info("Authorizing user for login")
|
||||||
|
|
||||||
client, auth, err := bridge.api.NewClientWithLogin(ctx, username, password)
|
client, auth, err := bridge.api.NewClientWithLogin(ctx, username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -127,7 +126,7 @@ func (bridge *Bridge) LoginUser(
|
|||||||
auth liteapi.Auth,
|
auth liteapi.Auth,
|
||||||
keyPass []byte,
|
keyPass []byte,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
logrus.WithField("userID", auth.UserID).Debug("Logging in authorized user")
|
logrus.WithField("userID", auth.UserID).Info("Logging in authorized user")
|
||||||
|
|
||||||
userID, err := try.CatchVal(
|
userID, err := try.CatchVal(
|
||||||
func() (string, error) {
|
func() (string, error) {
|
||||||
@ -158,7 +157,7 @@ func (bridge *Bridge) LoginFull(
|
|||||||
getTOTP func() (string, error),
|
getTOTP func() (string, error),
|
||||||
getKeyPass func() ([]byte, error),
|
getKeyPass func() ([]byte, error),
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
logrus.WithField("username", hash(username)).Debug("Performing full user login")
|
logrus.WithField("username", logging.Sensitive(username)).Info("Performing full user login")
|
||||||
|
|
||||||
client, auth, err := bridge.LoginAuth(ctx, username, password)
|
client, auth, err := bridge.LoginAuth(ctx, username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -166,7 +165,7 @@ func (bridge *Bridge) LoginFull(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if auth.TwoFA.Enabled == liteapi.TOTPEnabled {
|
if auth.TwoFA.Enabled == liteapi.TOTPEnabled {
|
||||||
logrus.WithField("userID", auth.UserID).Debug("Requesting TOTP")
|
logrus.WithField("userID", auth.UserID).Info("Requesting TOTP")
|
||||||
|
|
||||||
totp, err := getTOTP()
|
totp, err := getTOTP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -181,7 +180,7 @@ func (bridge *Bridge) LoginFull(
|
|||||||
var keyPass []byte
|
var keyPass []byte
|
||||||
|
|
||||||
if auth.PasswordMode == liteapi.TwoPasswordMode {
|
if auth.PasswordMode == liteapi.TwoPasswordMode {
|
||||||
logrus.WithField("userID", auth.UserID).Debug("Requesting mailbox password")
|
logrus.WithField("userID", auth.UserID).Info("Requesting mailbox password")
|
||||||
|
|
||||||
userKeyPass, err := getKeyPass()
|
userKeyPass, err := getKeyPass()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -198,7 +197,7 @@ func (bridge *Bridge) LoginFull(
|
|||||||
|
|
||||||
// LogoutUser logs out the given user.
|
// LogoutUser logs out the given user.
|
||||||
func (bridge *Bridge) LogoutUser(ctx context.Context, userID string) error {
|
func (bridge *Bridge) LogoutUser(ctx context.Context, userID string) error {
|
||||||
logrus.WithField("userID", userID).Debug("Logging out user")
|
logrus.WithField("userID", userID).Info("Logging out user")
|
||||||
|
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
user, ok := bridge.users[userID]
|
user, ok := bridge.users[userID]
|
||||||
@ -220,7 +219,7 @@ func (bridge *Bridge) LogoutUser(ctx context.Context, userID string) error {
|
|||||||
|
|
||||||
// DeleteUser deletes the given user.
|
// DeleteUser deletes the given user.
|
||||||
func (bridge *Bridge) DeleteUser(ctx context.Context, userID string) error {
|
func (bridge *Bridge) DeleteUser(ctx context.Context, userID string) error {
|
||||||
logrus.WithField("userID", userID).Debug("Deleting user")
|
logrus.WithField("userID", userID).Info("Deleting user")
|
||||||
|
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
if !bridge.vault.HasUser(userID) {
|
if !bridge.vault.HasUser(userID) {
|
||||||
@ -246,7 +245,7 @@ func (bridge *Bridge) DeleteUser(ctx context.Context, userID string) error {
|
|||||||
|
|
||||||
// SetAddressMode sets the address mode for the given user.
|
// SetAddressMode sets the address mode for the given user.
|
||||||
func (bridge *Bridge) SetAddressMode(ctx context.Context, userID string, mode vault.AddressMode) error {
|
func (bridge *Bridge) SetAddressMode(ctx context.Context, userID string, mode vault.AddressMode) error {
|
||||||
logrus.WithField("userID", userID).WithField("mode", mode).Debug("Setting address mode")
|
logrus.WithField("userID", userID).WithField("mode", mode).Info("Setting address mode")
|
||||||
|
|
||||||
return safe.RLockRet(func() error {
|
return safe.RLockRet(func() error {
|
||||||
user, ok := bridge.users[userID]
|
user, ok := bridge.users[userID]
|
||||||
@ -319,7 +318,7 @@ func (bridge *Bridge) loadUsers(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.WithField("userID", user.UserID()).Debug("Loading connected user")
|
logrus.WithField("userID", user.UserID()).Info("Loading connected user")
|
||||||
|
|
||||||
bridge.publish(events.UserLoading{
|
bridge.publish(events.UserLoading{
|
||||||
UserID: user.UserID(),
|
UserID: user.UserID(),
|
||||||
@ -330,9 +329,10 @@ func (bridge *Bridge) loadUsers(ctx context.Context) error {
|
|||||||
|
|
||||||
bridge.publish(events.UserLoadFail{
|
bridge.publish(events.UserLoadFail{
|
||||||
UserID: user.UserID(),
|
UserID: user.UserID(),
|
||||||
|
Error: err,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
logrus.WithField("userID", user.UserID()).Debug("Loaded user")
|
logrus.WithField("userID", user.UserID()).Info("Successfully loaded user")
|
||||||
|
|
||||||
bridge.publish(events.UserLoadSuccess{
|
bridge.publish(events.UserLoadSuccess{
|
||||||
UserID: user.UserID(),
|
UserID: user.UserID(),
|
||||||
@ -414,11 +414,11 @@ func (bridge *Bridge) addUserWithVault(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *liteapi.Client,
|
client *liteapi.Client,
|
||||||
apiUser liteapi.User,
|
apiUser liteapi.User,
|
||||||
vaultUser *vault.User,
|
vault *vault.User,
|
||||||
) error {
|
) error {
|
||||||
user, err := user.New(
|
user, err := user.New(
|
||||||
ctx,
|
ctx,
|
||||||
vaultUser,
|
vault,
|
||||||
client,
|
client,
|
||||||
apiUser,
|
apiUser,
|
||||||
bridge.vault.SyncWorkers(),
|
bridge.vault.SyncWorkers(),
|
||||||
@ -438,6 +438,11 @@ func (bridge *Bridge) addUserWithVault(
|
|||||||
// For example, if the user's addresses change, we need to update them in gluon.
|
// For example, if the user's addresses change, we need to update them in gluon.
|
||||||
bridge.tasks.Once(func(ctx context.Context) {
|
bridge.tasks.Once(func(ctx context.Context) {
|
||||||
async.RangeContext(ctx, user.GetEventCh(), func(event events.Event) {
|
async.RangeContext(ctx, user.GetEventCh(), func(event events.Event) {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"userID": apiUser.ID,
|
||||||
|
"event": event,
|
||||||
|
}).Debug("Received user event")
|
||||||
|
|
||||||
if err := bridge.handleUserEvent(ctx, user, event); err != nil {
|
if err := bridge.handleUserEvent(ctx, user, event); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to handle user event")
|
logrus.WithError(err).Error("Failed to handle user event")
|
||||||
} else {
|
} else {
|
||||||
@ -448,8 +453,8 @@ func (bridge *Bridge) addUserWithVault(
|
|||||||
|
|
||||||
// Gluon will set the IMAP ID in the context, if known, before making requests on behalf of this user.
|
// Gluon will set the IMAP ID in the context, if known, before making requests on behalf of this user.
|
||||||
// As such, if we find this ID in the context, we should use it to update our user agent.
|
// As such, if we find this ID in the context, we should use it to update our user agent.
|
||||||
client.AddPreRequestHook(func(ctx context.Context, req *resty.Request) error {
|
client.AddPreRequestHook(func(_ *resty.Client, r *resty.Request) error {
|
||||||
if imapID, ok := imap.GetIMAPIDFromContext(ctx); ok {
|
if imapID, ok := imap.GetIMAPIDFromContext(r.Context()); ok {
|
||||||
bridge.identifier.SetClient(imapID.Name, imapID.Version)
|
bridge.identifier.SetClient(imapID.Name, imapID.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,13 +541,3 @@ func mapHas[Key comparable, Val any](m map[Key]Val, key Key) bool {
|
|||||||
_, ok := m[key]
|
_, ok := m[key]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func hash(s string) string {
|
|
||||||
h := sha256.New()
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(s)); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -17,6 +17,12 @@
|
|||||||
|
|
||||||
package events
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
|
)
|
||||||
|
|
||||||
type UserAddressCreated struct {
|
type UserAddressCreated struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
@ -25,6 +31,10 @@ type UserAddressCreated struct {
|
|||||||
Email string
|
Email string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserAddressCreated) String() string {
|
||||||
|
return fmt.Sprintf("UserAddressCreated: UserID: %s, AddressID: %s, Email: %s", event.UserID, event.AddressID, logging.Sensitive(event.Email))
|
||||||
|
}
|
||||||
|
|
||||||
type UserAddressUpdated struct {
|
type UserAddressUpdated struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
@ -33,6 +43,10 @@ type UserAddressUpdated struct {
|
|||||||
Email string
|
Email string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserAddressUpdated) String() string {
|
||||||
|
return fmt.Sprintf("UserAddressUpdated: UserID: %s, AddressID: %s, Email: %s", event.UserID, event.AddressID, logging.Sensitive(event.Email))
|
||||||
|
}
|
||||||
|
|
||||||
type UserAddressDeleted struct {
|
type UserAddressDeleted struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
@ -40,3 +54,7 @@ type UserAddressDeleted struct {
|
|||||||
AddressID string
|
AddressID string
|
||||||
Email string
|
Email string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserAddressDeleted) String() string {
|
||||||
|
return fmt.Sprintf("UserAddressDeleted: UserID: %s, AddressID: %s, Email: %s", event.UserID, event.AddressID, logging.Sensitive(event.Email))
|
||||||
|
}
|
||||||
|
|||||||
@ -21,10 +21,22 @@ type TLSIssue struct {
|
|||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event TLSIssue) String() string {
|
||||||
|
return "TLSIssue"
|
||||||
|
}
|
||||||
|
|
||||||
type ConnStatusUp struct {
|
type ConnStatusUp struct {
|
||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event ConnStatusUp) String() string {
|
||||||
|
return "ConnStatusUp"
|
||||||
|
}
|
||||||
|
|
||||||
type ConnStatusDown struct {
|
type ConnStatusDown struct {
|
||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event ConnStatusDown) String() string {
|
||||||
|
return "ConnStatusDown"
|
||||||
|
}
|
||||||
|
|||||||
@ -17,8 +17,14 @@
|
|||||||
|
|
||||||
package events
|
package events
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event Error) String() string {
|
||||||
|
return fmt.Sprintf("Error: %s", event.Error)
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +17,12 @@
|
|||||||
|
|
||||||
package events
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
|
)
|
||||||
|
|
||||||
type UserLabelCreated struct {
|
type UserLabelCreated struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
@ -25,6 +31,10 @@ type UserLabelCreated struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserLabelCreated) String() string {
|
||||||
|
return fmt.Sprintf("UserLabelCreated: UserID: %s, LabelID: %s, Name: %s", event.UserID, event.LabelID, logging.Sensitive(event.Name))
|
||||||
|
}
|
||||||
|
|
||||||
type UserLabelUpdated struct {
|
type UserLabelUpdated struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
@ -33,6 +43,10 @@ type UserLabelUpdated struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserLabelUpdated) String() string {
|
||||||
|
return fmt.Sprintf("UserLabelUpdated: UserID: %s, LabelID: %s, Name: %s", event.UserID, event.LabelID, logging.Sensitive(event.Name))
|
||||||
|
}
|
||||||
|
|
||||||
type UserLabelDeleted struct {
|
type UserLabelDeleted struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
@ -40,3 +54,7 @@ type UserLabelDeleted struct {
|
|||||||
LabelID string
|
LabelID string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserLabelDeleted) String() string {
|
||||||
|
return fmt.Sprintf("UserLabelDeleted: UserID: %s, LabelID: %s, Name: %s", event.UserID, event.LabelID, logging.Sensitive(event.Name))
|
||||||
|
}
|
||||||
|
|||||||
@ -20,3 +20,7 @@ package events
|
|||||||
type Raise struct {
|
type Raise struct {
|
||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event Raise) String() string {
|
||||||
|
return "Raise"
|
||||||
|
}
|
||||||
|
|||||||
@ -17,7 +17,10 @@
|
|||||||
|
|
||||||
package events
|
package events
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type SyncStarted struct {
|
type SyncStarted struct {
|
||||||
eventBase
|
eventBase
|
||||||
@ -25,6 +28,10 @@ type SyncStarted struct {
|
|||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event SyncStarted) String() string {
|
||||||
|
return fmt.Sprintf("SyncStarted: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type SyncProgress struct {
|
type SyncProgress struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
@ -34,15 +41,33 @@ type SyncProgress struct {
|
|||||||
Remaining time.Duration
|
Remaining time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event SyncProgress) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"SyncProgress: UserID: %s, Progress: %f, Elapsed: %0.1fs, Remaining: %0.1fs",
|
||||||
|
event.UserID,
|
||||||
|
event.Progress,
|
||||||
|
event.Elapsed.Seconds(),
|
||||||
|
event.Remaining.Seconds(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type SyncFinished struct {
|
type SyncFinished struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event SyncFinished) String() string {
|
||||||
|
return fmt.Sprintf("SyncFinished: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type SyncFailed struct {
|
type SyncFailed struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
Err error
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (event SyncFailed) String() string {
|
||||||
|
return fmt.Sprintf("SyncFailed: UserID: %s, Err: %s", event.UserID, event.Error)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,11 @@
|
|||||||
|
|
||||||
package events
|
package events
|
||||||
|
|
||||||
import "github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
|
)
|
||||||
|
|
||||||
type UpdateAvailable struct {
|
type UpdateAvailable struct {
|
||||||
eventBase
|
eventBase
|
||||||
@ -27,16 +31,32 @@ type UpdateAvailable struct {
|
|||||||
CanInstall bool
|
CanInstall bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UpdateAvailable) String() string {
|
||||||
|
return fmt.Sprintf("UpdateAvailable: Version %s, CanInstall %t", event.Version.Version, event.CanInstall)
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateNotAvailable struct {
|
type UpdateNotAvailable struct {
|
||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UpdateNotAvailable) String() string {
|
||||||
|
return "UpdateNotAvailable"
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateInstalled struct {
|
type UpdateInstalled struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
Version updater.VersionInfo
|
Version updater.VersionInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UpdateInstalled) String() string {
|
||||||
|
return fmt.Sprintf("UpdateInstalled: Version %s", event.Version.Version)
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateForced struct {
|
type UpdateForced struct {
|
||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UpdateForced) String() string {
|
||||||
|
return "UpdateForced"
|
||||||
|
}
|
||||||
|
|||||||
@ -17,28 +17,49 @@
|
|||||||
|
|
||||||
package events
|
package events
|
||||||
|
|
||||||
import "github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
||||||
|
)
|
||||||
|
|
||||||
type AllUsersLoaded struct {
|
type AllUsersLoaded struct {
|
||||||
eventBase
|
eventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event AllUsersLoaded) String() string {
|
||||||
|
return "AllUsersLoaded"
|
||||||
|
}
|
||||||
|
|
||||||
type UserLoading struct {
|
type UserLoading struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserLoading) String() string {
|
||||||
|
return fmt.Sprintf("UserLoading: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type UserLoadSuccess struct {
|
type UserLoadSuccess struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserLoadSuccess) String() string {
|
||||||
|
return fmt.Sprintf("UserLoadSuccess: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type UserLoadFail struct {
|
type UserLoadFail struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (event UserLoadFail) String() string {
|
||||||
|
return fmt.Sprintf("UserLoadFail: UserID: %s, Error: %s", event.UserID, event.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserLoggedIn struct {
|
type UserLoggedIn struct {
|
||||||
@ -47,33 +68,58 @@ type UserLoggedIn struct {
|
|||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserLoggedIn) String() string {
|
||||||
|
return fmt.Sprintf("UserLoggedIn: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type UserLoggedOut struct {
|
type UserLoggedOut struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserLoggedOut) String() string {
|
||||||
|
return fmt.Sprintf("UserLoggedOut: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type UserDeauth struct {
|
type UserDeauth struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserDeauth) String() string {
|
||||||
|
return fmt.Sprintf("UserDeauth: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type UserDeleted struct {
|
type UserDeleted struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserDeleted) String() string {
|
||||||
|
return fmt.Sprintf("UserDeleted: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type UserChanged struct {
|
type UserChanged struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event UserChanged) String() string {
|
||||||
|
return fmt.Sprintf("UserChanged: UserID: %s", event.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
type AddressModeChanged struct {
|
type AddressModeChanged struct {
|
||||||
eventBase
|
eventBase
|
||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
|
|
||||||
AddressMode vault.AddressMode
|
AddressMode vault.AddressMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (event AddressModeChanged) String() string {
|
||||||
|
return fmt.Sprintf("AddressModeChanged: UserID: %s, AddressMode: %s", event.UserID, event.AddressMode)
|
||||||
|
}
|
||||||
|
|||||||
@ -40,11 +40,7 @@ func (f *frontendCLI) listAccounts(c *ishell.Context) {
|
|||||||
if user.Connected {
|
if user.Connected {
|
||||||
connected = "connected"
|
connected = "connected"
|
||||||
}
|
}
|
||||||
mode := "split"
|
f.Printf(spacing, idx, user.Username, connected, user.AddressMode)
|
||||||
if user.AddressMode == vault.CombinedMode {
|
|
||||||
mode = "combined"
|
|
||||||
}
|
|
||||||
f.Printf(spacing, idx, user.Username, connected, mode)
|
|
||||||
}
|
}
|
||||||
f.Println()
|
f.Println()
|
||||||
}
|
}
|
||||||
@ -265,16 +261,7 @@ func (f *frontendCLI) changeMode(c *ishell.Context) {
|
|||||||
targetMode = vault.CombinedMode
|
targetMode = vault.CombinedMode
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetModeName string
|
if !f.yesNoQuestion("Are you sure you want to change the mode for account " + bold(user.Username) + " to " + bold(targetMode.String())) {
|
||||||
|
|
||||||
switch targetMode {
|
|
||||||
case vault.CombinedMode:
|
|
||||||
targetModeName = "combined"
|
|
||||||
case vault.SplitMode:
|
|
||||||
targetModeName = "split"
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.yesNoQuestion("Are you sure you want to change the mode for account " + bold(user.Username) + " to " + bold(targetModeName)) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +269,7 @@ func (f *frontendCLI) changeMode(c *ishell.Context) {
|
|||||||
f.printAndLogError("Cannot switch address mode:", err)
|
f.printAndLogError("Cannot switch address mode:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Printf("Address mode for account %s changed to %s\n", user.Username, targetModeName)
|
f.Printf("Address mode for account %s changed to %s\n", user.Username, targetMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *frontendCLI) configureAppleMail(c *ishell.Context) {
|
func (f *frontendCLI) configureAppleMail(c *ishell.Context) {
|
||||||
|
|||||||
@ -368,17 +368,20 @@ func (s *Service) Login(ctx context.Context, login *LoginRequest) (*emptypb.Empt
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
defer s.loginClean()
|
defer s.loginClean()
|
||||||
|
|
||||||
switch {
|
if errors.Is(err, bridge.ErrUserAlreadyLoggedIn) {
|
||||||
case errors.Is(err, bridge.ErrUserAlreadyLoggedIn):
|
|
||||||
_ = s.SendEvent(NewLoginAlreadyLoggedInEvent(auth.UserID))
|
_ = s.SendEvent(NewLoginAlreadyLoggedInEvent(auth.UserID))
|
||||||
|
} else if apiErr := new(liteapi.Error); errors.As(err, &apiErr) {
|
||||||
|
switch apiErr.Code { // nolint:exhaustive
|
||||||
|
case liteapi.PasswordWrong:
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, ""))
|
||||||
|
|
||||||
case errors.Is(err, liteapi.ErrIncorrectLoginCredentials):
|
case liteapi.PaidPlanRequired:
|
||||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, ""))
|
_ = s.SendEvent(NewLoginError(LoginErrorType_FREE_USER, ""))
|
||||||
|
|
||||||
case errors.Is(err, liteapi.ErrPaidPlanRequired):
|
default:
|
||||||
_ = s.SendEvent(NewLoginError(LoginErrorType_FREE_USER, ""))
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, err.Error()))
|
||||||
|
}
|
||||||
default:
|
} else {
|
||||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, err.Error()))
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,12 +429,10 @@ func (s *Service) Login2FA(ctx context.Context, login *LoginRequest) (*emptypb.E
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.authClient.Auth2FA(context.Background(), liteapi.Auth2FAReq{TwoFactorCode: string(twoFA)}); err != nil {
|
if err := s.authClient.Auth2FA(context.Background(), liteapi.Auth2FAReq{TwoFactorCode: string(twoFA)}); err != nil {
|
||||||
switch {
|
if apiErr := new(liteapi.Error); errors.As(err, &apiErr) && apiErr.Code == liteapi.PasswordWrong {
|
||||||
case errors.Is(err, liteapi.ErrBad2FACode):
|
|
||||||
s.log.Warn("Login 2FA: retry 2fa")
|
s.log.Warn("Login 2FA: retry 2fa")
|
||||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ERROR, ""))
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ERROR, ""))
|
||||||
|
} else {
|
||||||
default:
|
|
||||||
s.log.WithError(err).Warn("Login 2FA: failed")
|
s.log.WithError(err).Warn("Login 2FA: failed")
|
||||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, err.Error()))
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, err.Error()))
|
||||||
s.loginClean()
|
s.loginClean()
|
||||||
|
|||||||
40
internal/logging/sensitive_default.go
Normal file
40
internal/logging/sensitive_default.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//go:build !sensitive
|
||||||
|
|
||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sensitive(s string) string {
|
||||||
|
return fmt.Sprintf("******** (%s)", hash(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(s string) string {
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
if _, err := h.Write([]byte(s)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(h.Sum(nil))[0:8]
|
||||||
|
}
|
||||||
24
internal/logging/sensitive_sensitive.go
Normal file
24
internal/logging/sensitive_sensitive.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//go:build sensitive
|
||||||
|
|
||||||
|
package logging
|
||||||
|
|
||||||
|
func Sensitive(s string) string {
|
||||||
|
return s
|
||||||
|
}
|
||||||
@ -25,9 +25,11 @@ import (
|
|||||||
"github.com/ProtonMail/gluon/queue"
|
"github.com/ProtonMail/gluon/queue"
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/safe"
|
"github.com/ProtonMail/proton-bridge/v2/internal/safe"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
||||||
"github.com/bradenaw/juniper/xslices"
|
"github.com/bradenaw/juniper/xslices"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"gitlab.protontech.ch/go/liteapi"
|
"gitlab.protontech.ch/go/liteapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -63,6 +65,11 @@ func (user *User) handleAPIEvent(ctx context.Context, event liteapi.Event) error
|
|||||||
// handleUserEvent handles the given user event.
|
// handleUserEvent handles the given user event.
|
||||||
func (user *User) handleUserEvent(_ context.Context, userEvent liteapi.User) error {
|
func (user *User) handleUserEvent(_ context.Context, userEvent liteapi.User) error {
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
|
user.log.WithFields(logrus.Fields{
|
||||||
|
"userID": userEvent.ID,
|
||||||
|
"username": logging.Sensitive(userEvent.Name),
|
||||||
|
}).Info("Handling user event")
|
||||||
|
|
||||||
user.apiUser = userEvent
|
user.apiUser = userEvent
|
||||||
|
|
||||||
user.eventCh.Enqueue(events.UserChanged{
|
user.eventCh.Enqueue(events.UserChanged{
|
||||||
@ -100,6 +107,11 @@ func (user *User) handleAddressEvents(ctx context.Context, addressEvents []litea
|
|||||||
|
|
||||||
func (user *User) handleCreateAddressEvent(ctx context.Context, event liteapi.AddressEvent) error {
|
func (user *User) handleCreateAddressEvent(ctx context.Context, event liteapi.AddressEvent) error {
|
||||||
if err := safe.LockRet(func() error {
|
if err := safe.LockRet(func() error {
|
||||||
|
user.log.WithFields(logrus.Fields{
|
||||||
|
"addressID": event.ID,
|
||||||
|
"email": logging.Sensitive(event.Address.Email),
|
||||||
|
}).Info("Handling address created event")
|
||||||
|
|
||||||
if _, ok := user.apiAddrs[event.Address.ID]; ok {
|
if _, ok := user.apiAddrs[event.Address.ID]; ok {
|
||||||
return fmt.Errorf("address %q already exists", event.ID)
|
return fmt.Errorf("address %q already exists", event.ID)
|
||||||
}
|
}
|
||||||
@ -143,6 +155,11 @@ func (user *User) handleCreateAddressEvent(ctx context.Context, event liteapi.Ad
|
|||||||
|
|
||||||
func (user *User) handleUpdateAddressEvent(_ context.Context, event liteapi.AddressEvent) error { //nolint:unparam
|
func (user *User) handleUpdateAddressEvent(_ context.Context, event liteapi.AddressEvent) error { //nolint:unparam
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
|
user.log.WithFields(logrus.Fields{
|
||||||
|
"addressID": event.ID,
|
||||||
|
"email": logging.Sensitive(event.Address.Email),
|
||||||
|
}).Info("Handling address updated event")
|
||||||
|
|
||||||
if _, ok := user.apiAddrs[event.Address.ID]; !ok {
|
if _, ok := user.apiAddrs[event.Address.ID]; !ok {
|
||||||
return fmt.Errorf("address %q does not exist", event.Address.ID)
|
return fmt.Errorf("address %q does not exist", event.Address.ID)
|
||||||
}
|
}
|
||||||
@ -161,6 +178,8 @@ func (user *User) handleUpdateAddressEvent(_ context.Context, event liteapi.Addr
|
|||||||
|
|
||||||
func (user *User) handleDeleteAddressEvent(_ context.Context, event liteapi.AddressEvent) error {
|
func (user *User) handleDeleteAddressEvent(_ context.Context, event liteapi.AddressEvent) error {
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
|
user.log.WithField("addressID", event.ID).Info("Handling address deleted event")
|
||||||
|
|
||||||
addr, ok := user.apiAddrs[event.ID]
|
addr, ok := user.apiAddrs[event.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("address %q does not exist", event.ID)
|
return fmt.Errorf("address %q does not exist", event.ID)
|
||||||
@ -209,6 +228,11 @@ func (user *User) handleLabelEvents(ctx context.Context, labelEvents []liteapi.L
|
|||||||
|
|
||||||
func (user *User) handleCreateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
func (user *User) handleCreateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
|
user.log.WithFields(logrus.Fields{
|
||||||
|
"labelID": event.ID,
|
||||||
|
"name": logging.Sensitive(event.Label.Name),
|
||||||
|
}).Info("Handling label created event")
|
||||||
|
|
||||||
if _, ok := user.apiLabels[event.Label.ID]; ok {
|
if _, ok := user.apiLabels[event.Label.ID]; ok {
|
||||||
return fmt.Errorf("label %q already exists", event.ID)
|
return fmt.Errorf("label %q already exists", event.ID)
|
||||||
}
|
}
|
||||||
@ -231,6 +255,11 @@ func (user *User) handleCreateLabelEvent(_ context.Context, event liteapi.LabelE
|
|||||||
|
|
||||||
func (user *User) handleUpdateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
func (user *User) handleUpdateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
|
user.log.WithFields(logrus.Fields{
|
||||||
|
"labelID": event.ID,
|
||||||
|
"name": logging.Sensitive(event.Label.Name),
|
||||||
|
}).Info("Handling label updated event")
|
||||||
|
|
||||||
if _, ok := user.apiLabels[event.Label.ID]; !ok {
|
if _, ok := user.apiLabels[event.Label.ID]; !ok {
|
||||||
return fmt.Errorf("label %q does not exist", event.ID)
|
return fmt.Errorf("label %q does not exist", event.ID)
|
||||||
}
|
}
|
||||||
@ -253,6 +282,8 @@ func (user *User) handleUpdateLabelEvent(_ context.Context, event liteapi.LabelE
|
|||||||
|
|
||||||
func (user *User) handleDeleteLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
func (user *User) handleDeleteLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
||||||
return safe.LockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
|
user.log.WithField("labelID", event.ID).Info("Handling label deleted event")
|
||||||
|
|
||||||
label, ok := user.apiLabels[event.ID]
|
label, ok := user.apiLabels[event.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("label %q does not exist", event.ID)
|
return fmt.Errorf("label %q does not exist", event.ID)
|
||||||
@ -305,6 +336,11 @@ func (user *User) handleCreateMessageEvent(ctx context.Context, event liteapi.Me
|
|||||||
}
|
}
|
||||||
|
|
||||||
return safe.RLockRet(func() error {
|
return safe.RLockRet(func() error {
|
||||||
|
user.log.WithFields(logrus.Fields{
|
||||||
|
"messageID": event.ID,
|
||||||
|
"subject": logging.Sensitive(event.Message.Subject),
|
||||||
|
}).Info("Handling message created event")
|
||||||
|
|
||||||
return withAddrKR(user.apiUser, user.apiAddrs[event.Message.AddressID], user.vault.KeyPass(), func(_, addrKR *crypto.KeyRing) error {
|
return withAddrKR(user.apiUser, user.apiAddrs[event.Message.AddressID], user.vault.KeyPass(), func(_, addrKR *crypto.KeyRing) error {
|
||||||
buildRes, err := buildRFC822(full, addrKR)
|
buildRes, err := buildRFC822(full, addrKR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -320,6 +356,11 @@ func (user *User) handleCreateMessageEvent(ctx context.Context, event liteapi.Me
|
|||||||
|
|
||||||
func (user *User) handleUpdateMessageEvent(_ context.Context, event liteapi.MessageEvent) error { //nolint:unparam
|
func (user *User) handleUpdateMessageEvent(_ context.Context, event liteapi.MessageEvent) error { //nolint:unparam
|
||||||
return safe.RLockRet(func() error {
|
return safe.RLockRet(func() error {
|
||||||
|
user.log.WithFields(logrus.Fields{
|
||||||
|
"messageID": event.ID,
|
||||||
|
"subject": logging.Sensitive(event.Message.Subject),
|
||||||
|
}).Info("Handling message updated event")
|
||||||
|
|
||||||
update := imap.NewMessageMailboxesUpdated(
|
update := imap.NewMessageMailboxesUpdated(
|
||||||
imap.MessageID(event.ID),
|
imap.MessageID(event.ID),
|
||||||
mapTo[string, imap.MailboxID](xslices.Filter(event.Message.LabelIDs, wantLabelID)),
|
mapTo[string, imap.MailboxID](xslices.Filter(event.Message.LabelIDs, wantLabelID)),
|
||||||
@ -335,6 +376,8 @@ func (user *User) handleUpdateMessageEvent(_ context.Context, event liteapi.Mess
|
|||||||
|
|
||||||
func (user *User) handleDeleteMessageEvent(_ context.Context, event liteapi.MessageEvent) error { //nolint:unparam
|
func (user *User) handleDeleteMessageEvent(_ context.Context, event liteapi.MessageEvent) error { //nolint:unparam
|
||||||
return safe.RLockRet(func() error {
|
return safe.RLockRet(func() error {
|
||||||
|
user.log.WithField("messageID", event.ID).Info("Handling message deleted event")
|
||||||
|
|
||||||
for _, updateCh := range user.updateCh {
|
for _, updateCh := range user.updateCh {
|
||||||
update := imap.NewMessagesDeleted(
|
update := imap.NewMessagesDeleted(
|
||||||
imap.MessageID(event.ID),
|
imap.MessageID(event.ID),
|
||||||
|
|||||||
@ -257,7 +257,7 @@ func (conn *imapConnector) CreateMessage(
|
|||||||
if messageID, ok, err := conn.sendHash.hasEntryWait(ctx, hash, time.Now().Add(90*time.Second)); err != nil {
|
if messageID, ok, err := conn.sendHash.hasEntryWait(ctx, hash, time.Now().Add(90*time.Second)); err != nil {
|
||||||
return imap.Message{}, nil, fmt.Errorf("failed to check send hash: %w", err)
|
return imap.Message{}, nil, fmt.Errorf("failed to check send hash: %w", err)
|
||||||
} else if ok {
|
} else if ok {
|
||||||
conn.log.WithField("messageID", messageID).Debug("Message already sent")
|
conn.log.WithField("messageID", messageID).Warn("Message already sent")
|
||||||
|
|
||||||
message, err := conn.client.GetMessage(ctx, messageID)
|
message, err := conn.client.GetMessage(ctx, messageID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -46,24 +46,26 @@ const (
|
|||||||
// It sends a SyncStarted event and then either SyncFinished or SyncFailed
|
// It sends a SyncStarted event and then either SyncFinished or SyncFailed
|
||||||
// depending on whether the sync was successful.
|
// depending on whether the sync was successful.
|
||||||
func (user *User) doSync(ctx context.Context) error {
|
func (user *User) doSync(ctx context.Context) error {
|
||||||
user.log.Debug("Beginning user sync")
|
start := time.Now()
|
||||||
|
|
||||||
|
user.log.WithField("start", start).Info("Beginning user sync")
|
||||||
|
|
||||||
user.eventCh.Enqueue(events.SyncStarted{
|
user.eventCh.Enqueue(events.SyncStarted{
|
||||||
UserID: user.ID(),
|
UserID: user.ID(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := user.sync(ctx); err != nil {
|
if err := user.sync(ctx); err != nil {
|
||||||
user.log.WithError(err).Debug("Failed to sync user")
|
user.log.WithError(err).Warn("Failed to sync user")
|
||||||
|
|
||||||
user.eventCh.Enqueue(events.SyncFailed{
|
user.eventCh.Enqueue(events.SyncFailed{
|
||||||
UserID: user.ID(),
|
UserID: user.ID(),
|
||||||
Err: err,
|
Error: err,
|
||||||
})
|
})
|
||||||
|
|
||||||
return fmt.Errorf("failed to sync: %w", err)
|
return fmt.Errorf("failed to sync: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.log.Debug("Finished user sync")
|
user.log.WithField("duration", time.Since(start)).Info("Finished user sync")
|
||||||
|
|
||||||
user.eventCh.Enqueue(events.SyncFinished{
|
user.eventCh.Enqueue(events.SyncFinished{
|
||||||
UserID: user.ID(),
|
UserID: user.ID(),
|
||||||
@ -76,7 +78,7 @@ func (user *User) sync(ctx context.Context) error {
|
|||||||
return safe.RLockRet(func() error {
|
return safe.RLockRet(func() error {
|
||||||
return withAddrKRs(user.apiUser, user.apiAddrs, user.vault.KeyPass(), func(_ *crypto.KeyRing, addrKRs map[string]*crypto.KeyRing) error {
|
return withAddrKRs(user.apiUser, user.apiAddrs, user.vault.KeyPass(), func(_ *crypto.KeyRing, addrKRs map[string]*crypto.KeyRing) error {
|
||||||
if !user.vault.SyncStatus().HasLabels {
|
if !user.vault.SyncStatus().HasLabels {
|
||||||
user.log.Debug("Syncing labels")
|
user.log.Info("Syncing labels")
|
||||||
|
|
||||||
if err := syncLabels(ctx, user.client, xslices.Unique(maps.Values(user.updateCh))...); err != nil {
|
if err := syncLabels(ctx, user.client, xslices.Unique(maps.Values(user.updateCh))...); err != nil {
|
||||||
return fmt.Errorf("failed to sync labels: %w", err)
|
return fmt.Errorf("failed to sync labels: %w", err)
|
||||||
@ -86,13 +88,13 @@ func (user *User) sync(ctx context.Context) error {
|
|||||||
return fmt.Errorf("failed to set has labels: %w", err)
|
return fmt.Errorf("failed to set has labels: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.log.Debug("Synced labels")
|
user.log.Info("Synced labels")
|
||||||
} else {
|
} else {
|
||||||
user.log.Debug("Labels are already synced, skipping")
|
user.log.Info("Labels are already synced, skipping")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.vault.SyncStatus().HasMessages {
|
if !user.vault.SyncStatus().HasMessages {
|
||||||
user.log.Debug("Syncing messages")
|
user.log.Info("Syncing messages")
|
||||||
|
|
||||||
if err := syncMessages(
|
if err := syncMessages(
|
||||||
ctx,
|
ctx,
|
||||||
@ -112,9 +114,9 @@ func (user *User) sync(ctx context.Context) error {
|
|||||||
return fmt.Errorf("failed to set has messages: %w", err)
|
return fmt.Errorf("failed to set has messages: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.log.Debug("Synced messages")
|
user.log.Info("Synced messages")
|
||||||
} else {
|
} else {
|
||||||
user.log.Debug("Messages are already synced, skipping")
|
user.log.Info("Messages are already synced, skipping")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -39,6 +39,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/message/parser"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/message/parser"
|
||||||
"github.com/bradenaw/juniper/xslices"
|
"github.com/bradenaw/juniper/xslices"
|
||||||
"github.com/bradenaw/juniper/xsync"
|
"github.com/bradenaw/juniper/xsync"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gitlab.protontech.ch/go/liteapi"
|
"gitlab.protontech.ch/go/liteapi"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
@ -91,7 +92,7 @@ func New(
|
|||||||
syncWorkers, syncBuffer int,
|
syncWorkers, syncBuffer int,
|
||||||
showAllMail bool,
|
showAllMail bool,
|
||||||
) (*User, error) { //nolint:funlen
|
) (*User, error) { //nolint:funlen
|
||||||
logrus.WithField("userID", apiUser.ID).Debug("Creating new user")
|
logrus.WithField("userID", apiUser.ID).Info("Creating new user")
|
||||||
|
|
||||||
// Get the user's API addresses.
|
// Get the user's API addresses.
|
||||||
apiAddrs, err := client.GetAddresses(ctx)
|
apiAddrs, err := client.GetAddresses(ctx)
|
||||||
@ -183,6 +184,12 @@ func New(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Log all requests made by the user.
|
||||||
|
user.client.AddPostRequestHook(func(_ *resty.Client, r *resty.Response) error {
|
||||||
|
user.log.Infof("%v: %v %v", r.Status(), r.Request.Method, r.Request.URL)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Stream events from the API, logging any errors that occur.
|
// Stream events from the API, logging any errors that occur.
|
||||||
// This does nothing until the sync has been marked as complete.
|
// This does nothing until the sync has been marked as complete.
|
||||||
// When we receive an API event, we attempt to handle it.
|
// When we receive an API event, we attempt to handle it.
|
||||||
@ -204,6 +211,8 @@ func New(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.log.WithField("event", event).Info("Received event")
|
||||||
|
|
||||||
if err := user.handleAPIEvent(ctx, event); err != nil {
|
if err := user.handleAPIEvent(ctx, event); err != nil {
|
||||||
user.log.WithError(err).Error("Failed to handle API event")
|
user.log.WithError(err).Error("Failed to handle API event")
|
||||||
return
|
return
|
||||||
@ -219,11 +228,15 @@ func New(
|
|||||||
|
|
||||||
// When triggered, attempt to sync the user.
|
// When triggered, attempt to sync the user.
|
||||||
user.goSync = user.tasks.Trigger(func(ctx context.Context) {
|
user.goSync = user.tasks.Trigger(func(ctx context.Context) {
|
||||||
|
user.log.Debug("Sync triggered")
|
||||||
|
|
||||||
user.abortable.Do(ctx, func(ctx context.Context) {
|
user.abortable.Do(ctx, func(ctx context.Context) {
|
||||||
if !user.vault.SyncStatus().IsComplete() {
|
if !user.vault.SyncStatus().IsComplete() {
|
||||||
if err := user.doSync(ctx); err != nil {
|
if err := user.doSync(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
user.log.Debug("Sync is already complete, skipping")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -120,6 +120,19 @@ const (
|
|||||||
SplitMode
|
SplitMode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (mode AddressMode) String() string {
|
||||||
|
switch mode {
|
||||||
|
case CombinedMode:
|
||||||
|
return "combined"
|
||||||
|
|
||||||
|
case SplitMode:
|
||||||
|
return "split"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type SyncStatus struct {
|
type SyncStatus struct {
|
||||||
HasLabels bool
|
HasLabels bool
|
||||||
HasMessages bool
|
HasMessages bool
|
||||||
|
|||||||
Reference in New Issue
Block a user