Compare commits
24 Commits
722c0b8291
...
9fc0e6e125
Author | SHA1 | Date |
---|---|---|
Mark Conway | 9fc0e6e125 | |
Jakub Cuth | f34a7ff0ed | |
Jakub | da069a0155 | |
Jakub | 8e49c84a12 | |
Jakub | 754d80d097 | |
Jakub | 63e272e270 | |
Xavier Michelon | 54859a34b2 | |
Jakub | 9b1feed68b | |
Jakub | c9b6cc162b | |
Jakub | bf3c90b8e9 | |
Jakub | 8d63fb2301 | |
Jakub | 7953306cc8 | |
Jakub Cuth | 37352d44d2 | |
Jakub | 2a1aeb208d | |
gzafirova | 94fbe260e4 | |
gzafirova | 6d4937222e | |
gzafirova | e33bad7bf1 | |
gzafirova | 70fdc91aff | |
gzafirova | bde8e45b37 | |
gzafirova | 6cb2d944d0 | |
Jakub | cf0f59afc0 | |
Jakub | 65d8fbbf31 | |
Gjorgji Slamkov | d919c0accf | |
Gjorgji Slamkov | 0ca07066db |
23
Changelog.md
23
Changelog.md
|
@ -3,6 +3,23 @@
|
|||
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||
|
||||
|
||||
## Zaehringen Bridge 3.10.0
|
||||
|
||||
### Added
|
||||
* GODT-3199: Add package log field.
|
||||
* GODT-3220: Add more test scenarios.
|
||||
|
||||
### Changed
|
||||
* GODT-3193: Preserve attachment encoding.
|
||||
* GODT-3214: Encrypt only with primary key.
|
||||
* GODT-2662: Use tart runner for darwin jobs.
|
||||
* GODT-1602: Test: run integration tests against black 🖤.
|
||||
* GODT-3257: Test: quad9 provider test not working on CI.
|
||||
|
||||
### Fixed
|
||||
* GODT-3290: Fix test failing because of leap day.
|
||||
|
||||
|
||||
## Ypsilon Bridge 3.9.1
|
||||
|
||||
### Fixed
|
||||
|
@ -43,6 +60,12 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
|||
* GODT-3188: Happy new year.
|
||||
|
||||
|
||||
## Xikou Bridge 3.8.2
|
||||
|
||||
### Fixed
|
||||
* GODT-3235: Update bridge update key.
|
||||
|
||||
|
||||
## Xikou Bridge 3.8.1
|
||||
|
||||
### Added
|
||||
|
|
2
Makefile
2
Makefile
|
@ -11,7 +11,7 @@ ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
|||
.PHONY: build build-gui build-nogui build-launcher versioner hasher
|
||||
|
||||
# Keep version hardcoded so app build works also without Git repository.
|
||||
BRIDGE_APP_VERSION?=3.9.1+git
|
||||
BRIDGE_APP_VERSION?=3.10.0+git
|
||||
APP_VERSION:=${BRIDGE_APP_VERSION}
|
||||
APP_FULL_NAME:=Proton Mail Bridge
|
||||
APP_VENDOR:=Proton AG
|
||||
|
|
18
ci/env.yml
18
ci/env.yml
|
@ -13,12 +13,22 @@
|
|||
- windows-bridge
|
||||
|
||||
.env-darwin:
|
||||
extends:
|
||||
- .image-darwin-build
|
||||
before_script:
|
||||
- export BRIDGE_SYNC_FORCE_MINIMUM_SPEC=1
|
||||
- !reference [.before-script-darwin-build, before_script]
|
||||
cache: {}
|
||||
tags:
|
||||
- macos-m1-bridge
|
||||
- !reference [.before-script-darwin-tart-build, before_script]
|
||||
- !reference [.before-script-git-config, before_script]
|
||||
- mkdir -p .cache/bin
|
||||
- export PATH=$(pwd)/.cache/bin:$PATH
|
||||
- export GOPATH="$CI_PROJECT_DIR/.cache"
|
||||
variables:
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${CI_PROJECT_DIR}/.cache
|
||||
cache:
|
||||
key: darwin-go-and-vcpkg
|
||||
paths:
|
||||
- .cache
|
||||
when: 'always'
|
||||
|
||||
.env-linux-build:
|
||||
extends:
|
||||
|
|
4
go.mod
4
go.mod
|
@ -5,9 +5,9 @@ go 1.21
|
|||
require (
|
||||
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557
|
||||
github.com/Masterminds/semver/v3 v3.2.0
|
||||
github.com/ProtonMail/gluon v0.17.1-0.20240102132144-89b40fb6fe7e
|
||||
github.com/ProtonMail/gluon v0.17.1-0.20240227105633-3734c7694bcd
|
||||
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20231130083229-e8aa47d7a366
|
||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20240226161523-ec58ed7ea4b9
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.4-proton
|
||||
github.com/PuerkitoBio/goquery v1.8.1
|
||||
github.com/abiosoft/ishell v2.0.0+incompatible
|
||||
|
|
8
go.sum
8
go.sum
|
@ -27,8 +27,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
|
|||
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
|
||||
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug=
|
||||
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
|
||||
github.com/ProtonMail/gluon v0.17.1-0.20240102132144-89b40fb6fe7e h1:DR97ydcuS4/EjTTCkp7F9IRCi+ykD1UoAP7UBFtEcRA=
|
||||
github.com/ProtonMail/gluon v0.17.1-0.20240102132144-89b40fb6fe7e/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo=
|
||||
github.com/ProtonMail/gluon v0.17.1-0.20240227105633-3734c7694bcd h1:AjJsf5xQGmZPg6GLn+wB+eBoGRopJlG70lQBfSyfX+M=
|
||||
github.com/ProtonMail/gluon v0.17.1-0.20240227105633-3734c7694bcd/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo=
|
||||
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4=
|
||||
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
|
||||
|
@ -38,8 +38,8 @@ github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7 h1:+j+Kd/
|
|||
github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
|
||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20231130083229-e8aa47d7a366 h1:W9P5GdDnuGkB3tbzKnXmUrTjIs6zk/K+4lpPTWzsoRE=
|
||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20231130083229-e8aa47d7a366/go.mod h1:t+hb0BfkmZ9fpvzVRpHC7limoowym6ln/j0XL9a8DDw=
|
||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20240226161523-ec58ed7ea4b9 h1:tcQpGQljNsZmfuA6L4hAzio8/AIx5OXcU2JUdyX/qxw=
|
||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20240226161523-ec58ed7ea4b9/go.mod h1:t+hb0BfkmZ9fpvzVRpHC7limoowym6ln/j0XL9a8DDw=
|
||||
github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865 h1:EP1gnxLL5Z7xBSymE9nSTM27nRYINuvssAtDmG0suD8=
|
||||
github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
|
||||
|
|
|
@ -40,7 +40,7 @@ func defaultAPIOptions(
|
|||
proton.WithAppVersion(constants.AppVersion(version.Original())),
|
||||
proton.WithCookieJar(cookieJar),
|
||||
proton.WithTransport(transport),
|
||||
proton.WithLogger(logrus.StandardLogger()),
|
||||
proton.WithLogger(logrus.WithField("pkg", "gpa/client")),
|
||||
proton.WithPanicHandler(panicHandler),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,6 +132,8 @@ type Bridge struct {
|
|||
syncService *syncservice.Service
|
||||
}
|
||||
|
||||
var logPkg = logrus.WithField("pkg", "bridge") //nolint:gochecknoglobals
|
||||
|
||||
// New creates a new bridge.
|
||||
func New(
|
||||
locator Locator, // the locator to provide paths to store data
|
||||
|
@ -322,7 +324,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
|
||||
// Handle connection up/down events.
|
||||
bridge.api.AddStatusObserver(func(status proton.Status) {
|
||||
logrus.Info("API status changed: ", status)
|
||||
logPkg.Info("API status changed: ", status)
|
||||
|
||||
switch {
|
||||
case status == proton.StatusUp:
|
||||
|
@ -337,7 +339,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
|
||||
// If any call returns a bad version code, we need to update.
|
||||
bridge.api.AddErrorHandler(proton.AppVersionBadCode, func() {
|
||||
logrus.Warn("App version is bad")
|
||||
logPkg.Warn("App version is bad")
|
||||
bridge.publish(events.UpdateForced{})
|
||||
})
|
||||
|
||||
|
@ -350,7 +352,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
// Log all manager API requests (client requests are logged separately).
|
||||
bridge.api.AddPostRequestHook(func(_ *resty.Client, r *resty.Response) error {
|
||||
if _, ok := proton.ClientIDFromContext(r.Request.Context()); !ok {
|
||||
logrus.Infof("[MANAGER] %v: %v %v", r.Status(), r.Request.Method, r.Request.URL)
|
||||
logrus.WithField("pkg", "gpa/manager").Infof("%v: %v %v", r.Status(), r.Request.Method, r.Request.URL)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -359,7 +361,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
// Publish a TLS issue event if a TLS issue is encountered.
|
||||
bridge.tasks.Once(func(ctx context.Context) {
|
||||
async.RangeContext(ctx, tlsReporter.GetTLSIssueCh(), func(struct{}) {
|
||||
logrus.Warn("TLS issue encountered")
|
||||
logPkg.Warn("TLS issue encountered")
|
||||
bridge.publish(events.TLSIssue{})
|
||||
})
|
||||
})
|
||||
|
@ -367,7 +369,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
// Publish a raise event if the focus service is called.
|
||||
bridge.tasks.Once(func(ctx context.Context) {
|
||||
async.RangeContext(ctx, bridge.focusService.GetRaiseCh(), func(struct{}) {
|
||||
logrus.Info("Focus service requested raise")
|
||||
logPkg.Info("Focus service requested raise")
|
||||
bridge.publish(events.Raise{})
|
||||
})
|
||||
})
|
||||
|
@ -375,7 +377,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
// Handle any IMAP events that are forwarded to the bridge from gluon.
|
||||
bridge.tasks.Once(func(ctx context.Context) {
|
||||
async.RangeContext(ctx, bridge.imapEventCh, func(event imapEvents.Event) {
|
||||
logrus.WithField("event", fmt.Sprintf("%T", event)).Debug("Received IMAP event")
|
||||
logPkg.WithField("event", fmt.Sprintf("%T", event)).Debug("Received IMAP event")
|
||||
bridge.handleIMAPEvent(event)
|
||||
})
|
||||
})
|
||||
|
@ -383,7 +385,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
// Attempt to load users from the vault when triggered.
|
||||
bridge.goLoad = bridge.tasks.Trigger(func(ctx context.Context) {
|
||||
if err := bridge.loadUsers(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to load users")
|
||||
logPkg.WithError(err).Error("Failed to load users")
|
||||
if netErr := new(proton.NetError); !errors.As(err, &netErr) {
|
||||
sentry.ReportError(bridge.reporter, "Failed to load users", err)
|
||||
}
|
||||
|
@ -396,7 +398,7 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
|||
|
||||
// Check for updates when triggered.
|
||||
bridge.goUpdate = bridge.tasks.PeriodicOrTrigger(constants.UpdateCheckInterval, 0, func(ctx context.Context) {
|
||||
logrus.Info("Checking for updates")
|
||||
logPkg.Info("Checking for updates")
|
||||
|
||||
version, err := bridge.updater.GetVersionInfo(ctx, bridge.api, bridge.vault.GetUpdateChannel())
|
||||
if err != nil {
|
||||
|
@ -434,7 +436,7 @@ func (bridge *Bridge) GetErrors() []error {
|
|||
}
|
||||
|
||||
func (bridge *Bridge) Close(ctx context.Context) {
|
||||
logrus.Info("Closing bridge")
|
||||
logPkg.Info("Closing bridge")
|
||||
|
||||
// Stop heart beat before closing users.
|
||||
bridge.heartbeat.stop()
|
||||
|
@ -448,7 +450,7 @@ func (bridge *Bridge) Close(ctx context.Context) {
|
|||
|
||||
// Close the servers
|
||||
if err := bridge.serverManager.CloseServers(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to close servers")
|
||||
logPkg.WithError(err).Error("Failed to close servers")
|
||||
}
|
||||
|
||||
bridge.syncService.Close()
|
||||
|
@ -474,12 +476,12 @@ func (bridge *Bridge) publish(event events.Event) {
|
|||
bridge.watchersLock.RLock()
|
||||
defer bridge.watchersLock.RUnlock()
|
||||
|
||||
logrus.WithField("event", event).Debug("Publishing event")
|
||||
logPkg.WithField("event", event).Debug("Publishing event")
|
||||
|
||||
for _, watcher := range bridge.watchers {
|
||||
if watcher.IsWatching(event) {
|
||||
if ok := watcher.Send(event); !ok {
|
||||
logrus.WithField("event", event).Warn("Failed to send event to watcher")
|
||||
logPkg.WithField("event", event).Warn("Failed to send event to watcher")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -512,13 +514,13 @@ func (bridge *Bridge) remWatcher(watcher *watcher.Watcher[events.Event]) {
|
|||
}
|
||||
|
||||
func (bridge *Bridge) onStatusUp(_ context.Context) {
|
||||
logrus.Info("Handling API status up")
|
||||
logPkg.Info("Handling API status up")
|
||||
|
||||
bridge.goLoad()
|
||||
}
|
||||
|
||||
func (bridge *Bridge) onStatusDown(ctx context.Context) {
|
||||
logrus.Info("Handling API status down")
|
||||
logPkg.Info("Handling API status down")
|
||||
|
||||
for backoff := time.Second; ; backoff = min(backoff*2, 30*time.Second) {
|
||||
select {
|
||||
|
@ -526,10 +528,10 @@ func (bridge *Bridge) onStatusDown(ctx context.Context) {
|
|||
return
|
||||
|
||||
case <-time.After(backoff):
|
||||
logrus.Info("Pinging API")
|
||||
logPkg.Info("Pinging API")
|
||||
|
||||
if err := bridge.api.Ping(ctx); err != nil {
|
||||
logrus.WithError(err).Warn("Ping failed, API is still unreachable")
|
||||
logPkg.WithError(err).Warn("Ping failed, API is still unreachable")
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ import (
|
|||
// ConfigureAppleMail configures Apple Mail for the given userID and address.
|
||||
// If configuring Apple Mail for Catalina or newer, it ensures Bridge is using SSL.
|
||||
func (bridge *Bridge) ConfigureAppleMail(ctx context.Context, userID, address string) error {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
logPkg.WithFields(logrus.Fields{
|
||||
"userID": userID,
|
||||
"address": logging.Sensitive(address),
|
||||
}).Info("Configuring Apple Mail")
|
||||
|
|
|
@ -65,7 +65,11 @@ func (bridge *Bridge) CheckClientState(ctx context.Context, checkFlags bool, pro
|
|||
if progressCB != nil {
|
||||
progressCB(fmt.Sprintf("Checking state for user %v", usr.Name()))
|
||||
}
|
||||
log := logrus.WithField("user", usr.Name()).WithField("diag", "state-check")
|
||||
log := logrus.WithFields(logrus.Fields{
|
||||
"pkg": "bridge/debug",
|
||||
"user": usr.Name(),
|
||||
"diag": "state-check",
|
||||
})
|
||||
log.Debug("Retrieving all server metadata")
|
||||
meta, err := usr.GetDiagnosticMetadata(ctx)
|
||||
if err != nil {
|
||||
|
@ -280,7 +284,7 @@ func clientGetMessageIDs(client *goimapclient.Client, mailbox string) (map[strin
|
|||
|
||||
internalID, ok := header.GetChecked("X-Pm-Internal-Id")
|
||||
if !ok {
|
||||
logrus.Errorf("Message %v does not have internal id", internalID)
|
||||
logrus.WithField("pkg", "bridge/debug").Errorf("Message %v does not have internal id", internalID)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ func (h *heartBeatState) start() {
|
|||
h.taskStarted = true
|
||||
|
||||
h.task.PeriodicOrTrigger(h.taskInterval, 0, func(ctx context.Context) {
|
||||
logrus.Debug("Checking for heartbeat")
|
||||
logrus.WithField("pkg", "bridge/heartbeat").Debug("Checking for heartbeat")
|
||||
|
||||
h.TrySending(ctx)
|
||||
})
|
||||
|
@ -135,7 +135,7 @@ func (bridge *Bridge) SendHeartbeat(ctx context.Context, heartbeat *telemetry.He
|
|||
if err := bridge.reporter.ReportMessageWithContext("Cannot parse heartbeat data.", reporter.Context{
|
||||
"error": err,
|
||||
}); err != nil {
|
||||
logrus.WithError(err).Error("Failed to parse heartbeat data.")
|
||||
logrus.WithField("pkg", "bridge/heartbeat").WithError(err).Error("Failed to parse heartbeat data.")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -35,10 +35,12 @@ func (bridge *Bridge) restartIMAP(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
|
||||
log := logrus.WithField("pkg", "bridge/event/imap")
|
||||
|
||||
switch event := event.(type) {
|
||||
case imapEvents.UserAdded:
|
||||
for labelID, count := range event.Counts {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
log.WithFields(logrus.Fields{
|
||||
"gluonID": event.UserID,
|
||||
"labelID": labelID,
|
||||
"count": count,
|
||||
|
@ -46,7 +48,7 @@ func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
|
|||
}
|
||||
|
||||
case imapEvents.IMAPID:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
log.WithFields(logrus.Fields{
|
||||
"sessionID": event.SessionID,
|
||||
"name": event.IMAPID.Name,
|
||||
"version": event.IMAPID.Version,
|
||||
|
@ -57,7 +59,7 @@ func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
|
|||
}
|
||||
|
||||
case imapEvents.LoginFailed:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
log.WithFields(logrus.Fields{
|
||||
"sessionID": event.SessionID,
|
||||
"username": event.Username,
|
||||
"pkg": "imap",
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
"github.com/ProtonMail/proton-bridge/v3/internal/services/userevents"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (bridge *Bridge) GetKeychainApp() (string, error) {
|
||||
|
@ -134,7 +133,7 @@ func (bridge *Bridge) SetGluonDir(ctx context.Context, newGluonDir string) error
|
|||
bridge.usersLock.RLock()
|
||||
|
||||
defer func() {
|
||||
logrus.Info("Restarting user event loops")
|
||||
logPkg.Info("Restarting user event loops")
|
||||
for _, u := range bridge.users {
|
||||
u.ResumeEventLoop()
|
||||
}
|
||||
|
@ -149,20 +148,20 @@ func (bridge *Bridge) SetGluonDir(ctx context.Context, newGluonDir string) error
|
|||
|
||||
waiters := make([]waiter, 0, len(bridge.users))
|
||||
|
||||
logrus.Info("Pausing user event loops for gluon dir change")
|
||||
logPkg.Info("Pausing user event loops for gluon dir change")
|
||||
for id, u := range bridge.users {
|
||||
waiters = append(waiters, waiter{w: u.PauseEventLoopWithWaiter(), id: id})
|
||||
}
|
||||
|
||||
logrus.Info("Waiting on user event loop completion")
|
||||
logPkg.Info("Waiting on user event loop completion")
|
||||
for _, waiter := range waiters {
|
||||
if err := waiter.w.WaitPollFinished(ctx); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to wait on event loop pause for user %v", waiter.id)
|
||||
logPkg.WithError(err).Errorf("Failed to wait on event loop pause for user %v", waiter.id)
|
||||
return fmt.Errorf("failed on event loop pause: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Info("Changing gluon directory")
|
||||
logPkg.Info("Changing gluon directory")
|
||||
return bridge.serverManager.SetGluonDir(ctx, newGluonDir)
|
||||
}
|
||||
|
||||
|
@ -330,13 +329,13 @@ func (bridge *Bridge) FactoryReset(ctx context.Context) {
|
|||
// Wipe the vault.
|
||||
gluonCacheDir, err := bridge.locator.ProvideGluonCachePath()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to provide gluon dir")
|
||||
logPkg.WithError(err).Error("Failed to provide gluon dir")
|
||||
} else if err := bridge.vault.Reset(gluonCacheDir); err != nil {
|
||||
logrus.WithError(err).Error("Failed to reset vault")
|
||||
logPkg.WithError(err).Error("Failed to reset vault")
|
||||
}
|
||||
|
||||
// Lastly, delete all files except the vault.
|
||||
if err := bridge.locator.Clear(bridge.vault.Path()); err != nil {
|
||||
logrus.WithError(err).Error("Failed to clear data paths")
|
||||
logPkg.WithError(err).Error("Failed to clear data paths")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var logUser = logrus.WithField("pkg", "bridge/user") //nolint:gochecknoglobals
|
||||
|
||||
type UserState int
|
||||
|
||||
const (
|
||||
|
@ -122,7 +124,7 @@ func (bridge *Bridge) QueryUserInfo(query string) (UserInfo, error) {
|
|||
|
||||
// LoginAuth begins the login process. It returns an authorized client that might need 2FA.
|
||||
func (bridge *Bridge) LoginAuth(ctx context.Context, username string, password []byte) (*proton.Client, proton.Auth, error) {
|
||||
logrus.WithField("username", logging.Sensitive(username)).Info("Authorizing user for login")
|
||||
logUser.WithField("username", logging.Sensitive(username)).Info("Authorizing user for login")
|
||||
|
||||
if username == "crash@bandicoot" {
|
||||
panic("Your wish is my command.. I crash!")
|
||||
|
@ -134,10 +136,10 @@ func (bridge *Bridge) LoginAuth(ctx context.Context, username string, password [
|
|||
}
|
||||
|
||||
if ok := safe.RLockRet(func() bool { return mapHas(bridge.users, auth.UserID) }, bridge.usersLock); ok {
|
||||
logrus.WithField("userID", auth.UserID).Warn("User already logged in")
|
||||
logUser.WithField("userID", auth.UserID).Warn("User already logged in")
|
||||
|
||||
if err := client.AuthDelete(ctx); err != nil {
|
||||
logrus.WithError(err).Warn("Failed to delete auth")
|
||||
logUser.WithError(err).Warn("Failed to delete auth")
|
||||
}
|
||||
|
||||
return nil, proton.Auth{}, ErrUserAlreadyLoggedIn
|
||||
|
@ -153,7 +155,7 @@ func (bridge *Bridge) LoginUser(
|
|||
auth proton.Auth,
|
||||
keyPass []byte,
|
||||
) (string, error) {
|
||||
logrus.WithField("userID", auth.UserID).Info("Logging in authorized user")
|
||||
logUser.WithField("userID", auth.UserID).Info("Logging in authorized user")
|
||||
|
||||
userID, err := try.CatchVal(
|
||||
func() (string, error) {
|
||||
|
@ -165,7 +167,7 @@ func (bridge *Bridge) LoginUser(
|
|||
// Failure to unlock will allow retries, so we do not delete auth.
|
||||
if !errors.Is(err, ErrFailedToUnlock) {
|
||||
if deleteErr := client.AuthDelete(ctx); deleteErr != nil {
|
||||
logrus.WithError(deleteErr).Error("Failed to delete auth")
|
||||
logUser.WithError(deleteErr).Error("Failed to delete auth")
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("failed to login user: %w", err)
|
||||
|
@ -188,7 +190,7 @@ func (bridge *Bridge) LoginFull(
|
|||
getTOTP func() (string, error),
|
||||
getKeyPass func() ([]byte, error),
|
||||
) (string, error) {
|
||||
logrus.WithField("username", logging.Sensitive(username)).Info("Performing full user login")
|
||||
logUser.WithField("username", logging.Sensitive(username)).Info("Performing full user login")
|
||||
|
||||
client, auth, err := bridge.LoginAuth(ctx, username, password)
|
||||
if err != nil {
|
||||
|
@ -196,7 +198,7 @@ func (bridge *Bridge) LoginFull(
|
|||
}
|
||||
|
||||
if auth.TwoFA.Enabled&proton.HasTOTP != 0 {
|
||||
logrus.WithField("userID", auth.UserID).Info("Requesting TOTP")
|
||||
logUser.WithField("userID", auth.UserID).Info("Requesting TOTP")
|
||||
|
||||
totp, err := getTOTP()
|
||||
if err != nil {
|
||||
|
@ -211,7 +213,7 @@ func (bridge *Bridge) LoginFull(
|
|||
var keyPass []byte
|
||||
|
||||
if auth.PasswordMode == proton.TwoPasswordMode {
|
||||
logrus.WithField("userID", auth.UserID).Info("Requesting mailbox password")
|
||||
logUser.WithField("userID", auth.UserID).Info("Requesting mailbox password")
|
||||
|
||||
userKeyPass, err := getKeyPass()
|
||||
if err != nil {
|
||||
|
@ -226,7 +228,7 @@ func (bridge *Bridge) LoginFull(
|
|||
userID, err := bridge.LoginUser(ctx, client, auth, keyPass)
|
||||
if err != nil {
|
||||
if deleteErr := client.AuthDelete(ctx); deleteErr != nil {
|
||||
logrus.WithError(err).Error("Failed to delete auth")
|
||||
logUser.WithError(err).Error("Failed to delete auth")
|
||||
}
|
||||
|
||||
return "", err
|
||||
|
@ -237,7 +239,7 @@ func (bridge *Bridge) LoginFull(
|
|||
|
||||
// LogoutUser logs out the given user.
|
||||
func (bridge *Bridge) LogoutUser(ctx context.Context, userID string) error {
|
||||
logrus.WithField("userID", userID).Info("Logging out user")
|
||||
logUser.WithField("userID", userID).Info("Logging out user")
|
||||
|
||||
return safe.LockRet(func() error {
|
||||
user, ok := bridge.users[userID]
|
||||
|
@ -257,7 +259,7 @@ func (bridge *Bridge) LogoutUser(ctx context.Context, userID string) error {
|
|||
|
||||
// DeleteUser deletes the given user.
|
||||
func (bridge *Bridge) DeleteUser(ctx context.Context, userID string) error {
|
||||
logrus.WithField("userID", userID).Info("Deleting user")
|
||||
logUser.WithField("userID", userID).Info("Deleting user")
|
||||
|
||||
syncConfigDir, err := bridge.locator.ProvideIMAPSyncConfigPath()
|
||||
if err != nil {
|
||||
|
@ -278,7 +280,7 @@ func (bridge *Bridge) DeleteUser(ctx context.Context, userID string) error {
|
|||
}
|
||||
|
||||
if err := bridge.vault.DeleteUser(userID); err != nil {
|
||||
logrus.WithError(err).Error("Failed to delete vault user")
|
||||
logUser.WithError(err).Error("Failed to delete vault user")
|
||||
}
|
||||
|
||||
bridge.publish(events.UserDeleted{
|
||||
|
@ -291,7 +293,7 @@ func (bridge *Bridge) DeleteUser(ctx context.Context, userID string) error {
|
|||
|
||||
// SetAddressMode sets the address mode for the given user.
|
||||
func (bridge *Bridge) SetAddressMode(ctx context.Context, userID string, mode vault.AddressMode) error {
|
||||
logrus.WithField("userID", userID).WithField("mode", mode).Info("Setting address mode")
|
||||
logUser.WithField("userID", userID).WithField("mode", mode).Info("Setting address mode")
|
||||
|
||||
return safe.RLockRet(func() error {
|
||||
user, ok := bridge.users[userID]
|
||||
|
@ -327,7 +329,7 @@ func (bridge *Bridge) SetAddressMode(ctx context.Context, userID string, mode va
|
|||
|
||||
// SendBadEventUserFeedback passes the feedback to the given user.
|
||||
func (bridge *Bridge) SendBadEventUserFeedback(_ context.Context, userID string, doResync bool) error {
|
||||
logrus.WithField("userID", userID).WithField("doResync", doResync).Info("Passing bad event feedback to user")
|
||||
logUser.WithField("userID", userID).WithField("doResync", doResync).Info("Passing bad event feedback to user")
|
||||
|
||||
return safe.RLockRet(func() error {
|
||||
ctx := context.Background()
|
||||
|
@ -338,7 +340,7 @@ func (bridge *Bridge) SendBadEventUserFeedback(_ context.Context, userID string,
|
|||
"Failed to handle event: feedback failed: no such user",
|
||||
reporter.Context{"user_id": userID},
|
||||
); rerr != nil {
|
||||
logrus.WithError(rerr).Error("Failed to report feedback failure")
|
||||
logUser.WithError(rerr).Error("Failed to report feedback failure")
|
||||
}
|
||||
|
||||
return ErrNoSuchUser
|
||||
|
@ -349,7 +351,7 @@ func (bridge *Bridge) SendBadEventUserFeedback(_ context.Context, userID string,
|
|||
"Failed to handle event: feedback resync",
|
||||
reporter.Context{"user_id": userID},
|
||||
); rerr != nil {
|
||||
logrus.WithError(rerr).Error("Failed to report feedback failure")
|
||||
logUser.WithError(rerr).Error("Failed to report feedback failure")
|
||||
}
|
||||
|
||||
return user.BadEventFeedbackResync(ctx)
|
||||
|
@ -359,7 +361,7 @@ func (bridge *Bridge) SendBadEventUserFeedback(_ context.Context, userID string,
|
|||
"Failed to handle event: feedback logout",
|
||||
reporter.Context{"user_id": userID},
|
||||
); rerr != nil {
|
||||
logrus.WithError(rerr).Error("Failed to report feedback failure")
|
||||
logUser.WithError(rerr).Error("Failed to report feedback failure")
|
||||
}
|
||||
|
||||
bridge.logoutUser(ctx, user, true, false, false)
|
||||
|
@ -403,11 +405,11 @@ func (bridge *Bridge) loginUser(ctx context.Context, client *proton.Client, auth
|
|||
|
||||
// loadUsers tries to load each user in the vault that isn't already loaded.
|
||||
func (bridge *Bridge) loadUsers(ctx context.Context) error {
|
||||
logrus.WithField("count", len(bridge.vault.GetUserIDs())).Info("Loading users")
|
||||
defer logrus.Info("Finished loading users")
|
||||
logUser.WithField("count", len(bridge.vault.GetUserIDs())).Info("Loading users")
|
||||
defer logUser.Info("Finished loading users")
|
||||
|
||||
return bridge.vault.ForUser(runtime.NumCPU(), func(user *vault.User) error {
|
||||
log := logrus.WithField("userID", user.UserID())
|
||||
log := logUser.WithField("userID", user.UserID())
|
||||
|
||||
if user.AuthUID() == "" {
|
||||
log.Info("User is not connected (skipping)")
|
||||
|
@ -451,7 +453,7 @@ func (bridge *Bridge) loadUser(ctx context.Context, user *vault.User) error {
|
|||
if apiErr := new(proton.APIError); errors.As(err, &apiErr) && (apiErr.Code == proton.AuthRefreshTokenInvalid) {
|
||||
// The session cannot be refreshed, we sign out the user by clearing his auth secrets.
|
||||
if err := user.Clear(); err != nil {
|
||||
logrus.WithError(err).Warn("Failed to clear user secrets")
|
||||
logUser.WithError(err).Warn("Failed to clear user secrets")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,24 +498,24 @@ func (bridge *Bridge) addUser(
|
|||
|
||||
if err := bridge.addUserWithVault(ctx, client, apiUser, vaultUser, isNew); err != nil {
|
||||
if _, ok := err.(*resty.ResponseError); ok || isLogin {
|
||||
logrus.WithError(err).Error("Failed to add user, clearing its secrets from vault")
|
||||
logUser.WithError(err).Error("Failed to add user, clearing its secrets from vault")
|
||||
|
||||
if err := vaultUser.Clear(); err != nil {
|
||||
logrus.WithError(err).Error("Failed to clear user secrets")
|
||||
logUser.WithError(err).Error("Failed to clear user secrets")
|
||||
}
|
||||
} else {
|
||||
logrus.WithError(err).Error("Failed to add user")
|
||||
logUser.WithError(err).Error("Failed to add user")
|
||||
}
|
||||
|
||||
if err := vaultUser.Close(); err != nil {
|
||||
logrus.WithError(err).Error("Failed to close vault user")
|
||||
logUser.WithError(err).Error("Failed to close vault user")
|
||||
}
|
||||
|
||||
if isNew {
|
||||
logrus.Warn("Deleting newly added vault user")
|
||||
logUser.Warn("Deleting newly added vault user")
|
||||
|
||||
if err := bridge.vault.DeleteUser(apiUser.ID); err != nil {
|
||||
logrus.WithError(err).Error("Failed to delete vault user")
|
||||
logUser.WithError(err).Error("Failed to delete vault user")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,7 +569,7 @@ func (bridge *Bridge) addUserWithVault(
|
|||
// For example, if the user's addresses change, we need to update them in gluon.
|
||||
bridge.tasks.Once(func(ctx context.Context) {
|
||||
async.RangeContext(ctx, user.GetEventCh(), func(event events.Event) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
logUser.WithFields(logrus.Fields{
|
||||
"userID": apiUser.ID,
|
||||
"event": event,
|
||||
}).Debug("Received user event")
|
||||
|
@ -618,14 +620,14 @@ func (bridge *Bridge) logoutUser(ctx context.Context, user *user.User, withAPI,
|
|||
user.SendConfigStatusAbort(ctx, withTelemetry)
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
logUser.WithFields(logrus.Fields{
|
||||
"userID": user.ID(),
|
||||
"withAPI": withAPI,
|
||||
"withData": withData,
|
||||
}).Debug("Logging out user")
|
||||
|
||||
if err := user.Logout(ctx, withAPI); err != nil {
|
||||
logrus.WithError(err).Error("Failed to logout user")
|
||||
logUser.WithError(err).Error("Failed to logout user")
|
||||
}
|
||||
|
||||
bridge.heartbeat.SetNbAccount(len(bridge.users))
|
||||
|
|
|
@ -58,7 +58,7 @@ func (bridge *Bridge) handleUserBadEvent(ctx context.Context, user *user.User, e
|
|||
"error": event.Error,
|
||||
"error_type": internal.ErrCauseType(event.Error),
|
||||
}); rerr != nil {
|
||||
logrus.WithError(rerr).Error("Failed to report failed event handling")
|
||||
logrus.WithField("pkg", "bridge/event").WithError(rerr).Error("Failed to report failed event handling")
|
||||
}
|
||||
|
||||
user.OnBadEvent(ctx)
|
||||
|
@ -70,6 +70,6 @@ func (bridge *Bridge) handleUncategorizedErrorEvent(event events.UncategorizedEv
|
|||
"error_type": internal.ErrCauseType(event.Error),
|
||||
"error": event.Error,
|
||||
}); rerr != nil {
|
||||
logrus.WithError(rerr).Error("Failed to report failed event handling")
|
||||
logrus.WithField("pkg", "bridge/event").WithError(rerr).Error("Failed to report failed event handling")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,6 @@ func TestConfigurationProgress_fed_year_change(t *testing.T) {
|
|||
|
||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||
require.Equal(t, "bridge_config_progress", req.Event)
|
||||
require.Equal(t, 370, req.Values.NbDay)
|
||||
require.True(t, (req.Values.NbDay == 370) || (req.Values.NbDay == 371)) // leap year is accounted for in the simplest manner.
|
||||
require.Equal(t, 2, req.Values.NbDaySinceLast)
|
||||
}
|
||||
|
|
|
@ -29,27 +29,34 @@ type TLSDialer interface {
|
|||
DialTLSContext(ctx context.Context, network, address string) (conn net.Conn, err error)
|
||||
}
|
||||
|
||||
func SetBasicTransportTimeouts(t *http.Transport) {
|
||||
t.MaxIdleConns = 100
|
||||
t.MaxIdleConnsPerHost = 100
|
||||
t.IdleConnTimeout = 5 * time.Minute
|
||||
|
||||
t.ExpectContinueTimeout = 500 * time.Millisecond
|
||||
|
||||
// GODT-126: this was initially 10s but logs from users showed a significant number
|
||||
// were hitting this timeout, possibly due to flaky wifi taking >10s to reconnect.
|
||||
// Bumping to 30s for now to avoid this problem.
|
||||
t.ResponseHeaderTimeout = 30 * time.Second
|
||||
|
||||
// If we allow up to 30 seconds for response headers, it is reasonable to allow up
|
||||
// to 30 seconds for the TLS handshake to take place.
|
||||
t.TLSHandshakeTimeout = 30 * time.Second
|
||||
}
|
||||
|
||||
// CreateTransportWithDialer creates an http.Transport that uses the given dialer to make TLS connections.
|
||||
func CreateTransportWithDialer(dialer TLSDialer) *http.Transport {
|
||||
return &http.Transport{
|
||||
t := &http.Transport{
|
||||
DialTLSContext: dialer.DialTLSContext,
|
||||
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
IdleConnTimeout: 5 * time.Minute,
|
||||
|
||||
ExpectContinueTimeout: 500 * time.Millisecond,
|
||||
|
||||
// GODT-126: this was initially 10s but logs from users showed a significant number
|
||||
// were hitting this timeout, possibly due to flaky wifi taking >10s to reconnect.
|
||||
// Bumping to 30s for now to avoid this problem.
|
||||
ResponseHeaderTimeout: 30 * time.Second,
|
||||
|
||||
// If we allow up to 30 seconds for response headers, it is reasonable to allow up
|
||||
// to 30 seconds for the TLS handshake to take place.
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
|
||||
SetBasicTransportTimeouts(t)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// BasicTLSDialer implements TLSDialer.
|
||||
|
|
|
@ -144,7 +144,8 @@ func TestProxyProvider_FindProxy_CanReachTimeout(t *testing.T) {
|
|||
r.Error(t, err)
|
||||
}
|
||||
|
||||
func TestProxyProvider_DoHLookup_Quad9(t *testing.T) {
|
||||
// DISABLED_TestProxyProvider_DoHLookup_Quad9 cannot run on CI, see GODT-3257.
|
||||
func DISABLED_TestProxyProvider_DoHLookup_Quad9(t *testing.T) {
|
||||
p := newProxyProvider(NewBasicTLSDialer(""), "", []string{Quad9Provider, GoogleProvider}, async.NoopPanicHandler{})
|
||||
|
||||
records, err := p.dohLookup(context.Background(), proxyQuery, Quad9Provider)
|
||||
|
|
|
@ -27,5 +27,5 @@ func NewIMAPLogger() *IMAPLogger {
|
|||
}
|
||||
|
||||
func (l *IMAPLogger) Write(p []byte) (n int, err error) {
|
||||
return logrus.WithField("pkg", "IMAP").WriterLevel(logrus.TraceLevel).Write(p)
|
||||
return logrus.WithField("pkg", "log/IMAP").WriterLevel(logrus.TraceLevel).Write(p)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ type SMTPErrorLogger struct {
|
|||
}
|
||||
|
||||
func NewSMTPLogger() *SMTPErrorLogger {
|
||||
return &SMTPErrorLogger{l: logrus.WithField("pkg", "SMTP")}
|
||||
return &SMTPErrorLogger{l: logrus.WithField("pkg", "log/SMTP")}
|
||||
}
|
||||
|
||||
func (s *SMTPErrorLogger) Printf(format string, args ...interface{}) {
|
||||
|
@ -44,7 +44,7 @@ type SMTPDebugLogger struct {
|
|||
}
|
||||
|
||||
func NewSMTPDebugLogger() *SMTPDebugLogger {
|
||||
return &SMTPDebugLogger{l: logrus.WithField("pkg", "SMTP")}
|
||||
return &SMTPDebugLogger{l: logrus.WithField("pkg", "log/SMTP")}
|
||||
}
|
||||
|
||||
func (l *SMTPDebugLogger) Write(p []byte) (n int, err error) {
|
||||
|
|
|
@ -677,13 +677,18 @@ func (s *Connector) importMessage(
|
|||
}
|
||||
|
||||
if err := s.identityState.WithAddrKR(s.addrID, func(_, addrKR *crypto.KeyRing) error {
|
||||
primaryKey, errKey := addrKR.FirstKey()
|
||||
if errKey != nil {
|
||||
return fmt.Errorf("failed to get primary key for import: %w", errKey)
|
||||
}
|
||||
|
||||
var messageID string
|
||||
p, err2 := parser.New(bytes.NewReader(literal))
|
||||
if err2 != nil {
|
||||
return fmt.Errorf("failed to parse literal: %w", err2)
|
||||
}
|
||||
if slices.Contains(labelIDs, proton.DraftsLabel) {
|
||||
msg, err := s.createDraftWithParser(ctx, p, addrKR, addr)
|
||||
msg, err := s.createDraftWithParser(ctx, p, primaryKey, addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create draft: %w", err)
|
||||
}
|
||||
|
@ -699,7 +704,7 @@ func (s *Connector) importMessage(
|
|||
}
|
||||
literal = buf.Bytes()
|
||||
}
|
||||
str, err := s.client.ImportMessages(ctx, addrKR, 1, 1, []proton.ImportReq{{
|
||||
str, err := s.client.ImportMessages(ctx, primaryKey, 1, 1, []proton.ImportReq{{
|
||||
Metadata: proton.ImportMetadata{
|
||||
AddressID: s.addrID,
|
||||
LabelIDs: labelIDs,
|
||||
|
@ -726,7 +731,7 @@ func (s *Connector) importMessage(
|
|||
return fmt.Errorf("failed to fetch message: %w", err)
|
||||
}
|
||||
|
||||
if literal, err = message.DecryptAndBuildRFC822(addrKR, full.Message, full.AttData, defaultMessageJobOpts()); err != nil {
|
||||
if literal, err = message.DecryptAndBuildRFC822(primaryKey, full.Message, full.AttData, defaultMessageJobOpts()); err != nil {
|
||||
return fmt.Errorf("failed to build message: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var logIMAP = logrus.WithField("pkg", "server/imap") //nolint:gochecknoglobals
|
||||
|
||||
type IMAPSettingsProvider interface {
|
||||
TLSConfig() *tls.Config
|
||||
LogClient() bool
|
||||
|
@ -79,7 +81,7 @@ func newIMAPServer(
|
|||
gluonCacheDir = ApplyGluonCachePathSuffix(gluonCacheDir)
|
||||
gluonConfigDir = ApplyGluonConfigPathSuffix(gluonConfigDir)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
logIMAP.WithFields(logrus.Fields{
|
||||
"gluonStore": gluonCacheDir,
|
||||
"gluonDB": gluonConfigDir,
|
||||
"version": version,
|
||||
|
@ -88,10 +90,9 @@ func newIMAPServer(
|
|||
}).Info("Creating IMAP server")
|
||||
|
||||
if logClient || logServer {
|
||||
log := logrus.WithField("protocol", "IMAP")
|
||||
log.Warning("================================================")
|
||||
log.Warning("THIS LOG WILL CONTAIN **DECRYPTED** MESSAGE DATA")
|
||||
log.Warning("================================================")
|
||||
logIMAP.Warning("================================================")
|
||||
logIMAP.Warning("THIS LOG WILL CONTAIN **DECRYPTED** MESSAGE DATA")
|
||||
logIMAP.Warning("================================================")
|
||||
}
|
||||
|
||||
var imapClientLog io.Writer
|
||||
|
@ -143,7 +144,7 @@ func newIMAPServer(
|
|||
|
||||
tasks.Once(func(ctx context.Context) {
|
||||
async.RangeContext(ctx, imapServer.GetErrorCh(), func(err error) {
|
||||
logrus.WithError(err).Error("IMAP server error")
|
||||
logIMAP.WithError(err).Error("IMAP server error")
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -176,7 +177,7 @@ func (*storeBuilder) Delete(path, userID string) error {
|
|||
}
|
||||
|
||||
func moveGluonCacheDir(settings IMAPSettingsProvider, oldGluonDir, newGluonDir string) error {
|
||||
logrus.Infof("gluon cache moving from %s to %s", oldGluonDir, newGluonDir)
|
||||
logIMAP.WithField("pkg", "service/imap").Infof("gluon cache moving from %s to %s", oldGluonDir, newGluonDir)
|
||||
oldCacheDir := ApplyGluonCachePathSuffix(oldGluonDir)
|
||||
if err := files.CopyDir(oldCacheDir, ApplyGluonCachePathSuffix(newGluonDir)); err != nil {
|
||||
return fmt.Errorf("failed to copy gluon dir: %w", err)
|
||||
|
@ -187,7 +188,7 @@ func moveGluonCacheDir(settings IMAPSettingsProvider, oldGluonDir, newGluonDir s
|
|||
}
|
||||
|
||||
if err := os.RemoveAll(oldCacheDir); err != nil {
|
||||
logrus.WithError(err).Error("failed to remove old gluon cache dir")
|
||||
logIMAP.WithError(err).Error("failed to remove old gluon cache dir")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -190,16 +190,16 @@ func (sm *Service) run(ctx context.Context, subscription events.Subscription) {
|
|||
case evt := <-eventSub.GetChannel():
|
||||
switch evt.(type) {
|
||||
case events.ConnStatusDown:
|
||||
logrus.Info("Server Manager, network down stopping listeners")
|
||||
sm.log.Info("Server Manager, network down stopping listeners")
|
||||
if err := sm.closeSMTPServer(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to close SMTP server")
|
||||
sm.log.WithError(err).Error("Failed to close SMTP server")
|
||||
}
|
||||
|
||||
if err := sm.stopIMAPListener(ctx); err != nil {
|
||||
logrus.WithError(err)
|
||||
sm.log.WithError(err)
|
||||
}
|
||||
case events.ConnStatusUp:
|
||||
logrus.Info("Server Manager, network up starting listeners")
|
||||
sm.log.Info("Server Manager, network up starting listeners")
|
||||
sm.handleLoadedUserCountChange(ctx)
|
||||
}
|
||||
|
||||
|
@ -241,12 +241,12 @@ func (sm *Service) run(ctx context.Context, subscription events.Subscription) {
|
|||
request.Reply(ctx, nil, err)
|
||||
|
||||
case *smRequestAddSMTPAccount:
|
||||
logrus.WithField("user", r.account.UserID()).Debug("Adding SMTP Account")
|
||||
sm.log.WithField("user", r.account.UserID()).Debug("Adding SMTP Account")
|
||||
sm.smtpAccounts.AddAccount(r.account)
|
||||
request.Reply(ctx, nil, nil)
|
||||
|
||||
case *smRequestRemoveSMTPAccount:
|
||||
logrus.WithField("user", r.account.UserID()).Debug("Removing SMTP Account")
|
||||
sm.log.WithField("user", r.account.UserID()).Debug("Removing SMTP Account")
|
||||
sm.smtpAccounts.RemoveAccount(r.account)
|
||||
request.Reply(ctx, nil, nil)
|
||||
}
|
||||
|
@ -255,29 +255,29 @@ func (sm *Service) run(ctx context.Context, subscription events.Subscription) {
|
|||
}
|
||||
|
||||
func (sm *Service) handleLoadedUserCountChange(ctx context.Context) {
|
||||
logrus.Infof("Validating Listener State %v", sm.loadedUserCount)
|
||||
sm.log.Infof("Validating Listener State %v", sm.loadedUserCount)
|
||||
if sm.shouldStartServers() {
|
||||
if sm.imapListener == nil {
|
||||
if err := sm.serveIMAP(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to start IMAP server")
|
||||
sm.log.WithError(err).Error("Failed to start IMAP server")
|
||||
}
|
||||
}
|
||||
|
||||
if sm.smtpListener == nil {
|
||||
if err := sm.restartSMTP(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to start SMTP server")
|
||||
sm.log.WithError(err).Error("Failed to start SMTP server")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if sm.imapListener != nil {
|
||||
if err := sm.stopIMAPListener(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to stop IMAP server")
|
||||
sm.log.WithError(err).Error("Failed to stop IMAP server")
|
||||
}
|
||||
}
|
||||
|
||||
if sm.smtpListener != nil {
|
||||
if err := sm.closeSMTPServer(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to stop SMTP server")
|
||||
sm.log.WithError(err).Error("Failed to stop SMTP server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,12 +286,12 @@ func (sm *Service) handleLoadedUserCountChange(ctx context.Context) {
|
|||
func (sm *Service) handleClose(ctx context.Context) {
|
||||
// Close the IMAP server.
|
||||
if err := sm.closeIMAPServer(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to close IMAP server")
|
||||
sm.log.WithError(err).Error("Failed to close IMAP server")
|
||||
}
|
||||
|
||||
// Close the SMTP server.
|
||||
if err := sm.closeSMTPServer(ctx); err != nil {
|
||||
logrus.WithError(err).Error("Failed to close SMTP server")
|
||||
sm.log.WithError(err).Error("Failed to close SMTP server")
|
||||
}
|
||||
|
||||
// Cancel and wait needs to be called here since the SMTP server does not have a way to exit
|
||||
|
@ -325,7 +325,7 @@ func (sm *Service) handleAddIMAPUserImpl(ctx context.Context,
|
|||
return fmt.Errorf("no imap server instance running")
|
||||
}
|
||||
|
||||
log := logrus.WithFields(logrus.Fields{
|
||||
log := sm.log.WithFields(logrus.Fields{
|
||||
"addrID": addrID,
|
||||
})
|
||||
log.Info("Adding user to imap server")
|
||||
|
@ -341,7 +341,7 @@ func (sm *Service) handleAddIMAPUserImpl(ctx context.Context,
|
|||
|
||||
if isNew {
|
||||
// If the DB was newly created, clear the sync status; gluon's DB was not found.
|
||||
logrus.Warn("IMAP user DB was newly created, clearing sync status")
|
||||
sm.log.Warn("IMAP user DB was newly created, clearing sync status")
|
||||
|
||||
// Remove the user from IMAP so we can clear the sync status.
|
||||
if err := sm.imapServer.RemoveUser(ctx, gluonID, false); err != nil {
|
||||
|
@ -415,7 +415,7 @@ func (sm *Service) handleRemoveIMAPUser(ctx context.Context, withData bool, idPr
|
|||
return fmt.Errorf("no imap server instance running")
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
sm.log.WithFields(logrus.Fields{
|
||||
"withData": withData,
|
||||
"addresses": addrIDs,
|
||||
}).Debug("Removing IMAP user")
|
||||
|
@ -423,7 +423,7 @@ func (sm *Service) handleRemoveIMAPUser(ctx context.Context, withData bool, idPr
|
|||
for _, addrID := range addrIDs {
|
||||
gluonID, ok := idProvider.GetGluonID(addrID)
|
||||
if !ok {
|
||||
logrus.Warnf("Could not find Gluon ID for addrID %v", addrID)
|
||||
sm.log.Warnf("Could not find Gluon ID for addrID %v", addrID)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -480,7 +480,7 @@ func (sm *Service) closeSMTPServer(ctx context.Context) error {
|
|||
// even after the server has been closed. So we close the listener ourselves to unblock it.
|
||||
|
||||
if sm.smtpListener != nil {
|
||||
logrus.Info("Closing SMTP Listener")
|
||||
sm.log.Info("Closing SMTP Listener")
|
||||
if err := sm.smtpListener.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close SMTP listener: %w", err)
|
||||
}
|
||||
|
@ -489,9 +489,9 @@ func (sm *Service) closeSMTPServer(ctx context.Context) error {
|
|||
}
|
||||
|
||||
if sm.smtpServer != nil {
|
||||
logrus.Info("Closing SMTP server")
|
||||
sm.log.Info("Closing SMTP server")
|
||||
if err := sm.smtpServer.Close(); err != nil {
|
||||
logrus.WithError(err).Debug("Failed to close SMTP server (expected -- we close the listener ourselves)")
|
||||
sm.log.WithError(err).Debug("Failed to close SMTP server (expected -- we close the listener ourselves)")
|
||||
}
|
||||
|
||||
sm.smtpServer = nil
|
||||
|
@ -504,7 +504,7 @@ func (sm *Service) closeSMTPServer(ctx context.Context) error {
|
|||
|
||||
func (sm *Service) closeIMAPServer(ctx context.Context) error {
|
||||
if sm.imapListener != nil {
|
||||
logrus.Info("Closing IMAP Listener")
|
||||
sm.log.Info("Closing IMAP Listener")
|
||||
|
||||
if err := sm.imapListener.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close IMAP listener: %w", err)
|
||||
|
@ -516,7 +516,7 @@ func (sm *Service) closeIMAPServer(ctx context.Context) error {
|
|||
}
|
||||
|
||||
if sm.imapServer != nil {
|
||||
logrus.Info("Closing IMAP server")
|
||||
sm.log.Info("Closing IMAP server")
|
||||
if err := sm.imapServer.Close(ctx); err != nil {
|
||||
return fmt.Errorf("failed to close IMAP server: %w", err)
|
||||
}
|
||||
|
@ -530,7 +530,7 @@ func (sm *Service) closeIMAPServer(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (sm *Service) restartIMAP(ctx context.Context) error {
|
||||
logrus.Info("Restarting IMAP server")
|
||||
sm.log.Info("Restarting IMAP server")
|
||||
|
||||
if sm.imapListener != nil {
|
||||
if err := sm.imapListener.Close(); err != nil {
|
||||
|
@ -550,7 +550,7 @@ func (sm *Service) restartIMAP(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (sm *Service) restartSMTP(ctx context.Context) error {
|
||||
logrus.Info("Restarting SMTP server")
|
||||
sm.log.Info("Restarting SMTP server")
|
||||
|
||||
if err := sm.closeSMTPServer(ctx); err != nil {
|
||||
return fmt.Errorf("failed to close SMTP: %w", err)
|
||||
|
@ -569,7 +569,7 @@ func (sm *Service) restartSMTP(ctx context.Context) error {
|
|||
|
||||
func (sm *Service) serveSMTP(ctx context.Context) error {
|
||||
port, err := func() (int, error) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
sm.log.WithFields(logrus.Fields{
|
||||
"port": sm.smtpSettings.Port(),
|
||||
"ssl": sm.smtpSettings.UseSSL(),
|
||||
}).Info("Starting SMTP server")
|
||||
|
@ -583,7 +583,7 @@ func (sm *Service) serveSMTP(ctx context.Context) error {
|
|||
|
||||
sm.tasks.Once(func(context.Context) {
|
||||
if err := sm.smtpServer.Serve(smtpListener); err != nil {
|
||||
logrus.WithError(err).Info("SMTP server stopped")
|
||||
sm.log.WithError(err).Info("SMTP server stopped")
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -615,7 +615,7 @@ func (sm *Service) serveIMAP(ctx context.Context) error {
|
|||
return 0, fmt.Errorf("no IMAP server instance running")
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
sm.log.WithFields(logrus.Fields{
|
||||
"port": sm.imapSettings.Port(),
|
||||
"ssl": sm.imapSettings.UseSSL(),
|
||||
}).Info("Starting IMAP server")
|
||||
|
@ -654,7 +654,7 @@ func (sm *Service) serveIMAP(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (sm *Service) stopIMAPListener(ctx context.Context) error {
|
||||
logrus.Info("Stopping IMAP listener")
|
||||
sm.log.Info("Stopping IMAP listener")
|
||||
if sm.imapListener != nil {
|
||||
if err := sm.imapListener.Close(); err != nil {
|
||||
return err
|
||||
|
@ -682,7 +682,7 @@ func (sm *Service) handleSetGluonDir(ctx context.Context, newGluonDir string) er
|
|||
sm.loadedUserCount = 0
|
||||
|
||||
if err := moveGluonCacheDir(sm.imapSettings, currentGluonDir, newGluonDir); err != nil {
|
||||
logrus.WithError(err).Error("failed to move GluonCacheDir")
|
||||
sm.log.WithError(err).Error("failed to move GluonCacheDir")
|
||||
|
||||
if err := sm.imapSettings.SetCacheDirectory(currentGluonDir); err != nil {
|
||||
return fmt.Errorf("failed to revert GluonCacheDir: %w", err)
|
||||
|
|
|
@ -29,6 +29,8 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var logSMTP = logrus.WithField("pkg", "server/smtp") //nolint:gochecknoglobals
|
||||
|
||||
type SMTPSettingsProvider interface {
|
||||
TLSConfig() *tls.Config
|
||||
Log() bool
|
||||
|
@ -39,7 +41,7 @@ type SMTPSettingsProvider interface {
|
|||
}
|
||||
|
||||
func newSMTPServer(accounts *smtpservice.Accounts, settings SMTPSettingsProvider) *smtp.Server {
|
||||
logrus.WithField("logSMTP", settings.Log()).Info("Creating SMTP server")
|
||||
logSMTP.WithField("logSMTP", settings.Log()).Info("Creating SMTP server")
|
||||
|
||||
smtpServer := smtp.NewServer(smtpservice.NewBackend(accounts, settings.Identifier()))
|
||||
|
||||
|
@ -57,10 +59,9 @@ func newSMTPServer(accounts *smtpservice.Accounts, settings SMTPSettingsProvider
|
|||
})
|
||||
|
||||
if settings.Log() {
|
||||
log := logrus.WithField("protocol", "SMTP")
|
||||
log.Warning("================================================")
|
||||
log.Warning("THIS LOG WILL CONTAIN **DECRYPTED** MESSAGE DATA")
|
||||
log.Warning("================================================")
|
||||
logSMTP.Warning("================================================")
|
||||
logSMTP.Warning("THIS LOG WILL CONTAIN **DECRYPTED** MESSAGE DATA")
|
||||
logSMTP.Warning("================================================")
|
||||
|
||||
smtpServer.Debug = logging.NewSMTPDebugLogger()
|
||||
}
|
||||
|
|
|
@ -294,7 +294,7 @@ func newImpl(
|
|||
|
||||
// Log all requests made by the user.
|
||||
user.client.AddPostRequestHook(func(_ *resty.Client, r *resty.Response) error {
|
||||
user.log.Infof("%v: %v %v", r.Status(), r.Request.Method, r.Request.URL)
|
||||
user.log.WithField("pkg", "gpa/client").Infof("%v: %v %v", r.Status(), r.Request.Method, r.Request.URL)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
|
|
@ -205,6 +205,10 @@ func convertForeignEncodings(p *parser.Parser) error {
|
|||
return p.ConvertMetaCharset()
|
||||
}).
|
||||
RegisterContentTypeHandler("text/.*", func(p *parser.Part) error {
|
||||
if p.IsAttachment() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.ConvertToUTF8()
|
||||
}).
|
||||
Walk()
|
||||
|
@ -548,6 +552,11 @@ func parseAttachment(h message.Header, body []byte) (Attachment, error) {
|
|||
att.Header = mimeHeader
|
||||
mimeType, mimeTypeParams, err := pmmime.ParseMediaType(h.Get("Content-Type"))
|
||||
|
||||
if err == pmmime.EmptyContentTypeErr {
|
||||
mimeType = "text/plain"
|
||||
err = nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Attachment{}, err
|
||||
}
|
||||
|
|
|
@ -837,6 +837,17 @@ func TestPatchNewLineWithHtmlBreaks(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseCp1250Attachment(t *testing.T) {
|
||||
r := require.New(t)
|
||||
f := getFileReader("text_plain_xml_attachment_cp1250.eml")
|
||||
|
||||
m, err := Parse(f)
|
||||
r.NoError(err)
|
||||
|
||||
r.Len(m.Attachments, 1)
|
||||
r.Equal("text/xml; charset=windows-1250; name=\"cp1250.xml\"", m.Attachments[0].Header.Get("Content-Type"))
|
||||
}
|
||||
|
||||
func getFileReader(filename string) io.Reader {
|
||||
f, err := os.Open(filepath.Join("testdata", filename))
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
From: Sender <sender@pm.me>
|
||||
To: Receiver <receiver@pm.me>
|
||||
Content-Type: multipart/mixed; boundary="------------iOaeSk0jVHjMucH3kQFwS1LB"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------iOaeSk0jVHjMucH3kQFwS1LB
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
created by me 😎
|
||||
|
||||
--------------iOaeSk0jVHjMucH3kQFwS1LB
|
||||
Content-Type: text/xml; charset=windows-1250; name="cp1250.xml"
|
||||
Content-Disposition: attachment; filename="cp1250.xml"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
<?xml version="1.0" encoding="WINDOWS-1250" ?>
|
||||
<pvpoj xmlns="http://schemas.cssz.cz/POJ/PVPOJ2023">
|
||||
<ulice>Horní námìstí</ulice>
|
||||
<prijmeni>ŠCheckCharsá</prijmeni>
|
||||
</pvpoj>
|
||||
--------------iOaeSk0jVHjMucH3kQFwS1LB--
|
|
@ -35,6 +35,8 @@ import (
|
|||
"golang.org/x/text/encoding/htmlindex"
|
||||
)
|
||||
|
||||
var EmptyContentTypeErr = errors.New("empty content type")
|
||||
|
||||
func init() {
|
||||
rfc822.ParseMediaType = ParseMediaType
|
||||
proton.CharsetReader = CharsetReader
|
||||
|
@ -257,7 +259,7 @@ func DecodeCharset(original []byte, contentType string) ([]byte, error) {
|
|||
// ParseMediaType from MIME doesn't support RFC2231 for non asci / utf8 encodings so we have to pre-parse it.
|
||||
func ParseMediaType(v string) (string, map[string]string, error) {
|
||||
if v == "" {
|
||||
return "", nil, errors.New("empty media type")
|
||||
return "", nil, EmptyContentTypeErr
|
||||
}
|
||||
decoded, err := DecodeHeader(v)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
# Bridge Integration tests
|
||||
|
||||
Tests defined in this folder are using `github.com/cucumber/godog` library to
|
||||
define scenarios.
|
||||
|
||||
The scenarios are defined in `./features/` folder.
|
||||
The step definition can be found in `./steps_test.go`.
|
||||
|
||||
|
||||
# How to run
|
||||
All features are run as sub-test of `TestFeatures` in `./bdd_test.go`.
|
||||
The most simple way to execute is `make test-integration` from project source directory.
|
||||
|
||||
|
||||
There are several environment variables which can be used to control the tests:
|
||||
|
||||
|
||||
* `FEATURES` sets the path to folder / file / line in file to select which
|
||||
scenarios to run.
|
||||
|
||||
FEATURES=${PWD}/tests/features/user/addressmode.feature:162
|
||||
|
||||
* `FEATURE_TEST_LOG_LEVEL` the logrus level for tests (affects also testing
|
||||
bridge instance)
|
||||
|
||||
FEATURE_TEST_LOG_LEVEL=trace
|
||||
|
||||
* `BRIDGE_API_DEBUG` when enabled
|
||||
[GPA](https://github.com/ProtonMail/go-proton-api/)
|
||||
client used in testing bridge instance will log http communication and logrus
|
||||
is automatically set to `trace`
|
||||
|
||||
BRIDGE_API_DEBUG=1
|
||||
|
||||
* `GO_PROTON_API_SERVER_LOGGER_ENABLED` GPA mock server will print log line per
|
||||
each request to stdout (not logrus)
|
||||
|
||||
GO_PROTON_API_SERVER_LOGGER_ENABLED=1
|
||||
|
||||
* `FEATURE_API_DEBUG` when enabled GPA client for preparation of test
|
||||
condiditions (see `./ctx_helper_test.go`) will dump http communication to
|
||||
stdoout.
|
||||
|
||||
FEATURE_API_DEBUG=1
|
||||
|
||||
* `FEATURE_TEST_LOG_IMAP` when enabled
|
||||
bridge will dump all (client and server) IMAP communication to logs
|
||||
and logrus is automatically set to `trace`
|
||||
|
||||
FEATURE_TEST_LOG_IMAP=1
|
||||
|
||||
* `GLUON_LOG_IMAP_LINE_LIMIT` controls maximal number of lines (by default 1)
|
||||
which are printed into imap trace log (logrus).
|
||||
Needs `FEATURE_TEST_LOG_IMAP` enabled to take effect.
|
||||
|
||||
GLUON_LOG_IMAP_LINE_LIMIT=1048576
|
||||
|
||||
|
||||
* `FEATURE_TEST_LOG_SMTP` when enabled
|
||||
bridge will dump all SMTP communication to logs
|
||||
and logrus is automatically set to `trace`
|
||||
|
||||
FEATURE_TEST_LOG_SMTP=1
|
||||
|
||||
|
||||
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -26,6 +25,7 @@ import (
|
|||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/ProtonMail/go-proton-api"
|
||||
"github.com/ProtonMail/go-proton-api/server"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/dialer"
|
||||
)
|
||||
|
||||
type API interface {
|
||||
|
@ -73,13 +73,14 @@ func newLiveAPI(hostURL string) API {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
tr := proton.InsecureTransport()
|
||||
dialer.SetBasicTransportTimeouts(tr)
|
||||
tr.Proxy = http.ProxyFromEnvironment
|
||||
|
||||
return &liveAPI{
|
||||
Server: server.New(
|
||||
server.WithProxyOrigin(hostURL),
|
||||
server.WithProxyTransport(&http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}),
|
||||
server.WithProxyTransport(tr),
|
||||
),
|
||||
domain: url.Hostname(),
|
||||
}
|
||||
|
|
|
@ -132,9 +132,19 @@ func getFeatureTags() string {
|
|||
tags = ""
|
||||
case "smoke": // Currently this is just a placeholder, as there are no scenarios tagged with @smoke
|
||||
tags = "@smoke"
|
||||
case "black": // Currently this is just a placeholder, as there are no scenarios tagged with @smoke
|
||||
tags = "~@skip-black"
|
||||
default:
|
||||
tags = "~@regression && ~@smoke" // To exclude more add `&& ~@tag`
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
func isBlack() bool {
|
||||
if len(os.Args) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return os.Args[len(os.Args)-1] == "black"
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ func newTestBugReport(br *bridge.Bridge) *testBugReport {
|
|||
Title: "title",
|
||||
Description: "description",
|
||||
Username: "username",
|
||||
Email: "email",
|
||||
Email: "email@pm.me",
|
||||
EmailClient: "client",
|
||||
IncludeLogs: false,
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -34,6 +35,7 @@ import (
|
|||
"github.com/ProtonMail/proton-bridge/v3/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/cookies"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/dialer"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/events"
|
||||
frontend "github.com/ProtonMail/proton-bridge/v3/internal/frontend/grpc"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/service"
|
||||
|
@ -146,6 +148,16 @@ func (t *testCtx) initBridge() (<-chan events.Event, error) {
|
|||
logrus.SetLevel(logrus.TraceLevel)
|
||||
}
|
||||
|
||||
rt := t.netCtl.NewRoundTripper(&tls.Config{InsecureSkipVerify: true})
|
||||
if isBlack() {
|
||||
// GODT-1602 make sure we don't time out test server
|
||||
t, ok := rt.(*http.Transport)
|
||||
if !ok {
|
||||
panic("expecting http.Transport")
|
||||
}
|
||||
dialer.SetBasicTransportTimeouts(t)
|
||||
}
|
||||
|
||||
// Create the bridge.
|
||||
bridge, eventCh, err := bridge.New(
|
||||
// App stuff
|
||||
|
@ -161,7 +173,7 @@ func (t *testCtx) initBridge() (<-chan events.Event, error) {
|
|||
persister,
|
||||
useragent.New(),
|
||||
t.mocks.TLSReporter,
|
||||
t.netCtl.NewRoundTripper(&tls.Config{InsecureSkipVerify: true}),
|
||||
rt,
|
||||
t.mocks.ProxyCtl,
|
||||
t.mocks.CrashHandler,
|
||||
t.reporter,
|
||||
|
|
|
@ -26,14 +26,20 @@ import (
|
|||
"github.com/ProtonMail/gluon/async"
|
||||
"github.com/ProtonMail/go-proton-api"
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/dialer"
|
||||
"github.com/bradenaw/juniper/stream"
|
||||
)
|
||||
|
||||
// withProton executes the given function with a proton manager configured to use the test API.
|
||||
func (t *testCtx) withProton(fn func(*proton.Manager) error) error {
|
||||
tr := proton.InsecureTransport()
|
||||
if isBlack() {
|
||||
dialer.SetBasicTransportTimeouts(tr)
|
||||
}
|
||||
|
||||
m := proton.New(
|
||||
proton.WithHostURL(t.api.GetHostURL()),
|
||||
proton.WithTransport(proton.InsecureTransport()),
|
||||
proton.WithTransport(tr),
|
||||
proton.WithAppVersion(t.api.GetAppVersion()),
|
||||
proton.WithDebug(os.Getenv("FEATURE_API_DEBUG") != ""),
|
||||
)
|
||||
|
@ -88,6 +94,15 @@ func (t *testCtx) runQuarkCmd(ctx context.Context, command string, args ...strin
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (t *testCtx) decryptID(id string) ([]byte, error) {
|
||||
return t.runQuarkCmd(context.Background(),
|
||||
"encryption:id",
|
||||
"--decrypt",
|
||||
"--",
|
||||
id,
|
||||
)
|
||||
}
|
||||
|
||||
func (t *testCtx) withAddrKR(
|
||||
ctx context.Context,
|
||||
c *proton.Client,
|
||||
|
|
|
@ -91,12 +91,6 @@ func (user *testUser) addAddress(addrID, email string) {
|
|||
user.addresses = append(user.addresses, newTestAddr(addrID, email))
|
||||
}
|
||||
|
||||
func (user *testUser) remAddress(addrID string) {
|
||||
user.addresses = xslices.Filter(user.addresses, func(addr *testAddr) bool {
|
||||
return addr.addrID != addrID
|
||||
})
|
||||
}
|
||||
|
||||
func (user *testUser) getUserPass() string {
|
||||
return user.userPass
|
||||
}
|
||||
|
@ -229,7 +223,13 @@ func (t *testCtx) replace(value string) string {
|
|||
|
||||
// Create a new user if it doesn't exist yet.
|
||||
if _, ok := t.userUUIDByName[name]; !ok {
|
||||
t.userUUIDByName[name] = uuid.NewString()
|
||||
val := uuid.NewString()
|
||||
|
||||
if name != strings.ToLower(name) {
|
||||
val = "Mixed-Caps-" + val
|
||||
}
|
||||
|
||||
t.userUUIDByName[name] = val
|
||||
}
|
||||
|
||||
return t.userUUIDByName[name]
|
||||
|
@ -290,6 +290,18 @@ func (t *testCtx) getUserByID(userID string) *testUser {
|
|||
return t.userByID[userID]
|
||||
}
|
||||
|
||||
func (t *testCtx) getUserByAddress(email string) *testUser {
|
||||
for _, user := range t.userByID {
|
||||
for _, addr := range user.addresses {
|
||||
if addr.email == email {
|
||||
return user
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unknown email %q", email))
|
||||
}
|
||||
|
||||
func (t *testCtx) getMBoxID(userID string, name string) string {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
|
|
@ -33,7 +33,6 @@ Feature: Configuration Status Telemetry
|
|||
And config status event "bridge_config_success" is eventually send 1 time
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Config Status Success send only once
|
||||
Then bridge telemetry feature is enabled
|
||||
When the user logs in with username "[user:user]" and password "password"
|
||||
|
@ -77,4 +76,4 @@ Feature: Configuration Status Telemetry
|
|||
And bridge stops
|
||||
And force config status progress to be sent for user"[user:user]"
|
||||
And bridge starts
|
||||
Then config status event "bridge_config_progress" is eventually send 1 time
|
||||
Then config status event "bridge_config_progress" is eventually send 1 time
|
||||
|
|
|
@ -21,9 +21,9 @@ Feature: Bridge checks for updates
|
|||
Then bridge sends a manual update event for version "2.4.0"
|
||||
|
||||
Scenario: Update is required to continue using bridge
|
||||
Given there exists an account with username "user" and password "password"
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And bridge is version "2.3.0" and the latest available version is "2.3.0" reachable from "2.3.0"
|
||||
And the API requires bridge version at least "2.4.0"
|
||||
When bridge starts
|
||||
And the user logs in with username "user" and password "password"
|
||||
Then bridge sends a forced update event
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
Then bridge sends a forced update event
|
||||
|
|
|
@ -3,7 +3,6 @@ Feature: A user can authenticate an IMAP client
|
|||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And there exists an account with username "[user:user2]" and password "password2"
|
||||
And the account "[user:user]" has additional address "[alias:alias]@[domain]"
|
||||
And the account "[user:user2]" has additional disabled address "[alias:alias2]@[domain]"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
|
@ -21,8 +20,12 @@ Feature: A user can authenticate an IMAP client
|
|||
Scenario: IMAP client can authenticate successfully with secondary address
|
||||
Given user "[user:user]" connects and authenticates IMAP client "1" with address "[alias:alias]@[domain]"
|
||||
|
||||
# Need to find way to setup disabled address on black
|
||||
@skip-black
|
||||
Scenario: IMAP client can not authenticate successfully with disable address
|
||||
Given user "[user:user2]" connects and can not authenticate IMAP client "1" with address "[alias:alias2]@[domain]"
|
||||
Given the account "[user:user2]" has additional disabled address "[alias:disabled]@[domain]"
|
||||
And it succeeds
|
||||
Then user "[user:user2]" connects and can not authenticate IMAP client "1" with address "[alias:disabled]@[domain]"
|
||||
|
||||
Scenario: IMAP client can authenticate successfully
|
||||
When user "[user:user]" connects IMAP client "1"
|
||||
|
|
|
@ -40,6 +40,8 @@ Feature: IMAP list mailboxes
|
|||
Then IMAP client "2" counts 20 mailboxes under "Folders"
|
||||
And IMAP client "2" counts 60 mailboxes under "Labels"
|
||||
|
||||
# need to implement _schedule message_ test step for black
|
||||
@skip-black
|
||||
Scenario: List with scheduled mail
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Scheduled":
|
||||
|
|
|
@ -12,6 +12,8 @@ Feature: IMAP get mailbox info
|
|||
And user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then it succeeds
|
||||
|
||||
# with black subfolder is not renamed (maybe missing event?)
|
||||
@skip-black
|
||||
Scenario: Rename folder with subfolders
|
||||
When IMAP client "1" renames "Folders/f1" to "Folders/f3"
|
||||
And it succeeds
|
||||
|
|
|
@ -6,7 +6,6 @@ Feature: IMAP remove messages from mailbox
|
|||
| mbox | folder |
|
||||
| label | label |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has 10 messages in "Folders/mbox"
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has 1 messages in "Scheduled"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
|
@ -48,10 +47,3 @@ Feature: IMAP remove messages from mailbox
|
|||
And it succeeds
|
||||
And IMAP client "1" expunges
|
||||
Then it fails
|
||||
|
||||
Scenario: Not possible to delete from Scheduled and expunge does nothing
|
||||
When IMAP client "1" selects "Scheduled"
|
||||
And IMAP client "1" marks message 1 as deleted
|
||||
Then it succeeds
|
||||
And IMAP client "1" expunges
|
||||
Then it fails
|
||||
|
|
|
@ -42,6 +42,8 @@ Feature: IMAP Draft messages
|
|||
And IMAP client "1" eventually sees 1 messages in "Drafts"
|
||||
And IMAP client "1" does not see header "Reply-To" in message with subject "Basic Draft" in "Drafts"
|
||||
|
||||
# The draft event is received from black but it's not processed to IMAP
|
||||
@skip-black
|
||||
Scenario: Draft edited remotely
|
||||
When the following fields were changed in draft 1 for address "[user:user]@[domain]" of account "[user:user]":
|
||||
| to | subject | body |
|
||||
|
@ -52,6 +54,8 @@ Feature: IMAP Draft messages
|
|||
And IMAP client "1" eventually sees 1 messages in "Drafts"
|
||||
And IMAP client "1" does not see header "Reply-To" in message with subject "Basic Draft" in "Drafts"
|
||||
|
||||
# The draft event is received from black but it's not processed to IMAP
|
||||
@skip-black
|
||||
@regression
|
||||
Scenario: Draft edited remotely and sent from client
|
||||
When IMAP client "1" selects "Drafts"
|
||||
|
@ -103,6 +107,8 @@ Feature: IMAP Draft messages
|
|||
And IMAP client "1" eventually sees 0 messages in "Drafts"
|
||||
|
||||
|
||||
# The draft event is received from black but it's not processed to IMAP
|
||||
@skip-black
|
||||
Scenario: Draft moved to trash remotely
|
||||
When draft 1 for address "[user:user]@[domain]" of account "[user:user]" was moved to trash
|
||||
Then IMAP client "1" eventually sees the following messages in "Trash":
|
||||
|
|
|
@ -15,6 +15,8 @@ Feature: IMAP Fetch
|
|||
And user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then it succeeds
|
||||
|
||||
# The date returned from black is server time.. Black is probably correct we need to fix GPA server
|
||||
@skip-black
|
||||
Scenario: Fetch very old message
|
||||
Given IMAP client "1" eventually sees the following messages in "INBOX":
|
||||
| from | to | subject | date |
|
||||
|
@ -22,6 +24,8 @@ Feature: IMAP Fetch
|
|||
Then IMAP client "1" sees header "X-Original-Date: Sun, 13 Jul 1969 00:00:00 +0000" in message with subject "foo" in "INBOX"
|
||||
|
||||
|
||||
# The date returned from black is server time.. Black is probably correct we need to fix GPA server
|
||||
@skip-black
|
||||
Scenario: Fetch from deleted cache
|
||||
When the user deletes the gluon cache
|
||||
Then IMAP client "1" eventually sees the following messages in "INBOX":
|
||||
|
|
|
@ -274,6 +274,8 @@ Feature: IMAP import messages
|
|||
| Archive |
|
||||
| Sent |
|
||||
|
||||
# The date returned from black is server time.. Black is probably correct we need to fix GPA server
|
||||
@skip-black
|
||||
Scenario: Import message without sender to Drafts
|
||||
When IMAP client "1" appends the following message to "Drafts":
|
||||
"""
|
||||
|
@ -648,4 +650,4 @@ Feature: IMAP import messages
|
|||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
"""
|
||||
|
|
|
@ -16,9 +16,6 @@ Feature: IMAP move messages
|
|||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Sent":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | bax | false |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Scheduled":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | sch | false |
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
|
@ -124,15 +121,7 @@ Feature: IMAP move messages
|
|||
| jane.doe@mail.com | name@[domain] | bar | true |
|
||||
| john.doe@mail.com | [user:user]@[domain] | baz | false |
|
||||
| john.doe@mail.com | [user:user]@[domain] | bax | false |
|
||||
| john.doe@mail.com | [user:user]@[domain] | sch | false |
|
||||
|
||||
Scenario: Move message from Scheduled is not possible
|
||||
Given test skips reporter checks
|
||||
When IMAP client "1" moves the message with subject "sch" from "Scheduled" to "Inbox"
|
||||
Then it fails
|
||||
And IMAP client "1" eventually sees the following messages in "Scheduled":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | sch | false |
|
||||
|
||||
Scenario: Move message from Inbox to Sent is not possible
|
||||
Given test skips reporter checks
|
||||
|
|
|
@ -60,9 +60,49 @@ Feature: IMAP move messages by append and delete (without MOVE support, e.g., Ou
|
|||
| INBOX | Folders/mbox | DELETE APPEND EXPUNGE |
|
||||
| INBOX | Spam | DELETE APPEND EXPUNGE |
|
||||
| INBOX | Trash | DELETE APPEND EXPUNGE |
|
||||
| Trash | INBOX | DELETE EXPUNGE APPEND |
|
||||
| Spam | INBOX | DELETE EXPUNGE APPEND |
|
||||
| INBOX | Archive | DELETE EXPUNGE APPEND |
|
||||
| INBOX | Folders/mbox | DELETE EXPUNGE APPEND |
|
||||
| INBOX | Spam | DELETE EXPUNGE APPEND |
|
||||
| INBOX | Trash | DELETE EXPUNGE APPEND |
|
||||
|
||||
# black cannot pass this test, test timimng probably needs to be different. Once fixed it can be merged again
|
||||
@skip-black
|
||||
Scenario Outline: Move message from <srcMailbox> to <dstMailbox> by <order>, second batch
|
||||
When IMAP client "source" appends the following message to "<srcMailbox>":
|
||||
"""
|
||||
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
|
||||
From: sndr1@[domain]
|
||||
Date: 01 Jan 1980 00:00:00 +0000
|
||||
To: rcvr1@[domain]
|
||||
Subject: subj1
|
||||
|
||||
body1
|
||||
"""
|
||||
Then it succeeds
|
||||
When IMAP client "source" appends the following message to "<srcMailbox>":
|
||||
"""
|
||||
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
|
||||
From: sndr2@[domain]
|
||||
Date: 01 Jan 1980 00:00:00 +0000
|
||||
To: rcvr2@[domain]
|
||||
Subject: subj2
|
||||
|
||||
body2
|
||||
"""
|
||||
Then it succeeds
|
||||
And IMAP client "source" selects "<srcMailbox>"
|
||||
And IMAP client "target" selects "<dstMailbox>"
|
||||
When IMAP clients "source" and "target" move message with subject "subj2" of "[user:user]" to "<dstMailbox>" by <order>
|
||||
And IMAP client "source" eventually sees 1 messages in "<srcMailbox>"
|
||||
And IMAP client "source" eventually sees the following messages in "<srcMailbox>":
|
||||
| from | to | subject |
|
||||
| sndr1@[domain] | rcvr1@[domain] | subj1 |
|
||||
And IMAP client "target" eventually sees 1 messages in "<dstMailbox>"
|
||||
And IMAP client "target" eventually sees the following messages in "<dstMailbox>":
|
||||
| from | to | subject |
|
||||
| sndr2@[domain] | rcvr2@[domain] | subj2 |
|
||||
Examples:
|
||||
| srcMailbox | dstMailbox | order |
|
||||
| Trash | INBOX | DELETE EXPUNGE APPEND |
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# need to implement _schedule message_ test step for black
|
||||
@skip-black
|
||||
Feature: IMAP interaction with scheduled
|
||||
|
||||
Scenario: Not possible to delete from Scheduled and expunge does nothing
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And the account "[user:user]" has the following custom mailboxes:
|
||||
| name | type |
|
||||
| mbox | folder |
|
||||
| label | label |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has 10 messages in "Folders/mbox"
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has 1 messages in "Scheduled"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
And user "[user:user]" finishes syncing
|
||||
And user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then it succeeds
|
||||
When IMAP client "1" selects "Scheduled"
|
||||
And IMAP client "1" marks message 1 as deleted
|
||||
Then it succeeds
|
||||
And IMAP client "1" expunges
|
||||
Then it fails
|
||||
|
||||
Scenario: Move message from Scheduled is not possible
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And the account "[user:user]" has the following custom mailboxes:
|
||||
| name | type |
|
||||
| mbox | folder |
|
||||
| label | label |
|
||||
| label2 | label |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Inbox":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | foo | false |
|
||||
| jane.doe@mail.com | name@[domain] | bar | true |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Labels/label2":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | baz | false |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Sent":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | bax | false |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Scheduled":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | sch | false |
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
And user "[user:user]" finishes syncing
|
||||
And user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then it succeeds
|
||||
Given test skips reporter checks
|
||||
When IMAP client "1" moves the message with subject "sch" from "Scheduled" to "Inbox"
|
||||
Then it fails
|
||||
And IMAP client "1" eventually sees the following messages in "Scheduled":
|
||||
| from | to | subject | unread |
|
||||
| john.doe@mail.com | [user:user]@[domain] | sch | false |
|
|
@ -6,7 +6,7 @@ Feature: IMAP change state of message in mailbox
|
|||
| one | folder |
|
||||
| two | folder |
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has 5 messages in "Folders/one"
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has 150 messages in "Folders/two"
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has 5 messages in "Folders/two"
|
||||
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Inbox":
|
||||
| from | to | subject | unread |
|
||||
| a@example.com | b@example.com | one | true |
|
||||
|
|
|
@ -2,15 +2,11 @@ Feature: A user can authenticate an SMTP client
|
|||
Background:
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And there exists an account with username "[user:user2]" and password "password2"
|
||||
And there exists a disabled account with username "[user:user3]" and password "password3"
|
||||
And the account "[user:user]" has additional address "[alias:alias]@[domain]"
|
||||
And the account "[user:user2]" has additional disabled address "[alias:alias2]@[domain]"
|
||||
And the account "[user:user3]" has additional address "[alias:alias3]@[domain]"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
And the user logs in with username "[user:user2]" and password "password2"
|
||||
And the user logs in with username "[user:user3]" and password "password3"
|
||||
Then it succeeds
|
||||
|
||||
Scenario: SMTP client can authenticate successfully
|
||||
|
@ -40,8 +36,12 @@ Feature: A user can authenticate an SMTP client
|
|||
When user "[user:user]" connects and authenticates SMTP client "1" with address "[alias:alias]@[domain]"
|
||||
Then it succeeds
|
||||
|
||||
# Need to find way to setup disabled address on black
|
||||
@skip-black
|
||||
Scenario: SMTP client can not authenticate with disabled address
|
||||
When user "[user:user2]" connects and authenticates SMTP client "1" with address "[alias:alias2]@[domain]"
|
||||
Given the account "[user:user2]" has additional disabled address "[alias:disabled]@[domain]"
|
||||
And it succeeds
|
||||
When user "[user:user2]" connects and authenticates SMTP client "1" with address "[alias:disabled]@[domain]"
|
||||
Then it fails
|
||||
|
||||
Scenario: SMTP Logs out user
|
||||
|
@ -55,7 +55,13 @@ Feature: A user can authenticate an SMTP client
|
|||
When user "[user:user2]" connects SMTP client "2"
|
||||
Then SMTP client "2" can authenticate
|
||||
|
||||
@ignore-live
|
||||
# Need to find way to setup disabled address on black
|
||||
@skip-black
|
||||
Scenario: SMTP Authenticates with secondary address of account with disabled primary address
|
||||
Given there exists a disabled account with username "[user:user3]" and password "password3"
|
||||
And the account "[user:user3]" has additional address "[alias:alias3]@[domain]"
|
||||
And it succeeds
|
||||
And the user logs in with username "[user:user3]" and password "password3"
|
||||
And it succeeds
|
||||
When user "[user:user3]" connects and authenticates SMTP client "1" with address "[alias:alias3]@[domain]"
|
||||
Then it succeeds
|
||||
|
|
|
@ -11,7 +11,8 @@ Feature: SMTP sending with attachment
|
|||
And user "[user:user1]" connects and authenticates IMAP client "1"
|
||||
Then it succeeds
|
||||
|
||||
@long-black
|
||||
# black has issues with cyrilic char
|
||||
@skip-black
|
||||
Scenario: Sending with cyrillic PDF attachment
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
"""
|
||||
|
@ -77,7 +78,8 @@ Feature: SMTP sending with attachment
|
|||
"""
|
||||
|
||||
|
||||
@long-black
|
||||
# black has issues with cyrilic char
|
||||
@skip-black
|
||||
Scenario: Sending with cyrillic docx attachment
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
"""
|
||||
|
|
|
@ -3,10 +3,12 @@ Feature: SMTP with bcc
|
|||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And there exists an account with username "[user:to]" and password "password"
|
||||
And there exists an account with username "[user:bcc]" and password "password"
|
||||
And there exists an account with username "[user:bcc2]" and password "password"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
And the user logs in with username "[user:bcc]" and password "password"
|
||||
And the user logs in with username "[user:bcc2]" and password "password"
|
||||
And user "[user:user]" connects and authenticates SMTP client "1"
|
||||
Then it succeeds
|
||||
|
||||
|
@ -46,8 +48,6 @@ Feature: SMTP with bcc
|
|||
}
|
||||
"""
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Send message only to bcc
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:bcc]@[domain]":
|
||||
"""
|
||||
|
@ -81,3 +81,52 @@ Feature: SMTP with bcc
|
|||
Then IMAP client "2" eventually sees the following messages in "Inbox":
|
||||
| from | to | bcc | subject | unread |
|
||||
| [user:user]@[domain] | | | hello | true |
|
||||
|
||||
Scenario: Send message to bcc and bcc2
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:bcc]@[domain], [user:bcc2]@[domain]":
|
||||
"""
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
From: <[user:user]@[domain]>
|
||||
Bcc: <[user:bcc]@[domain]>, <[user:bcc2]@[domain]>
|
||||
Subject: hi
|
||||
|
||||
hello
|
||||
|
||||
"""
|
||||
Then it succeeds
|
||||
When user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then IMAP client "1" eventually sees the following message in "Sent" with this structure:
|
||||
"""
|
||||
{
|
||||
"from": "[user:user]@[domain]",
|
||||
"BCC": "[user:bcc]@[domain]; [user:bcc2]@[domain]",
|
||||
"subject": "hi",
|
||||
"content":{
|
||||
"content-type": "text/plain",
|
||||
"content-type-charset": "utf-8",
|
||||
"transfer-encoding": "quoted-printable",
|
||||
"body-is": "hello"
|
||||
}
|
||||
}
|
||||
"""
|
||||
When user "[user:bcc]" connects and authenticates IMAP client "2"
|
||||
Then IMAP client "2" eventually sees the following messages in "Inbox":
|
||||
| from | to | bcc | subject | unread |
|
||||
| [user:user]@[domain] | | | hi | true |
|
||||
When user "[user:bcc2]" connects and authenticates IMAP client "2"
|
||||
Then IMAP client "2" eventually sees the following messages in "Inbox":
|
||||
| from | to | bcc | subject | unread |
|
||||
| [user:user]@[domain] | | | hi | true |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ Feature: SMTP sending embedded message
|
|||
And user "[user:user]" connects and authenticates SMTP client "1"
|
||||
Then it succeeds
|
||||
|
||||
@long-black
|
||||
Scenario: Send it
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
|
||||
"""
|
||||
|
@ -49,4 +48,4 @@ Feature: SMTP sending embedded message
|
|||
When user "[user:to]" connects and authenticates IMAP client "2"
|
||||
Then IMAP client "2" eventually sees the following messages in "Inbox":
|
||||
| from | to | subject | attachments | unread |
|
||||
| [user:user]@[domain] | [user:to]@[domain] | Embedded message | embedded.eml | true |
|
||||
| [user:user]@[domain] | [user:to]@[domain] | Embedded message | embedded.eml | true |
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
Feature: SMTP wrong messages
|
||||
Background:
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And the account "[user:user]" has additional disabled address "[user:disabled]@[domain]"
|
||||
And there exists an account with username "[user:to]" and password "password"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
|
@ -48,13 +47,3 @@ Feature: SMTP wrong messages
|
|||
|
||||
"""
|
||||
Then it fails with error "invalid return path"
|
||||
|
||||
Scenario: Send from a valid address that cannot send
|
||||
When SMTP client "1" sends the following message from "[user:disabled]@[domain]" to "[user:to]@[domain]":
|
||||
"""
|
||||
From: Bridge Test Disabled <[user:disabled]@[domain]>
|
||||
To: Internal Bridge <[user:to]@[domain]>
|
||||
|
||||
Hello
|
||||
"""
|
||||
And it fails with error "Error: can't send on address: [user:disabled]@[domain]"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
Feature: SMTP wrong messages
|
||||
Background:
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And the account "[user:user]" has additional disabled address "[user:disabled]@[domain]"
|
||||
And there exists an account with username "[user:to]" and password "password"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
And the user logs in with username "[user:user]" and password "password"
|
||||
And user "[user:user]" connects and authenticates SMTP client "1"
|
||||
Then it succeeds
|
||||
|
||||
# Need to find way to setup disabled address on black
|
||||
@skip-black
|
||||
Scenario: Send from a valid address that cannot send
|
||||
Given the account "[user:user]" has additional disabled address "[user:disabled]@[domain]"
|
||||
When SMTP client "1" sends the following message from "[user:disabled]@[domain]" to "[user:to]@[domain]":
|
||||
"""
|
||||
From: Bridge Test Disabled <[user:disabled]@[domain]>
|
||||
To: Internal Bridge <[user:to]@[domain]>
|
||||
|
||||
Hello
|
||||
"""
|
||||
And it fails with error "Error: can't send on address: [user:disabled]@[domain]"
|
||||
|
|
@ -9,6 +9,8 @@ Feature: SMTP sending of plain messages
|
|||
And user "[user:user]" connects and authenticates SMTP client "1"
|
||||
Then it succeeds
|
||||
|
||||
# black fails to get parent ID
|
||||
@skip-black
|
||||
Scenario: HTML message to external account
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "pm.bridge.qa@gmail.com":
|
||||
"""
|
||||
|
@ -49,6 +51,8 @@ Feature: SMTP sending of plain messages
|
|||
}
|
||||
"""
|
||||
|
||||
# black is changing order of attachments
|
||||
@skip-black
|
||||
Scenario: HTML message with inline image to external account
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "pm.bridge.qa@gmail.com":
|
||||
"""
|
||||
|
@ -311,6 +315,8 @@ Feature: SMTP sending of plain messages
|
|||
}
|
||||
"""
|
||||
|
||||
# black fails to get parent ID
|
||||
@skip-black
|
||||
Scenario: HTML message with extremely long line (greater than default 2000 line limit) to external account
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "pm.bridge.qa@gmail.com":
|
||||
"""
|
||||
|
@ -352,15 +358,13 @@ Feature: SMTP sending of plain messages
|
|||
"""
|
||||
|
||||
Scenario: HTML message with Foreign/Nonascii chars in Subject and Body to external
|
||||
When there exists an account with username "bridgetest" and password "password"
|
||||
And the user logs in with username "bridgetest" and password "password"
|
||||
And user "bridgetest" connects and authenticates SMTP client "1"
|
||||
And SMTP client "1" sends the following EML "html/foreign_ascii_subject_body.eml" from "bridgetest@proton.local" to "pm.bridge.qa@gmail.com"
|
||||
When user "[user:user]" connects and authenticates SMTP client "1"
|
||||
And SMTP client "1" sends the following EML "html/foreign_ascii_subject_body.template.eml" from "[user:user]@[domain]" to "pm.bridge.qa@gmail.com"
|
||||
Then it succeeds
|
||||
When user "bridgetest" connects and authenticates IMAP client "1"
|
||||
When user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then IMAP client "1" eventually sees the following messages in "Sent":
|
||||
| from | to | subject |
|
||||
| bridgetest@proton.local | pm.bridge.qa@gmail.com | Subjεέςτ ¶ Ä È |
|
||||
| from | to | subject |
|
||||
| [user:user]@[domain] | pm.bridge.qa@gmail.com | Subjεέςτ ¶ Ä È |
|
||||
And the body in the "POST" request to "/mail/v4/messages" is:
|
||||
"""
|
||||
{
|
||||
|
@ -384,11 +388,13 @@ Feature: SMTP sending of plain messages
|
|||
|
||||
# It is expected for the structure check to look a bit different. More info on GODT-3011
|
||||
@regression
|
||||
# Black changes order of attachments
|
||||
@skip-black
|
||||
Scenario: HTML message with remote content in Body
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:user2]@[domain]":
|
||||
"""
|
||||
Date: 01 Jan 1980 00:00:00 +0000
|
||||
To: Internal Bridge Test <[user:to]@[domain]>
|
||||
To: Internal Bridge Test <[user:user2]@[domain]>
|
||||
From: Bridge Test <[user:user]@[domain]>
|
||||
Subject: MESSAGE WITH REMOTE CONTENT SENT
|
||||
Content-Type: multipart/alternative;
|
||||
|
@ -442,7 +448,7 @@ Feature: SMTP sending of plain messages
|
|||
"""
|
||||
{
|
||||
"date": "01 Jan 01 00:00 +0000",
|
||||
"to": "Internal Bridge Test <[user:to]@[domain]>",
|
||||
"to": "Internal Bridge Test <[user:user2]@[domain]>",
|
||||
"from": "Bridge Test <[user:user]@[domain]>",
|
||||
"subject": "MESSAGE WITH REMOTE CONTENT SENT",
|
||||
"content": {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,8 +8,6 @@ Feature: SMTP sending two messages
|
|||
And the user logs in with username "[user:recp]" and password "password"
|
||||
Then it succeeds
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Send from one account to the other
|
||||
When user "[user:user]" connects and authenticates SMTP client "1"
|
||||
And SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:recp]@[domain]":
|
||||
|
@ -64,8 +62,6 @@ Feature: SMTP sending two messages
|
|||
| from | to | subject | body |
|
||||
| [user:user]@[domain] | [user:recp]@[domain] | One account to the other | hello |
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Send from one account to the other with attachments
|
||||
When user "[user:user]" connects and authenticates SMTP client "1"
|
||||
And SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:recp]@[domain]":
|
||||
|
@ -137,4 +133,4 @@ Feature: SMTP sending two messages
|
|||
When user "[user:recp]" connects and authenticates IMAP client "2"
|
||||
Then IMAP client "2" eventually sees the following messages in "Inbox":
|
||||
| from | to | subject | body | attachments | unread |
|
||||
| [user:user]@[domain] | [user:recp]@[domain] | Plain with attachment internal | This is the body | outline-light-instagram-48.png | true |
|
||||
| [user:user]@[domain] | [user:recp]@[domain] | Plain with attachment internal | This is the body | outline-light-instagram-48.png | true |
|
||||
|
|
|
@ -171,15 +171,13 @@ Feature: SMTP sending of plain messages
|
|||
"""
|
||||
|
||||
Scenario: Basic message with multiple different attachments to internal account
|
||||
When there exists an account with username "bridgetest" and password "password"
|
||||
And the user logs in with username "bridgetest" and password "password"
|
||||
And user "bridgetest" connects and authenticates SMTP client "1"
|
||||
And SMTP client "1" sends the following EML "plain/text_plain_multiple_attachments.eml" from "bridgetest@proton.local" to "internalbridgetest@proton.local"
|
||||
When user "[user:user]" connects and authenticates SMTP client "1"
|
||||
And SMTP client "1" sends the following EML "plain/text_plain_multiple_attachments.template.eml" from "[user:user]@[domain]" to "[user:to]@[domain]"
|
||||
Then it succeeds
|
||||
When user "bridgetest" connects and authenticates IMAP client "1"
|
||||
When user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then IMAP client "1" eventually sees the following messages in "Sent":
|
||||
| from | to | subject |
|
||||
| bridgetest@proton.local | internalbridgetest@proton.local | Plain with multiple different attachments |
|
||||
| from | to | subject |
|
||||
| [user:user]@[domain] | [user:to]@[domain] | Plain with multiple different attachments |
|
||||
And the body in the "POST" request to "/mail/v4/messages" is:
|
||||
"""
|
||||
{
|
||||
|
@ -190,7 +188,7 @@ Feature: SMTP sending of plain messages
|
|||
},
|
||||
"ToList": [
|
||||
{
|
||||
"Address": "internalbridgetest@proton.local",
|
||||
"Address": "[user:to]@[domain]",
|
||||
"Name": "Internal Bridge"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -164,6 +164,8 @@ Feature: SMTP sending of PLAIN messages to Internal recipient
|
|||
}
|
||||
"""
|
||||
|
||||
# black changes order of attachments
|
||||
@skip-black
|
||||
Scenario: Plain message with multiple attachments to Internal
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
|
||||
"""
|
||||
|
@ -660,6 +662,8 @@ Feature: SMTP sending of PLAIN messages to Internal recipient
|
|||
}
|
||||
"""
|
||||
|
||||
# black is changing order of attachments
|
||||
@skip-black
|
||||
Scenario: Forward a Plain message containing various attachments
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
|
||||
"""
|
||||
|
|
|
@ -17,7 +17,6 @@ Feature: SMTP sending the same message twice
|
|||
"""
|
||||
And it succeeds
|
||||
|
||||
@long-black
|
||||
Scenario: The exact same message is not sent twice
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
|
||||
"""
|
||||
|
@ -37,8 +36,6 @@ Feature: SMTP sending the same message twice
|
|||
| from | to | subject | body |
|
||||
| [user:user]@[domain] | [user:to]@[domain] | Hello | World |
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Slight change means different message and is sent twice
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
|
||||
"""
|
||||
|
@ -58,4 +55,4 @@ Feature: SMTP sending the same message twice
|
|||
Then IMAP client "2" eventually sees the following messages in "Inbox":
|
||||
| from | to | subject | body |
|
||||
| [user:user]@[domain] | [user:to]@[domain] | Hello | World |
|
||||
| [user:user]@[domain] | [user:to]@[domain] | Hello. | World |
|
||||
| [user:user]@[domain] | [user:to]@[domain] | Hello. | World |
|
||||
|
|
|
@ -11,7 +11,6 @@ Feature: SMTP send reply
|
|||
And user "[user:user1]" connects and authenticates IMAP client "1"
|
||||
Then it succeeds
|
||||
|
||||
@long-black
|
||||
Scenario: Reply with In-Reply-To but no References
|
||||
# User1 send the initial message.
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
|
@ -58,7 +57,6 @@ Feature: SMTP send reply
|
|||
| from | subject | body | in-reply-to | references | reply-to |
|
||||
| [user:user2]@[domain] | FW - Please Reply | Heya | <something@protonmail.ch> | <something@protonmail.ch> | [user:user2]@[domain] |
|
||||
|
||||
@long-black
|
||||
Scenario: Reply with References but no In-Reply-To
|
||||
# User1 send the initial message.
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
|
@ -106,7 +104,6 @@ Feature: SMTP send reply
|
|||
| [user:user2]@[domain] | FW - Please Reply | Heya | <something@protonmail.ch> | <something@protonmail.ch> | [user:user2]@[domain] |
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Reply with both References and In-Reply-To
|
||||
# User1 send the initial message.
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
|
@ -155,7 +152,6 @@ Feature: SMTP send reply
|
|||
| [user:user2]@[domain] | FW - Please Reply | Heya | <something@protonmail.ch> | <something@protonmail.ch> | [user:user2]@[domain] |
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Reply with In-Reply-To matching several received ExternalID
|
||||
# User1 send the initial message.
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
|
@ -216,7 +212,6 @@ Feature: SMTP send reply
|
|||
| [user:user2]@[domain] | FW - Please Reply | Heya | | |
|
||||
|
||||
|
||||
@long-black
|
||||
Scenario: Reply with In-Reply-To matching several ExternalID but one sent by us
|
||||
# User1 send the initial message.
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
|
@ -279,7 +274,6 @@ Feature: SMTP send reply
|
|||
| [user:user2]@[domain] | FW - Please Reply | <something@external.com> | <something@external.com> |
|
||||
| [user:user2]@[domain] | FW - Please Reply Again | <something@external.com> | <something@external.com> |
|
||||
|
||||
@long-black
|
||||
Scenario: Reply with In-Reply-To and X-Forwarded-Message-Id sets forwarded flag
|
||||
# User1 send the initial message.
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
|
@ -334,7 +328,8 @@ Feature: SMTP send reply
|
|||
| from | subject | in-reply-to | references |
|
||||
| [user:user2]@[domain] | FW - Please Reply | <something@external.com> | <something@external.com> |
|
||||
|
||||
@long-black
|
||||
# black: missing answered flag
|
||||
@skip-black
|
||||
Scenario: Reply with In-Reply-To sets answered flag
|
||||
# User1 send the initial message.
|
||||
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
|
||||
|
@ -386,4 +381,4 @@ Feature: SMTP send reply
|
|||
# User1 receive the reply.|
|
||||
And IMAP client "1" eventually sees the following messages in "INBOX":
|
||||
| from | subject | in-reply-to | references |
|
||||
| [user:user2]@[domain] | FW - Please Reply | <something@external.com> | <something@external.com> |
|
||||
| [user:user2]@[domain] | FW - Please Reply | <something@external.com> | <something@external.com> |
|
||||
|
|
|
@ -2,7 +2,7 @@ Feature: SMTP sending two messages
|
|||
Background:
|
||||
Given there exists an account with username "[user:user]" and password "password"
|
||||
And there exists an account with username "[user:multi]" and password "password"
|
||||
And the account "[user:multi]" has additional address "[user:multi-alias]@[domain]"
|
||||
And the account "[user:multi]" has additional address "[alias:multi]@[domain]"
|
||||
And there exists an account with username "[user:to]" and password "password"
|
||||
Then it succeeds
|
||||
When bridge starts
|
||||
|
@ -34,7 +34,7 @@ Feature: SMTP sending two messages
|
|||
|
||||
Scenario: Send with two addresses of the same user in split mode
|
||||
When user "[user:multi]" connects and authenticates SMTP client "1" with address "[user:multi]@[domain]"
|
||||
And user "[user:multi]" connects and authenticates SMTP client "2" with address "[user:multi-alias]@[domain]"
|
||||
And user "[user:multi]" connects and authenticates SMTP client "2" with address "[alias:multi]@[domain]"
|
||||
And SMTP client "1" sends the following message from "[user:multi]@[domain]" to "[user:to]@[domain]>":
|
||||
"""
|
||||
From: Bridge Test <[user:multi]@[domain]>
|
||||
|
|
|
@ -111,9 +111,9 @@ Feature: Address mode
|
|||
| b@[domain] | b@[domain] | two | false |
|
||||
| c@[domain] | c@[domain] | three | true |
|
||||
| d@[domain] | d@[domain] | four | false |
|
||||
Given the account "[user:user]" has additional address "other@[domain]"
|
||||
Given the account "[user:user]" has additional address "[user:other]@[domain]"
|
||||
And bridge sends an address created event for user "[user:user]"
|
||||
When user "[user:user]" connects and authenticates IMAP client "3" with address "other@[domain]"
|
||||
When user "[user:user]" connects and authenticates IMAP client "3" with address "[user:other]@[domain]"
|
||||
Then IMAP client "3" eventually sees the following messages in "All Mail":
|
||||
| from | to | subject | unread |
|
||||
| a@[domain] | a@[domain] | one | true |
|
||||
|
@ -134,11 +134,13 @@ Feature: Address mode
|
|||
| from | to | subject | unread |
|
||||
| c@[domain] | c@[domain] | three | true |
|
||||
| d@[domain] | d@[domain] | four | false |
|
||||
Given the account "[user:user]" has additional address "other@[domain]"
|
||||
Given the account "[user:user]" has additional address "[user:other]@[domain]"
|
||||
And bridge sends an address created event for user "[user:user]"
|
||||
When user "[user:user]" connects and authenticates IMAP client "3" with address "other@[domain]"
|
||||
When user "[user:user]" connects and authenticates IMAP client "3" with address "[user:other]@[domain]"
|
||||
Then IMAP client "3" eventually sees 0 messages in "All Mail"
|
||||
|
||||
# Cannot delete primary address on black
|
||||
@skip-black
|
||||
Scenario: The user deletes an address while in combined mode
|
||||
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
|
||||
Then IMAP client "1" eventually sees the following messages in "All Mail":
|
||||
|
@ -159,6 +161,8 @@ Feature: Address mode
|
|||
When user "[user:user]" connects IMAP client "3"
|
||||
Then IMAP client "3" cannot authenticate with address "[alias:alias]@[domain]"
|
||||
|
||||
# Cannot delete primary address on black
|
||||
@skip-black
|
||||
Scenario: The user deletes an address while in split mode
|
||||
Given the user sets the address mode of user "[user:user]" to "split"
|
||||
And user "[user:user]" finishes syncing
|
||||
|
@ -179,4 +183,4 @@ Feature: Address mode
|
|||
|
||||
Scenario: The user makes an alias the primary address while in combined mode
|
||||
|
||||
Scenario: The user makes an alias the primary address while in split mode
|
||||
Scenario: The user makes an alias the primary address while in split mode
|
||||
|
|
|
@ -12,6 +12,8 @@ Feature: user's contact
|
|||
Then it succeeds
|
||||
|
||||
|
||||
# Implement contacts on black
|
||||
@skip-black
|
||||
Scenario: Playing with contact settings
|
||||
When the contact "SuperTester@proton.me" of user "[user:user]" has message format "plain"
|
||||
When the contact "SuperTester@proton.me" of user "[user:user]" has message format "HTML"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
Feature: A user can login
|
||||
Background:
|
||||
Given there exists an account with username "[user:user]" and password "password2"
|
||||
And there exists an account with username "[user:MixedCaps]" and password "password3"
|
||||
And there exists a disabled account with username "[user:disabled]" and password "password4"
|
||||
Then it succeeds
|
||||
And bridge starts
|
||||
Then it succeeds
|
||||
|
@ -24,11 +22,18 @@ Feature: A user can login
|
|||
When the user logs in with username "[user:user]" and password "password2"
|
||||
Then user "[user:user]" is not listed
|
||||
|
||||
# Mixed caps doesn't work on black
|
||||
@skip-black
|
||||
Scenario: Login to account with caps
|
||||
Given there exists an account with username "[user:MixedCaps]" and password "password3"
|
||||
And it succeeds
|
||||
When the user logs in with username "[user:MixedCaps]" and password "password3"
|
||||
Then user "[user:MixedCaps]" is eventually listed and connected
|
||||
|
||||
# Mixed caps doesn't work on black
|
||||
@skip-black
|
||||
Scenario: Login to account with disabled primary
|
||||
Given there exists a disabled account with username "[user:disabled]" and password "password4"
|
||||
When the user logs in with username "[user:disabled]" and password "password4"
|
||||
Then user "[user:disabled]" is eventually listed and connected
|
||||
|
||||
|
@ -45,4 +50,9 @@ Feature: A user can login
|
|||
When the user logs in with username "[user:user]" and password "password2"
|
||||
And the user logs in with username "[user:additional]" and password "password"
|
||||
Then user "[user:user]" is eventually listed and connected
|
||||
And user "[user:additional]" is eventually listed and connected
|
||||
And user "[user:additional]" is eventually listed and connected
|
||||
|
||||
Scenario: Login to account with an alias address
|
||||
Given the account "[user:user]" has additional address "[user:alias]@[domain]"
|
||||
When the user logs in with alias address "[user:alias]@[domain]" and password "password2"
|
||||
Then user "[user:user]" is eventually listed and connected
|
||||
|
|
|
@ -17,10 +17,10 @@ Feature: A logged out user can login again
|
|||
Then user "[user:user]" is not listed
|
||||
|
||||
Scenario: Bridge password persists after logout/login
|
||||
Given there exists an account with username "testUser" and password "password"
|
||||
And the user logs in with username "testUser" and password "password"
|
||||
And the bridge password of user "testUser" is changed to "YnJpZGdlcGFzc3dvcmQK"
|
||||
And user "testUser" is deleted
|
||||
And the user logs in with username "testUser" and password "password"
|
||||
Then user "testUser" is eventually listed and connected
|
||||
And the bridge password of user "testUser" is equal to "YnJpZGdlcGFzc3dvcmQK"
|
||||
Given there exists an account with username "[user:test]" and password "password"
|
||||
And the user logs in with username "[user:test]" and password "password"
|
||||
And the bridge password of user "[user:test]" is changed to "YnJpZGdlcGFzc3dvcmQK"
|
||||
And user "[user:test]" is deleted
|
||||
And the user logs in with username "[user:test]" and password "password"
|
||||
Then user "[user:test]" is eventually listed and connected
|
||||
And the bridge password of user "[user:test]" is equal to "YnJpZGdlcGFzc3dvcmQK"
|
||||
|
|
|
@ -6,7 +6,7 @@ Feature: The user reports a problem
|
|||
And the user logs in with username "[user:user]" and password "password"
|
||||
And user "[user:user]" finishes syncing
|
||||
Then it succeeds
|
||||
|
||||
|
||||
Scenario: User sends a problem report without logs attached
|
||||
When the user reports a bug
|
||||
Then the header in the "POST" multipart request to "/core/v4/reports/bug" has "Title" set to "[Bridge] Bug - title"
|
||||
|
@ -22,7 +22,7 @@ Feature: The user reports a problem
|
|||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has "Username" set to "[user:user]"
|
||||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has file "logs.zip"
|
||||
|
||||
|
||||
|
||||
@regression
|
||||
Scenario: User sends a problem report while signed out of Bridge
|
||||
When user "[user:user]" logs out
|
||||
|
@ -30,7 +30,7 @@ Feature: The user reports a problem
|
|||
Then it succeeds
|
||||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has "Username" set to "[user:user]"
|
||||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has "Email" set to "[user:user]@[domain]"
|
||||
|
||||
|
||||
@regression
|
||||
Scenario: User sends a problem report with changed Title
|
||||
When the user reports a bug with field "Title" set to "Testing title"
|
||||
|
@ -61,4 +61,4 @@ Feature: The user reports a problem
|
|||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has "Username" set to "[user:user]"
|
||||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has "Email" set to "[user:user]@[domain]"
|
||||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has "Client" set to "Apple Mail"
|
||||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has file "logs.zip"
|
||||
And the header in the "POST" multipart request to "/core/v4/reports/bug" has file "logs.zip"
|
||||
|
|
|
@ -20,7 +20,9 @@ Feature: Bridge can fully synchronize an account with high number of messages, a
|
|||
Then it succeeds
|
||||
When bridge starts
|
||||
Then it succeeds
|
||||
|
||||
|
||||
# Too many messages need to use fixture on black
|
||||
@skip-black
|
||||
Scenario: The account is synced when the user logs in and the number of messages is correct
|
||||
When the user logs in with username "[user:user]" and password "password"
|
||||
Then bridge sends sync started and finished events for user "[user:user]"
|
||||
|
|
|
@ -358,6 +358,24 @@ func (s *scenario) imapClientSeesMessageInMailboxWithStructure(clientID, mailbox
|
|||
return err
|
||||
}
|
||||
|
||||
debug := false
|
||||
for iFetch := range fetch {
|
||||
if !debug {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("\n\n\n fetch %d %#v\n evenlope %+v\n",
|
||||
iFetch, fetch[iFetch],
|
||||
fetch[iFetch].Envelope,
|
||||
)
|
||||
|
||||
for _, v := range fetch[iFetch].Body {
|
||||
fmt.Println("body literal", v)
|
||||
}
|
||||
|
||||
fmt.Printf("\n\n\n")
|
||||
}
|
||||
|
||||
haveMessages := xslices.Map(fetch, newMessageStructFromIMAP)
|
||||
|
||||
return matchStructure(haveMessages, msgStruct)
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright (c) 2024 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.Bridge.
|
||||
//
|
||||
// Proton Mail 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.
|
||||
//
|
||||
// Proton Mail 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-proton-api/server/backend"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/certs"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/user"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Use the fast key generation for tests.
|
||||
backend.GenerateKey = backend.FastGenerateKey
|
||||
|
||||
// Use the fast cert generation for tests.
|
||||
certs.GenerateCert = FastGenerateCert
|
||||
|
||||
// Set the event period to 1 second for more responsive tests.
|
||||
user.EventPeriod = time.Second
|
||||
|
||||
// Don't use jitter during tests.
|
||||
user.EventJitter = 0
|
||||
}
|
|
@ -20,12 +20,29 @@ package tests
|
|||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-proton-api/server/backend"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/certs"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/user"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Use the fast key generation for tests.
|
||||
backend.GenerateKey = backend.FastGenerateKey
|
||||
|
||||
// Use the fast cert generation for tests.
|
||||
certs.GenerateCert = FastGenerateCert
|
||||
|
||||
if !isBlack() {
|
||||
// Set the event period to 1 second for more responsive tests.
|
||||
user.EventPeriod = time.Second
|
||||
// Don't use jitter during tests.
|
||||
user.EventJitter = 0
|
||||
}
|
||||
|
||||
level := os.Getenv("FEATURE_TEST_LOG_LEVEL")
|
||||
|
||||
if os.Getenv("BRIDGE_API_DEBUG") != "" {
|
||||
|
|
|
@ -150,7 +150,7 @@ func (s *scenario) smtpClientSendsTheFollowingEmlFromTo(clientID, file, from, to
|
|||
return err
|
||||
}
|
||||
|
||||
if err := clientSend(client, from, to, string(b)); err != nil {
|
||||
if err := clientSend(client, from, to, s.t.replace(string(b))); err != nil {
|
||||
s.t.pushError(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ func (s *scenario) steps(ctx *godog.ScenarioContext) {
|
|||
|
||||
// ==== USER ====
|
||||
ctx.Step(`^the user logs in with username "([^"]*)" and password "([^"]*)"$`, s.userLogsInWithUsernameAndPassword)
|
||||
ctx.Step(`^the user logs in with alias address "([^"]*)" and password "([^"]*)"$`, s.userLogsInWithAliasAddressAndPassword)
|
||||
ctx.Step(`^user "([^"]*)" logs out$`, s.userLogsOut)
|
||||
ctx.Step(`^user "([^"]*)" is deleted$`, s.userIsDeleted)
|
||||
ctx.Step(`^the auth of user "([^"]*)" is revoked$`, s.theAuthOfUserIsRevoked)
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
From: Bridge Test <bridgetest@proton.local>
|
||||
To: External Bridge <pm.bridge.qa@gmail.com>
|
||||
Subject: =?UTF-8?B?U3Vias61zq3Pgs+EIMK2IMOEIMOI?=
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
Subjεέςτ ¶ Ä È
|
||||
</body>
|
||||
</html>
|
||||
From: Bridge Test <[user:user]@[domain]>
|
||||
To: External Bridge <pm.bridge.qa@gmail.com>
|
||||
Subject: =?UTF-8?B?U3Vias61zq3Pgs+EIMK2IMOEIMOI?=
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
Subjεέςτ ¶ Ä È
|
||||
</body>
|
||||
</html>
|
|
@ -1,69 +1,69 @@
|
|||
From: Bridge Test <bridgetest@proton.local>
|
||||
To: Internal Bridge <internalbridgetest@proton.local>
|
||||
Subject: Plain with multiple different attachments
|
||||
Content-Type: multipart/mixed; boundary="bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606"
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Body of plain text message with multiple attachments
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/zip; name="PINProtected.zip"
|
||||
Content-Disposition: attachment; filename="PINProtected.zip"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQACAAIAHhlwVYAAAAAAAAAABADAAAMACAAbWVzc2FnZTIudHh0VVQNAAdkdnhk7nZ4
|
||||
AABQSwUGAAAAAAIAAgC/AAAAewMAAAAA
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; name="test.docx"
|
||||
Content-Disposition: attachment; filename="test.docx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQDfpNJsWgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
AAAAgB4AAHdvcmQvc3R5bGVzLnhtbFBLBQYAAAAACwALAMECAADXKQAAAAA=
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/pdf; name="test.pdf"
|
||||
Content-Disposition: attachment; filename="test.pdf"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
JVBERi0xLjUKJeLjz9MKNyAwIG9iago8PAovVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnRO
|
||||
MjM0NAolJUVPRgo=
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; name="test.xlsx"
|
||||
Content-Disposition: attachment; filename="test.xlsx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQBi7p1oXgEAAJAEAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
AAoACgCAAgAAexwAAAAA
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/xml; charset=UTF-8; name="testxml.xml"
|
||||
Content-Disposition: attachment; filename="testxml.xml"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN1aXRl
|
||||
VUtUZXN0Ii8+CiAgICAgICAgPC9jbGFzc2VzPgogICAgPC90ZXN0PgoKPC9zdWl0ZT4=
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/plain; charset=UTF-8; name="update.txt"
|
||||
Content-Disposition: attachment; filename="update.txt"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
DQpHb2NlQERFU0tUT1AtQ0dONkZENiBNSU5HVzY0IC9jL1Byb2dyYW0gRmlsZXMvUHJvdG9u
|
||||
NFdqRUw5WkplbnJZcUZucXVvSFBEa0w5VWZFeTA0VlBYRkViVERWLVlQaS1BSWc9PSINCg==
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/calendar; charset=UTF-8; name="=?UTF-8?B?6YCZ5piv5ryi5a2X55qE5LiA5YCL5L6L5a2QLmljcw==?="
|
||||
Content-Disposition: attachment; filename*0*=UTF-8''%E9%80%99%E6%98%AF%E6%BC%A2%E5%AD%97%E7%9A%84%E4%B8%80; filename*1*=%E5%80%8B%E4%BE%8B%E5%AD%90%2E%69%63%73
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
QkVHSU46VkNBTEVOREFSCk1FVEhPRDpQVUJMSVNIClZFUlNJT046Mi4wClgtV1ItQ0FMTkFN
|
||||
RDpWQUxBUk0KRU5EOlZFVkVOVApFTkQ6VkNBTEVOREFSCg==
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606--
|
||||
|
||||
From: Bridge Test <[user:user]@[domain]>
|
||||
To: Internal Bridge <[user:to]@[domain]>
|
||||
Subject: Plain with multiple different attachments
|
||||
Content-Type: multipart/mixed; boundary="bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606"
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Body of plain text message with multiple attachments
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/zip; name="PINProtected.zip"
|
||||
Content-Disposition: attachment; filename="PINProtected.zip"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQACAAIAHhlwVYAAAAAAAAAABADAAAMACAAbWVzc2FnZTIudHh0VVQNAAdkdnhk7nZ4
|
||||
AABQSwUGAAAAAAIAAgC/AAAAewMAAAAA
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; name="test.docx"
|
||||
Content-Disposition: attachment; filename="test.docx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQDfpNJsWgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
AAAAgB4AAHdvcmQvc3R5bGVzLnhtbFBLBQYAAAAACwALAMECAADXKQAAAAA=
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/pdf; name="test.pdf"
|
||||
Content-Disposition: attachment; filename="test.pdf"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
JVBERi0xLjUKJeLjz9MKNyAwIG9iago8PAovVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnRO
|
||||
MjM0NAolJUVPRgo=
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; name="test.xlsx"
|
||||
Content-Disposition: attachment; filename="test.xlsx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQBi7p1oXgEAAJAEAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
AAoACgCAAgAAexwAAAAA
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/xml; charset=UTF-8; name="testxml.xml"
|
||||
Content-Disposition: attachment; filename="testxml.xml"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN1aXRl
|
||||
VUtUZXN0Ii8+CiAgICAgICAgPC9jbGFzc2VzPgogICAgPC90ZXN0PgoKPC9zdWl0ZT4=
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/plain; charset=UTF-8; name="update.txt"
|
||||
Content-Disposition: attachment; filename="update.txt"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
DQpHb2NlQERFU0tUT1AtQ0dONkZENiBNSU5HVzY0IC9jL1Byb2dyYW0gRmlsZXMvUHJvdG9u
|
||||
NFdqRUw5WkplbnJZcUZucXVvSFBEa0w5VWZFeTA0VlBYRkViVERWLVlQaS1BSWc9PSINCg==
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606
|
||||
Content-Type: text/calendar; charset=UTF-8; name="=?UTF-8?B?6YCZ5piv5ryi5a2X55qE5LiA5YCL5L6L5a2QLmljcw==?="
|
||||
Content-Disposition: attachment; filename*0*=UTF-8''%E9%80%99%E6%98%AF%E6%BC%A2%E5%AD%97%E7%9A%84%E4%B8%80; filename*1*=%E5%80%8B%E4%BE%8B%E5%AD%90%2E%69%63%73
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
QkVHSU46VkNBTEVOREFSCk1FVEhPRDpQVUJMSVNIClZFUlNJT046Mi4wClgtV1ItQ0FMTkFN
|
||||
RDpWQUxBUk0KRU5EOlZFVkVOVApFTkQ6VkNBTEVOREFSCg==
|
||||
|
||||
--bc5bd30245232f31b6c976adcd59bb0069c9b13f986f9e40c2571bb80aa16606--
|
||||
|
|
@ -448,19 +448,19 @@ func eventually(condition func() error) error {
|
|||
var timerDuration = 30 * time.Second
|
||||
// Extend to 5min for live API.
|
||||
if hostURL := os.Getenv("FEATURE_TEST_HOST_URL"); hostURL != "" {
|
||||
timerDuration = 600 * time.Second
|
||||
timerDuration = 300 * time.Second
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timerDuration)
|
||||
defer timer.Stop()
|
||||
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
ticker := time.NewTicker(timerDuration / 300)
|
||||
defer ticker.Stop()
|
||||
|
||||
for tick := ticker.C; ; {
|
||||
select {
|
||||
case <-timer.C:
|
||||
return fmt.Errorf("timed out: %w", lastErr)
|
||||
return fmt.Errorf("eventually timed out: %w", lastErr)
|
||||
|
||||
case <-tick:
|
||||
tick = nil
|
||||
|
|
|
@ -57,7 +57,7 @@ func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address s
|
|||
userID := s.t.getUserByName(username).getUserID()
|
||||
|
||||
// Decrypt the user's encrypted ID for use with quark.
|
||||
userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", userID)
|
||||
userDecID, err := s.t.decryptID(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address s
|
|||
if _, err := s.t.runQuarkCmd(
|
||||
context.Background(),
|
||||
"user:create:address",
|
||||
"--",
|
||||
string(userDecID),
|
||||
s.t.getUserByID(userID).getUserPass(),
|
||||
|
||||
|
@ -88,7 +89,6 @@ func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address s
|
|||
}
|
||||
|
||||
func (s *scenario) theAccountNoLongerHasAdditionalAddress(username, address string) error {
|
||||
userID := s.t.getUserByName(username).getUserID()
|
||||
addrID := s.t.getUserByName(username).getAddrID(address)
|
||||
|
||||
if err := s.t.withClient(context.Background(), username, func(ctx context.Context, c *proton.Client) error {
|
||||
|
@ -101,8 +101,6 @@ func (s *scenario) theAccountNoLongerHasAdditionalAddress(username, address stri
|
|||
return err
|
||||
}
|
||||
|
||||
s.t.getUserByID(userID).remAddress(addrID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -367,6 +365,42 @@ func (s *scenario) userLogsInWithUsernameAndPassword(username, password string)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *scenario) userLogsInWithAliasAddressAndPassword(alias, password string) error {
|
||||
smtpEvtCh, cancelSMTP := s.t.bridge.GetEvents(events.SMTPServerReady{})
|
||||
defer cancelSMTP()
|
||||
imapEvtCh, cancelIMAP := s.t.bridge.GetEvents(events.IMAPServerReady{})
|
||||
defer cancelIMAP()
|
||||
|
||||
userID, err := s.t.bridge.LoginFull(context.Background(), s.t.getUserByAddress(alias).getName(), []byte(password), nil, nil)
|
||||
if err != nil {
|
||||
s.t.pushError(err)
|
||||
} else {
|
||||
// We need to wait for server to be up or we won't be able to connect. It should only happen once to avoid
|
||||
// blocking on multiple Logins.
|
||||
if !s.t.imapServerStarted {
|
||||
<-imapEvtCh
|
||||
s.t.imapServerStarted = true
|
||||
}
|
||||
if !s.t.smtpServerStarted {
|
||||
<-smtpEvtCh
|
||||
s.t.smtpServerStarted = true
|
||||
}
|
||||
|
||||
if userID != s.t.getUserByAddress(alias).getUserID() {
|
||||
return errors.New("user ID mismatch")
|
||||
}
|
||||
|
||||
info, err := s.t.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.t.getUserByID(userID).setBridgePass(string(info.BridgePass))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *scenario) userLogsOut(username string) error {
|
||||
return s.t.bridge.LogoutUser(context.Background(), s.t.getUserByName(username).getUserID())
|
||||
}
|
||||
|
@ -480,7 +514,7 @@ func (s *scenario) addAdditionalAddressToAccount(username, address string, disab
|
|||
userID := s.t.getUserByName(username).getUserID()
|
||||
|
||||
// Decrypt the user's encrypted ID for use with quark.
|
||||
userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", userID)
|
||||
userDecID, err := s.t.decryptID(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -494,6 +528,7 @@ func (s *scenario) addAdditionalAddressToAccount(username, address string, disab
|
|||
}
|
||||
|
||||
args = append(args,
|
||||
"--",
|
||||
string(userDecID),
|
||||
s.t.getUserByID(userID).getUserPass(),
|
||||
address,
|
||||
|
@ -524,6 +559,14 @@ func (s *scenario) addAdditionalAddressToAccount(username, address string, disab
|
|||
func (s *scenario) createUserAccount(username, password string, disabled bool) error {
|
||||
// Create the user and generate its default address (with keys).
|
||||
|
||||
if len(username) == 0 || username[0] == '-' {
|
||||
panic("username must be non-empty and not start with minus")
|
||||
}
|
||||
|
||||
if len(password) == 0 || password[0] == '-' {
|
||||
panic("password must be non-empty and not start with minus")
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"--name", username,
|
||||
"--password", password,
|
||||
|
@ -549,7 +592,7 @@ func (s *scenario) createUserAccount(username, password string, disabled bool) e
|
|||
}
|
||||
|
||||
// Decrypt the user's encrypted ID for use with quark.
|
||||
userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", user.ID)
|
||||
userDecID, err := s.t.decryptID(user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -559,6 +602,7 @@ func (s *scenario) createUserAccount(username, password string, disabled bool) e
|
|||
context.Background(),
|
||||
"user:create:subscription",
|
||||
"--planID", "visionary2022",
|
||||
"--",
|
||||
string(userDecID),
|
||||
); err != nil {
|
||||
return err
|
||||
|
|
Loading…
Reference in New Issue