Other: Linter fixes after bumping linter version

This commit is contained in:
James Houlahan
2022-10-11 18:04:39 +02:00
parent 4a5c411665
commit 14a578f319
23 changed files with 273 additions and 206 deletions

View File

@ -218,10 +218,10 @@ change-copyright-year:
./utils/missing_license.sh change-year
test: gofiles
go test -v -failfast -count=1 -p=1 -coverprofile=/tmp/coverage.out -run=${TESTRUN} ./internal/... ./pkg/...
go test -v -coverprofile=/tmp/coverage.out -run=${TESTRUN} ./internal/... ./pkg/...
test-integration: gofiles
go test -v -failfast -count=1 -p=1 github.com/ProtonMail/proton-bridge/v2/tests
go test -v github.com/ProtonMail/proton-bridge/v2/tests
bench:
go test -run '^$$' -bench=. -memprofile bench_mem.pprof -cpuprofile bench_cpu.pprof ./internal/store

2
go.mod
View File

@ -38,7 +38,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
github.com/urfave/cli/v2 v2.16.3
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011093920-6c0cf0847bcf
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011164043-97f5d601ba2b
golang.org/x/exp v0.0.0-20220921164117-439092de6870
golang.org/x/net v0.1.0
golang.org/x/sys v0.1.0

4
go.sum
View File

@ -397,8 +397,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/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011093920-6c0cf0847bcf h1:WBUv+vl0zTc+VEWied2YDv/HmLwjAMxqsqzNBoT7d4Y=
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011093920-6c0cf0847bcf/go.mod h1:NfsxXn1T81sz0gHnxuAfyCI4Agzm5UWVRyEtdQSch/4=
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011164043-97f5d601ba2b h1:9bTndevIV9WTSbRsoLXmLj8bycla6O3KU7fFzEV09n0=
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011164043-97f5d601ba2b/go.mod h1:NfsxXn1T81sz0gHnxuAfyCI4Agzm5UWVRyEtdQSch/4=
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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=

View File

