diff --git a/cmd/launcher/main.go b/cmd/launcher/main.go
index e2b5fa53..ddabed2e 100644
--- a/cmd/launcher/main.go
+++ b/cmd/launcher/main.go
@@ -25,13 +25,14 @@ import (
"runtime"
"github.com/ProtonMail/gopenpgp/v2/crypto"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/constants"
"github.com/ProtonMail/proton-bridge/internal/crash"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/logging"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/internal/versioner"
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -44,7 +45,7 @@ var (
)
func main() { // nolint[funlen]
- reporter := sentry.NewReporter(appName, constants.Version)
+ reporter := sentry.NewReporter(appName, constants.Version, useragent.New())
crashHandler := crash.NewHandler(reporter.ReportException)
defer crashHandler.HandlePanic()
diff --git a/internal/app/base/base.go b/internal/app/base/base.go
index bfcdfabd..11a7254d 100644
--- a/internal/app/base/base.go
+++ b/internal/app/base/base.go
@@ -43,19 +43,20 @@ import (
"github.com/ProtonMail/proton-bridge/internal/config/cache"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/config/tls"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/constants"
"github.com/ProtonMail/proton-bridge/internal/cookies"
"github.com/ProtonMail/proton-bridge/internal/crash"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/logging"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
"github.com/ProtonMail/proton-bridge/internal/versioner"
"github.com/ProtonMail/proton-bridge/pkg/keychain"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
"github.com/allan-simon/go-singleinstance"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
@@ -86,6 +87,7 @@ type Base struct {
Creds *credentials.Store
CM *pmapi.ClientManager
CookieJar *cookies.Jar
+ UserAgent *useragent.UserAgent
Updater *updater.Updater
Versioner *versioner.Versioner
TLS *tls.TLS
@@ -107,7 +109,10 @@ func New( // nolint[funlen]
keychainName,
cacheVersion string,
) (*Base, error) {
- sentryReporter := sentry.NewReporter(appName, constants.Version)
+ userAgent := useragent.New()
+
+ sentryReporter := sentry.NewReporter(appName, constants.Version, userAgent)
+
crashHandler := crash.NewHandler(
sentryReporter.ReportException,
crash.ShowErrorNotification(appName),
@@ -181,20 +186,9 @@ func New( // nolint[funlen]
return nil, err
}
- apiConfig := pmapi.GetAPIConfig(configName, constants.Version)
- apiConfig.ConnectionOffHandler = func() {
- listener.Emit(events.InternetOffEvent, "")
- }
- apiConfig.ConnectionOnHandler = func() {
- listener.Emit(events.InternetOnEvent, "")
- }
- apiConfig.UpgradeApplicationHandler = func() {
- listener.Emit(events.UpgradeApplicationEvent, "")
- }
- cm := pmapi.NewClientManager(apiConfig)
+ cm := pmapi.NewClientManager(getAPIConfig(configName, listener), userAgent)
cm.SetRoundTripper(pmapi.GetRoundTripper(cm, listener))
cm.SetCookieJar(jar)
- sentryReporter.SetUserAgentProvider(cm)
key, err := crypto.NewKeyFromArmored(updater.DefaultPublicKey)
if err != nil {
@@ -245,6 +239,7 @@ func New( // nolint[funlen]
Creds: credentials.NewStore(kc),
CM: cm,
CookieJar: jar,
+ UserAgent: userAgent,
Updater: updater,
Versioner: versioner,
TLS: tls.New(settingsPath),
@@ -380,3 +375,13 @@ func (b *Base) doTeardown() error {
return nil
}
+
+func getAPIConfig(configName string, listener listener.Listener) *pmapi.ClientConfig {
+ apiConfig := pmapi.GetAPIConfig(configName, constants.Version)
+
+ apiConfig.ConnectionOffHandler = func() { listener.Emit(events.InternetOffEvent, "") }
+ apiConfig.ConnectionOnHandler = func() { listener.Emit(events.InternetOnEvent, "") }
+ apiConfig.UpgradeApplicationHandler = func() { listener.Emit(events.UpgradeApplicationEvent, "") }
+
+ return apiConfig
+}
diff --git a/internal/app/bridge/bridge.go b/internal/app/bridge/bridge.go
index 54544254..ac19deb9 100644
--- a/internal/app/bridge/bridge.go
+++ b/internal/app/bridge/bridge.go
@@ -87,7 +87,7 @@ func run(b *base.Base, c *cli.Context) error { // nolint[funlen]
b.CrashHandler,
c.String(flagLogIMAP) == "client" || c.String(flagLogIMAP) == "all",
c.String(flagLogIMAP) == "server" || c.String(flagLogIMAP) == "all",
- imapPort, tlsConfig, imapBackend, b.Listener).ListenAndServe()
+ imapPort, tlsConfig, imapBackend, b.UserAgent, b.Listener).ListenAndServe()
}()
go func() {
@@ -130,6 +130,7 @@ func run(b *base.Base, c *cli.Context) error { // nolint[funlen]
b.Settings,
b.Listener,
b.Updater,
+ b.UserAgent,
bridge,
smtpBackend,
b.Autostart,
diff --git a/internal/bridge/bridge.go b/internal/bridge/bridge.go
index b5249315..4f950c46 100644
--- a/internal/bridge/bridge.go
+++ b/internal/bridge/bridge.go
@@ -26,10 +26,10 @@ import (
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/constants"
"github.com/ProtonMail/proton-bridge/internal/metrics"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/internal/users"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
"github.com/ProtonMail/proton-bridge/pkg/listener"
logrus "github.com/sirupsen/logrus"
@@ -47,10 +47,6 @@ type Bridge struct {
clientManager users.ClientManager
updater Updater
versioner Versioner
-
- userAgentClientName string
- userAgentClientVersion string
- userAgentOS string
}
func New(
@@ -120,40 +116,6 @@ func (b *Bridge) heartbeat() {
}
}
-// GetCurrentClient returns currently connected client (e.g. Thunderbird).
-func (b *Bridge) GetCurrentClient() string {
- res := b.userAgentClientName
- if b.userAgentClientVersion != "" {
- res = res + " " + b.userAgentClientVersion
- }
- return res
-}
-
-// SetCurrentClient updates client info (e.g. Thunderbird) and sets the user agent
-// on pmapi. By default no client is used, IMAP has to detect it on first login.
-func (b *Bridge) SetCurrentClient(clientName, clientVersion string) {
- b.userAgentClientName = clientName
- b.userAgentClientVersion = clientVersion
- b.updateUserAgent()
-}
-
-// SetCurrentOS updates OS and sets the user agent on pmapi. By default we use
-// `runtime.GOOS`, but this can be overridden in case of better detection.
-func (b *Bridge) SetCurrentOS(os string) {
- b.userAgentOS = os
- b.updateUserAgent()
-}
-
-func (b *Bridge) updateUserAgent() {
- logrus.
- WithField("clientName", b.userAgentClientName).
- WithField("clientVersion", b.userAgentClientVersion).
- WithField("OS", b.userAgentOS).
- Info("Updating user agent")
-
- b.clientManager.SetUserAgent(b.userAgentClientName, b.userAgentClientVersion, b.userAgentOS)
-}
-
// ReportBug reports a new bug from the user.
func (b *Bridge) ReportBug(osType, osVersion, description, accountName, address, emailClient string) error {
c := b.clientManager.GetAnonymousClient()
diff --git a/internal/bridge/store_factory.go b/internal/bridge/store_factory.go
index ba15b870..4765ab49 100644
--- a/internal/bridge/store_factory.go
+++ b/internal/bridge/store_factory.go
@@ -21,10 +21,10 @@ import (
"fmt"
"path/filepath"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/ProtonMail/proton-bridge/internal/store"
"github.com/ProtonMail/proton-bridge/internal/users"
"github.com/ProtonMail/proton-bridge/pkg/listener"
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
)
type storeFactory struct {
diff --git a/pkg/useragent/useragent.go b/internal/config/useragent/platform.go
similarity index 68%
rename from pkg/useragent/useragent.go
rename to internal/config/useragent/platform.go
index 4b8cb45b..bca20121 100644
--- a/pkg/useragent/useragent.go
+++ b/internal/config/useragent/platform.go
@@ -25,29 +25,27 @@ import (
"github.com/Masterminds/semver/v3"
)
-// IsCatalinaOrNewer checks that host is MacOS Catalina 10.15.x or higher.
+// IsCatalinaOrNewer checks whether host is MacOS Catalina 10.15.x or higher.
func IsCatalinaOrNewer() bool {
if runtime.GOOS != "darwin" {
return false
}
- return isVersionCatalinaOrNewer(getMacVersion())
-}
-func getMacVersion() string {
- out, err := exec.Command("sw_vers", "-productVersion").Output()
- if err != nil {
- return ""
- }
-
- return strings.TrimSpace(string(out))
-}
-
-func isVersionCatalinaOrNewer(version string) bool {
- v, err := semver.NewVersion(version)
+ rawVersion, err := exec.Command("sw_vers", "-productVersion").Output()
if err != nil {
return false
}
- catalina := semver.MustParse("10.15.0")
- return v.GreaterThan(catalina) || v.Equal(catalina)
+ return isVersionCatalinaOrNewer(strings.TrimSpace(string(rawVersion)))
+}
+
+func isVersionCatalinaOrNewer(rawVersion string) bool {
+ semVersion, err := semver.NewVersion(rawVersion)
+ if err != nil {
+ return false
+ }
+
+ minVersion := semver.MustParse("10.15.0")
+
+ return semVersion.GreaterThan(minVersion) || semVersion.Equal(minVersion)
}
diff --git a/pkg/useragent/useragent_test.go b/internal/config/useragent/platform_test.go
similarity index 100%
rename from pkg/useragent/useragent_test.go
rename to internal/config/useragent/platform_test.go
diff --git a/pkg/pmapi/useragent.go b/internal/config/useragent/useragent.go
similarity index 50%
rename from pkg/pmapi/useragent.go
rename to internal/config/useragent/useragent.go
index e82c0c13..5e9559ea 100644
--- a/pkg/pmapi/useragent.go
+++ b/internal/config/useragent/useragent.go
@@ -15,38 +15,45 @@
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see .
-package pmapi
+package useragent
import (
"fmt"
+ "regexp"
"runtime"
- "strings"
)
-// removeBrackets handle unwanted brackets in client identification string and join with given joinBy parameter.
-// Mac OS X Mail/13.0 (3601.0.4) -> Mac OS X Mail/13.0-3601.0.4 (joinBy = "-")
-func removeBrackets(s string, joinBy string) (r string) {
- r = strings.ReplaceAll(s, " (", joinBy)
- r = strings.ReplaceAll(r, "(", joinBy) // Should be faster than regex.
- r = strings.ReplaceAll(r, ")", "")
-
- return
+type UserAgent struct {
+ client, platform string
}
-func formatUserAgent(clientName, clientVersion, os string) string {
- client := ""
- if clientName != "" {
- client = removeBrackets(clientName, "-")
- if clientVersion != "" {
- client += "/" + removeBrackets(clientVersion, "-")
- }
+func New() *UserAgent {
+ return &UserAgent{
+ client: "",
+ platform: runtime.GOOS,
}
-
- if os == "" {
- os = runtime.GOOS
- }
-
- os = removeBrackets(os, " ")
-
- return fmt.Sprintf("%s (%s)", client, os)
+}
+
+func (ua *UserAgent) SetClient(name, version string) {
+ ua.client = fmt.Sprintf("%v/%v", name, regexp.MustCompile(`(.*) \((.*)\)`).ReplaceAllString(version, "$1-$2"))
+}
+
+func (ua *UserAgent) HasClient() bool {
+ return ua.client != ""
+}
+
+func (ua *UserAgent) SetPlatform(platform string) {
+ ua.platform = platform
+}
+
+func (ua *UserAgent) String() string {
+ var client string
+
+ if ua.client != "" {
+ client = ua.client
+ } else {
+ client = "NoClient/0.0.1"
+ }
+
+ return fmt.Sprintf("%v (%v)", client, ua.platform)
}
diff --git a/internal/config/useragent/useragent_test.go b/internal/config/useragent/useragent_test.go
new file mode 100644
index 00000000..356fbf3f
--- /dev/null
+++ b/internal/config/useragent/useragent_test.go
@@ -0,0 +1,86 @@
+// Copyright (c) 2021 Proton Technologies AG
+//
+// This file is part of ProtonMail Bridge.
+//
+// ProtonMail Bridge is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// ProtonMail Bridge is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ProtonMail Bridge. If not, see .
+
+package useragent
+
+import (
+ "fmt"
+ "runtime"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestUserAgent(t *testing.T) {
+ tests := []struct {
+ name, version, platform string
+ want string
+ }{
+ // No name/version, no platform.
+ {
+ want: fmt.Sprintf("NoClient/0.0.1 (%v)", runtime.GOOS),
+ },
+
+ // No name/version, with platform.
+ {
+ platform: "macOS 10.15",
+ want: "NoClient/0.0.1 (macOS 10.15)",
+ },
+
+ // With name/version, with platform.
+ {
+ name: "Mac OS X Mail",
+ version: "1.0.0",
+ platform: "macOS 10.15",
+ want: "Mac OS X Mail/1.0.0 (macOS 10.15)",
+ },
+
+ // With name/version, with platform.
+ {
+ name: "Mac OS X Mail",
+ version: "13.4 (3608.120.23.2.4)",
+ platform: "macOS 10.15",
+ want: "Mac OS X Mail/13.4-3608.120.23.2.4 (macOS 10.15)",
+ },
+
+ // With name/version, with platform.
+ {
+ name: "Thunderbird",
+ version: "78.6.1",
+ platform: "Windows 10 (10.0)",
+ want: "Thunderbird/78.6.1 (Windows 10 (10.0))",
+ },
+ }
+
+ for _, test := range tests {
+ test := test
+
+ t.Run(test.want, func(t *testing.T) {
+ ua := New()
+
+ if test.name != "" && test.version != "" {
+ ua.SetClient(test.name, test.version)
+ }
+
+ if test.platform != "" {
+ ua.SetPlatform(test.platform)
+ }
+
+ assert.Equal(t, test.want, ua.String())
+ })
+ }
+}
diff --git a/internal/crash/handler.go b/internal/crash/handler.go
index 115f0773..62729e0f 100644
--- a/internal/crash/handler.go
+++ b/internal/crash/handler.go
@@ -19,7 +19,7 @@
package crash
import (
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/sirupsen/logrus"
)
diff --git a/internal/frontend/frontend.go b/internal/frontend/frontend.go
index 85c21aa3..0335d91c 100644
--- a/internal/frontend/frontend.go
+++ b/internal/frontend/frontend.go
@@ -22,6 +22,7 @@ import (
"github.com/ProtonMail/go-autostart"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/frontend/cli"
cliie "github.com/ProtonMail/proton-bridge/internal/frontend/cli-ie"
"github.com/ProtonMail/proton-bridge/internal/frontend/qt"
@@ -60,6 +61,7 @@ func New(
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
+ userAgent *useragent.UserAgent,
bridge *bridge.Bridge,
noEncConfirmator types.NoEncConfirmator,
autostart *autostart.App,
@@ -77,6 +79,7 @@ func New(
settings,
eventListener,
updater,
+ userAgent,
bridgeWrap,
noEncConfirmator,
autostart,
@@ -95,6 +98,7 @@ func newBridgeFrontend(
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
+ userAgent *useragent.UserAgent,
bridge types.Bridger,
noEncConfirmator types.NoEncConfirmator,
autostart *autostart.App,
@@ -122,6 +126,7 @@ func newBridgeFrontend(
settings,
eventListener,
updater,
+ userAgent,
bridge,
noEncConfirmator,
autostart,
diff --git a/internal/frontend/qt/frontend.go b/internal/frontend/qt/frontend.go
index 3a23aef1..e7d1a5ad 100644
--- a/internal/frontend/qt/frontend.go
+++ b/internal/frontend/qt/frontend.go
@@ -39,6 +39,7 @@ import (
"github.com/ProtonMail/go-autostart"
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/frontend/autoconfig"
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
@@ -49,7 +50,6 @@ import (
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/pkg/ports"
- "github.com/ProtonMail/proton-bridge/pkg/useragent"
"github.com/sirupsen/logrus"
"github.com/skratchdot/open-golang/open"
"github.com/therecipe/qt/core"
@@ -75,6 +75,7 @@ type FrontendQt struct {
settings *settings.Settings
eventListener listener.Listener
updater types.Updater
+ userAgent *useragent.UserAgent
bridge types.Bridger
noEncConfirmator types.NoEncConfirmator
@@ -114,12 +115,15 @@ func New(
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
+ userAgent *useragent.UserAgent,
bridge types.Bridger,
noEncConfirmator types.NoEncConfirmator,
autostart *autostart.App,
restarter types.Restarter,
) *FrontendQt {
- tmp := &FrontendQt{
+ userAgent.SetPlatform(core.QSysInfo_PrettyProductName())
+
+ f := &FrontendQt{
version: version,
buildVersion: buildVersion,
programName: programName,
@@ -129,6 +133,7 @@ func New(
settings: settings,
eventListener: eventListener,
updater: updater,
+ userAgent: userAgent,
bridge: bridge,
noEncConfirmator: noEncConfirmator,
programVer: "v" + version,
@@ -138,13 +143,9 @@ func New(
// Initializing.Done is only called sync.Once. Please keep the increment
// set to 1
- tmp.initializing.Add(1)
+ f.initializing.Add(1)
- // Nicer string for OS.
- currentOS := core.QSysInfo_PrettyProductName()
- bridge.SetCurrentOS(currentOS)
-
- return tmp
+ return f
}
// InstanceExistAlert is a global warning window indicating an instance already exists.
@@ -506,7 +507,7 @@ func (s *FrontendQt) sendBug(description, client, address string) (isOK bool) {
}
func (s *FrontendQt) getLastMailClient() string {
- return s.bridge.GetCurrentClient()
+ return s.userAgent.String()
}
func (s *FrontendQt) configureAppleMail(iAccount, iAddress int) {
diff --git a/internal/frontend/qt/frontend_nogui.go b/internal/frontend/qt/frontend_nogui.go
index c3125e21..6db03ead 100644
--- a/internal/frontend/qt/frontend_nogui.go
+++ b/internal/frontend/qt/frontend_nogui.go
@@ -25,6 +25,7 @@ import (
"github.com/ProtonMail/go-autostart"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
@@ -71,6 +72,7 @@ func New(
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
+ userAgent *useragent.UserAgent,
bridge types.Bridger,
noEncConfirmator types.NoEncConfirmator,
autostart *autostart.App,
diff --git a/internal/frontend/types/types.go b/internal/frontend/types/types.go
index 3e4079e5..3b375b70 100644
--- a/internal/frontend/types/types.go
+++ b/internal/frontend/types/types.go
@@ -75,8 +75,6 @@ type User interface {
type Bridger interface {
UserManager
- GetCurrentClient() string
- SetCurrentOS(os string)
ReportBug(osType, osVersion, description, accountName, address, emailClient string) error
AllowProxy()
DisallowProxy()
diff --git a/internal/imap/bridge.go b/internal/imap/bridge.go
index e950bd59..2be46194 100644
--- a/internal/imap/bridge.go
+++ b/internal/imap/bridge.go
@@ -29,7 +29,6 @@ type cacheProvider interface {
}
type bridger interface {
- SetCurrentClient(clientName, clientVersion string)
GetUser(query string) (bridgeUser, error)
}
diff --git a/internal/imap/id/extension.go b/internal/imap/id/extension.go
index bf7deb2b..63a5a54c 100644
--- a/internal/imap/id/extension.go
+++ b/internal/imap/id/extension.go
@@ -23,13 +23,13 @@ import (
)
type currentClientSetter interface {
- SetCurrentClient(name, version string)
+ SetClient(name, version string)
}
// Extension for IMAP server
type extension struct {
- extID imapserver.ConnExtension
- setter currentClientSetter
+ extID imapserver.ConnExtension
+ clientSetter currentClientSetter
}
func (ext *extension) Capabilities(conn imapserver.Conn) []string {
@@ -44,8 +44,8 @@ func (ext *extension) Command(name string) imapserver.HandlerFactory {
return func() imapserver.Handler {
if hdlrID, ok := newIDHandler().(*imapid.Handler); ok {
return &handler{
- hdlrID: hdlrID,
- setter: ext.setter,
+ hdlrID: hdlrID,
+ clientSetter: ext.clientSetter,
}
}
return nil
@@ -57,8 +57,8 @@ func (ext *extension) NewConn(conn imapserver.Conn) imapserver.Conn {
}
type handler struct {
- hdlrID *imapid.Handler
- setter currentClientSetter
+ hdlrID *imapid.Handler
+ clientSetter currentClientSetter
}
func (hdlr *handler) Parse(fields []interface{}) error {
@@ -69,21 +69,18 @@ func (hdlr *handler) Handle(conn imapserver.Conn) error {
err := hdlr.hdlrID.Handle(conn)
if err == nil {
id := hdlr.hdlrID.Command.ID
- hdlr.setter.SetCurrentClient(
- id[imapid.FieldName],
- id[imapid.FieldVersion],
- )
+ hdlr.clientSetter.SetClient(id[imapid.FieldName], id[imapid.FieldVersion])
}
return err
}
// NewExtension returns extension which is adding RFC2871 ID capability, with
// direct interface to set information about email client to backend.
-func NewExtension(serverID imapid.ID, setter currentClientSetter) imapserver.Extension {
+func NewExtension(serverID imapid.ID, clientSetter currentClientSetter) imapserver.Extension {
if conExtID, ok := imapid.NewExtension(serverID).(imapserver.ConnExtension); ok {
return &extension{
- extID: conExtID,
- setter: setter,
+ extID: conExtID,
+ clientSetter: clientSetter,
}
}
return nil
diff --git a/internal/imap/server.go b/internal/imap/server.go
index b01195ca..4fa2e84c 100644
--- a/internal/imap/server.go
+++ b/internal/imap/server.go
@@ -28,6 +28,7 @@ import (
imapid "github.com/ProtonMail/go-imap-id"
"github.com/ProtonMail/proton-bridge/internal/bridge"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/imap/id"
"github.com/ProtonMail/proton-bridge/internal/imap/uidplus"
@@ -39,6 +40,7 @@ import (
imapmove "github.com/emersion/go-imap-move"
imapquota "github.com/emersion/go-imap-quota"
imapunselect "github.com/emersion/go-imap-unselect"
+ "github.com/emersion/go-imap/backend"
imapserver "github.com/emersion/go-imap/server"
"github.com/emersion/go-sasl"
"github.com/sirupsen/logrus"
@@ -47,6 +49,7 @@ import (
type imapServer struct {
panicHandler panicHandler
server *imapserver.Server
+ userAgent *useragent.UserAgent
eventListener listener.Listener
debugClient bool
debugServer bool
@@ -55,7 +58,7 @@ type imapServer struct {
}
// NewIMAPServer constructs a new IMAP server configured with the given options.
-func NewIMAPServer(panicHandler panicHandler, debugClient, debugServer bool, port int, tls *tls.Config, imapBackend *imapBackend, eventListener listener.Listener) *imapServer { //nolint[golint]
+func NewIMAPServer(panicHandler panicHandler, debugClient, debugServer bool, port int, tls *tls.Config, imapBackend backend.Backend, userAgent *useragent.UserAgent, eventListener listener.Listener) *imapServer { // nolint[golint]
s := imapserver.New(imapBackend)
s.Addr = fmt.Sprintf("%v:%v", bridge.Host, port)
s.TLSConfig = tls
@@ -93,7 +96,7 @@ func NewIMAPServer(panicHandler panicHandler, debugClient, debugServer bool, por
s.Enable(
imapidle.NewExtension(),
imapmove.NewExtension(),
- id.NewExtension(serverID, imapBackend.bridge),
+ id.NewExtension(serverID, userAgent),
imapquota.NewExtension(),
imapappendlimit.NewExtension(),
imapunselect.NewExtension(),
@@ -103,6 +106,7 @@ func NewIMAPServer(panicHandler panicHandler, debugClient, debugServer bool, por
server := &imapServer{
panicHandler: panicHandler,
server: s,
+ userAgent: userAgent,
eventListener: eventListener,
debugClient: debugClient,
debugServer: debugServer,
@@ -144,9 +148,10 @@ func (s *imapServer) listenAndServe(retries int) {
return
}
- err = s.server.Serve(&debugListener{
- Listener: l,
- server: s,
+ err = s.server.Serve(&connListener{
+ Listener: l,
+ server: s,
+ userAgent: s.userAgent,
})
// Serve returns error every time, even after closing the server.
// User shouldn't be notified about error if server shouldn't be running,
@@ -233,18 +238,19 @@ func (s *imapServer) monitorDisconnectedUsers() {
}
}
-// debugListener sets debug loggers on server containing fields with local
+// connListener sets debug loggers on server containing fields with local
// and remote addresses right after new connection is accepted.
-type debugListener struct {
+type connListener struct {
net.Listener
- server *imapServer
+ server *imapServer
+ userAgent *useragent.UserAgent
}
-func (dl *debugListener) Accept() (net.Conn, error) {
- conn, err := dl.Listener.Accept()
+func (l *connListener) Accept() (net.Conn, error) {
+ conn, err := l.Listener.Accept()
- if err == nil && (dl.server.debugServer || dl.server.debugClient) {
+ if err == nil && (l.server.debugServer || l.server.debugClient) {
debugLog := log
if addr := conn.LocalAddr(); addr != nil {
debugLog = debugLog.WithField("loc", addr.String())
@@ -254,14 +260,18 @@ func (dl *debugListener) Accept() (net.Conn, error) {
}
var localDebug, remoteDebug io.Writer
- if dl.server.debugServer {
+ if l.server.debugServer {
localDebug = debugLog.WithField("pkg", "imap/server").WriterLevel(logrus.DebugLevel)
}
- if dl.server.debugClient {
+ if l.server.debugClient {
remoteDebug = debugLog.WithField("pkg", "imap/client").WriterLevel(logrus.DebugLevel)
}
- dl.server.server.Debug = imap.NewDebugWriter(localDebug, remoteDebug)
+ l.server.server.Debug = imap.NewDebugWriter(localDebug, remoteDebug)
+ }
+
+ if !l.userAgent.HasClient() {
+ l.userAgent.SetClient("UnknownClient", "0.0.1")
}
return conn, err
diff --git a/internal/imap/server_test.go b/internal/imap/server_test.go
index 565e045e..d8e497c0 100644
--- a/internal/imap/server_test.go
+++ b/internal/imap/server_test.go
@@ -23,6 +23,7 @@ import (
"time"
"github.com/ProtonMail/proton-bridge/internal/bridge"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/ports"
@@ -48,6 +49,7 @@ func TestIMAPServerTurnOffAndOnAgain(t *testing.T) {
panicHandler: panicHandler,
server: server,
eventListener: eventListener,
+ userAgent: useragent.New(),
}
s.isRunning.Store(false)
diff --git a/pkg/sentry/reporter.go b/internal/sentry/reporter.go
similarity index 90%
rename from pkg/sentry/reporter.go
rename to internal/sentry/reporter.go
index 051b9cc6..b50fa0a6 100644
--- a/pkg/sentry/reporter.go
+++ b/internal/sentry/reporter.go
@@ -45,28 +45,21 @@ func init() { // nolint[noinit]
})
}
-type userAgentProvider interface {
- GetUserAgent() string
-}
-
type Reporter struct {
appName string
appVersion string
- uap userAgentProvider
+ userAgent fmt.Stringer
}
// NewReporter creates new sentry reporter with appName and appVersion to report.
-func NewReporter(appName, appVersion string) *Reporter {
+func NewReporter(appName, appVersion string, userAgent fmt.Stringer) *Reporter {
return &Reporter{
appName: appName,
appVersion: appVersion,
+ userAgent: userAgent,
}
}
-func (r *Reporter) SetUserAgentProvider(uap userAgentProvider) {
- r.uap = uap
-}
-
func (r *Reporter) ReportException(i interface{}) error {
err := fmt.Errorf("recover: %v", i)
@@ -97,19 +90,11 @@ func (r *Reporter) scopedReport(doReport func()) error {
return nil
}
- // In case clientManager is not yet created we can get at least OS string.
- var userAgent string
- if r.uap != nil {
- userAgent = r.uap.GetUserAgent()
- } else {
- userAgent = runtime.GOOS
- }
-
tags := map[string]string{
"OS": runtime.GOOS,
"Client": r.appName,
"Version": r.appVersion,
- "UserAgent": userAgent,
+ "UserAgent": r.userAgent.String(),
"UserID": "",
}
diff --git a/pkg/sentry/reporter_test.go b/internal/sentry/reporter_test.go
similarity index 79%
rename from pkg/sentry/reporter_test.go
rename to internal/sentry/reporter_test.go
index 6936afb2..8b36fef3 100644
--- a/pkg/sentry/reporter_test.go
+++ b/internal/sentry/reporter_test.go
@@ -35,8 +35,8 @@ func TestSkipDuringUnwind(t *testing.T) {
}()
wantSkippedFunctions := []string{
- "github.com/ProtonMail/proton-bridge/pkg/sentry.TestSkipDuringUnwind",
- "github.com/ProtonMail/proton-bridge/pkg/sentry.TestSkipDuringUnwind.func1",
+ "github.com/ProtonMail/proton-bridge/internal/sentry.TestSkipDuringUnwind",
+ "github.com/ProtonMail/proton-bridge/internal/sentry.TestSkipDuringUnwind.func1",
}
r.Equal(t, wantSkippedFunctions, skippedFunctions)
}
@@ -45,8 +45,8 @@ func TestFilterOutPanicHandlers(t *testing.T) {
skippedFunctions = []string{
"github.com/ProtonMail/proton-bridge/pkg/config.(*PanicHandler).HandlePanic",
"github.com/ProtonMail/proton-bridge/pkg/config.HandlePanic",
- "github.com/ProtonMail/proton-bridge/pkg/sentry.ReportSentryCrash",
- "github.com/ProtonMail/proton-bridge/pkg/sentry.ReportSentryCrash.func1",
+ "github.com/ProtonMail/proton-bridge/internal/sentry.ReportSentryCrash",
+ "github.com/ProtonMail/proton-bridge/internal/sentry.ReportSentryCrash.func1",
}
frames := []sentry.Frame{
@@ -57,8 +57,8 @@ func TestFilterOutPanicHandlers(t *testing.T) {
{Module: "main", Function: "run"},
{Module: "github.com/ProtonMail/proton-bridge/pkg/config", Function: "(*PanicHandler).HandlePanic"},
{Module: "github.com/ProtonMail/proton-bridge/pkg/config", Function: "HandlePanic"},
- {Module: "github.com/ProtonMail/proton-bridge/pkg/sentry", Function: "ReportSentryCrash"},
- {Module: "github.com/ProtonMail/proton-bridge/pkg/sentry", Function: "ReportSentryCrash.func1"},
+ {Module: "github.com/ProtonMail/proton-bridge/internal/sentry", Function: "ReportSentryCrash"},
+ {Module: "github.com/ProtonMail/proton-bridge/internal/sentry", Function: "ReportSentryCrash.func1"},
}
gotFrames := filterOutPanicHandlers(frames)
diff --git a/internal/store/store.go b/internal/store/store.go
index 6f7482f0..48bb7a03 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -24,9 +24,9 @@ import (
"sync"
"time"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
diff --git a/internal/users/mocks/mocks.go b/internal/users/mocks/mocks.go
index 78fcd248..3bf8ff39 100644
--- a/internal/users/mocks/mocks.go
+++ b/internal/users/mocks/mocks.go
@@ -188,18 +188,6 @@ func (mr *MockClientManagerMockRecorder) GetClient(arg0 interface{}) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClient", reflect.TypeOf((*MockClientManager)(nil).GetClient), arg0)
}
-// SetUserAgent mocks base method
-func (m *MockClientManager) SetUserAgent(arg0, arg1, arg2 string) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "SetUserAgent", arg0, arg1, arg2)
-}
-
-// SetUserAgent indicates an expected call of SetUserAgent
-func (mr *MockClientManagerMockRecorder) SetUserAgent(arg0, arg1, arg2 interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUserAgent", reflect.TypeOf((*MockClientManager)(nil).SetUserAgent), arg0, arg1, arg2)
-}
-
// MockCredentialsStorer is a mock of CredentialsStorer interface
type MockCredentialsStorer struct {
ctrl *gomock.Controller
diff --git a/internal/users/types.go b/internal/users/types.go
index 975d9325..52e442ad 100644
--- a/internal/users/types.go
+++ b/internal/users/types.go
@@ -55,7 +55,6 @@ type ClientManager interface {
DisallowProxy()
GetAuthUpdateChannel() chan pmapi.ClientAuth
CheckConnection() error
- SetUserAgent(clientName, clientVersion, os string)
}
type StoreMaker interface {
diff --git a/internal/users/users_test.go b/internal/users/users_test.go
index f75289ad..f58ddbbc 100644
--- a/internal/users/users_test.go
+++ b/internal/users/users_test.go
@@ -26,12 +26,12 @@ import (
"time"
"github.com/ProtonMail/proton-bridge/internal/events"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/ProtonMail/proton-bridge/internal/store"
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
usersmocks "github.com/ProtonMail/proton-bridge/internal/users/mocks"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
pmapimocks "github.com/ProtonMail/proton-bridge/pkg/pmapi/mocks"
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
gomock "github.com/golang/mock/gomock"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
diff --git a/pkg/pmapi/client.go b/pkg/pmapi/client.go
index c4de4526..528a17e9 100644
--- a/pkg/pmapi/client.go
+++ b/pkg/pmapi/client.go
@@ -78,13 +78,6 @@ type ClientConfig struct {
// The client application name and version.
AppVersion string
- // The client application user agent in format `client name/client version (os)`, e.g.:
- // (Intel Mac OS X 10_15_3)
- // Mac OS X Mail/13.0 (3608.60.0.2.5) (Intel Mac OS X 10_15_3)
- // Thunderbird/1.5.0 (Ubuntu 18.04.4 LTS)
- // MSOffice 12 (Windows 10 (10.0))
- UserAgent string
-
// The client ID.
ClientID string
@@ -236,7 +229,7 @@ func (c *client) Do(req *http.Request, retryUnauthorized bool) (res *http.Respon
func (c *client) doBuffered(req *http.Request, bodyBuffer []byte, retryUnauthorized bool) (res *http.Response, err error) { // nolint[funlen]
isAuthReq := strings.Contains(req.URL.Path, "/auth")
- req.Header.Set("User-Agent", c.cm.config.UserAgent)
+ req.Header.Set("User-Agent", c.cm.userAgent.String())
req.Header.Set("x-pm-appversion", c.cm.config.AppVersion)
if c.uid != "" {
diff --git a/pkg/pmapi/clientmanager.go b/pkg/pmapi/clientmanager.go
index 15eafe17..b787a14b 100644
--- a/pkg/pmapi/clientmanager.go
+++ b/pkg/pmapi/clientmanager.go
@@ -24,6 +24,7 @@ import (
"sync"
"time"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -37,6 +38,7 @@ type ClientManager struct { //nolint[maligned]
newClient func(userID string) Client
config *ClientConfig
+ userAgent *useragent.UserAgent
roundTripper http.RoundTripper
clients map[string]Client
@@ -86,9 +88,10 @@ type tokenExpiration struct {
}
// NewClientManager creates a new ClientMan which manages clients configured with the given client config.
-func NewClientManager(config *ClientConfig) (cm *ClientManager) {
+func NewClientManager(config *ClientConfig, userAgent *useragent.UserAgent) (cm *ClientManager) {
cm = &ClientManager{
config: config,
+ userAgent: userAgent,
roundTripper: http.DefaultTransport,
clients: make(map[string]Client),
@@ -118,7 +121,6 @@ func NewClientManager(config *ClientConfig) (cm *ClientManager) {
cm.newClient = func(userID string) Client {
return newClient(cm, userID)
}
- cm.SetUserAgent("", "", "") // Set default user agent.
go cm.watchTokenExpirations()
@@ -169,16 +171,12 @@ func (cm *ClientManager) SetRoundTripper(rt http.RoundTripper) {
cm.roundTripper = rt
}
-func (cm *ClientManager) GetClientConfig() *ClientConfig {
- return cm.config
-}
-
-func (cm *ClientManager) SetUserAgent(clientName, clientVersion, os string) {
- cm.config.UserAgent = formatUserAgent(clientName, clientVersion, os)
+func (cm *ClientManager) GetAppVersion() string {
+ return cm.config.AppVersion
}
func (cm *ClientManager) GetUserAgent() string {
- return cm.config.UserAgent
+ return cm.userAgent.String()
}
// GetClient returns a client for the given userID.
diff --git a/pkg/pmapi/clientmanager_test.go b/pkg/pmapi/clientmanager_test.go
index b4a77091..c482bf93 100644
--- a/pkg/pmapi/clientmanager_test.go
+++ b/pkg/pmapi/clientmanager_test.go
@@ -17,8 +17,10 @@
package pmapi
+import "github.com/ProtonMail/proton-bridge/internal/config/useragent"
+
func newTestClientManager(cfg *ClientConfig) *ClientManager {
- cm := NewClientManager(cfg)
+ cm := NewClientManager(cfg, useragent.New())
go func() {
for range cm.authUpdates {
diff --git a/pkg/pmapi/pin_checker.go b/pkg/pmapi/pin_checker.go
index 3b2cf1c7..11661f13 100644
--- a/pkg/pmapi/pin_checker.go
+++ b/pkg/pmapi/pin_checker.go
@@ -75,17 +75,18 @@ func certFingerprint(cert *x509.Certificate) string {
return fmt.Sprintf(`pin-sha256=%q`, base64.StdEncoding.EncodeToString(hash[:]))
}
-type clientConfigProvider interface {
- GetClientConfig() *ClientConfig
+type clientInfoProvider interface {
+ GetAppVersion() string
+ GetUserAgent() string
}
type tlsReporter struct {
- cm clientConfigProvider
+ cm clientInfoProvider
p *pinChecker
sentReports []sentReport
}
-func newTLSReporter(p *pinChecker, cm clientConfigProvider) *tlsReporter {
+func newTLSReporter(p *pinChecker, cm clientInfoProvider) *tlsReporter {
return &tlsReporter{
cm: cm,
p: p,
@@ -102,13 +103,14 @@ func (r *tlsReporter) reportCertIssue(remoteURI, host, port string, connState tl
certChain = marshalCert7468(connState.PeerCertificates)
}
- cfg := r.cm.GetClientConfig()
+ appVersion := r.cm.GetAppVersion()
+ userAgent := r.cm.GetUserAgent()
- report := newTLSReport(host, port, connState.ServerName, certChain, r.p.trustedPins, cfg.AppVersion)
+ report := newTLSReport(host, port, connState.ServerName, certChain, r.p.trustedPins, appVersion)
if !r.hasRecentlySentReport(report) {
r.recordReport(report)
- go report.sendReport(remoteURI, cfg.UserAgent)
+ go report.sendReport(remoteURI, userAgent)
}
}
diff --git a/pkg/pmapi/pin_checker_test.go b/pkg/pmapi/pin_checker_test.go
index 1c0ad0f4..7511660c 100644
--- a/pkg/pmapi/pin_checker_test.go
+++ b/pkg/pmapi/pin_checker_test.go
@@ -27,12 +27,16 @@ import (
"github.com/stretchr/testify/assert"
)
-type fakeClientConfigProvider struct {
+type fakeClientInfoProvider struct {
version, useragent string
}
-func (c *fakeClientConfigProvider) GetClientConfig() *ClientConfig {
- return &ClientConfig{AppVersion: c.version, UserAgent: c.useragent}
+func (c *fakeClientInfoProvider) GetAppVersion() string {
+ return c.version
+}
+
+func (c *fakeClientInfoProvider) GetUserAgent() string {
+ return c.useragent
}
func TestPinCheckerDoubleReport(t *testing.T) {
@@ -42,7 +46,7 @@ func TestPinCheckerDoubleReport(t *testing.T) {
reportCounter++
}))
- r := newTLSReporter(newPinChecker(TrustedAPIPins), &fakeClientConfigProvider{version: "3", useragent: "useragent"})
+ r := newTLSReporter(newPinChecker(TrustedAPIPins), &fakeClientInfoProvider{version: "3", useragent: "useragent"})
// Report the same issue many times.
for i := 0; i < 10; i++ {
diff --git a/pkg/pmapi/useragent_test.go b/pkg/pmapi/useragent_test.go
deleted file mode 100644
index 32dff8ce..00000000
--- a/pkg/pmapi/useragent_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2021 Proton Technologies AG
-//
-// This file is part of ProtonMail Bridge.
-//
-// ProtonMail Bridge is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// ProtonMail Bridge is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with ProtonMail Bridge. If not, see .
-
-package pmapi
-
-import (
- "runtime"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestUpdateCurrentUserAgentGOOS(t *testing.T) {
- userAgent := formatUserAgent("", "", "")
- assert.Equal(t, " ("+runtime.GOOS+")", userAgent)
-}
-
-func TestUpdateCurrentUserAgentOS(t *testing.T) {
- userAgent := formatUserAgent("", "", "os")
- assert.Equal(t, " (os)", userAgent)
-}
-
-func TestUpdateCurrentUserAgentClientVer(t *testing.T) {
- userAgent := formatUserAgent("", "ver", "os")
- assert.Equal(t, " (os)", userAgent)
-}
-
-func TestUpdateCurrentUserAgentClientName(t *testing.T) {
- userAgent := formatUserAgent("mail", "", "os")
- assert.Equal(t, "mail (os)", userAgent)
-}
-
-func TestUpdateCurrentUserAgentClientNameAndVersion(t *testing.T) {
- userAgent := formatUserAgent("mail", "ver", "os")
- assert.Equal(t, "mail/ver (os)", userAgent)
-}
-
-func TestRemoveBrackets(t *testing.T) {
- userAgent := formatUserAgent("mail (submail)", "ver (subver)", "os (subos)")
- assert.Equal(t, "mail-submail/ver-subver (os subos)", userAgent)
-}
diff --git a/test/api_checks_test.go b/test/api_checks_test.go
index 6eafa77f..0c05de66 100644
--- a/test/api_checks_test.go
+++ b/test/api_checks_test.go
@@ -41,7 +41,7 @@ func APIChecksFeatureContext(s *godog.Suite) {
s.Step(`^API mailbox "([^"]*)" for address "([^"]*)" of "([^"]*)" has (\d+) message(?:s)?$`, apiMailboxForAddressOfUserHasNumberOfMessages)
s.Step(`^API mailbox "([^"]*)" for "([^"]*)" has messages$`, apiMailboxForUserHasMessages)
s.Step(`^API mailbox "([^"]*)" for address "([^"]*)" of "([^"]*)" has messages$`, apiMailboxForAddressOfUserHasMessages)
- s.Step(`^API client manager user-agent is "([^"]*)"$`, clientManagerUserAgent)
+ s.Step(`^API user-agent is "([^"]*)"$`, userAgent)
}
func apiIsCalled(endpoint string) error {
@@ -187,12 +187,11 @@ func getPMAPIMessages(account *accounts.TestAccount, mailboxName string) ([]*pma
return ctx.GetPMAPIController().GetMessages(account.Username(), labelID)
}
-func clientManagerUserAgent(expectedUserAgent string) error {
+func userAgent(expectedUserAgent string) error {
expectedUserAgent = strings.ReplaceAll(expectedUserAgent, "[GOOS]", runtime.GOOS)
assert.Eventually(ctx.GetTestingT(), func() bool {
- userAgent := ctx.GetClientManager().GetUserAgent()
- return userAgent == expectedUserAgent
+ return ctx.GetUserAgent() == expectedUserAgent
}, 5*time.Second, time.Second)
return nil
diff --git a/test/context/bridge.go b/test/context/bridge.go
index c3878e24..7f0632ee 100644
--- a/test/context/bridge.go
+++ b/test/context/bridge.go
@@ -21,10 +21,11 @@ import (
"time"
"github.com/ProtonMail/proton-bridge/internal/bridge"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/constants"
+ "github.com/ProtonMail/proton-bridge/internal/sentry"
"github.com/ProtonMail/proton-bridge/internal/users"
"github.com/ProtonMail/proton-bridge/pkg/listener"
- "github.com/ProtonMail/proton-bridge/pkg/sentry"
)
// GetBridge returns bridge instance.
@@ -69,7 +70,7 @@ func newBridgeInstance(
eventListener listener.Listener,
clientManager users.ClientManager,
) *bridge.Bridge {
- sentryReporter := sentry.NewReporter("bridge", constants.Version)
+ sentryReporter := sentry.NewReporter("bridge", constants.Version, useragent.New())
panicHandler := &panicHandler{t: t}
updater := newFakeUpdater()
versioner := newFakeVersioner()
diff --git a/test/context/context.go b/test/context/context.go
index 2b1d4237..eb3be80d 100644
--- a/test/context/context.go
+++ b/test/context/context.go
@@ -22,6 +22,7 @@ import (
"sync"
"github.com/ProtonMail/proton-bridge/internal/bridge"
+ "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/constants"
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/transfer"
@@ -46,6 +47,7 @@ type TestContext struct {
locations *fakeLocations
settings *fakeSettings
listener listener.Listener
+ userAgent *useragent.UserAgent
testAccounts *accounts.TestAccounts
// pmapiController is used to control real or fake pmapi clients.
@@ -95,11 +97,12 @@ type TestContext struct {
func New(app string) *TestContext {
setLogrusVerbosityFromEnv()
- configName := app
- if app == "ie" {
- configName = "importExport"
- }
- cm := pmapi.NewClientManager(pmapi.GetAPIConfig(configName, constants.Version))
+ userAgent := useragent.New()
+
+ cm := pmapi.NewClientManager(
+ pmapi.GetAPIConfig(getConfigName(app), constants.Version),
+ userAgent,
+ )
ctx := &TestContext{
t: &bddT{},
@@ -107,6 +110,7 @@ func New(app string) *TestContext {
locations: newFakeLocations(),
settings: newFakeSettings(),
listener: listener.New(),
+ userAgent: userAgent,
pmapiController: newPMAPIController(cm),
clientManager: cm,
testAccounts: newTestAccounts(),
@@ -137,6 +141,14 @@ func New(app string) *TestContext {
return ctx
}
+func getConfigName(app string) string {
+ if app == "ie" {
+ return "importExport"
+ }
+
+ return app
+}
+
// Cleanup runs through all cleanup steps.
// This can be a deferred call so that it is run even if the test steps failed the test.
func (ctx *TestContext) Cleanup() *TestContext {
@@ -156,6 +168,11 @@ func (ctx *TestContext) GetClientManager() *pmapi.ClientManager {
return ctx.clientManager
}
+// GetUserAgent returns the current user agent.
+func (ctx *TestContext) GetUserAgent() string {
+ return ctx.userAgent.String()
+}
+
// GetTestingT returns testing.T compatible struct.
func (ctx *TestContext) GetTestingT() *bddT { //nolint[golint]
return ctx.t
diff --git a/test/context/imap.go b/test/context/imap.go
index edcbf74b..1f85f802 100644
--- a/test/context/imap.go
+++ b/test/context/imap.go
@@ -59,7 +59,7 @@ func (ctx *TestContext) withIMAPServer() {
tls, _ := tls.New(settingsPath).GetConfig()
backend := imap.NewIMAPBackend(ph, ctx.listener, ctx.cache, ctx.bridge)
- server := imap.NewIMAPServer(ph, true, true, port, tls, backend, ctx.listener)
+ server := imap.NewIMAPServer(ph, true, true, port, tls, backend, ctx.userAgent, ctx.listener)
go server.ListenAndServe()
require.NoError(ctx.t, waitForPort(port, 5*time.Second))
diff --git a/test/features/bridge/imap/user_agent.feature b/test/features/bridge/imap/user_agent.feature
index 4919ab12..30adc3e3 100644
--- a/test/features/bridge/imap/user_agent.feature
+++ b/test/features/bridge/imap/user_agent.feature
@@ -4,21 +4,23 @@ Feature: User agent
Scenario: Get user agent
Given there is IMAP client logged in as "user"
+ Then API user-agent is "UnknownClient/0.0.1 ([GOOS])"
When IMAP client sends ID with argument:
"""
"name" "Foo" "version" "1.4.0"
"""
- Then API client manager user-agent is "Foo/1.4.0 ([GOOS])"
+ Then API user-agent is "Foo/1.4.0 ([GOOS])"
Scenario: Update user agent
Given there is IMAP client logged in as "user"
+ Then API user-agent is "UnknownClient/0.0.1 ([GOOS])"
When IMAP client sends ID with argument:
"""
"name" "Foo" "version" "1.4.0"
"""
- Then API client manager user-agent is "Foo/1.4.0 ([GOOS])"
+ Then API user-agent is "Foo/1.4.0 ([GOOS])"
When IMAP client sends ID with argument:
"""
"name" "Bar" "version" "4.2.0"
"""
- Then API client manager user-agent is "Bar/4.2.0 ([GOOS])"
+ Then API user-agent is "Bar/4.2.0 ([GOOS])"