mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 04:36:43 +00:00
Other: Fix all linter errors
This commit is contained in:
committed by
James Houlahan
parent
b36972ce71
commit
7c62312220
@ -24,6 +24,17 @@ issues:
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gosec
|
||||
- goconst
|
||||
- dogsled
|
||||
- path: test
|
||||
linters:
|
||||
- dupl
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gosec
|
||||
- goconst
|
||||
- dogsled
|
||||
|
||||
linters-settings:
|
||||
godox:
|
||||
|
||||
@ -23,12 +23,9 @@ Proton Mail Bridge includes the following 3rd party software:
|
||||
<!-- START AUTOGEN -->
|
||||
* [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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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])
|
||||
}
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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{}{}:
|
||||
|
||||
@ -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()) }()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"))
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)})
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user