@ -1,3 +1,4 @@
// Package bridge implements the Bridge, which acts as the backend to the UI.
package bridge
import (
@ -59,7 +60,7 @@ type Bridge struct {
updateCheckCh chan struct{}
// focusService is used to raise the bridge window when needed.
focusService *focus.FocusService
focusService *focus.Service
// autostarter is the bridge's autostarter.
autostarter Autostarter
@ -70,36 +71,30 @@ type Bridge struct {
// errors contains errors encountered during startup.
errors []error
// These control the bridge's IMAP and SMTP logging behaviour.
logIMAPClient bool
logIMAPServer bool
logSMTP bool
// stopCh is used to stop ongoing goroutines when the bridge is closed.
stopCh chan struct{}
logIMAPClientCommands bool
logIMAPServerCommands bool
logSMTPCommands bool
}
// New creates a new bridge.
func New(
apiURL string, // the URL of the API to use
locator Locator, // the locator to provide paths to store data
vault *vault.Vault, // the bridge's encrypted data store
identifier Identifier, // the identifier to keep track of the user agent
tlsReporter TLSReporter, // the TLS reporter to report TLS errors
apiURL string, // the URL of the API to use
locator Locator, // the locator to provide paths to store data
vault *vault.Vault, // the bridge's encrypted data store
identifier Identifier, // the identifier to keep track of the user agent
tlsReporter TLSReporter, // the TLS reporter to report TLS errors
roundTripper http.RoundTripper, // the round tripper to use for API requests
proxyCtl ProxyController, // the DoH controller
autostarter Autostarter, // the autostarter to manage autostart settings
updater Updater, // the updater to fetch and install updates
curVersion *semver.Version, // the current version of the bridge
logIMAPClientCommands bool,
logIMAPServerCommands bool,
logSMTPCommands bool,
proxyCtl ProxyController, // the DoH controller
autostarter Autostarter, // the autostarter to manage autostart settings
updater Updater, // the updater to fetch and install updates
curVersion *semver.Version, // the current version of the bridge
logIMAPClient, logIMAPServer bool, // whether to log IMAP client/server activity
logSMTP bool, // whether to log SMTP activity
) (*Bridge, error) {
if vault.GetProxyAllowed() {
proxyCtl.AllowProxy()
} else {
proxyCtl.DisallowProxy()
}
cookieJar, err := cookies.NewCookieJar(vault)
if err != nil {
return nil, fmt.Errorf("failed to create cookie jar: %w", err)
@ -117,25 +112,19 @@ func New(
return nil, fmt.Errorf("failed to load TLS config: %w", err)
}
// TODO: Handle case that the gluon directory is missing!
gluonDir, err := getGluonDir(vault)
if err != nil {
return nil, fmt.Errorf("failed to get Gluon directory: %w", err)
}
imapServer, err := newIMAPServer(gluonDir, curVersion, tlsConfig, logIMAPClientCommands, logIMAPServerCommands)
if err != nil {
return nil, fmt.Errorf("failed to create IMAP server: %w", err)
}
smtpBackend, err := newSMTPBackend()
if err != nil {
return nil, fmt.Errorf("failed to create SMTP backend: %w", err)
}
smtpServer, err := newSMTPServer(smtpBackend, tlsConfig, logSMTPCommands)
imapServer, err := newIMAPServer(gluonDir, curVersion, tlsConfig, logIMAPClient, logIMAPServer)
if err != nil {
return nil, fmt.Errorf("failed to create SMTP server: %w", err)
return nil, fmt.Errorf("failed to create IMAP server: %w", err)
}
focusService, err := focus.NewService()
@ -143,7 +132,49 @@ func New(
return nil, fmt.Errorf("failed to create focus service: %w", err)
}
bridge := &Bridge{
bridge := newBridge(
vault,
api,
cookieJar,
proxyCtl,
identifier,
tlsConfig,
imapServer,
smtpBackend,
updater,
curVersion,
focusService,
autostarter,
locator,
logIMAPClient,
logIMAPServer,
logSMTP,
)
if err := bridge.init(tlsReporter); err != nil {
return nil, fmt.Errorf("failed to initialize bridge: %w", err)
}
return bridge, nil
}
func newBridge(
vault *vault.Vault,
api *liteapi.Manager,
cookieJar *cookies.Jar,
proxyCtl ProxyController,
identifier Identifier,
tlsConfig *tls.Config,
imapServer *gluon.Server,
smtpBackend *smtpBackend,
updater Updater,
curVersion *semver.Version,
focusService *focus.Service,
autostarter Autostarter,
locator Locator,
logIMAPClient, logIMAPServer, logSMTP bool,
) *Bridge {
return &Bridge{
vault: vault,
users: make(map[string]*user.User),
@ -154,7 +185,7 @@ func New(
tlsConfig: tlsConfig,
imapServer: imapServer,
smtpServer: smtpServer,
smtpServer: newSMTPServer(smtpBackend, tlsConfig, logSMTP),
smtpBackend: smtpBackend,
updater: updater,
@ -165,14 +196,22 @@ func New(
autostarter: autostarter,
locator: locator,
stopCh: make(chan struct{}),
logIMAPClient: logIMAPClient,
logIMAPServer: logIMAPServer,
logSMTP: logSMTP,
logIMAPClientCommands: logIMAPClientCommands,
logIMAPServerCommands: logIMAPServerCommands,
logSMTPCommands: logSMTPCommands,
stopCh: make(chan struct{}),
}
}
func (bridge *Bridge) init(tlsReporter TLSReporter) error {
if bridge.vault.GetProxyAllowed() {
bridge.proxyCtl.AllowProxy()
} else {
bridge.proxyCtl.DisallowProxy()
}
api.AddStatusObserver(func(status liteapi.Status) {
bridge.api.AddStatusObserver(func(status liteapi.Status) {
switch {
case status == liteapi.StatusUp:
go bridge.onStatusUp()
@ -182,17 +221,17 @@ func New(
}
})
api.AddErrorHandler(liteapi.AppVersionBadCode, func() {
bridge.api.AddErrorHandler(liteapi.AppVersionBadCode, func() {
bridge.publish(events.UpdateForced{})
})
api.AddPreRequestHook(func(_ *resty.Client, req *resty.Request) error {
bridge.api.AddPreRequestHook(func(_ *resty.Client, req *resty.Request) error {
req.SetHeader("User-Agent", bridge.identifier.GetUserAgent())
return nil
})
if err := bridge.loadUsers(); err != nil {
return nil, fmt.Errorf("failed to load users: %w", err)
return fmt.Errorf("failed to load users: %w", err)
}
go func() {
@ -202,13 +241,13 @@ func New(
}()
go func() {
for range focusService.GetRaiseCh() {
for range bridge.focusService.GetRaiseCh() {
bridge.publish(events.Raise{})
}
}()
go func() {
for event := range imapServer.AddWatcher() {
for event := range bridge.imapServer.AddWatcher() {
bridge.handleIMAPEvent(event)
}
}()
@ -225,7 +264,7 @@ func New(
bridge.PushError(ErrWatchUpdates)
}
return bridge, nil
return nil
}
// GetEvents returns a channel of events of the given type.
@ -363,6 +402,7 @@ func loadTLSConfig(vault *vault.Vault) (*tls.Config, error) {
return nil, err
}
// TODO: Do we have to set MinVersion to tls.VersionTLS12?
return &tls.Config{
Certificates: []tls.Certificate{cert},
}, nil

View File

@ -45,7 +45,7 @@ func init() {
func TestBridge_ConnStatus(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Get a stream of connection status events.
eventCh, done := bridge.GetEvents(events.ConnStatusUp{}, events.ConnStatusDown{})
defer done()
@ -76,7 +76,7 @@ func TestBridge_ConnStatus(t *testing.T) {
func TestBridge_TLSIssue(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Get a stream of TLS issue events.
tlsEventCh, done := bridge.GetEvents(events.TLSIssue{})
defer done()
@ -94,7 +94,7 @@ func TestBridge_TLSIssue(t *testing.T) {
func TestBridge_Focus(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Get a stream of TLS issue events.
raiseCh, done := bridge.GetEvents(events.Raise{})
defer done()
@ -116,7 +116,7 @@ func TestBridge_UserAgent(t *testing.T) {
calls = append(calls, call)
})
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Set the platform to something other than the default.
bridge.SetCurrentPlatform("platform")
@ -148,13 +148,13 @@ func TestBridge_Cookies(t *testing.T) {
})
// Start bridge and add a user so that API assigns us a session ID via cookie.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
_, err := bridge.LoginUser(context.Background(), username, password, nil, nil)
require.NoError(t, err)
})
// Start bridge again and check that it uses the same session ID.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// ...
})
@ -167,7 +167,7 @@ func TestBridge_Cookies(t *testing.T) {
func TestBridge_CheckUpdate(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Disable autoupdate for this test.
require.NoError(t, bridge.SetAutoUpdate(false))
@ -206,7 +206,7 @@ func TestBridge_CheckUpdate(t *testing.T) {
func TestBridge_AutoUpdate(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Enable autoupdate for this test.
require.NoError(t, bridge.SetAutoUpdate(true))
@ -234,7 +234,7 @@ func TestBridge_AutoUpdate(t *testing.T) {
func TestBridge_ManualUpdate(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Disable autoupdate for this test.
require.NoError(t, bridge.SetAutoUpdate(false))
@ -263,7 +263,7 @@ func TestBridge_ManualUpdate(t *testing.T) {
func TestBridge_ForceUpdate(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Get a stream of update events.
updateCh, done := bridge.GetEvents(events.UpdateForced{})
defer done()
@ -286,7 +286,7 @@ func TestBridge_BadVaultKey(t *testing.T) {
var userID string
// Login a user.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
newUserID, err := bridge.LoginUser(context.Background(), username, password, nil, nil)
require.NoError(t, err)
@ -294,17 +294,17 @@ func TestBridge_BadVaultKey(t *testing.T) {
})
// Start bridge with the correct vault key -- it should load the users correctly.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.ElementsMatch(t, []string{userID}, bridge.GetUserIDs())
})
// Start bridge with a bad vault key, the vault will be wiped and bridge will show no users.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, []byte("bad"), func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, []byte("bad"), func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.Empty(t, bridge.GetUserIDs())
})
// Start bridge with a nil vault key, the vault will be wiped and bridge will show no users.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, nil, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, nil, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.Empty(t, bridge.GetUserIDs())
})
})
@ -314,12 +314,12 @@ func TestBridge_MissingGluonDir(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
var gluonDir string
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
_, err := bridge.LoginUser(context.Background(), username, password, nil, nil)
require.NoError(t, err)
// Move the gluon dir.
bridge.SetGluonDir(ctx, t.TempDir())
require.NoError(t, bridge.SetGluonDir(ctx, t.TempDir()))
// Get the gluon dir.
gluonDir = bridge.GetGluonDir()
@ -329,7 +329,7 @@ func TestBridge_MissingGluonDir(t *testing.T) {
require.NoError(t, os.RemoveAll(gluonDir))
// Bridge starts but can't find the gluon dir; there should be no error.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// ...
})
})
@ -337,30 +337,12 @@ func TestBridge_MissingGluonDir(t *testing.T) {
// withTLSEnv creates the full test environment and runs the tests.
func withTLSEnv(t *testing.T, tests func(context.Context, *server.Server, *liteapi.NetCtl, bridge.Locator, []byte)) {
// Create test API.
server := server.NewTLS()
defer server.Close()
// Add test user.
_, _, err := server.CreateUser(username, username+"@pm.me", password)
require.NoError(t, err)
// Generate a random vault key.
vaultKey, err := crypto.RandomToken(32)
require.NoError(t, err)
// Create a context used for the test.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create a net controller so we can simulate network connectivity issues.
netCtl := liteapi.NewNetCtl()
// Create a locations object to provide temporary locations for bridge data during the test.
locations := locations.New(bridge.NewTestLocationsProvider(t.TempDir()), "config-name")
// Run the tests.
tests(ctx, server, netCtl, locations, vaultKey)
withEnv(t, server, func(ctx context.Context, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) {
tests(ctx, server, netCtl, locator, vaultKey)
})
}
// withEnv creates the full test environment and runs the tests.
@ -389,8 +371,8 @@ func withEnv(t *testing.T, server *server.Server, tests func(context.Context, *l
// withBridge creates a new bridge which points to the given API URL and uses the given keychain, and closes it when done.
func withBridge(
t *testing.T,
ctx context.Context,
t *testing.T,
apiURL string,
netCtl *liteapi.NetCtl,
locator bridge.Locator,
@ -435,7 +417,7 @@ func withBridge(
require.NoError(t, err)
// Close the bridge when done.
defer bridge.Close(ctx)
defer func() { require.NoError(t, bridge.Close(ctx)) }()
// Use the bridge.
tests(bridge, mocks)

View File

@ -31,7 +31,7 @@ import (
)
const (
MaxAttachmentSize = 7 * (1 << 20) // MaxAttachmentSize 7 MB total size of all attachments.
MaxTotalAttachmentSize = 7 * (1 << 20)
MaxCompressedFilesCount = 6
)
@ -166,7 +166,7 @@ func zipFiles(filenames []string) (io.Reader, error) {
return nil, nil
}
buf := newLimitedBuffer(MaxAttachmentSize)
buf := newLimitedBuffer(MaxTotalAttachmentSize)
w := zip.NewWriter(buf)
defer w.Close() //nolint:errcheck

View File

@ -39,17 +39,17 @@ func move(from, to string) error {
return err
}
f, err := os.Open(from)
f, err := os.Open(from) // nolint:gosec
if err != nil {
return err
}
defer f.Close()
defer func() { _ = f.Close() }()
c, err := os.Create(to)
c, err := os.Create(to) // nolint:gosec
if err != nil {
return err
}
defer c.Close()
defer func() { _ = f.Close() }()
if err := os.Chmod(to, 0600); err != nil {
return err

View File

@ -63,7 +63,7 @@ func (bridge *Bridge) serveIMAP() error {
return nil
}
func (bridge *Bridge) restartIMAP(ctx context.Context) error {
func (bridge *Bridge) restartIMAP() error {
if err := bridge.imapListener.Close(); err != nil {
logrus.WithError(err).Warn("Failed to close IMAP listener")
}
@ -73,11 +73,11 @@ func (bridge *Bridge) restartIMAP(ctx context.Context) error {
func (bridge *Bridge) closeIMAP(ctx context.Context) error {
if err := bridge.imapServer.Close(ctx); err != nil {
logrus.WithError(err).Warn("Failed to close IMAP server")
return fmt.Errorf("failed to close IMAP server: %w", err)
}
if err := bridge.imapListener.Close(); err != nil {
logrus.WithError(err).Warn("Failed to close IMAP listener")
return fmt.Errorf("failed to close IMAP listener: %w", err)
}
return nil
@ -98,11 +98,18 @@ func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
}
func getGluonDir(encVault *vault.Vault) (string, error) {
empty, err := isEmpty(encVault.GetGluonDir())
empty, exists, err := isEmpty(encVault.GetGluonDir())
if err != nil {
return "", fmt.Errorf("failed to check if gluon dir is empty: %w", err)
}
// TODO: Handle case that the gluon directory is missing and we can't create it!
if !exists {
if err := os.MkdirAll(encVault.GetGluonDir(), 0700); err != nil {
return "", fmt.Errorf("failed to create gluon dir: %w", err)
}
}
if empty {
if err := encVault.ForUser(func(user *vault.User) error {
return user.ClearSyncStatus()
@ -114,27 +121,33 @@ func getGluonDir(encVault *vault.Vault) (string, error) {
return encVault.GetGluonDir(), nil
}
func newIMAPServer(gluonDir string, version *semver.Version, tlsConfig *tls.Config, logIMAPCommandsClient, logIMAPCommandsServer bool) (*gluon.Server, error) {
var imapClientLog io.Writer
var imapServerLog io.Writer
if logIMAPCommandsClient || logIMAPCommandsServer {
func newIMAPServer(
gluonDir string,
version *semver.Version,
tlsConfig *tls.Config,
logClient, logServer bool,
) (*gluon.Server, error) {
if logClient || logServer {
log := logrus.WithField("protocol", "IMAP")
log.Warning("================================================")
log.Warning("THIS LOG WILL CONTAIN **DECRYPTED** MESSAGE DATA")
log.Warning("================================================")
}
if logIMAPCommandsClient {
var imapClientLog io.Writer
if logClient {
imapClientLog = logging.NewIMAPLogger()
} else {
imapClientLog = io.Discard
}
if logIMAPCommandsServer {
var imapServerLog io.Writer
if logServer {
imapServerLog = logging.NewIMAPLogger()
} else {
imapClientLog = io.Discard
imapServerLog = io.Discard
}
imapServer, err := gluon.New(
@ -160,19 +173,21 @@ func newIMAPServer(gluonDir string, version *semver.Version, tlsConfig *tls.Conf
return imapServer, nil
}
func isEmpty(dir string) (bool, error) {
// isEmpty returns whether the given directory is empty.
// If the directory does not exist, the second return value is false.
func isEmpty(dir string) (bool, bool, error) {
if _, err := os.Stat(dir); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return false, fmt.Errorf("failed to stat %s: %w", dir, err)
return false, false, fmt.Errorf("failed to stat %s: %w", dir, err)
}
return true, nil
return true, false, nil
}
entries, err := os.ReadDir(dir)
if err != nil {
return false, fmt.Errorf("failed to read dir %s: %w", dir, err)
return false, false, fmt.Errorf("failed to read dir %s: %w", dir, err)
}
return len(entries) == 0, nil
return len(entries) == 0, true, nil
}

View File

@ -94,10 +94,10 @@ func (testUpdater *TestUpdater) SetLatestVersion(version, minAuto *semver.Versio
}
}
func (updater *TestUpdater) GetVersionInfo(downloader updater.Downloader, channel updater.Channel) (updater.VersionInfo, error) {
return updater.latest, nil
func (testUpdater *TestUpdater) GetVersionInfo(downloader updater.Downloader, channel updater.Channel) (updater.VersionInfo, error) {
return testUpdater.latest, nil
}
func (updater *TestUpdater) InstallUpdate(downloader updater.Downloader, update updater.VersionInfo) error {
func (testUpdater *TestUpdater) InstallUpdate(downloader updater.Downloader, update updater.VersionInfo) error {
return nil
}

View File

@ -40,7 +40,7 @@ func (bridge *Bridge) SetIMAPPort(newPort int) error {
return err
}
return bridge.restartIMAP(context.Background())
return bridge.restartIMAP()
}
func (bridge *Bridge) GetIMAPSSL() bool {
@ -56,7 +56,7 @@ func (bridge *Bridge) SetIMAPSSL(newSSL bool) error {
return err
}
return bridge.restartIMAP(context.Background())
return bridge.restartIMAP()
}
func (bridge *Bridge) GetSMTPPort() int {
@ -112,7 +112,7 @@ func (bridge *Bridge) SetGluonDir(ctx context.Context, newGluonDir string) error
return fmt.Errorf("failed to set new gluon dir: %w", err)
}
imapServer, err := newIMAPServer(bridge.vault.GetGluonDir(), bridge.curVersion, bridge.tlsConfig, bridge.logIMAPClientCommands, bridge.logIMAPServerCommands)
imapServer, err := newIMAPServer(bridge.vault.GetGluonDir(), bridge.curVersion, bridge.tlsConfig, bridge.logIMAPClient, bridge.logIMAPServer)
if err != nil {
return fmt.Errorf("failed to create new IMAP server: %w", err)
}
@ -193,7 +193,7 @@ func (bridge *Bridge) SetAutoUpdate(autoUpdate bool) error {
}
func (bridge *Bridge) GetUpdateChannel() updater.Channel {
return updater.Channel(bridge.vault.GetUpdateChannel())
return bridge.vault.GetUpdateChannel()
}
func (bridge *Bridge) SetUpdateChannel(channel updater.Channel) error {

View File

@ -13,7 +13,7 @@ import (
func TestBridge_Settings_GluonDir(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Create a user.
_, err := bridge.LoginUser(context.Background(), username, password, nil, nil)
require.NoError(t, err)
@ -36,7 +36,7 @@ func TestBridge_Settings_GluonDir(t *testing.T) {
func TestBridge_Settings_IMAPPort(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
curPort := bridge.GetIMAPPort()
// Set the port to 1144.
@ -53,7 +53,7 @@ func TestBridge_Settings_IMAPPort(t *testing.T) {
func TestBridge_Settings_IMAPSSL(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// By default, IMAP SSL is disabled.
require.False(t, bridge.GetIMAPSSL())
@ -68,7 +68,7 @@ func TestBridge_Settings_IMAPSSL(t *testing.T) {
func TestBridge_Settings_SMTPPort(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
curPort := bridge.GetSMTPPort()
// Set the port to 1024.
@ -79,14 +79,13 @@ func TestBridge_Settings_SMTPPort(t *testing.T) {
// Assert that it has changed.
require.NotEqual(t, curPort, bridge.GetSMTPPort())
})
})
}
func TestBridge_Settings_SMTPSSL(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// By default, SMTP SSL is disabled.
require.False(t, bridge.GetSMTPSSL())
@ -101,7 +100,7 @@ func TestBridge_Settings_SMTPSSL(t *testing.T) {
func TestBridge_Settings_Proxy(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// By default, proxy is allowed.
require.True(t, bridge.GetProxyAllowed())
@ -117,7 +116,7 @@ func TestBridge_Settings_Proxy(t *testing.T) {
func TestBridge_Settings_Autostart(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// By default, autostart is disabled.
require.False(t, bridge.GetAutostart())
@ -133,7 +132,7 @@ func TestBridge_Settings_Autostart(t *testing.T) {
func TestBridge_Settings_FirstStart(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// By default, first start is true.
require.True(t, bridge.GetFirstStart())
@ -148,7 +147,7 @@ func TestBridge_Settings_FirstStart(t *testing.T) {
func TestBridge_Settings_FirstStartGUI(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// By default, first start is true.
require.True(t, bridge.GetFirstStartGUI())

View File

@ -3,10 +3,11 @@ package bridge
import (
"crypto/tls"
"fmt"
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
"net"
"strconv"
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
@ -49,12 +50,7 @@ func (bridge *Bridge) restartSMTP() error {
return err
}
smtpServer, err := newSMTPServer(bridge.smtpBackend, bridge.tlsConfig, bridge.logSMTPCommands)
if err != nil {
return err
}
bridge.smtpServer = smtpServer
bridge.smtpServer = newSMTPServer(bridge.smtpBackend, bridge.tlsConfig, bridge.logSMTP)
return bridge.serveSMTP()
}
@ -69,7 +65,7 @@ func (bridge *Bridge) closeSMTP() error {
return nil
}
func newSMTPServer(smtpBackend *smtpBackend, tlsConfig *tls.Config, shouldLog bool) (*smtp.Server, error) {
func newSMTPServer(smtpBackend *smtpBackend, tlsConfig *tls.Config, shouldLog bool) *smtp.Server {
smtpServer := smtp.NewServer(smtpBackend)
smtpServer.TLSConfig = tlsConfig
@ -99,5 +95,5 @@ func newSMTPServer(smtpBackend *smtpBackend, tlsConfig *tls.Config, shouldLog bo
})
})
return smtpServer, nil
return smtpServer
}

View File

@ -44,18 +44,18 @@ func TestBridge_Sync(t *testing.T) {
})
// The initial user should be fully synced.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
syncCh, done := bridge.GetEvents(events.SyncFinished{})
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
syncCh, done := chToType[events.Event, events.SyncFinished](bridge.GetEvents(events.SyncFinished{}))
defer done()
userID, err := bridge.LoginUser(ctx, "imap", password, nil, nil)
require.NoError(t, err)
require.Equal(t, userID, (<-syncCh).(events.SyncFinished).UserID)
require.Equal(t, userID, (<-syncCh).UserID)
})
// If we then connect an IMAP client, it should see all the messages.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
info, err := bridge.GetUserInfo(userID)
require.NoError(t, err)
require.True(t, info.Connected)
@ -63,7 +63,7 @@ func TestBridge_Sync(t *testing.T) {
client, err := client.Dial(fmt.Sprintf(":%v", bridge.GetIMAPPort()))
require.NoError(t, err)
require.NoError(t, client.Login("imap@pm.me", string(info.BridgePass)))
defer client.Logout()
defer func() { _ = client.Logout() }()
status, err := client.Select(`Folders/folder`, false)
require.NoError(t, err)
@ -71,7 +71,7 @@ func TestBridge_Sync(t *testing.T) {
})
// Now let's remove the user and simulate a network error.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.NoError(t, bridge.DeleteUser(ctx, userID))
})
@ -79,14 +79,14 @@ func TestBridge_Sync(t *testing.T) {
netCtl.SetReadLimit(2 * read / 3)
// Login the user; its sync should fail.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
syncCh, done := bridge.GetEvents(events.SyncFailed{})
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
syncCh, done := chToType[events.Event, events.SyncFailed](bridge.GetEvents(events.SyncFailed{}))
defer done()
userID, err := bridge.LoginUser(ctx, "imap", password, nil, nil)
require.NoError(t, err)
require.Equal(t, userID, (<-syncCh).(events.SyncFailed).UserID)
require.Equal(t, userID, (<-syncCh).UserID)
info, err := bridge.GetUserInfo(userID)
require.NoError(t, err)
@ -95,7 +95,7 @@ func TestBridge_Sync(t *testing.T) {
client, err := client.Dial(fmt.Sprintf(":%v", bridge.GetIMAPPort()))
require.NoError(t, err)
require.NoError(t, client.Login("imap@pm.me", string(info.BridgePass)))
defer client.Logout()
defer func() { _ = client.Logout() }()
status, err := client.Select(`Folders/folder`, false)
require.NoError(t, err)
@ -107,11 +107,11 @@ func TestBridge_Sync(t *testing.T) {
// Login the user; its sync should now finish.
// If we then connect an IMAP client, it should eventually see all the messages.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
syncCh, done := bridge.GetEvents(events.SyncFinished{})
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
syncCh, done := chToType[events.Event, events.SyncFinished](bridge.GetEvents(events.SyncFinished{}))
defer done()
require.Equal(t, userID, (<-syncCh).(events.SyncFinished).UserID)
require.Equal(t, userID, (<-syncCh).UserID)
info, err := bridge.GetUserInfo(userID)
require.NoError(t, err)
@ -120,7 +120,7 @@ func TestBridge_Sync(t *testing.T) {
client, err := client.Dial(fmt.Sprintf(":%v", bridge.GetIMAPPort()))
require.NoError(t, err)
require.NoError(t, client.Login("imap@pm.me", string(info.BridgePass)))
defer client.Logout()
defer func() { _ = client.Logout() }()
status, err := client.Select(`Folders/folder`, false)
require.NoError(t, err)
@ -128,3 +128,22 @@ func TestBridge_Sync(t *testing.T) {
})
})
}
func chToType[In, Out any](inCh <-chan In, done func()) (<-chan Out, func()) {
outCh := make(chan Out)
go func() {
defer close(outCh)
for in := range inCh {
out, ok := any(in).(Out)
if !ok {
panic(fmt.Sprintf("unexpected type %T", in))
}
outCh <- out
}
}()
return outCh, done
}

View File

@ -13,6 +13,10 @@ func (bridge *Bridge) CheckForUpdates() {
}
func (bridge *Bridge) watchForUpdates() error {
if _, err := bridge.updater.GetVersionInfo(bridge.api, bridge.vault.GetUpdateChannel()); err != nil {
return err
}
ticker := time.NewTicker(constants.UpdateCheckInterval)
go func() {
@ -22,7 +26,10 @@ func (bridge *Bridge) watchForUpdates() error {
return
case <-bridge.updateCheckCh:
// ...
case <-ticker.C:
// ...
}
version, err := bridge.updater.GetVersionInfo(bridge.api, bridge.vault.GetUpdateChannel())

View File

@ -73,7 +73,7 @@ func (bridge *Bridge) handleUserAddressCreated(ctx context.Context, user *user.U
}
// TODO: Handle addresses that have been disabled!
func (bridge *Bridge) handleUserAddressUpdated(ctx context.Context, user *user.User, event events.UserAddressUpdated) error {
func (bridge *Bridge) handleUserAddressUpdated(_ context.Context, user *user.User, _ events.UserAddressUpdated) error {
switch user.GetAddressMode() {
case vault.CombinedMode:
return fmt.Errorf("not implemented")

View File

@ -15,12 +15,12 @@ import (
func TestBridge_WithoutUsers(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.Empty(t, bridge.GetUserIDs())
require.Empty(t, getConnectedUserIDs(t, bridge))
})
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.Empty(t, bridge.GetUserIDs())
require.Empty(t, getConnectedUserIDs(t, bridge))
})
@ -29,7 +29,7 @@ func TestBridge_WithoutUsers(t *testing.T) {
func TestBridge_Login(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID, err := bridge.LoginUser(ctx, username, password, nil, nil)
require.NoError(t, err)
@ -43,7 +43,7 @@ func TestBridge_Login(t *testing.T) {
func TestBridge_LoginLogoutLogin(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID := must(bridge.LoginUser(ctx, username, password, nil, nil))
@ -71,7 +71,7 @@ func TestBridge_LoginLogoutLogin(t *testing.T) {
func TestBridge_LoginDeleteLogin(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID := must(bridge.LoginUser(ctx, username, password, nil, nil))
@ -99,7 +99,7 @@ func TestBridge_LoginDeleteLogin(t *testing.T) {
func TestBridge_LoginDeauthLogin(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID := must(bridge.LoginUser(ctx, username, password, nil, nil))
@ -135,7 +135,7 @@ func TestBridge_LoginExpireLogin(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
s.SetAuthLife(authLife)
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user. Its auth will only be valid for a short time.
userID := must(bridge.LoginUser(ctx, username, password, nil, nil))
@ -153,7 +153,7 @@ func TestBridge_FailToLoad(t *testing.T) {
var userID string
// Login the user.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
userID = must(bridge.LoginUser(ctx, username, password, nil, nil))
})
@ -161,7 +161,7 @@ func TestBridge_FailToLoad(t *testing.T) {
require.NoError(t, s.RevokeUser(userID))
// When bridge starts, the user will not be logged in.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.Equal(t, []string{userID}, bridge.GetUserIDs())
require.Empty(t, getConnectedUserIDs(t, bridge))
})
@ -173,7 +173,7 @@ func TestBridge_LoadWithoutInternet(t *testing.T) {
var userID string
// Login the user.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
userID = must(bridge.LoginUser(ctx, username, password, nil, nil))
})
@ -181,7 +181,7 @@ func TestBridge_LoadWithoutInternet(t *testing.T) {
netCtl.Disable()
// Start bridge without internet.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Initially, users are not connected.
require.Equal(t, []string{userID}, bridge.GetUserIDs())
require.Empty(t, getConnectedUserIDs(t, bridge))
@ -203,11 +203,11 @@ func TestBridge_LoginRestart(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
var userID string
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
userID = must(bridge.LoginUser(ctx, username, password, nil, nil))
})
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.Equal(t, []string{userID}, bridge.GetUserIDs())
require.Equal(t, []string{userID}, getConnectedUserIDs(t, bridge))
})
@ -218,7 +218,7 @@ func TestBridge_LoginLogoutRestart(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
var userID string
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID = must(bridge.LoginUser(ctx, username, password, nil, nil))
@ -226,7 +226,7 @@ func TestBridge_LoginLogoutRestart(t *testing.T) {
require.NoError(t, bridge.LogoutUser(ctx, userID))
})
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// The user is still disconnected.
require.Equal(t, []string{userID}, bridge.GetUserIDs())
require.Empty(t, getConnectedUserIDs(t, bridge))
@ -238,7 +238,7 @@ func TestBridge_LoginDeleteRestart(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
var userID string
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID = must(bridge.LoginUser(ctx, username, password, nil, nil))
@ -246,7 +246,7 @@ func TestBridge_LoginDeleteRestart(t *testing.T) {
require.NoError(t, bridge.DeleteUser(ctx, userID))
})
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// The user is still gone.
require.Empty(t, bridge.GetUserIDs())
require.Empty(t, getConnectedUserIDs(t, bridge))
@ -263,7 +263,7 @@ func TestBridge_FailLoginRecover(t *testing.T) {
})
// Log the user in and record how much data was read.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
userID := must(bridge.LoginUser(ctx, username, password, nil, nil))
require.NoError(t, bridge.LogoutUser(ctx, userID))
})
@ -272,7 +272,7 @@ func TestBridge_FailLoginRecover(t *testing.T) {
netCtl.SetReadLimit(read / 2)
// We should fail to log the user in because we can't fully read its data.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
require.Error(t, getErr(bridge.LoginUser(ctx, username, password, nil, nil)))
// There should be no users.
@ -283,7 +283,7 @@ func TestBridge_FailLoginRecover(t *testing.T) {
func TestBridge_FailLoadRecover(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
must(bridge.LoginUser(ctx, username, password, nil, nil))
})
@ -294,7 +294,7 @@ func TestBridge_FailLoadRecover(t *testing.T) {
})
// Start bridge and record how much data was read.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// ...
})
@ -302,7 +302,7 @@ func TestBridge_FailLoadRecover(t *testing.T) {
netCtl.SetReadLimit(read / 2)
// We should fail to load the user; it should be disconnected.
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
userIDs := bridge.GetUserIDs()
require.False(t, must(bridge.GetUserInfo(userIDs[0])).Connected)
@ -316,7 +316,7 @@ func TestBridge_BridgePass(t *testing.T) {
var pass []byte
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID = must(bridge.LoginUser(ctx, username, password, nil, nil))
@ -333,7 +333,7 @@ func TestBridge_BridgePass(t *testing.T) {
require.Equal(t, pass, pass)
})
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// The bridge should load the user.
require.Equal(t, []string{userID}, bridge.GetUserIDs())
require.Equal(t, []string{userID}, getConnectedUserIDs(t, bridge))
@ -346,7 +346,7 @@ func TestBridge_BridgePass(t *testing.T) {
func TestBridge_AddressMode(t *testing.T) {
withTLSEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) {
withBridge(t, ctx, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Login the user.
userID, err := bridge.LoginUser(ctx, username, password, nil, nil)
require.NoError(t, err)

View File

@ -1,3 +1,4 @@
// Package proto provides the gRPC definition of the focus service.
package proto
//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative focus.proto

View File

@ -1,3 +1,4 @@
// Package focus provides a gRPC service for raising the application.
package focus
import (
@ -14,10 +15,10 @@ import (
const Host = "127.0.0.1"
// Port is the port to listen on.
var Port = 1042
var Port = 1042 // nolint:gochecknoglobals
// FocusService is a gRPC service that can be used to raise the application.
type FocusService struct {
// Service is a gRPC service that can be used to raise the application.
type Service struct {
proto.UnimplementedFocusServer
server *grpc.Server
@ -27,13 +28,13 @@ type FocusService struct {
// NewService creates a new focus service.
// It listens on the local host and port 1042 (by default).
func NewService() (*FocusService, error) {
func NewService() (*Service, error) {
listener, err := net.Listen("tcp", net.JoinHostPort(Host, fmt.Sprint(Port)))
if err != nil {
return nil, fmt.Errorf("failed to listen: %w", err)
}
service := &FocusService{
service := &Service{
server: grpc.NewServer(),
listener: listener,
raiseCh: make(chan struct{}, 1),
@ -51,18 +52,18 @@ func NewService() (*FocusService, error) {
}
// Raise implements the gRPC FocusService interface; it raises the application.
func (service *FocusService) Raise(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
func (service *Service) Raise(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
service.raiseCh <- struct{}{}
return &emptypb.Empty{}, nil
}
// GetRaiseCh returns a channel on which events are sent when the application should be raised.
func (service *FocusService) GetRaiseCh() <-chan struct{} {
func (service *Service) GetRaiseCh() <-chan struct{} {
return service.raiseCh
}
// Close closes the service.
func (service *FocusService) Close() {
func (service *Service) Close() {
service.server.Stop()
close(service.raiseCh)
}

View File

@ -2,46 +2,46 @@ package safe
import "sync"
type Type[T any] struct {
type Value[T any] struct {
data T
lock sync.RWMutex
}
func NewType[T any](data T) *Type[T] {
return &Type[T]{
func NewValue[T any](data T) *Value[T] {
return &Value[T]{
data: data,
}
}
func (s *Type[T]) Get(fn func(data T)) {
func (s *Value[T]) Get(fn func(data T)) {
s.lock.RLock()
defer s.lock.RUnlock()
fn(s.data)
}
func (s *Type[T]) GetErr(fn func(data T) error) error {
func (s *Value[T]) GetErr(fn func(data T) error) error {
s.lock.RLock()
defer s.lock.RUnlock()
return fn(s.data)
}
func (s *Type[T]) Set(data T) {
func (s *Value[T]) Set(data T) {
s.lock.Lock()
defer s.lock.Unlock()
s.data = data
}
func GetType[T, Ret any](s *Type[T], fn func(data T) Ret) Ret {
func GetType[T, Ret any](s *Value[T], fn func(data T) Ret) Ret {
s.lock.RLock()
defer s.lock.RUnlock()
return fn(s.data)
}
func GetTypeErr[T, Ret any](s *Type[T], fn func(data T) (Ret, error)) (Ret, error) {
func GetTypeErr[T, Ret any](s *Value[T], fn func(data T) (Ret, error)) (Ret, error) {
s.lock.RLock()
defer s.lock.RUnlock()

View File

@ -14,9 +14,9 @@ import (
)
var (
defaultFlags = imap.NewFlagSet(imap.FlagSeen, imap.FlagFlagged, imap.FlagDeleted)
defaultPermanentFlags = imap.NewFlagSet(imap.FlagSeen, imap.FlagFlagged, imap.FlagDeleted)
defaultAttributes = imap.NewFlagSet()
defaultFlags = imap.NewFlagSet(imap.FlagSeen, imap.FlagFlagged, imap.FlagDeleted) // nolint:gochecknoglobals
defaultPermanentFlags = imap.NewFlagSet(imap.FlagSeen, imap.FlagFlagged, imap.FlagDeleted) // nolint:gochecknoglobals
defaultAttributes = imap.NewFlagSet() // nolint:gochecknoglobals
)
const (

View File

@ -75,7 +75,6 @@ func (session *smtpSession) Mail(from string, opts smtp.MailOptions) error {
logrus.Info("SMTP session mail")
return session.apiAddrs.GetErr(func(apiAddrs []liteapi.Address) error {
switch {
case opts.RequireTLS:
return ErrNotImplemented

View File

@ -1,12 +1,20 @@
package user
import "reflect"
import (
"fmt"
"reflect"
)
func mapTo[From, To any](from []From) []To {
to := make([]To, 0, len(from))
for _, from := range from {
to = append(to, reflect.ValueOf(from).Convert(reflect.TypeOf(to).Elem()).Interface().(To))
val, ok := reflect.ValueOf(from).Convert(reflect.TypeOf(to).Elem()).Interface().(To)
if !ok {
panic(fmt.Sprintf("cannot convert %T to %T", from, *new(To)))
}
to = append(to, val)
}
return to

View File

@ -22,8 +22,8 @@ import (
)
var (
EventPeriod = 20 * time.Second
EventJitter = 20 * time.Second
EventPeriod = 20 * time.Second // nolint:gochecknoglobals
EventJitter = 20 * time.Second // nolint:gochecknoglobals
)
type User struct {
@ -31,9 +31,9 @@ type User struct {
client *liteapi.Client
eventCh *queue.QueuedChannel[events.Event]
apiUser *safe.Type[liteapi.User]
apiUser *safe.Value[liteapi.User]
apiAddrs *safe.Slice[liteapi.Address]
settings *safe.Type[liteapi.MailSettings]
settings *safe.Value[liteapi.MailSettings]
userKR *crypto.KeyRing
addrKRs map[string]*crypto.KeyRing
@ -90,9 +90,9 @@ func New(ctx context.Context, encVault *vault.User, client *liteapi.Client, apiU
client: client,
eventCh: queue.NewQueuedChannel[events.Event](0, 0),
apiUser: safe.NewType(apiUser),
apiUser: safe.NewValue(apiUser),
apiAddrs: safe.NewSlice(apiAddrs),
settings: safe.NewType(settings),
settings: safe.NewValue(settings),
userKR: userKR,
addrKRs: addrKRs,