diff --git a/.golangci.yml b/.golangci.yml index 6f176e51..5b406a47 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,6 +24,17 @@ issues: - gochecknoglobals - gochecknoinits - gosec + - goconst + - dogsled + - path: test + linters: + - dupl + - funlen + - gochecknoglobals + - gochecknoinits + - gosec + - goconst + - dogsled linters-settings: godox: diff --git a/COPYING_NOTES.md b/COPYING_NOTES.md index d18a0b67..150c8a90 100644 --- a/COPYING_NOTES.md +++ b/COPYING_NOTES.md @@ -23,12 +23,9 @@ Proton Mail Bridge includes the following 3rd party software: * [notificator](https://github.com/0xAX/notificator) available under [license](https://github.com/0xAX/notificator/blob/master/LICENSE) * [semver](https://github.com/Masterminds/semver/v3) available under [license](https://github.com/Masterminds/semver/v3/blob/master/LICENSE) +* [gluon](https://github.com/ProtonMail/gluon) available under [license](https://github.com/ProtonMail/gluon/blob/master/LICENSE) * [go-autostart](https://github.com/ProtonMail/go-autostart) available under [license](https://github.com/ProtonMail/go-autostart/blob/master/LICENSE) -* [go-crypto](https://github.com/ProtonMail/go-crypto) available under [license](https://github.com/ProtonMail/go-crypto/blob/master/LICENSE) -* [go-imap-id](https://github.com/ProtonMail/go-imap-id) available under [license](https://github.com/ProtonMail/go-imap-id/blob/master/LICENSE) * [go-rfc5322](https://github.com/ProtonMail/go-rfc5322) available under [license](https://github.com/ProtonMail/go-rfc5322/blob/master/LICENSE) -* [go-srp](https://github.com/ProtonMail/go-srp) available under [license](https://github.com/ProtonMail/go-srp/blob/master/LICENSE) -* [go-vcard](https://github.com/ProtonMail/go-vcard) available under [license](https://github.com/ProtonMail/go-vcard/blob/master/LICENSE) * [gopenpgp](https://github.com/ProtonMail/gopenpgp/v2) available under [license](https://github.com/ProtonMail/gopenpgp/v2/blob/master/LICENSE) * [goquery](https://github.com/PuerkitoBio/goquery) available under [license](https://github.com/PuerkitoBio/goquery/blob/master/LICENSE) * [ishell](https://github.com/abiosoft/ishell) available under [license](https://github.com/abiosoft/ishell/blob/master/LICENSE) @@ -39,17 +36,14 @@ Proton Mail Bridge includes the following 3rd party software: * [docker-credential-helpers](https://github.com/docker/docker-credential-helpers) available under [license](https://github.com/docker/docker-credential-helpers/blob/master/LICENSE) * [go-sysinfo](https://github.com/elastic/go-sysinfo) available under [license](https://github.com/elastic/go-sysinfo/blob/master/LICENSE) * [go-imap](https://github.com/emersion/go-imap) available under [license](https://github.com/emersion/go-imap/blob/master/LICENSE) -* [go-imap-appendlimit](https://github.com/emersion/go-imap-appendlimit) available under [license](https://github.com/emersion/go-imap-appendlimit/blob/master/LICENSE) -* [go-imap-move](https://github.com/emersion/go-imap-move) available under [license](https://github.com/emersion/go-imap-move/blob/master/LICENSE) -* [go-imap-quota](https://github.com/emersion/go-imap-quota) available under [license](https://github.com/emersion/go-imap-quota/blob/master/LICENSE) -* [go-imap-unselect](https://github.com/emersion/go-imap-unselect) available under [license](https://github.com/emersion/go-imap-unselect/blob/master/LICENSE) +* [go-imap-id](https://github.com/emersion/go-imap-id) available under [license](https://github.com/emersion/go-imap-id/blob/master/LICENSE) * [go-message](https://github.com/emersion/go-message) available under [license](https://github.com/emersion/go-message/blob/master/LICENSE) * [go-sasl](https://github.com/emersion/go-sasl) available under [license](https://github.com/emersion/go-sasl/blob/master/LICENSE) * [go-smtp](https://github.com/emersion/go-smtp) available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE) -* [go-textwrapper](https://github.com/emersion/go-textwrapper) available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE) * [color](https://github.com/fatih/color) available under [license](https://github.com/fatih/color/blob/master/LICENSE) * [sentry-go](https://github.com/getsentry/sentry-go) available under [license](https://github.com/getsentry/sentry-go/blob/master/LICENSE) * [resty](https://github.com/go-resty/resty/v2) available under [license](https://github.com/go-resty/resty/v2/blob/master/LICENSE) +* [go-json](https://github.com/goccy/go-json) available under [license](https://github.com/goccy/go-json/blob/master/LICENSE) * [dbus](https://github.com/godbus/dbus) available under [license](https://github.com/godbus/dbus/blob/master/LICENSE) * [mock](https://github.com/golang/mock) available under [license](https://github.com/golang/mock/blob/master/LICENSE) * [go-cmp](https://github.com/google/go-cmp) available under [license](https://github.com/google/go-cmp/blob/master/LICENSE) @@ -57,16 +51,13 @@ Proton Mail Bridge includes the following 3rd party software: * [go-multierror](https://github.com/hashicorp/go-multierror) available under [license](https://github.com/hashicorp/go-multierror/blob/master/LICENSE) * [html2text](https://github.com/jaytaylor/html2text) available under [license](https://github.com/jaytaylor/html2text/blob/master/LICENSE) * [go-keychain](https://github.com/keybase/go-keychain) available under [license](https://github.com/keybase/go-keychain/blob/master/LICENSE) -* [aurora](https://github.com/logrusorgru/aurora) available under [license](https://github.com/logrusorgru/aurora/blob/master/LICENSE) * [dns](https://github.com/miekg/dns) available under [license](https://github.com/miekg/dns/blob/master/LICENSE) -* [jsondiff](https://github.com/nsf/jsondiff) available under [license](https://github.com/nsf/jsondiff/blob/master/LICENSE) * [errors](https://github.com/pkg/errors) available under [license](https://github.com/pkg/errors/blob/master/LICENSE) -* [du](https://github.com/ricochet2200/go-disk-usage/du) available under [license](https://github.com/ricochet2200/go-disk-usage/du/blob/master/LICENSE) +* [profile](https://github.com/pkg/profile) available under [license](https://github.com/pkg/profile/blob/master/LICENSE) * [logrus](https://github.com/sirupsen/logrus) available under [license](https://github.com/sirupsen/logrus/blob/master/LICENSE) * [testify](https://github.com/stretchr/testify) available under [license](https://github.com/stretchr/testify/blob/master/LICENSE) * [cli](https://github.com/urfave/cli/v2) available under [license](https://github.com/urfave/cli/v2/blob/master/LICENSE) -* [msgpack](https://github.com/vmihailenco/msgpack/v5) available under [license](https://github.com/vmihailenco/msgpack/v5/blob/master/LICENSE) -* [bbolt](https://go.etcd.io/bbolt) available under [license](https://github.com/etcd-io/bbolt/blob/master/LICENSE) +* [liteapi](https://gitlab.protontech.ch/go/liteapi) * [exp](https://golang.org/x/exp) available under [license](https://cs.opensource.google/go/x/exp/+/master:LICENSE) * [net](https://golang.org/x/net) available under [license](https://cs.opensource.google/go/x/net/+/master:LICENSE) * [sys](https://golang.org/x/sys) available under [license](https://cs.opensource.google/go/x/sys/+/master:LICENSE) @@ -74,11 +65,17 @@ Proton Mail Bridge includes the following 3rd party software: * [grpc](https://google.golang.org/grpc) available under [license](https://github.com/grpc/grpc-go/blob/master/LICENSE) * [protobuf](https://google.golang.org/protobuf) available under [license](https://github.com/protocolbuffers/protobuf/blob/main/LICENSE) * [plist](https://howett.net/plist) available under [license](https://github.com/DHowett/go-plist/blob/main/LICENSE) +* [atlas](https://ariga.io/atlas) +* [ent](https://entgo.io/ent) * [bcrypt](https://github.com/ProtonMail/bcrypt) available under [license](https://github.com/ProtonMail/bcrypt/blob/master/LICENSE) +* [go-crypto](https://github.com/ProtonMail/go-crypto) available under [license](https://github.com/ProtonMail/go-crypto/blob/master/LICENSE) * [go-mime](https://github.com/ProtonMail/go-mime) available under [license](https://github.com/ProtonMail/go-mime/blob/master/LICENSE) +* [go-srp](https://github.com/ProtonMail/go-srp) available under [license](https://github.com/ProtonMail/go-srp/blob/master/LICENSE) * [readline](https://github.com/abiosoft/readline) available under [license](https://github.com/abiosoft/readline/blob/master/LICENSE) +* [levenshtein](https://github.com/agext/levenshtein) available under [license](https://github.com/agext/levenshtein/blob/master/LICENSE) * [cascadia](https://github.com/andybalholm/cascadia) available under [license](https://github.com/andybalholm/cascadia/blob/master/LICENSE) * [antlr](https://github.com/antlr/antlr4/runtime/Go/antlr) available under [license](https://github.com/antlr/antlr4/runtime/Go/antlr/blob/master/LICENSE) +* [go-textseg](https://github.com/apparentlymart/go-textseg/v13) available under [license](https://github.com/apparentlymart/go-textseg/v13/blob/master/LICENSE) * [test](https://github.com/chzyer/test) available under [license](https://github.com/chzyer/test/blob/master/LICENSE) * [circl](https://github.com/cloudflare/circl) available under [license](https://github.com/cloudflare/circl/blob/master/LICENSE) * [go-md2man](https://github.com/cpuguy83/go-md2man/v2) available under [license](https://github.com/cpuguy83/go-md2man/v2/blob/master/LICENSE) @@ -87,31 +84,49 @@ Proton Mail Bridge includes the following 3rd party software: * [wincred](https://github.com/danieljoos/wincred) available under [license](https://github.com/danieljoos/wincred/blob/master/LICENSE) * [go-spew](https://github.com/davecgh/go-spew) available under [license](https://github.com/davecgh/go-spew/blob/master/LICENSE) * [go-windows](https://github.com/elastic/go-windows) available under [license](https://github.com/elastic/go-windows/blob/master/LICENSE) +* [go-textwrapper](https://github.com/emersion/go-textwrapper) available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE) * [go-vcard](https://github.com/emersion/go-vcard) available under [license](https://github.com/emersion/go-vcard/blob/master/LICENSE) * [go-shlex](https://github.com/flynn-archive/go-shlex) available under [license](https://github.com/flynn-archive/go-shlex/blob/master/LICENSE) +* [sse](https://github.com/gin-contrib/sse) available under [license](https://github.com/gin-contrib/sse/blob/master/LICENSE) +* [gin](https://github.com/gin-gonic/gin) available under [license](https://github.com/gin-gonic/gin/blob/master/LICENSE) +* [inflect](https://github.com/go-openapi/inflect) available under [license](https://github.com/go-openapi/inflect/blob/master/LICENSE) +* [locales](https://github.com/go-playground/locales) available under [license](https://github.com/go-playground/locales/blob/master/LICENSE) +* [universal-translator](https://github.com/go-playground/universal-translator) available under [license](https://github.com/go-playground/universal-translator/blob/master/LICENSE) +* [validator](https://github.com/go-playground/validator/v10) available under [license](https://github.com/go-playground/validator/v10/blob/master/LICENSE) * [uuid](https://github.com/gofrs/uuid) available under [license](https://github.com/gofrs/uuid/blob/master/LICENSE) * [protobuf](https://github.com/golang/protobuf) available under [license](https://github.com/golang/protobuf/blob/master/LICENSE) * [errwrap](https://github.com/hashicorp/errwrap) available under [license](https://github.com/hashicorp/errwrap/blob/master/LICENSE) * [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix) available under [license](https://github.com/hashicorp/go-immutable-radix/blob/master/LICENSE) * [go-memdb](https://github.com/hashicorp/go-memdb) available under [license](https://github.com/hashicorp/go-memdb/blob/master/LICENSE) * [golang-lru](https://github.com/hashicorp/golang-lru) available under [license](https://github.com/hashicorp/golang-lru/blob/master/LICENSE) +* [hcl](https://github.com/hashicorp/hcl/v2) available under [license](https://github.com/hashicorp/hcl/v2/blob/master/LICENSE) * [multierror](https://github.com/joeshaw/multierror) available under [license](https://github.com/joeshaw/multierror/blob/master/LICENSE) +* [go](https://github.com/json-iterator/go) available under [license](https://github.com/json-iterator/go/blob/master/LICENSE) +* [go-urn](https://github.com/leodido/go-urn) available under [license](https://github.com/leodido/go-urn/blob/master/LICENSE) * [go-colorable](https://github.com/mattn/go-colorable) available under [license](https://github.com/mattn/go-colorable/blob/master/LICENSE) * [go-isatty](https://github.com/mattn/go-isatty) available under [license](https://github.com/mattn/go-isatty/blob/master/LICENSE) * [go-runewidth](https://github.com/mattn/go-runewidth) available under [license](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +* [go-sqlite3](https://github.com/mattn/go-sqlite3) available under [license](https://github.com/mattn/go-sqlite3/blob/master/LICENSE) +* [go-wordwrap](https://github.com/mitchellh/go-wordwrap) available under [license](https://github.com/mitchellh/go-wordwrap/blob/master/LICENSE) +* [concurrent](https://github.com/modern-go/concurrent) available under [license](https://github.com/modern-go/concurrent/blob/master/LICENSE) +* [reflect2](https://github.com/modern-go/reflect2) available under [license](https://github.com/modern-go/reflect2/blob/master/LICENSE) * [tablewriter](https://github.com/olekukonko/tablewriter) available under [license](https://github.com/olekukonko/tablewriter/blob/master/LICENSE) +* [go-toml](https://github.com/pelletier/go-toml/v2) available under [license](https://github.com/pelletier/go-toml/v2/blob/master/LICENSE) * [go-difflib](https://github.com/pmezard/go-difflib) available under [license](https://github.com/pmezard/go-difflib/blob/master/LICENSE) * [procfs](https://github.com/prometheus/procfs) available under [license](https://github.com/prometheus/procfs/blob/master/LICENSE) * [uniseg](https://github.com/rivo/uniseg) available under [license](https://github.com/rivo/uniseg/blob/master/LICENSE) * [blackfriday](https://github.com/russross/blackfriday/v2) available under [license](https://github.com/russross/blackfriday/v2/blob/master/LICENSE) * [pflag](https://github.com/spf13/pflag) available under [license](https://github.com/spf13/pflag/blob/master/LICENSE) * [bom](https://github.com/ssor/bom) available under [license](https://github.com/ssor/bom/blob/master/LICENSE) -* [tagparser](https://github.com/vmihailenco/tagparser/v2) available under [license](https://github.com/vmihailenco/tagparser/v2/blob/master/LICENSE) +* [codec](https://github.com/ugorji/go/codec) available under [license](https://github.com/ugorji/go/codec/blob/master/LICENSE) * [smetrics](https://github.com/xrash/smetrics) available under [license](https://github.com/xrash/smetrics/blob/master/LICENSE) +* [go-cty](https://github.com/zclconf/go-cty) available under [license](https://github.com/zclconf/go-cty/blob/master/LICENSE) * [crypto](https://golang.org/x/crypto) available under [license](https://cs.opensource.google/go/x/crypto/+/master:LICENSE) * [mod](https://golang.org/x/mod) available under [license](https://cs.opensource.google/go/x/mod/+/master:LICENSE) +* [sync](https://golang.org/x/sync) available under [license](https://cs.opensource.google/go/x/sync/+/master:LICENSE) * [tools](https://golang.org/x/tools) available under [license](https://cs.opensource.google/go/x/tools/+/master:LICENSE) * [genproto](https://google.golang.org/genproto) +gopkg.in/yaml.v2 gopkg.in/yaml.v3 * [docker-credential-helpers](https://github.com/ProtonMail/docker-credential-helpers) available under [license](https://github.com/ProtonMail/docker-credential-helpers/blob/master/LICENSE) * [go-imap](https://github.com/ProtonMail/go-imap) available under [license](https://github.com/ProtonMail/go-imap/blob/master/LICENSE) diff --git a/internal/app/app.go b/internal/app/app.go index 3857936f..0df6e3af 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -43,7 +43,7 @@ import ( "github.com/urfave/cli/v2" ) -// Visible flags +// Visible flags. const ( flagCPUProfile = "cpu-prof" flagCPUProfileShort = "p" @@ -63,7 +63,7 @@ const ( flagLogSMTP = "log-smtp" ) -// Hidden flags +// Hidden flags. const ( flagLauncher = "launcher" flagNoWindow = "no-window" @@ -130,7 +130,7 @@ func New() *cli.App { return app } -func run(c *cli.Context) error { +func run(c *cli.Context) error { //nolint:funlen // Get the current bridge version. version, err := semver.NewVersion(constants.Version) if err != nil { @@ -255,7 +255,11 @@ func withLocations(fn func(*locations.Locations) error) error { // Create a new locations object that will be used to provide paths to store files. locations := locations.New(provider, constants.ConfigName) - defer locations.Clean() + defer func() { + if err := locations.Clean(); err != nil { + logrus.WithError(err).Error("Failed to clean locations") + } + }() return fn(locations) } diff --git a/internal/app/bridge.go b/internal/app/bridge.go index 979a5b06..37b437b6 100644 --- a/internal/app/bridge.go +++ b/internal/app/bridge.go @@ -42,13 +42,13 @@ import ( const vaultSecretName = "bridge-vault-key" // withBridge creates creates and tears down the bridge. -func withBridge( +func withBridge( //nolint:funlen c *cli.Context, exe string, locations *locations.Locations, version *semver.Version, identifier *useragent.UserAgent, - reporter *sentry.Reporter, + _ *sentry.Reporter, vault *vault.Vault, cookieJar http.CookieJar, fn func(*bridge.Bridge, <-chan events.Event) error, @@ -65,10 +65,7 @@ func withBridge( proxyDialer := dialer.NewProxyTLSDialer(pinningDialer, constants.APIHost) // Create the autostarter. - autostarter, err := newAutostarter(exe) - if err != nil { - return fmt.Errorf("could not create autostarter: %w", err) - } + autostarter := newAutostarter(exe) // Create the update installer. updater, err := newUpdater(locations) @@ -112,12 +109,12 @@ func withBridge( return fn(bridge, eventCh) } -func newAutostarter(exe string) (*autostart.App, error) { +func newAutostarter(exe string) *autostart.App { return &autostart.App{ Name: constants.FullAppName, DisplayName: constants.FullAppName, Exec: []string{exe, "--" + flagNoWindow}, - }, nil + } } func newUpdater(locations *locations.Locations) (*updater.Updater, error) { diff --git a/internal/app/vault.go b/internal/app/vault.go index 1f7aae29..5f1a96c2 100644 --- a/internal/app/vault.go +++ b/internal/app/vault.go @@ -52,7 +52,7 @@ func withVault(locations *locations.Locations, fn func(*vault.Vault, bool, bool) } } - // TODO: Add teardown actions (e.g. to close the vault). + // GODT-1950: Add teardown actions (e.g. to close the vault). return fn(encVault, insecure, corrupt) } diff --git a/internal/bridge/bridge.go b/internal/bridge/bridge.go index dd0260f9..601e1202 100644 --- a/internal/bridge/bridge.go +++ b/internal/bridge/bridge.go @@ -97,7 +97,7 @@ type Bridge struct { } // New creates a new bridge. -func New( +func New( //nolint:funlen locator Locator, // the locator to provide paths to store data vault *vault.Vault, // the bridge's encrypted data store autostarter Autostarter, // the autostarter to manage autostart settings @@ -131,10 +131,7 @@ func New( return nil, nil, fmt.Errorf("failed to get Gluon directory: %w", err) } - smtpBackend, err := newSMTPBackend() - if err != nil { - return nil, nil, fmt.Errorf("failed to create SMTP backend: %w", err) - } + smtpBackend := newSMTPBackend() imapServer, err := newIMAPServer(gluonDir, curVersion, tlsConfig, logIMAPClient, logIMAPServer) if err != nil { @@ -416,9 +413,9 @@ 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}, + MinVersion: tls.VersionTLS12, }, nil } diff --git a/internal/bridge/bridge_test.go b/internal/bridge/bridge_test.go index 3fbe76b4..e8a6ded5 100644 --- a/internal/bridge/bridge_test.go +++ b/internal/bridge/bridge_test.go @@ -461,7 +461,7 @@ func waitForEvent[T any](t *testing.T, eventCh <-chan events.Event, wantEvent T) t.Helper() for event := range eventCh { - switch event.(type) { + switch event.(type) { // nolint:gocritic case T: return } diff --git a/internal/bridge/imap.go b/internal/bridge/imap.go index 79602fbb..aa5bda3b 100644 --- a/internal/bridge/imap.go +++ b/internal/bridge/imap.go @@ -120,7 +120,6 @@ func getGluonDir(encVault *vault.Vault) (string, error) { 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) diff --git a/internal/bridge/smtp_backend.go b/internal/bridge/smtp_backend.go index 5e43ead1..b7656ba9 100644 --- a/internal/bridge/smtp_backend.go +++ b/internal/bridge/smtp_backend.go @@ -29,13 +29,13 @@ type smtpBackend struct { usersLock sync.RWMutex } -func newSMTPBackend() (*smtpBackend, error) { +func newSMTPBackend() *smtpBackend { return &smtpBackend{ users: make(map[string]*user.User), - }, nil + } } -func (backend *smtpBackend) Login(state *smtp.ConnectionState, email, password string) (smtp.Session, error) { +func (backend *smtpBackend) Login(_ *smtp.ConnectionState, email, password string) (smtp.Session, error) { backend.usersLock.RLock() defer backend.usersLock.RUnlock() diff --git a/internal/bridge/user.go b/internal/bridge/user.go index c578508b..d41c033c 100644 --- a/internal/bridge/user.go +++ b/internal/bridge/user.go @@ -65,9 +65,7 @@ func (bridge *Bridge) GetUserIDs() []string { // GetUserInfo returns info about the given user. func (bridge *Bridge) GetUserInfo(userID string) (UserInfo, error) { - if info, ok := safe.MapGetRet(bridge.users, userID, func(user *user.User) UserInfo { - return getConnUserInfo(user) - }); ok { + if info, ok := safe.MapGetRet(bridge.users, userID, getConnUserInfo); ok { return info, nil } @@ -139,7 +137,7 @@ func (bridge *Bridge) LoginUser( return userID, nil } -// LoginUser authorizes a new bridge user with the given username and password. +// LoginFull authorizes a new bridge user with the given username and password. // If necessary, a TOTP and mailbox password are requested via the callbacks. // This is equivalent to doing LoginAuth and LoginUser separately. func (bridge *Bridge) LoginFull( @@ -282,7 +280,6 @@ func (bridge *Bridge) loadLoop() { return case <-bridge.loadCh: - // ... } } } @@ -440,7 +437,7 @@ func (bridge *Bridge) addUserWithVault( // newVaultUser creates a new vault user from the given auth information. // If one already exists in the vault, its data will be updated. func (bridge *Bridge) newVaultUser( - client *liteapi.Client, + _ *liteapi.Client, apiUser liteapi.User, authUID, authRef string, saltedKeyPass []byte, diff --git a/internal/bridge/user_events.go b/internal/bridge/user_events.go index 39ea263c..b9432172 100644 --- a/internal/bridge/user_events.go +++ b/internal/bridge/user_events.go @@ -79,7 +79,7 @@ func (bridge *Bridge) handleUserAddressCreated(ctx context.Context, user *user.U return nil } -// TODO: Handle addresses that have been disabled! +// GODT-1948: Handle addresses that have been disabled! func (bridge *Bridge) handleUserAddressUpdated(_ context.Context, user *user.User, _ events.UserAddressUpdated) error { switch user.GetAddressMode() { case vault.CombinedMode: diff --git a/internal/certs/tls.go b/internal/certs/tls.go index 66e52a93..f6d54adf 100644 --- a/internal/certs/tls.go +++ b/internal/certs/tls.go @@ -61,8 +61,8 @@ func NewTLSTemplate() (*x509.Certificate, error) { }, nil } -// GenerateTLSCert generates a new TLS certificate and returns it as PEM. -var GenerateCert = func(template *x509.Certificate) ([]byte, []byte, error) { +// GenerateCert generates a new TLS certificate and returns it as PEM. +var GenerateCert = func(template *x509.Certificate) ([]byte, []byte, error) { //nolint:gochecknoglobals priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, errors.Wrap(err, "failed to generate private key") diff --git a/internal/cookies/jar.go b/internal/cookies/jar.go index eca9691a..20ec8941 100644 --- a/internal/cookies/jar.go +++ b/internal/cookies/jar.go @@ -115,7 +115,7 @@ func loadCookies(persister Persister) (cookiesByHost, error) { var cookiesByHost cookiesByHost - if err := json.Unmarshal([]byte(rawCookies), &cookiesByHost); err != nil { + if err := json.Unmarshal(rawCookies, &cookiesByHost); err != nil { return nil, err } diff --git a/internal/dialer/dialer_basic.go b/internal/dialer/dialer_basic.go index 65b64ceb..6d3de820 100644 --- a/internal/dialer/dialer_basic.go +++ b/internal/dialer/dialer_basic.go @@ -64,14 +64,14 @@ func NewBasicTLSDialer(hostURL string) *BasicTLSDialer { } } -// DialTLS returns a connection to the given address using the given network. +// DialTLSContext returns a connection to the given address using the given network. func (d *BasicTLSDialer) DialTLSContext(ctx context.Context, network, address string) (conn net.Conn, err error) { return (&tls.Dialer{ NetDialer: &net.Dialer{ Timeout: 30 * time.Second, }, Config: &tls.Config{ - InsecureSkipVerify: address != d.hostURL, + InsecureSkipVerify: address != d.hostURL, //nolint:gosec }, }).DialContext(ctx, network, address) } diff --git a/internal/dialer/dialer_pinning.go b/internal/dialer/dialer_pinning.go index a137848f..5a69645f 100644 --- a/internal/dialer/dialer_pinning.go +++ b/internal/dialer/dialer_pinning.go @@ -83,7 +83,7 @@ func NewPinningTLSDialer(dialer TLSDialer, reporter Reporter, pinChecker PinChec } } -// DialTLS dials the given network/address, returning an error if the certificates don't match the trusted pins. +// DialTLSContext dials the given network/address, returning an error if the certificates don't match the trusted pins. func (p *PinningTLSDialer) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) { conn, err := p.dialer.DialTLSContext(ctx, network, address) if err != nil { diff --git a/internal/dialer/dialer_pinning_checker.go b/internal/dialer/dialer_pinning_checker.go index 571e58a3..5c918295 100644 --- a/internal/dialer/dialer_pinning_checker.go +++ b/internal/dialer/dialer_pinning_checker.go @@ -40,7 +40,7 @@ func NewTLSPinChecker(trustedPins []string) *TLSPinChecker { } } -// checkCertificate returns whether the connection presents a known TLS certificate. +// CheckCertificate returns whether the connection presents a known TLS certificate. func (p *TLSPinChecker) CheckCertificate(conn net.Conn) error { tlsConn, ok := conn.(*tls.Conn) if !ok { diff --git a/internal/dialer/dialer_pinning_reporter.go b/internal/dialer/dialer_pinning_reporter.go index 3333573c..3de9e4b8 100644 --- a/internal/dialer/dialer_pinning_reporter.go +++ b/internal/dialer/dialer_pinning_reporter.go @@ -51,7 +51,7 @@ func NewTLSReporter(hostURL, appVersion string, userAgent *useragent.UserAgent, } } -// reportCertIssue reports a TLS key mismatch. +// ReportCertIssue reports a TLS key mismatch. func (r *TLSReporter) ReportCertIssue(remoteURI, host, port string, connState tls.ConnectionState) { var certChain []string diff --git a/internal/dialer/dialer_pinning_test.go b/internal/dialer/dialer_pinning_test.go index e4bd8d01..62f0691f 100644 --- a/internal/dialer/dialer_pinning_test.go +++ b/internal/dialer/dialer_pinning_test.go @@ -36,7 +36,7 @@ func getRootURL() string { func TestTLSPinValid(t *testing.T) { called, _, _, _, cm := createClientWithPinningDialer(getRootURL()) - _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) + _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) //nolint:dogsled checkTLSIssueHandler(t, 0, called) } @@ -47,7 +47,7 @@ func TestTLSPinBackup(t *testing.T) { checker.trustedPins[1] = checker.trustedPins[0] checker.trustedPins[0] = "" - _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) + _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) //nolint:dogsled checkTLSIssueHandler(t, 0, called) } @@ -58,7 +58,7 @@ func TestTLSPinInvalid(t *testing.T) { called, _, _, _, cm := createClientWithPinningDialer(s.GetHostURL()) - _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) + _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) //nolint:dogsled checkTLSIssueHandler(t, 1, called) } @@ -73,8 +73,8 @@ func TestTLSPinNoMatch(t *testing.T) { checker.trustedPins[i] = "testing" } - _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) - _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) + _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) //nolint:dogsled + _, _, _ = cm.NewClientWithLogin(context.Background(), "username", []byte("password")) //nolint:dogsled // Check that it will be reported only once per session, but notified every time. r.Equal(t, 1, len(reporter.sentReports)) @@ -84,7 +84,7 @@ func TestTLSPinNoMatch(t *testing.T) { func TestTLSSignedCertWrongPublicKey(t *testing.T) { skipIfProxyIsSet(t) - _, dialer, _, _, _ := createClientWithPinningDialer("") + _, dialer, _, _, _ := createClientWithPinningDialer("") //nolint:dogsled _, err := dialer.DialTLSContext(context.Background(), "tcp", "rsa4096.badssl.com:443") r.Error(t, err, "expected dial to fail because of wrong public key") } diff --git a/internal/dialer/dialer_proxy.go b/internal/dialer/dialer_proxy.go index 41a16111..4ad7f4b1 100644 --- a/internal/dialer/dialer_proxy.go +++ b/internal/dialer/dialer_proxy.go @@ -75,7 +75,7 @@ func formatAsAddress(rawURL string) string { return net.JoinHostPort(host, port) } -// DialTLS dials the given network/address. If it fails, it retries using a proxy. +// DialTLSContext dials the given network/address. If it fails, it retries using a proxy. func (d *ProxyTLSDialer) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) { if address == d.directAddress { address = d.proxyAddress diff --git a/internal/focus/client.go b/internal/focus/client.go index 67006220..25cd04db 100644 --- a/internal/focus/client.go +++ b/internal/focus/client.go @@ -63,7 +63,7 @@ func TryRaise() bool { return true } -// TryRaise tries to raise the application by dialing the focus service. +// TryVersion tries to raise the application by dialing the focus service. // It returns true if the service is running and the application was told to raise. func TryVersion() (*semver.Version, bool) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) diff --git a/internal/frontend/cli/accounts.go b/internal/frontend/cli/accounts.go index f464e495..3a136f0a 100644 --- a/internal/frontend/cli/accounts.go +++ b/internal/frontend/cli/accounts.go @@ -70,14 +70,19 @@ func (f *frontendCLI) showAccountInfo(c *ishell.Context) { } func (f *frontendCLI) showAccountAddressInfo(user bridge.UserInfo, address string) { - imapSecurity := "STARTTLS" + const ( + StartTLS = "STARTTLS" + SSL = "SSL" + ) + + imapSecurity := StartTLS if f.bridge.GetIMAPSSL() { - imapSecurity = "SSL" + imapSecurity = SSL } - smtpSecurity := "STARTTLS" + smtpSecurity := StartTLS if f.bridge.GetSMTPSSL() { - smtpSecurity = "SSL" + smtpSecurity = SSL } f.Println(bold("Configuration for " + address)) diff --git a/internal/frontend/cli/frontend.go b/internal/frontend/cli/frontend.go index 96cdb12d..52dff6ba 100644 --- a/internal/frontend/cli/frontend.go +++ b/internal/frontend/cli/frontend.go @@ -40,7 +40,7 @@ type frontendCLI struct { } // New returns a new CLI frontend configured with the given options. -func New(bridge *bridge.Bridge, restarter *restarter.Restarter, eventCh <-chan events.Event) *frontendCLI { +func New(bridge *bridge.Bridge, restarter *restarter.Restarter, eventCh <-chan events.Event) *frontendCLI { //nolint:funlen,revive fe := &frontendCLI{ Shell: ishell.New(), bridge: bridge, @@ -261,8 +261,8 @@ func New(bridge *bridge.Bridge, restarter *restarter.Restarter, eventCh <-chan e return fe } -func (f *frontendCLI) watchEvents(eventCh <-chan events.Event) { - // TODO: Better error events. +func (f *frontendCLI) watchEvents(eventCh <-chan events.Event) { // nolint:funlen + // GODT-1949: Better error events. for _, err := range f.bridge.GetErrors() { switch { case errors.Is(err, bridge.ErrVaultCorrupt): diff --git a/internal/frontend/grpc/service.go b/internal/frontend/grpc/service.go index 2b49978b..9ea547ca 100644 --- a/internal/frontend/grpc/service.go +++ b/internal/frontend/grpc/service.go @@ -203,8 +203,8 @@ func (s *Service) WaitUntilFrontendIsReady() { s.initializing.Wait() } -func (s *Service) watchEvents() { - // TODO: Better error events. +func (s *Service) watchEvents() { //nolint:funlen + // GODT-1949 Better error events. for _, err := range s.bridge.GetErrors() { switch { case errors.Is(err, bridge.ErrVaultCorrupt): @@ -358,6 +358,7 @@ func newTLSConfig() (*tls.Config, []byte, error) { return &tls.Config{ Certificates: []tls.Certificate{cert}, ClientAuth: tls.NoClientCert, + MinVersion: tls.VersionTLS12, }, certPEM, nil } diff --git a/internal/logging/imap_logger.go b/internal/logging/imap_logger.go index 4d1f840c..0b213b6a 100644 --- a/internal/logging/imap_logger.go +++ b/internal/logging/imap_logger.go @@ -19,7 +19,7 @@ package logging import "github.com/sirupsen/logrus" -// IMAPLogger implements the writer interface for Gluon IMAP logs +// IMAPLogger implements the writer interface for Gluon IMAP logs. type IMAPLogger struct { l *logrus.Entry } diff --git a/internal/logging/smtp_logger.go b/internal/logging/smtp_logger.go index c893d5d8..00474fff 100644 --- a/internal/logging/smtp_logger.go +++ b/internal/logging/smtp_logger.go @@ -38,7 +38,7 @@ func (s *SMTPErrorLogger) Println(args ...interface{}) { s.l.Errorln(args...) } -// SMTPDebugLogger implements the writer interface for debug SMTP logs +// SMTPDebugLogger implements the writer interface for debug SMTP logs. type SMTPDebugLogger struct { l *logrus.Entry } diff --git a/internal/user/events.go b/internal/user/events.go index 79b4e2d1..72e8ed73 100644 --- a/internal/user/events.go +++ b/internal/user/events.go @@ -27,6 +27,7 @@ import ( "github.com/ProtonMail/proton-bridge/v2/internal/events" "github.com/ProtonMail/proton-bridge/v2/internal/vault" "github.com/bradenaw/juniper/xslices" + "github.com/sirupsen/logrus" "gitlab.protontech.ch/go/liteapi" ) @@ -60,7 +61,7 @@ func (user *User) handleAPIEvent(ctx context.Context, event liteapi.Event) error } // handleUserEvent handles the given user event. -func (user *User) handleUserEvent(ctx context.Context, userEvent liteapi.User) error { +func (user *User) handleUserEvent(_ context.Context, userEvent liteapi.User) error { user.apiUser.Save(userEvent) user.eventCh.Enqueue(events.UserChanged{ @@ -71,7 +72,7 @@ func (user *User) handleUserEvent(ctx context.Context, userEvent liteapi.User) e } // handleAddressEvents handles the given address events. -// TODO: If split address mode, need to signal back to bridge to update the addresses! +// GODT-1945: If split address mode, need to signal back to bridge to update the addresses. func (user *User) handleAddressEvents(ctx context.Context, addressEvents []liteapi.AddressEvent) error { for _, event := range addressEvents { switch event.Action { @@ -89,6 +90,9 @@ func (user *User) handleAddressEvents(ctx context.Context, addressEvents []litea if err := user.handleDeleteAddressEvent(ctx, event); err != nil { return fmt.Errorf("failed to delete address: %w", err) } + + case liteapi.EventUpdateFlags: + logrus.Warn("Not implemented yet.") } } @@ -127,7 +131,7 @@ func (user *User) handleCreateAddressEvent(ctx context.Context, event liteapi.Ad return nil } -func (user *User) handleUpdateAddressEvent(ctx context.Context, event liteapi.AddressEvent) error { +func (user *User) handleUpdateAddressEvent(_ context.Context, event liteapi.AddressEvent) error { //nolint:unparam user.apiAddrs.Set(event.Address.ID, event.Address) user.eventCh.Enqueue(events.UserAddressUpdated{ @@ -139,7 +143,7 @@ func (user *User) handleUpdateAddressEvent(ctx context.Context, event liteapi.Ad return nil } -func (user *User) handleDeleteAddressEvent(ctx context.Context, event liteapi.AddressEvent) error { +func (user *User) handleDeleteAddressEvent(_ context.Context, event liteapi.AddressEvent) error { var email string if ok := user.apiAddrs.GetDelete(event.ID, func(apiAddr liteapi.Address) { @@ -189,7 +193,7 @@ func (user *User) handleLabelEvents(ctx context.Context, labelEvents []liteapi.L return nil } -func (user *User) handleCreateLabelEvent(ctx context.Context, event liteapi.LabelEvent) error { +func (user *User) handleCreateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) { updateCh.Enqueue(newMailboxCreatedUpdate(imap.LabelID(event.ID), getMailboxName(event.Label))) }) @@ -197,7 +201,7 @@ func (user *User) handleCreateLabelEvent(ctx context.Context, event liteapi.Labe return nil } -func (user *User) handleUpdateLabelEvent(ctx context.Context, event liteapi.LabelEvent) error { +func (user *User) handleUpdateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) { updateCh.Enqueue(imap.NewMailboxUpdated(imap.LabelID(event.ID), getMailboxName(event.Label))) }) @@ -205,7 +209,7 @@ func (user *User) handleUpdateLabelEvent(ctx context.Context, event liteapi.Labe return nil } -func (user *User) handleDeleteLabelEvent(ctx context.Context, event liteapi.LabelEvent) error { +func (user *User) handleDeleteLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) { updateCh.Enqueue(imap.NewMailboxDeleted(imap.LabelID(event.ID))) }) @@ -258,7 +262,7 @@ func (user *User) handleCreateMessageEvent(ctx context.Context, event liteapi.Me }) } -func (user *User) handleUpdateMessageEvent(ctx context.Context, event liteapi.MessageEvent) error { +func (user *User) handleUpdateMessageEvent(_ context.Context, event liteapi.MessageEvent) error { //nolint:unparam update := imap.NewMessageLabelsUpdated( imap.MessageID(event.ID), mapTo[string, imap.LabelID](xslices.Filter(event.Message.LabelIDs, wantLabelID)), @@ -283,6 +287,10 @@ func getMailboxName(label liteapi.Label) []string { case liteapi.LabelTypeLabel: name = append([]string{labelPrefix}, label.Path...) + case liteapi.LabelTypeContactGroup: + fallthrough + case liteapi.LabelTypeSystem: + fallthrough default: name = label.Path } diff --git a/internal/user/imap.go b/internal/user/imap.go index 8d937c23..a4bc4d72 100644 --- a/internal/user/imap.go +++ b/internal/user/imap.go @@ -20,15 +20,15 @@ package user import ( "context" "fmt" - "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/google/uuid" - "github.com/sirupsen/logrus" "time" "github.com/ProtonMail/gluon/imap" "github.com/ProtonMail/gluon/queue" + "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/proton-bridge/v2/internal/safe" "github.com/ProtonMail/proton-bridge/v2/internal/vault" + "github.com/google/uuid" + "github.com/sirupsen/logrus" "gitlab.protontech.ch/go/liteapi" "golang.org/x/exp/slices" ) @@ -94,6 +94,10 @@ func (conn *imapConnector) GetLabel(ctx context.Context, labelID imap.LabelID) ( case liteapi.LabelTypeFolder: name = []string{folderPrefix, label.Name} + case liteapi.LabelTypeContactGroup: + fallthrough + case liteapi.LabelTypeSystem: + fallthrough default: name = []string{label.Name} } @@ -163,6 +167,9 @@ func (conn *imapConnector) UpdateLabel(ctx context.Context, labelID imap.LabelID case liteapi.LabelTypeSystem: return fmt.Errorf("cannot rename system label %q", label.Name) + + case liteapi.LabelTypeContactGroup: + return fmt.Errorf("cannot rename contact group label %q", label.Name) } if _, err := conn.client.UpdateLabel(ctx, label.ID, liteapi.UpdateLabelReq{ @@ -212,7 +219,6 @@ func (conn *imapConnector) CreateMessage( flags imap.FlagSet, date time.Time, ) (imap.Message, []byte, error) { - var msgFlags liteapi.MessageFlag switch labelID { @@ -288,18 +294,18 @@ func (conn *imapConnector) MoveMessages(ctx context.Context, messageIDs []imap.M func (conn *imapConnector) MarkMessagesSeen(ctx context.Context, messageIDs []imap.MessageID, seen bool) error { if seen { return conn.client.MarkMessagesRead(ctx, mapTo[imap.MessageID, string](messageIDs)...) - } else { - return conn.client.MarkMessagesUnread(ctx, mapTo[imap.MessageID, string](messageIDs)...) } + + return conn.client.MarkMessagesUnread(ctx, mapTo[imap.MessageID, string](messageIDs)...) } // MarkMessagesFlagged sets the flagged value of the given messages. func (conn *imapConnector) MarkMessagesFlagged(ctx context.Context, messageIDs []imap.MessageID, flagged bool) error { if flagged { return conn.client.LabelMessages(ctx, mapTo[imap.MessageID, string](messageIDs), liteapi.StarredLabel) - } else { - return conn.client.UnlabelMessages(ctx, mapTo[imap.MessageID, string](messageIDs), liteapi.StarredLabel) } + + return conn.client.UnlabelMessages(ctx, mapTo[imap.MessageID, string](messageIDs), liteapi.StarredLabel) } // GetUpdates returns a stream of updates that the gluon server should apply. diff --git a/internal/user/smtp.go b/internal/user/smtp.go index c2670946..24f21227 100644 --- a/internal/user/smtp.go +++ b/internal/user/smtp.go @@ -51,7 +51,7 @@ type smtpSession struct { // from is the current sending address (taken from the return path). from string - // fromAddrID is the ID of the curent sending address (taken from the return path). + // fromAddrID is the ID of the current sending address (taken from the return path). fromAddrID string // to holds all to for the current message. @@ -72,7 +72,7 @@ func newSMTPSession(user *User, email string) (*smtpSession, error) { }) } -// Discard currently processed message. +// Reset Discard currently processed message. func (session *smtpSession) Reset() { logrus.Info("SMTP session reset") @@ -82,7 +82,7 @@ func (session *smtpSession) Reset() { session.to = nil } -// Free all resources associated with session. +// Logout Free all resources associated with session. func (session *smtpSession) Logout() error { defer session.Reset() @@ -91,7 +91,7 @@ func (session *smtpSession) Logout() error { return nil } -// Set return path for currently processed message. +// Mail Set return path for currently processed message. func (session *smtpSession) Mail(from string, opts smtp.MailOptions) error { logrus.Info("SMTP session mail") @@ -127,7 +127,7 @@ func (session *smtpSession) Mail(from string, opts smtp.MailOptions) error { }) } -// Add recipient for currently processed message. +// Rcpt Add recipient for currently processed message. func (session *smtpSession) Rcpt(to string) error { logrus.Info("SMTP session rcpt") @@ -142,8 +142,8 @@ func (session *smtpSession) Rcpt(to string) error { return nil } -// Set currently processed message contents and send it. -func (session *smtpSession) Data(r io.Reader) error { +// Data Set currently processed message contents and send it. +func (session *smtpSession) Data(r io.Reader) error { //nolint:funlen logrus.Info("SMTP session data") ctx, cancel := context.WithCancel(context.Background()) @@ -234,7 +234,7 @@ func (session *smtpSession) Data(r io.Reader) error { } // sendWithKey sends the message with the given address key. -func sendWithKey( +func sendWithKey( //nolint:funlen ctx context.Context, client *liteapi.Client, authAddrID string, @@ -260,6 +260,14 @@ func sendWithKey( case rfc822.TextPlain: decBody = string(message.PlainBody) + case rfc822.MultipartRelated: + fallthrough + case rfc822.MultipartMixed: + fallthrough + case rfc822.MessageRFC822: + fallthrough + default: + break } encBody, err := addrKR.Encrypt(crypto.NewPlainMessageFromString(decBody), nil) @@ -311,7 +319,7 @@ func sendWithKey( return res, nil } -func getParentID( +func getParentID( //nolint:funlen ctx context.Context, client *liteapi.Client, authAddrID string, @@ -402,7 +410,7 @@ func createDraft( return strings.EqualFold(email, sanitizeEmail(template.Sender.Address)) }); idx < 0 { return liteapi.Message{}, fmt.Errorf("address %q is not owned by user", template.Sender.Address) - } else { + } else { //nolint:revive template.Sender.Address = constructEmail(template.Sender.Address, emails[idx]) } diff --git a/internal/user/smtp_prefs.go b/internal/user/smtp_prefs.go index 835eb026..04ce22c6 100644 --- a/internal/user/smtp_prefs.go +++ b/internal/user/smtp_prefs.go @@ -68,6 +68,17 @@ func newContactSettings(settings liteapi.ContactSettings) *contactSettings { case liteapi.PGPInlineScheme: metadata.Scheme = pgpInline + + case liteapi.InternalScheme: + fallthrough + case liteapi.EncryptedOutsideScheme: + fallthrough + case liteapi.ClearScheme: + fallthrough + case liteapi.ClearMIMEScheme: + fallthrough + default: + break } } @@ -253,7 +264,7 @@ func (b *sendPrefsBuilder) build() (p liteapi.SendPreferences) { p.EncryptionScheme = liteapi.ClearScheme } - return + return p } // setPGPSettings returns a SendPreferences with the following possible values: diff --git a/internal/user/sync.go b/internal/user/sync.go index e4257c3f..051dafce 100644 --- a/internal/user/sync.go +++ b/internal/user/sync.go @@ -137,7 +137,7 @@ func syncLabels(ctx context.Context, client *liteapi.Client, updateCh ...*queue. return nil } -func syncMessages( +func syncMessages( //nolint:funlen ctx context.Context, userID string, client *liteapi.Client, diff --git a/internal/user/sync_build.go b/internal/user/sync_build.go index 7944b782..b40d4463 100644 --- a/internal/user/sync_build.go +++ b/internal/user/sync_build.go @@ -47,7 +47,7 @@ func defaultJobOpts() message.JobOptions { } } -func buildRFC822(ctx context.Context, full liteapi.FullMessage, addrKR *crypto.KeyRing) (*buildRes, error) { +func buildRFC822(_ context.Context, full liteapi.FullMessage, addrKR *crypto.KeyRing) (*buildRes, error) { literal, err := message.BuildRFC822(addrKR, full.Message, full.AttData, defaultJobOpts()) if err != nil { return nil, fmt.Errorf("failed to build message %s: %w", full.ID, err) diff --git a/internal/user/types.go b/internal/user/types.go index 3f7294df..f6627de9 100644 --- a/internal/user/types.go +++ b/internal/user/types.go @@ -35,7 +35,7 @@ func mapTo[From, To any](from []From) []To { for _, from := range from { 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))) + panic(fmt.Sprintf("cannot convert %T to %T", from, *new(To))) //nolint:gocritic } to = append(to, val) diff --git a/internal/user/user.go b/internal/user/user.go index 355e65e2..e4522a37 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -38,8 +38,8 @@ import ( ) var ( - EventPeriod = 20 * time.Second // nolint:gochecknoglobals - EventJitter = 20 * time.Second // nolint:gochecknoglobals + EventPeriod = 20 * time.Second // nolint:gochecknoglobals,revive + EventJitter = 20 * time.Second // nolint:gochecknoglobals,revive ) type User struct { @@ -55,7 +55,7 @@ type User struct { syncLock try.Group } -func New(ctx context.Context, encVault *vault.User, client *liteapi.Client, apiUser liteapi.User) (*User, error) { +func New(ctx context.Context, encVault *vault.User, client *liteapi.Client, apiUser liteapi.User) (*User, error) { //nolint:funlen // Get the user's API addresses. apiAddrs, err := client.GetAddresses(ctx) if err != nil { @@ -125,7 +125,7 @@ func New(ctx context.Context, encVault *vault.User, client *liteapi.Client, apiU }) }) - // TODO: Don't start the event loop until the initial sync has finished! + // GODT-1946 - Don't start the event loop until the initial sync has finished. eventCh := user.client.NewEventStream(EventPeriod, EventJitter, user.vault.EventID()) // If we haven't synced yet, do it first. @@ -276,7 +276,7 @@ func (user *User) MaxSpace() int { }) } -// GetEventCh returns a channel which notifies of events happening to the user (such as deauth, address change) +// GetEventCh returns a channel which notifies of events happening to the user (such as deauth, address change). func (user *User) GetEventCh() <-chan events.Event { return user.eventCh.GetChannel() } @@ -464,7 +464,7 @@ func (user *User) startSync() <-chan error { } // AbortSync aborts any ongoing sync. -// TODO: Should probably be done automatically when one of the user's IMAP connectors is closed. +// GODT-1947: Should probably be done automatically when one of the user's IMAP connectors is closed. func (user *User) stopSync() { select { case user.syncStopCh <- struct{}{}: diff --git a/internal/user/user_test.go b/internal/user/user_test.go index f7b37c60..765187c0 100644 --- a/internal/user/user_test.go +++ b/internal/user/user_test.go @@ -96,7 +96,7 @@ func TestUser_Deauth(t *testing.T) { }) } -func withAPI(t *testing.T, ctx context.Context, fn func(context.Context, *server.Server, *liteapi.Manager)) { +func withAPI(_ *testing.T, ctx context.Context, fn func(context.Context, *server.Server, *liteapi.Manager)) { //nolint:revive server := server.New() defer server.Close() @@ -104,11 +104,11 @@ func withAPI(t *testing.T, ctx context.Context, fn func(context.Context, *server } func withAccount(t *testing.T, s *server.Server, username, password string, emails []string, fn func(string, []string)) { - var addrIDs []string - userID, addrID, err := s.CreateUser(username, emails[0], []byte(password)) require.NoError(t, err) + addrIDs := make([]string, 0, len(emails)) + addrIDs = append(addrIDs, addrID) for _, email := range emails[1:] { @@ -121,7 +121,7 @@ func withAccount(t *testing.T, s *server.Server, username, password string, emai fn(userID, addrIDs) } -func withUser(t *testing.T, ctx context.Context, s *server.Server, m *liteapi.Manager, username, password string, fn func(*user.User)) { +func withUser(t *testing.T, ctx context.Context, _ *server.Server, m *liteapi.Manager, username, password string, fn func(*user.User)) { //nolint:revive client, apiAuth, err := m.NewClientWithLogin(ctx, username, []byte(password)) require.NoError(t, err) defer func() { require.NoError(t, client.Close()) }() diff --git a/internal/vault/helper.go b/internal/vault/helper.go index 9ff5b4f4..92c0b3f6 100644 --- a/internal/vault/helper.go +++ b/internal/vault/helper.go @@ -32,11 +32,13 @@ type Keychain struct { func GetHelper(vaultDir string) (string, error) { var keychain Keychain - if _, err := os.Stat(filepath.Join(vaultDir, "keychain.json")); errors.Is(err, fs.ErrNotExist) { + filePath := filepath.Clean(filepath.Join(vaultDir, "keychain.json")) + + if _, err := os.Stat(filePath); errors.Is(err, fs.ErrNotExist) { return "", nil } - b, err := os.ReadFile(filepath.Join(vaultDir, "keychain.json")) + b, err := os.ReadFile(filePath) if err != nil { return "", err } @@ -54,5 +56,7 @@ func SetHelper(vaultDir, helper string) error { return err } - return os.WriteFile(filepath.Join(vaultDir, "keychain.json"), b, 0o600) + filePath := filepath.Clean(filepath.Join(vaultDir, "keychain.json")) + + return os.WriteFile(filePath, b, 0o600) } diff --git a/internal/vault/token.go b/internal/vault/token.go index fe6de9ca..b576d989 100644 --- a/internal/vault/token.go +++ b/internal/vault/token.go @@ -23,7 +23,7 @@ import ( // RandomToken is a function that returns a random token. // By default, we use crypto.RandomToken to generate tokens. -var RandomToken = crypto.RandomToken +var RandomToken = crypto.RandomToken // nolint:gochecknoglobals func newRandomToken(size int) []byte { token, err := RandomToken(size) diff --git a/internal/vault/types.go b/internal/vault/types.go index 171ad189..b760ff89 100644 --- a/internal/vault/types.go +++ b/internal/vault/types.go @@ -73,7 +73,7 @@ func newDefaultSettings(gluonDir string) Settings { SMTPSSL: false, UpdateChannel: updater.DefaultUpdateChannel, - UpdateRollout: rand.Float64(), + UpdateRollout: rand.Float64(), //nolint:gosec ColorScheme: "", ProxyAllowed: true, diff --git a/internal/vault/user_test.go b/internal/vault/user_test.go index 619cb24e..9f4a6470 100644 --- a/internal/vault/user_test.go +++ b/internal/vault/user_test.go @@ -154,7 +154,7 @@ func TestUser_ForEach(t *testing.T) { require.NoError(t, err) // Iterate through the users. - s.ForUser(func(user *vault.User) error { + err = s.ForUser(func(user *vault.User) error { switch user.UserID() { case "userID1": require.Equal(t, "username1", user.Username()) @@ -175,6 +175,8 @@ func TestUser_ForEach(t *testing.T) { return nil }) + require.NoError(t, err) + // Try to delete the first user; it should fail because it is still in use. require.Error(t, s.DeleteUser("userID1")) diff --git a/internal/vault/vault.go b/internal/vault/vault.go index f5020737..20dc37f0 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -20,12 +20,12 @@ package vault import ( "crypto/aes" "crypto/cipher" + "crypto/rand" "crypto/sha256" "encoding/json" "errors" "fmt" "io/fs" - "math/rand" "os" "path/filepath" "sync" @@ -184,7 +184,7 @@ func (vault *Vault) attachUser(userID string) *User { vault.refLock.Lock() defer vault.refLock.Unlock() - vault.ref[userID] += 1 + vault.ref[userID]++ return &User{ vault: vault, @@ -200,7 +200,7 @@ func (vault *Vault) detachUser(userID string) error { return fmt.Errorf("user %s is not attached", userID) } - vault.ref[userID] -= 1 + vault.ref[userID]-- if vault.ref[userID] == 0 { delete(vault.ref, userID) @@ -216,7 +216,7 @@ func newVault(path, gluonDir string, gcm cipher.AEAD) (*Vault, bool, error) { } } - enc, err := os.ReadFile(path) + enc, err := os.ReadFile(filepath.Clean(path)) if err != nil { return nil, false, err } diff --git a/pkg/message/build.go b/pkg/message/build.go index 531a4585..615b7fbe 100644 --- a/pkg/message/build.go +++ b/pkg/message/build.go @@ -489,7 +489,7 @@ func getTextPartHeader(hdr message.Header, body []byte, mimeType rfc822.MIMEType } func getAttachmentPartHeader(att liteapi.Attachment) message.Header { - hdr := toMessageHeader(liteapi.Headers(att.Headers)) + hdr := toMessageHeader(att.Headers) // All attachments have a content type. hdr.SetContentType(string(att.MIMEType), map[string]string{"name": mime.QEncoding.Encode("utf-8", att.Name)}) diff --git a/pkg/message/parser.go b/pkg/message/parser.go index ac85d45f..492e40e1 100644 --- a/pkg/message/parser.go +++ b/pkg/message/parser.go @@ -85,7 +85,7 @@ func Parse(r io.Reader) (m Message, err error) { return parse(p) } -// Parse parses an RFC822 message using an existing parser. +// ParseWithParser parses an RFC822 message using an existing parser. func ParseWithParser(p *parser.Parser) (m Message, err error) { defer func() { if r := recover(); r != nil { diff --git a/pkg/restarter/restarter.go b/pkg/restarter/restarter.go index 70a4c55b..d5742a13 100644 --- a/pkg/restarter/restarter.go +++ b/pkg/restarter/restarter.go @@ -71,7 +71,7 @@ func (restarter *Restarter) Restart() { delete(env, BridgeCrashCount) } - cmd := execabs.Command(restarter.exe, xslices.Join(os.Args[1:], restarter.flags)...) + cmd := execabs.Command(restarter.exe, xslices.Join(os.Args[1:], restarter.flags)...) //nolint:gosec cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout diff --git a/tests/ctx_test.go b/tests/ctx_test.go index ee11c7e7..01320552 100644 --- a/tests/ctx_test.go +++ b/tests/ctx_test.go @@ -241,17 +241,3 @@ func (t *testCtx) close(ctx context.Context) error { return nil } - -func chToType[In, Out any](inCh <-chan In, done any) <-chan Out { - outCh := make(chan Out) - - go func() { - defer close(outCh) - - for in := range inCh { - outCh <- any(in).(Out) - } - }() - - return outCh -} diff --git a/tests/imap_test.go b/tests/imap_test.go index f189f7f1..836af091 100644 --- a/tests/imap_test.go +++ b/tests/imap_test.go @@ -150,9 +150,7 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfo(clientID string, table return err } - haveMailboxes := xslices.Map(status, func(status *imap.MailboxStatus) Mailbox { - return newMailboxFromIMAP(status) - }) + haveMailboxes := xslices.Map(status, newMailboxFromIMAP) wantMailboxes, err := unmarshalTable[Mailbox](table) if err != nil { @@ -182,9 +180,7 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfoForMailbox(clientID, mai return status.Name == mailbox }) - haveMailboxes := xslices.Map(status, func(info *imap.MailboxStatus) Mailbox { - return newMailboxFromIMAP(info) - }) + haveMailboxes := xslices.Map(status, newMailboxFromIMAP) wantMailboxes, err := unmarshalTable[Mailbox](table) if err != nil { @@ -197,10 +193,7 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfoForMailbox(clientID, mai func (s *scenario) imapClientSeesTheFollowingMailboxes(clientID string, table *godog.Table) error { _, client := s.t.getIMAPClient(clientID) - mailboxes, err := clientList(client) - if err != nil { - return err - } + mailboxes := clientList(client) have := xslices.Map(mailboxes, func(info *imap.MailboxInfo) string { return info.Name @@ -220,10 +213,7 @@ func (s *scenario) imapClientSeesTheFollowingMailboxes(clientID string, table *g func (s *scenario) imapClientSeesMailbox(clientID, mailbox string) error { _, client := s.t.getIMAPClient(clientID) - mailboxes, err := clientList(client) - if err != nil { - return err - } + mailboxes := clientList(client) if !slices.Contains(xslices.Map(mailboxes, func(info *imap.MailboxInfo) string { return info.Name }), mailbox) { return fmt.Errorf("expected %v to contain %v but it doesn't", mailboxes, mailbox) @@ -235,10 +225,7 @@ func (s *scenario) imapClientSeesMailbox(clientID, mailbox string) error { func (s *scenario) imapClientDoesNotSeeMailbox(clientID, mailbox string) error { _, client := s.t.getIMAPClient(clientID) - mailboxes, err := clientList(client) - if err != nil { - return err - } + mailboxes := clientList(client) if slices.Contains(xslices.Map(mailboxes, func(info *imap.MailboxInfo) string { return info.Name }), mailbox) { return fmt.Errorf("expected %v to not contain %v but it does", mailboxes, mailbox) @@ -250,10 +237,7 @@ func (s *scenario) imapClientDoesNotSeeMailbox(clientID, mailbox string) error { func (s *scenario) imapClientCountsMailboxesUnder(clientID string, count int, parent string) error { _, client := s.t.getIMAPClient(clientID) - mailboxes, err := clientList(client) - if err != nil { - return err - } + mailboxes := clientList(client) mailboxes = xslices.Filter(mailboxes, func(info *imap.MailboxInfo) bool { return strings.HasPrefix(info.Name, parent) && info.Name != parent @@ -304,9 +288,7 @@ func (s *scenario) imapClientSeesTheFollowingMessagesInMailbox(clientID, mailbox return err } - haveMessages := xslices.Map(fetch, func(msg *imap.Message) Message { - return newMessageFromIMAP(msg) - }) + haveMessages := xslices.Map(fetch, newMessageFromIMAP) wantMessages, err := unmarshalTable[Message](table) if err != nil { @@ -405,7 +387,7 @@ func (s *scenario) imapClientExpunges(clientID string) error { return client.Expunge(nil) } -func clientList(client *client.Client) ([]*imap.MailboxInfo, error) { +func clientList(client *client.Client) []*imap.MailboxInfo { resCh := make(chan *imap.MailboxInfo) go func() { @@ -414,16 +396,13 @@ func clientList(client *client.Client) ([]*imap.MailboxInfo, error) { } }() - return iterator.Collect(iterator.Chan(resCh)), nil + return iterator.Collect(iterator.Chan(resCh)) } func clientStatus(client *client.Client) ([]*imap.MailboxStatus, error) { - var status []*imap.MailboxStatus + list := clientList(client) - list, err := clientList(client) - if err != nil { - return nil, err - } + status := make([]*imap.MailboxStatus, 0, len(list)) for _, info := range list { res, err := client.Status(info.Name, []imap.StatusItem{imap.StatusMessages, imap.StatusRecent, imap.StatusUidNext, imap.StatusUidValidity, imap.StatusUnseen}) @@ -502,7 +481,7 @@ func clientCopy(client *client.Client, from, to string, uid ...uint32) error { return client.UidCopy(seqset, to) } -func clientStore(client *client.Client, from, to int, item imap.StoreItem, flags ...string) ([]*imap.Message, error) { +func clientStore(client *client.Client, from, to int, item imap.StoreItem, flags ...string) ([]*imap.Message, error) { //nolint:unparam resCh := make(chan *imap.Message) go func() { diff --git a/tests/types_test.go b/tests/types_test.go index b6d17d52..4c1c9d1f 100644 --- a/tests/types_test.go +++ b/tests/types_test.go @@ -166,7 +166,7 @@ func matchMailboxes(have, want []Mailbox) error { return nil } -func eventually(condition func() error, waitFor, tick time.Duration) error { +func eventually(condition func() error, waitFor, tick time.Duration) error { //nolint:unparam ch := make(chan error, 1) timer := time.NewTimer(waitFor) @@ -200,7 +200,7 @@ func unmarshalTable[T any](table *messages.PickleTable) ([]T, error) { return nil, fmt.Errorf("empty table") } - var res []T + res := make([]T, 0, len(table.Rows)) for _, row := range table.Rows[1:] { var v T @@ -227,7 +227,7 @@ func unmarshalRow(header, row *messages.PickleTableRow, v any) error { continue } - switch field.Type.Kind() { + switch field.Type.Kind() { //nolint:exhaustive case reflect.String: reflect.ValueOf(v).Elem().Field(idx).SetString(cell)