mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 20:56:51 +00:00
GODT-980: placeholder for user agent
This commit is contained in:
@ -25,13 +25,14 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
"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/constants"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/crash"
|
"github.com/ProtonMail/proton-bridge/internal/crash"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/locations"
|
"github.com/ProtonMail/proton-bridge/internal/locations"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/logging"
|
"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/updater"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/versioner"
|
"github.com/ProtonMail/proton-bridge/internal/versioner"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -44,7 +45,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() { // nolint[funlen]
|
func main() { // nolint[funlen]
|
||||||
reporter := sentry.NewReporter(appName, constants.Version)
|
reporter := sentry.NewReporter(appName, constants.Version, useragent.New())
|
||||||
|
|
||||||
crashHandler := crash.NewHandler(reporter.ReportException)
|
crashHandler := crash.NewHandler(reporter.ReportException)
|
||||||
defer crashHandler.HandlePanic()
|
defer crashHandler.HandlePanic()
|
||||||
|
|||||||
@ -43,19 +43,20 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/internal/config/cache"
|
"github.com/ProtonMail/proton-bridge/internal/config/cache"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/config/tls"
|
"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/constants"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/cookies"
|
"github.com/ProtonMail/proton-bridge/internal/cookies"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/crash"
|
"github.com/ProtonMail/proton-bridge/internal/crash"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
"github.com/ProtonMail/proton-bridge/internal/events"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/locations"
|
"github.com/ProtonMail/proton-bridge/internal/locations"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/logging"
|
"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/updater"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/versioner"
|
"github.com/ProtonMail/proton-bridge/internal/versioner"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/keychain"
|
"github.com/ProtonMail/proton-bridge/pkg/keychain"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
||||||
"github.com/allan-simon/go-singleinstance"
|
"github.com/allan-simon/go-singleinstance"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -86,6 +87,7 @@ type Base struct {
|
|||||||
Creds *credentials.Store
|
Creds *credentials.Store
|
||||||
CM *pmapi.ClientManager
|
CM *pmapi.ClientManager
|
||||||
CookieJar *cookies.Jar
|
CookieJar *cookies.Jar
|
||||||
|
UserAgent *useragent.UserAgent
|
||||||
Updater *updater.Updater
|
Updater *updater.Updater
|
||||||
Versioner *versioner.Versioner
|
Versioner *versioner.Versioner
|
||||||
TLS *tls.TLS
|
TLS *tls.TLS
|
||||||
@ -107,7 +109,10 @@ func New( // nolint[funlen]
|
|||||||
keychainName,
|
keychainName,
|
||||||
cacheVersion string,
|
cacheVersion string,
|
||||||
) (*Base, error) {
|
) (*Base, error) {
|
||||||
sentryReporter := sentry.NewReporter(appName, constants.Version)
|
userAgent := useragent.New()
|
||||||
|
|
||||||
|
sentryReporter := sentry.NewReporter(appName, constants.Version, userAgent)
|
||||||
|
|
||||||
crashHandler := crash.NewHandler(
|
crashHandler := crash.NewHandler(
|
||||||
sentryReporter.ReportException,
|
sentryReporter.ReportException,
|
||||||
crash.ShowErrorNotification(appName),
|
crash.ShowErrorNotification(appName),
|
||||||
@ -181,20 +186,9 @@ func New( // nolint[funlen]
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apiConfig := pmapi.GetAPIConfig(configName, constants.Version)
|
cm := pmapi.NewClientManager(getAPIConfig(configName, listener), userAgent)
|
||||||
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.SetRoundTripper(pmapi.GetRoundTripper(cm, listener))
|
cm.SetRoundTripper(pmapi.GetRoundTripper(cm, listener))
|
||||||
cm.SetCookieJar(jar)
|
cm.SetCookieJar(jar)
|
||||||
sentryReporter.SetUserAgentProvider(cm)
|
|
||||||
|
|
||||||
key, err := crypto.NewKeyFromArmored(updater.DefaultPublicKey)
|
key, err := crypto.NewKeyFromArmored(updater.DefaultPublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -245,6 +239,7 @@ func New( // nolint[funlen]
|
|||||||
Creds: credentials.NewStore(kc),
|
Creds: credentials.NewStore(kc),
|
||||||
CM: cm,
|
CM: cm,
|
||||||
CookieJar: jar,
|
CookieJar: jar,
|
||||||
|
UserAgent: userAgent,
|
||||||
Updater: updater,
|
Updater: updater,
|
||||||
Versioner: versioner,
|
Versioner: versioner,
|
||||||
TLS: tls.New(settingsPath),
|
TLS: tls.New(settingsPath),
|
||||||
@ -380,3 +375,13 @@ func (b *Base) doTeardown() error {
|
|||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -87,7 +87,7 @@ func run(b *base.Base, c *cli.Context) error { // nolint[funlen]
|
|||||||
b.CrashHandler,
|
b.CrashHandler,
|
||||||
c.String(flagLogIMAP) == "client" || c.String(flagLogIMAP) == "all",
|
c.String(flagLogIMAP) == "client" || c.String(flagLogIMAP) == "all",
|
||||||
c.String(flagLogIMAP) == "server" || 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() {
|
go func() {
|
||||||
@ -130,6 +130,7 @@ func run(b *base.Base, c *cli.Context) error { // nolint[funlen]
|
|||||||
b.Settings,
|
b.Settings,
|
||||||
b.Listener,
|
b.Listener,
|
||||||
b.Updater,
|
b.Updater,
|
||||||
|
b.UserAgent,
|
||||||
bridge,
|
bridge,
|
||||||
smtpBackend,
|
smtpBackend,
|
||||||
b.Autostart,
|
b.Autostart,
|
||||||
|
|||||||
@ -26,10 +26,10 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/constants"
|
"github.com/ProtonMail/proton-bridge/internal/constants"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/metrics"
|
"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/updater"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/internal/users"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
logrus "github.com/sirupsen/logrus"
|
logrus "github.com/sirupsen/logrus"
|
||||||
@ -47,10 +47,6 @@ type Bridge struct {
|
|||||||
clientManager users.ClientManager
|
clientManager users.ClientManager
|
||||||
updater Updater
|
updater Updater
|
||||||
versioner Versioner
|
versioner Versioner
|
||||||
|
|
||||||
userAgentClientName string
|
|
||||||
userAgentClientVersion string
|
|
||||||
userAgentOS string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
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.
|
// ReportBug reports a new bug from the user.
|
||||||
func (b *Bridge) ReportBug(osType, osVersion, description, accountName, address, emailClient string) error {
|
func (b *Bridge) ReportBug(osType, osVersion, description, accountName, address, emailClient string) error {
|
||||||
c := b.clientManager.GetAnonymousClient()
|
c := b.clientManager.GetAnonymousClient()
|
||||||
|
|||||||
@ -21,10 +21,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/sentry"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/store"
|
"github.com/ProtonMail/proton-bridge/internal/store"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/internal/users"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type storeFactory struct {
|
type storeFactory struct {
|
||||||
|
|||||||
@ -25,29 +25,27 @@ import (
|
|||||||
"github.com/Masterminds/semver/v3"
|
"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 {
|
func IsCatalinaOrNewer() bool {
|
||||||
if runtime.GOOS != "darwin" {
|
if runtime.GOOS != "darwin" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return isVersionCatalinaOrNewer(getMacVersion())
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMacVersion() string {
|
rawVersion, err := exec.Command("sw_vers", "-productVersion").Output()
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
catalina := semver.MustParse("10.15.0")
|
return isVersionCatalinaOrNewer(strings.TrimSpace(string(rawVersion)))
|
||||||
return v.GreaterThan(catalina) || v.Equal(catalina)
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
@ -15,38 +15,45 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package pmapi
|
package useragent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// removeBrackets handle unwanted brackets in client identification string and join with given joinBy parameter.
|
type UserAgent struct {
|
||||||
// Mac OS X Mail/13.0 (3601.0.4) -> Mac OS X Mail/13.0-3601.0.4 (joinBy = "-")
|
client, platform string
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatUserAgent(clientName, clientVersion, os string) string {
|
func New() *UserAgent {
|
||||||
client := ""
|
return &UserAgent{
|
||||||
if clientName != "" {
|
client: "",
|
||||||
client = removeBrackets(clientName, "-")
|
platform: runtime.GOOS,
|
||||||
if clientVersion != "" {
|
|
||||||
client += "/" + removeBrackets(clientVersion, "-")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if os == "" {
|
func (ua *UserAgent) SetClient(name, version string) {
|
||||||
os = runtime.GOOS
|
ua.client = fmt.Sprintf("%v/%v", name, regexp.MustCompile(`(.*) \((.*)\)`).ReplaceAllString(version, "$1-$2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
os = removeBrackets(os, " ")
|
func (ua *UserAgent) HasClient() bool {
|
||||||
|
return ua.client != ""
|
||||||
return fmt.Sprintf("%s (%s)", client, os)
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
86
internal/config/useragent/useragent_test.go
Normal file
86
internal/config/useragent/useragent_test.go
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,7 +19,7 @@
|
|||||||
package crash
|
package crash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
"github.com/ProtonMail/proton-bridge/internal/sentry"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/ProtonMail/go-autostart"
|
"github.com/ProtonMail/go-autostart"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/config/useragent"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/frontend/cli"
|
"github.com/ProtonMail/proton-bridge/internal/frontend/cli"
|
||||||
cliie "github.com/ProtonMail/proton-bridge/internal/frontend/cli-ie"
|
cliie "github.com/ProtonMail/proton-bridge/internal/frontend/cli-ie"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/frontend/qt"
|
"github.com/ProtonMail/proton-bridge/internal/frontend/qt"
|
||||||
@ -60,6 +61,7 @@ func New(
|
|||||||
settings *settings.Settings,
|
settings *settings.Settings,
|
||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
updater types.Updater,
|
updater types.Updater,
|
||||||
|
userAgent *useragent.UserAgent,
|
||||||
bridge *bridge.Bridge,
|
bridge *bridge.Bridge,
|
||||||
noEncConfirmator types.NoEncConfirmator,
|
noEncConfirmator types.NoEncConfirmator,
|
||||||
autostart *autostart.App,
|
autostart *autostart.App,
|
||||||
@ -77,6 +79,7 @@ func New(
|
|||||||
settings,
|
settings,
|
||||||
eventListener,
|
eventListener,
|
||||||
updater,
|
updater,
|
||||||
|
userAgent,
|
||||||
bridgeWrap,
|
bridgeWrap,
|
||||||
noEncConfirmator,
|
noEncConfirmator,
|
||||||
autostart,
|
autostart,
|
||||||
@ -95,6 +98,7 @@ func newBridgeFrontend(
|
|||||||
settings *settings.Settings,
|
settings *settings.Settings,
|
||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
updater types.Updater,
|
updater types.Updater,
|
||||||
|
userAgent *useragent.UserAgent,
|
||||||
bridge types.Bridger,
|
bridge types.Bridger,
|
||||||
noEncConfirmator types.NoEncConfirmator,
|
noEncConfirmator types.NoEncConfirmator,
|
||||||
autostart *autostart.App,
|
autostart *autostart.App,
|
||||||
@ -122,6 +126,7 @@ func newBridgeFrontend(
|
|||||||
settings,
|
settings,
|
||||||
eventListener,
|
eventListener,
|
||||||
updater,
|
updater,
|
||||||
|
userAgent,
|
||||||
bridge,
|
bridge,
|
||||||
noEncConfirmator,
|
noEncConfirmator,
|
||||||
autostart,
|
autostart,
|
||||||
|
|||||||
@ -39,6 +39,7 @@ import (
|
|||||||
"github.com/ProtonMail/go-autostart"
|
"github.com/ProtonMail/go-autostart"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
"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/events"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/frontend/autoconfig"
|
"github.com/ProtonMail/proton-bridge/internal/frontend/autoconfig"
|
||||||
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
|
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/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/ports"
|
"github.com/ProtonMail/proton-bridge/pkg/ports"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/useragent"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/skratchdot/open-golang/open"
|
"github.com/skratchdot/open-golang/open"
|
||||||
"github.com/therecipe/qt/core"
|
"github.com/therecipe/qt/core"
|
||||||
@ -75,6 +75,7 @@ type FrontendQt struct {
|
|||||||
settings *settings.Settings
|
settings *settings.Settings
|
||||||
eventListener listener.Listener
|
eventListener listener.Listener
|
||||||
updater types.Updater
|
updater types.Updater
|
||||||
|
userAgent *useragent.UserAgent
|
||||||
bridge types.Bridger
|
bridge types.Bridger
|
||||||
noEncConfirmator types.NoEncConfirmator
|
noEncConfirmator types.NoEncConfirmator
|
||||||
|
|
||||||
@ -114,12 +115,15 @@ func New(
|
|||||||
settings *settings.Settings,
|
settings *settings.Settings,
|
||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
updater types.Updater,
|
updater types.Updater,
|
||||||
|
userAgent *useragent.UserAgent,
|
||||||
bridge types.Bridger,
|
bridge types.Bridger,
|
||||||
noEncConfirmator types.NoEncConfirmator,
|
noEncConfirmator types.NoEncConfirmator,
|
||||||
autostart *autostart.App,
|
autostart *autostart.App,
|
||||||
restarter types.Restarter,
|
restarter types.Restarter,
|
||||||
) *FrontendQt {
|
) *FrontendQt {
|
||||||
tmp := &FrontendQt{
|
userAgent.SetPlatform(core.QSysInfo_PrettyProductName())
|
||||||
|
|
||||||
|
f := &FrontendQt{
|
||||||
version: version,
|
version: version,
|
||||||
buildVersion: buildVersion,
|
buildVersion: buildVersion,
|
||||||
programName: programName,
|
programName: programName,
|
||||||
@ -129,6 +133,7 @@ func New(
|
|||||||
settings: settings,
|
settings: settings,
|
||||||
eventListener: eventListener,
|
eventListener: eventListener,
|
||||||
updater: updater,
|
updater: updater,
|
||||||
|
userAgent: userAgent,
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
noEncConfirmator: noEncConfirmator,
|
noEncConfirmator: noEncConfirmator,
|
||||||
programVer: "v" + version,
|
programVer: "v" + version,
|
||||||
@ -138,13 +143,9 @@ func New(
|
|||||||
|
|
||||||
// Initializing.Done is only called sync.Once. Please keep the increment
|
// Initializing.Done is only called sync.Once. Please keep the increment
|
||||||
// set to 1
|
// set to 1
|
||||||
tmp.initializing.Add(1)
|
f.initializing.Add(1)
|
||||||
|
|
||||||
// Nicer string for OS.
|
return f
|
||||||
currentOS := core.QSysInfo_PrettyProductName()
|
|
||||||
bridge.SetCurrentOS(currentOS)
|
|
||||||
|
|
||||||
return tmp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstanceExistAlert is a global warning window indicating an instance already exists.
|
// 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 {
|
func (s *FrontendQt) getLastMailClient() string {
|
||||||
return s.bridge.GetCurrentClient()
|
return s.userAgent.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FrontendQt) configureAppleMail(iAccount, iAddress int) {
|
func (s *FrontendQt) configureAppleMail(iAccount, iAddress int) {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ProtonMail/go-autostart"
|
"github.com/ProtonMail/go-autostart"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
"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/frontend/types"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/locations"
|
"github.com/ProtonMail/proton-bridge/internal/locations"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/updater"
|
"github.com/ProtonMail/proton-bridge/internal/updater"
|
||||||
@ -71,6 +72,7 @@ func New(
|
|||||||
settings *settings.Settings,
|
settings *settings.Settings,
|
||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
updater types.Updater,
|
updater types.Updater,
|
||||||
|
userAgent *useragent.UserAgent,
|
||||||
bridge types.Bridger,
|
bridge types.Bridger,
|
||||||
noEncConfirmator types.NoEncConfirmator,
|
noEncConfirmator types.NoEncConfirmator,
|
||||||
autostart *autostart.App,
|
autostart *autostart.App,
|
||||||
|
|||||||
@ -75,8 +75,6 @@ type User interface {
|
|||||||
type Bridger interface {
|
type Bridger interface {
|
||||||
UserManager
|
UserManager
|
||||||
|
|
||||||
GetCurrentClient() string
|
|
||||||
SetCurrentOS(os string)
|
|
||||||
ReportBug(osType, osVersion, description, accountName, address, emailClient string) error
|
ReportBug(osType, osVersion, description, accountName, address, emailClient string) error
|
||||||
AllowProxy()
|
AllowProxy()
|
||||||
DisallowProxy()
|
DisallowProxy()
|
||||||
|
|||||||
@ -29,7 +29,6 @@ type cacheProvider interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type bridger interface {
|
type bridger interface {
|
||||||
SetCurrentClient(clientName, clientVersion string)
|
|
||||||
GetUser(query string) (bridgeUser, error)
|
GetUser(query string) (bridgeUser, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,13 +23,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type currentClientSetter interface {
|
type currentClientSetter interface {
|
||||||
SetCurrentClient(name, version string)
|
SetClient(name, version string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extension for IMAP server
|
// Extension for IMAP server
|
||||||
type extension struct {
|
type extension struct {
|
||||||
extID imapserver.ConnExtension
|
extID imapserver.ConnExtension
|
||||||
setter currentClientSetter
|
clientSetter currentClientSetter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ext *extension) Capabilities(conn imapserver.Conn) []string {
|
func (ext *extension) Capabilities(conn imapserver.Conn) []string {
|
||||||
@ -45,7 +45,7 @@ func (ext *extension) Command(name string) imapserver.HandlerFactory {
|
|||||||
if hdlrID, ok := newIDHandler().(*imapid.Handler); ok {
|
if hdlrID, ok := newIDHandler().(*imapid.Handler); ok {
|
||||||
return &handler{
|
return &handler{
|
||||||
hdlrID: hdlrID,
|
hdlrID: hdlrID,
|
||||||
setter: ext.setter,
|
clientSetter: ext.clientSetter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -58,7 +58,7 @@ func (ext *extension) NewConn(conn imapserver.Conn) imapserver.Conn {
|
|||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
hdlrID *imapid.Handler
|
hdlrID *imapid.Handler
|
||||||
setter currentClientSetter
|
clientSetter currentClientSetter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hdlr *handler) Parse(fields []interface{}) error {
|
func (hdlr *handler) Parse(fields []interface{}) error {
|
||||||
@ -69,21 +69,18 @@ func (hdlr *handler) Handle(conn imapserver.Conn) error {
|
|||||||
err := hdlr.hdlrID.Handle(conn)
|
err := hdlr.hdlrID.Handle(conn)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
id := hdlr.hdlrID.Command.ID
|
id := hdlr.hdlrID.Command.ID
|
||||||
hdlr.setter.SetCurrentClient(
|
hdlr.clientSetter.SetClient(id[imapid.FieldName], id[imapid.FieldVersion])
|
||||||
id[imapid.FieldName],
|
|
||||||
id[imapid.FieldVersion],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExtension returns extension which is adding RFC2871 ID capability, with
|
// NewExtension returns extension which is adding RFC2871 ID capability, with
|
||||||
// direct interface to set information about email client to backend.
|
// 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 {
|
if conExtID, ok := imapid.NewExtension(serverID).(imapserver.ConnExtension); ok {
|
||||||
return &extension{
|
return &extension{
|
||||||
extID: conExtID,
|
extID: conExtID,
|
||||||
setter: setter,
|
clientSetter: clientSetter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import (
|
|||||||
|
|
||||||
imapid "github.com/ProtonMail/go-imap-id"
|
imapid "github.com/ProtonMail/go-imap-id"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"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/events"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/imap/id"
|
"github.com/ProtonMail/proton-bridge/internal/imap/id"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/imap/uidplus"
|
"github.com/ProtonMail/proton-bridge/internal/imap/uidplus"
|
||||||
@ -39,6 +40,7 @@ import (
|
|||||||
imapmove "github.com/emersion/go-imap-move"
|
imapmove "github.com/emersion/go-imap-move"
|
||||||
imapquota "github.com/emersion/go-imap-quota"
|
imapquota "github.com/emersion/go-imap-quota"
|
||||||
imapunselect "github.com/emersion/go-imap-unselect"
|
imapunselect "github.com/emersion/go-imap-unselect"
|
||||||
|
"github.com/emersion/go-imap/backend"
|
||||||
imapserver "github.com/emersion/go-imap/server"
|
imapserver "github.com/emersion/go-imap/server"
|
||||||
"github.com/emersion/go-sasl"
|
"github.com/emersion/go-sasl"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -47,6 +49,7 @@ import (
|
|||||||
type imapServer struct {
|
type imapServer struct {
|
||||||
panicHandler panicHandler
|
panicHandler panicHandler
|
||||||
server *imapserver.Server
|
server *imapserver.Server
|
||||||
|
userAgent *useragent.UserAgent
|
||||||
eventListener listener.Listener
|
eventListener listener.Listener
|
||||||
debugClient bool
|
debugClient bool
|
||||||
debugServer bool
|
debugServer bool
|
||||||
@ -55,7 +58,7 @@ type imapServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewIMAPServer constructs a new IMAP server configured with the given options.
|
// 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 := imapserver.New(imapBackend)
|
||||||
s.Addr = fmt.Sprintf("%v:%v", bridge.Host, port)
|
s.Addr = fmt.Sprintf("%v:%v", bridge.Host, port)
|
||||||
s.TLSConfig = tls
|
s.TLSConfig = tls
|
||||||
@ -93,7 +96,7 @@ func NewIMAPServer(panicHandler panicHandler, debugClient, debugServer bool, por
|
|||||||
s.Enable(
|
s.Enable(
|
||||||
imapidle.NewExtension(),
|
imapidle.NewExtension(),
|
||||||
imapmove.NewExtension(),
|
imapmove.NewExtension(),
|
||||||
id.NewExtension(serverID, imapBackend.bridge),
|
id.NewExtension(serverID, userAgent),
|
||||||
imapquota.NewExtension(),
|
imapquota.NewExtension(),
|
||||||
imapappendlimit.NewExtension(),
|
imapappendlimit.NewExtension(),
|
||||||
imapunselect.NewExtension(),
|
imapunselect.NewExtension(),
|
||||||
@ -103,6 +106,7 @@ func NewIMAPServer(panicHandler panicHandler, debugClient, debugServer bool, por
|
|||||||
server := &imapServer{
|
server := &imapServer{
|
||||||
panicHandler: panicHandler,
|
panicHandler: panicHandler,
|
||||||
server: s,
|
server: s,
|
||||||
|
userAgent: userAgent,
|
||||||
eventListener: eventListener,
|
eventListener: eventListener,
|
||||||
debugClient: debugClient,
|
debugClient: debugClient,
|
||||||
debugServer: debugServer,
|
debugServer: debugServer,
|
||||||
@ -144,9 +148,10 @@ func (s *imapServer) listenAndServe(retries int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.server.Serve(&debugListener{
|
err = s.server.Serve(&connListener{
|
||||||
Listener: l,
|
Listener: l,
|
||||||
server: s,
|
server: s,
|
||||||
|
userAgent: s.userAgent,
|
||||||
})
|
})
|
||||||
// Serve returns error every time, even after closing the server.
|
// Serve returns error every time, even after closing the server.
|
||||||
// User shouldn't be notified about error if server shouldn't be running,
|
// 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.
|
// and remote addresses right after new connection is accepted.
|
||||||
type debugListener struct {
|
type connListener struct {
|
||||||
net.Listener
|
net.Listener
|
||||||
|
|
||||||
server *imapServer
|
server *imapServer
|
||||||
|
userAgent *useragent.UserAgent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dl *debugListener) Accept() (net.Conn, error) {
|
func (l *connListener) Accept() (net.Conn, error) {
|
||||||
conn, err := dl.Listener.Accept()
|
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
|
debugLog := log
|
||||||
if addr := conn.LocalAddr(); addr != nil {
|
if addr := conn.LocalAddr(); addr != nil {
|
||||||
debugLog = debugLog.WithField("loc", addr.String())
|
debugLog = debugLog.WithField("loc", addr.String())
|
||||||
@ -254,14 +260,18 @@ func (dl *debugListener) Accept() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var localDebug, remoteDebug io.Writer
|
var localDebug, remoteDebug io.Writer
|
||||||
if dl.server.debugServer {
|
if l.server.debugServer {
|
||||||
localDebug = debugLog.WithField("pkg", "imap/server").WriterLevel(logrus.DebugLevel)
|
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)
|
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
|
return conn, err
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"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/events"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/ports"
|
"github.com/ProtonMail/proton-bridge/pkg/ports"
|
||||||
@ -48,6 +49,7 @@ func TestIMAPServerTurnOffAndOnAgain(t *testing.T) {
|
|||||||
panicHandler: panicHandler,
|
panicHandler: panicHandler,
|
||||||
server: server,
|
server: server,
|
||||||
eventListener: eventListener,
|
eventListener: eventListener,
|
||||||
|
userAgent: useragent.New(),
|
||||||
}
|
}
|
||||||
s.isRunning.Store(false)
|
s.isRunning.Store(false)
|
||||||
|
|
||||||
|
|||||||
@ -45,28 +45,21 @@ func init() { // nolint[noinit]
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type userAgentProvider interface {
|
|
||||||
GetUserAgent() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Reporter struct {
|
type Reporter struct {
|
||||||
appName string
|
appName string
|
||||||
appVersion string
|
appVersion string
|
||||||
uap userAgentProvider
|
userAgent fmt.Stringer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReporter creates new sentry reporter with appName and appVersion to report.
|
// 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{
|
return &Reporter{
|
||||||
appName: appName,
|
appName: appName,
|
||||||
appVersion: appVersion,
|
appVersion: appVersion,
|
||||||
|
userAgent: userAgent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reporter) SetUserAgentProvider(uap userAgentProvider) {
|
|
||||||
r.uap = uap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Reporter) ReportException(i interface{}) error {
|
func (r *Reporter) ReportException(i interface{}) error {
|
||||||
err := fmt.Errorf("recover: %v", i)
|
err := fmt.Errorf("recover: %v", i)
|
||||||
|
|
||||||
@ -97,19 +90,11 @@ func (r *Reporter) scopedReport(doReport func()) error {
|
|||||||
return nil
|
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{
|
tags := map[string]string{
|
||||||
"OS": runtime.GOOS,
|
"OS": runtime.GOOS,
|
||||||
"Client": r.appName,
|
"Client": r.appName,
|
||||||
"Version": r.appVersion,
|
"Version": r.appVersion,
|
||||||
"UserAgent": userAgent,
|
"UserAgent": r.userAgent.String(),
|
||||||
"UserID": "",
|
"UserID": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,8 +35,8 @@ func TestSkipDuringUnwind(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
wantSkippedFunctions := []string{
|
wantSkippedFunctions := []string{
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry.TestSkipDuringUnwind",
|
"github.com/ProtonMail/proton-bridge/internal/sentry.TestSkipDuringUnwind",
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry.TestSkipDuringUnwind.func1",
|
"github.com/ProtonMail/proton-bridge/internal/sentry.TestSkipDuringUnwind.func1",
|
||||||
}
|
}
|
||||||
r.Equal(t, wantSkippedFunctions, skippedFunctions)
|
r.Equal(t, wantSkippedFunctions, skippedFunctions)
|
||||||
}
|
}
|
||||||
@ -45,8 +45,8 @@ func TestFilterOutPanicHandlers(t *testing.T) {
|
|||||||
skippedFunctions = []string{
|
skippedFunctions = []string{
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/config.(*PanicHandler).HandlePanic",
|
"github.com/ProtonMail/proton-bridge/pkg/config.(*PanicHandler).HandlePanic",
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/config.HandlePanic",
|
"github.com/ProtonMail/proton-bridge/pkg/config.HandlePanic",
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry.ReportSentryCrash",
|
"github.com/ProtonMail/proton-bridge/internal/sentry.ReportSentryCrash",
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry.ReportSentryCrash.func1",
|
"github.com/ProtonMail/proton-bridge/internal/sentry.ReportSentryCrash.func1",
|
||||||
}
|
}
|
||||||
|
|
||||||
frames := []sentry.Frame{
|
frames := []sentry.Frame{
|
||||||
@ -57,8 +57,8 @@ func TestFilterOutPanicHandlers(t *testing.T) {
|
|||||||
{Module: "main", Function: "run"},
|
{Module: "main", Function: "run"},
|
||||||
{Module: "github.com/ProtonMail/proton-bridge/pkg/config", Function: "(*PanicHandler).HandlePanic"},
|
{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/config", Function: "HandlePanic"},
|
||||||
{Module: "github.com/ProtonMail/proton-bridge/pkg/sentry", Function: "ReportSentryCrash"},
|
{Module: "github.com/ProtonMail/proton-bridge/internal/sentry", Function: "ReportSentryCrash"},
|
||||||
{Module: "github.com/ProtonMail/proton-bridge/pkg/sentry", Function: "ReportSentryCrash.func1"},
|
{Module: "github.com/ProtonMail/proton-bridge/internal/sentry", Function: "ReportSentryCrash.func1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
gotFrames := filterOutPanicHandlers(frames)
|
gotFrames := filterOutPanicHandlers(frames)
|
||||||
@ -24,9 +24,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/sentry"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|||||||
@ -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)
|
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
|
// MockCredentialsStorer is a mock of CredentialsStorer interface
|
||||||
type MockCredentialsStorer struct {
|
type MockCredentialsStorer struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
|
|||||||
@ -55,7 +55,6 @@ type ClientManager interface {
|
|||||||
DisallowProxy()
|
DisallowProxy()
|
||||||
GetAuthUpdateChannel() chan pmapi.ClientAuth
|
GetAuthUpdateChannel() chan pmapi.ClientAuth
|
||||||
CheckConnection() error
|
CheckConnection() error
|
||||||
SetUserAgent(clientName, clientVersion, os string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StoreMaker interface {
|
type StoreMaker interface {
|
||||||
|
|||||||
@ -26,12 +26,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
"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/store"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
||||||
usersmocks "github.com/ProtonMail/proton-bridge/internal/users/mocks"
|
usersmocks "github.com/ProtonMail/proton-bridge/internal/users/mocks"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
pmapimocks "github.com/ProtonMail/proton-bridge/pkg/pmapi/mocks"
|
pmapimocks "github.com/ProtonMail/proton-bridge/pkg/pmapi/mocks"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|||||||
@ -78,13 +78,6 @@ type ClientConfig struct {
|
|||||||
// The client application name and version.
|
// The client application name and version.
|
||||||
AppVersion string
|
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.
|
// The client ID.
|
||||||
ClientID string
|
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]
|
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")
|
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)
|
req.Header.Set("x-pm-appversion", c.cm.config.AppVersion)
|
||||||
|
|
||||||
if c.uid != "" {
|
if c.uid != "" {
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/config/useragent"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -37,6 +38,7 @@ type ClientManager struct { //nolint[maligned]
|
|||||||
newClient func(userID string) Client
|
newClient func(userID string) Client
|
||||||
|
|
||||||
config *ClientConfig
|
config *ClientConfig
|
||||||
|
userAgent *useragent.UserAgent
|
||||||
roundTripper http.RoundTripper
|
roundTripper http.RoundTripper
|
||||||
|
|
||||||
clients map[string]Client
|
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.
|
// 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{
|
cm = &ClientManager{
|
||||||
config: config,
|
config: config,
|
||||||
|
userAgent: userAgent,
|
||||||
roundTripper: http.DefaultTransport,
|
roundTripper: http.DefaultTransport,
|
||||||
|
|
||||||
clients: make(map[string]Client),
|
clients: make(map[string]Client),
|
||||||
@ -118,7 +121,6 @@ func NewClientManager(config *ClientConfig) (cm *ClientManager) {
|
|||||||
cm.newClient = func(userID string) Client {
|
cm.newClient = func(userID string) Client {
|
||||||
return newClient(cm, userID)
|
return newClient(cm, userID)
|
||||||
}
|
}
|
||||||
cm.SetUserAgent("", "", "") // Set default user agent.
|
|
||||||
|
|
||||||
go cm.watchTokenExpirations()
|
go cm.watchTokenExpirations()
|
||||||
|
|
||||||
@ -169,16 +171,12 @@ func (cm *ClientManager) SetRoundTripper(rt http.RoundTripper) {
|
|||||||
cm.roundTripper = rt
|
cm.roundTripper = rt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ClientManager) GetClientConfig() *ClientConfig {
|
func (cm *ClientManager) GetAppVersion() string {
|
||||||
return cm.config
|
return cm.config.AppVersion
|
||||||
}
|
|
||||||
|
|
||||||
func (cm *ClientManager) SetUserAgent(clientName, clientVersion, os string) {
|
|
||||||
cm.config.UserAgent = formatUserAgent(clientName, clientVersion, os)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ClientManager) GetUserAgent() string {
|
func (cm *ClientManager) GetUserAgent() string {
|
||||||
return cm.config.UserAgent
|
return cm.userAgent.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClient returns a client for the given userID.
|
// GetClient returns a client for the given userID.
|
||||||
|
|||||||
@ -17,8 +17,10 @@
|
|||||||
|
|
||||||
package pmapi
|
package pmapi
|
||||||
|
|
||||||
|
import "github.com/ProtonMail/proton-bridge/internal/config/useragent"
|
||||||
|
|
||||||
func newTestClientManager(cfg *ClientConfig) *ClientManager {
|
func newTestClientManager(cfg *ClientConfig) *ClientManager {
|
||||||
cm := NewClientManager(cfg)
|
cm := NewClientManager(cfg, useragent.New())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range cm.authUpdates {
|
for range cm.authUpdates {
|
||||||
|
|||||||
@ -75,17 +75,18 @@ func certFingerprint(cert *x509.Certificate) string {
|
|||||||
return fmt.Sprintf(`pin-sha256=%q`, base64.StdEncoding.EncodeToString(hash[:]))
|
return fmt.Sprintf(`pin-sha256=%q`, base64.StdEncoding.EncodeToString(hash[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientConfigProvider interface {
|
type clientInfoProvider interface {
|
||||||
GetClientConfig() *ClientConfig
|
GetAppVersion() string
|
||||||
|
GetUserAgent() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type tlsReporter struct {
|
type tlsReporter struct {
|
||||||
cm clientConfigProvider
|
cm clientInfoProvider
|
||||||
p *pinChecker
|
p *pinChecker
|
||||||
sentReports []sentReport
|
sentReports []sentReport
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTLSReporter(p *pinChecker, cm clientConfigProvider) *tlsReporter {
|
func newTLSReporter(p *pinChecker, cm clientInfoProvider) *tlsReporter {
|
||||||
return &tlsReporter{
|
return &tlsReporter{
|
||||||
cm: cm,
|
cm: cm,
|
||||||
p: p,
|
p: p,
|
||||||
@ -102,13 +103,14 @@ func (r *tlsReporter) reportCertIssue(remoteURI, host, port string, connState tl
|
|||||||
certChain = marshalCert7468(connState.PeerCertificates)
|
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) {
|
if !r.hasRecentlySentReport(report) {
|
||||||
r.recordReport(report)
|
r.recordReport(report)
|
||||||
go report.sendReport(remoteURI, cfg.UserAgent)
|
go report.sendReport(remoteURI, userAgent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,12 +27,16 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeClientConfigProvider struct {
|
type fakeClientInfoProvider struct {
|
||||||
version, useragent string
|
version, useragent string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeClientConfigProvider) GetClientConfig() *ClientConfig {
|
func (c *fakeClientInfoProvider) GetAppVersion() string {
|
||||||
return &ClientConfig{AppVersion: c.version, UserAgent: c.useragent}
|
return c.version
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeClientInfoProvider) GetUserAgent() string {
|
||||||
|
return c.useragent
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPinCheckerDoubleReport(t *testing.T) {
|
func TestPinCheckerDoubleReport(t *testing.T) {
|
||||||
@ -42,7 +46,7 @@ func TestPinCheckerDoubleReport(t *testing.T) {
|
|||||||
reportCounter++
|
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.
|
// Report the same issue many times.
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
|
|||||||
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@ -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 address "([^"]*)" of "([^"]*)" has (\d+) message(?:s)?$`, apiMailboxForAddressOfUserHasNumberOfMessages)
|
||||||
s.Step(`^API mailbox "([^"]*)" for "([^"]*)" has messages$`, apiMailboxForUserHasMessages)
|
s.Step(`^API mailbox "([^"]*)" for "([^"]*)" has messages$`, apiMailboxForUserHasMessages)
|
||||||
s.Step(`^API mailbox "([^"]*)" for address "([^"]*)" of "([^"]*)" has messages$`, apiMailboxForAddressOfUserHasMessages)
|
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 {
|
func apiIsCalled(endpoint string) error {
|
||||||
@ -187,12 +187,11 @@ func getPMAPIMessages(account *accounts.TestAccount, mailboxName string) ([]*pma
|
|||||||
return ctx.GetPMAPIController().GetMessages(account.Username(), labelID)
|
return ctx.GetPMAPIController().GetMessages(account.Username(), labelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func clientManagerUserAgent(expectedUserAgent string) error {
|
func userAgent(expectedUserAgent string) error {
|
||||||
expectedUserAgent = strings.ReplaceAll(expectedUserAgent, "[GOOS]", runtime.GOOS)
|
expectedUserAgent = strings.ReplaceAll(expectedUserAgent, "[GOOS]", runtime.GOOS)
|
||||||
|
|
||||||
assert.Eventually(ctx.GetTestingT(), func() bool {
|
assert.Eventually(ctx.GetTestingT(), func() bool {
|
||||||
userAgent := ctx.GetClientManager().GetUserAgent()
|
return ctx.GetUserAgent() == expectedUserAgent
|
||||||
return userAgent == expectedUserAgent
|
|
||||||
}, 5*time.Second, time.Second)
|
}, 5*time.Second, time.Second)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -21,10 +21,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"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/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/internal/sentry"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/internal/users"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/sentry"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetBridge returns bridge instance.
|
// GetBridge returns bridge instance.
|
||||||
@ -69,7 +70,7 @@ func newBridgeInstance(
|
|||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
clientManager users.ClientManager,
|
clientManager users.ClientManager,
|
||||||
) *bridge.Bridge {
|
) *bridge.Bridge {
|
||||||
sentryReporter := sentry.NewReporter("bridge", constants.Version)
|
sentryReporter := sentry.NewReporter("bridge", constants.Version, useragent.New())
|
||||||
panicHandler := &panicHandler{t: t}
|
panicHandler := &panicHandler{t: t}
|
||||||
updater := newFakeUpdater()
|
updater := newFakeUpdater()
|
||||||
versioner := newFakeVersioner()
|
versioner := newFakeVersioner()
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"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/constants"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/importexport"
|
"github.com/ProtonMail/proton-bridge/internal/importexport"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||||
@ -46,6 +47,7 @@ type TestContext struct {
|
|||||||
locations *fakeLocations
|
locations *fakeLocations
|
||||||
settings *fakeSettings
|
settings *fakeSettings
|
||||||
listener listener.Listener
|
listener listener.Listener
|
||||||
|
userAgent *useragent.UserAgent
|
||||||
testAccounts *accounts.TestAccounts
|
testAccounts *accounts.TestAccounts
|
||||||
|
|
||||||
// pmapiController is used to control real or fake pmapi clients.
|
// pmapiController is used to control real or fake pmapi clients.
|
||||||
@ -95,11 +97,12 @@ type TestContext struct {
|
|||||||
func New(app string) *TestContext {
|
func New(app string) *TestContext {
|
||||||
setLogrusVerbosityFromEnv()
|
setLogrusVerbosityFromEnv()
|
||||||
|
|
||||||
configName := app
|
userAgent := useragent.New()
|
||||||
if app == "ie" {
|
|
||||||
configName = "importExport"
|
cm := pmapi.NewClientManager(
|
||||||
}
|
pmapi.GetAPIConfig(getConfigName(app), constants.Version),
|
||||||
cm := pmapi.NewClientManager(pmapi.GetAPIConfig(configName, constants.Version))
|
userAgent,
|
||||||
|
)
|
||||||
|
|
||||||
ctx := &TestContext{
|
ctx := &TestContext{
|
||||||
t: &bddT{},
|
t: &bddT{},
|
||||||
@ -107,6 +110,7 @@ func New(app string) *TestContext {
|
|||||||
locations: newFakeLocations(),
|
locations: newFakeLocations(),
|
||||||
settings: newFakeSettings(),
|
settings: newFakeSettings(),
|
||||||
listener: listener.New(),
|
listener: listener.New(),
|
||||||
|
userAgent: userAgent,
|
||||||
pmapiController: newPMAPIController(cm),
|
pmapiController: newPMAPIController(cm),
|
||||||
clientManager: cm,
|
clientManager: cm,
|
||||||
testAccounts: newTestAccounts(),
|
testAccounts: newTestAccounts(),
|
||||||
@ -137,6 +141,14 @@ func New(app string) *TestContext {
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getConfigName(app string) string {
|
||||||
|
if app == "ie" {
|
||||||
|
return "importExport"
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup runs through all cleanup steps.
|
// 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.
|
// This can be a deferred call so that it is run even if the test steps failed the test.
|
||||||
func (ctx *TestContext) Cleanup() *TestContext {
|
func (ctx *TestContext) Cleanup() *TestContext {
|
||||||
@ -156,6 +168,11 @@ func (ctx *TestContext) GetClientManager() *pmapi.ClientManager {
|
|||||||
return ctx.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.
|
// GetTestingT returns testing.T compatible struct.
|
||||||
func (ctx *TestContext) GetTestingT() *bddT { //nolint[golint]
|
func (ctx *TestContext) GetTestingT() *bddT { //nolint[golint]
|
||||||
return ctx.t
|
return ctx.t
|
||||||
|
|||||||
@ -59,7 +59,7 @@ func (ctx *TestContext) withIMAPServer() {
|
|||||||
tls, _ := tls.New(settingsPath).GetConfig()
|
tls, _ := tls.New(settingsPath).GetConfig()
|
||||||
|
|
||||||
backend := imap.NewIMAPBackend(ph, ctx.listener, ctx.cache, ctx.bridge)
|
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()
|
go server.ListenAndServe()
|
||||||
require.NoError(ctx.t, waitForPort(port, 5*time.Second))
|
require.NoError(ctx.t, waitForPort(port, 5*time.Second))
|
||||||
|
|||||||
@ -4,21 +4,23 @@ Feature: User agent
|
|||||||
|
|
||||||
Scenario: Get user agent
|
Scenario: Get user agent
|
||||||
Given there is IMAP client logged in as "user"
|
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:
|
When IMAP client sends ID with argument:
|
||||||
"""
|
"""
|
||||||
"name" "Foo" "version" "1.4.0"
|
"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
|
Scenario: Update user agent
|
||||||
Given there is IMAP client logged in as "user"
|
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:
|
When IMAP client sends ID with argument:
|
||||||
"""
|
"""
|
||||||
"name" "Foo" "version" "1.4.0"
|
"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:
|
When IMAP client sends ID with argument:
|
||||||
"""
|
"""
|
||||||
"name" "Bar" "version" "4.2.0"
|
"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])"
|
||||||
|
|||||||
Reference in New Issue
Block a user