GODT-1177: remove Import-Export from repo

This commit is contained in:
James Houlahan
2021-05-28 09:56:52 +02:00
committed by Jakub
parent 649195cc2b
commit ffb18adfd0
141 changed files with 140 additions and 8781 deletions

View File

@ -1,10 +1,9 @@
.PHONY: check-go check-godog install-godog test test-bridge test-ie test-live test-live-bridge test-live-ie test-stage test-debug test-live-debug bench
.PHONY: check-go check-godog install-godog test test-bridge test-live test-live-bridge test-stage test-debug test-live-debug bench
export GO111MODULE=on
export BRIDGE_VERSION:=1.8.12+integrationtests
export VERBOSITY?=fatal
export TEST_DATA=testdata
export TEST_APP?=bridge
# Tests do not run in parallel. This will overrule user settings.
MAKEFLAGS=-j1
@ -16,23 +15,17 @@ check-godog:
install-godog: check-go
go get github.com/cucumber/godog/cmd/godog@v0.12.1
test: test-bridge test-ie
test-bridge: FEATURES ?= features/bridge
test: test-bridge
test-bridge: FEATURES ?= features
test-bridge: check-godog
TEST_APP=bridge TEST_ENV=fake TEST_ACCOUNTS=accounts/fake.json godog --tags="~@ignore" $(FEATURES)
test-ie: FEATURES ?= features/ie
test-ie: check-godog
TEST_APP=ie TEST_ENV=fake TEST_ACCOUNTS=accounts/fake.json godog --tags="~@ignore" $(FEATURES)
TEST_ENV=fake TEST_ACCOUNTS=accounts/fake.json godog --tags="~@ignore" $(FEATURES)
# Doesn't work in parallel!
# Provide TEST_ACCOUNTS with your accounts.
test-live: test-live-bridge test-live-ie
test-live-bridge: FEATURES ?= features/bridge
test-live-bridge: FEATURES ?= features
test-live-bridge: check-godog
TEST_APP=bridge TEST_ENV=live godog --tags="~@ignore && ~@ignore-live" $(FEATURES)
test-live-ie: FEATURES ?= features/ie
test-live-ie: check-godog
TEST_APP=ie TEST_ENV=live godog --tags="~@ignore && ~@ignore-live" $(FEATURES)
TEST_ENV=live godog --tags="~@ignore && ~@ignore-live" $(FEATURES)
# Doesn't work in parallel!
# Provide TEST_ACCOUNTS with your accounts.
@ -47,12 +40,6 @@ test-debug:
test-live-debug:
TEST_ENV=live dlv test -- $(FEATURES)
test-ie-debug:
TEST_APP=ie TEST_ENV=fake TEST_ACCOUNTS=accounts/fake.json dlv test -- $(FEATURES)
test-live-ie-debug:
TEST_APP=ie TEST_ENV=live dlv test -- $(FEATURES)
# -run flag is not working anyway, but lets keep it there to note we really do not want to run tests.
# To properly benchmark sync/fetch, we need everything empty. For that is better to start everything
# again and safest way is to run only one loop per run.

View File

@ -19,7 +19,6 @@ package tests
import (
"context"
"os"
testContext "github.com/ProtonMail/proton-bridge/test/context"
"github.com/cucumber/godog"
@ -60,10 +59,6 @@ func ScenarioInitializer(s *godog.ScenarioContext) {
StoreChecksFeatureContext(s)
StoreSetupFeatureContext(s)
TransferActionsFeatureContext(s)
TransferChecksFeatureContext(s)
TransferSetupFeatureContext(s)
UsersActionsFeatureContext(s)
UsersSetupFeatureContext(s)
UsersChecksFeatureContext(s)
@ -72,9 +67,7 @@ func ScenarioInitializer(s *godog.ScenarioContext) {
var ctx *testContext.TestContext //nolint[gochecknoglobals]
func beforeScenario(scenarioCtx context.Context, _ *godog.Scenario) (context.Context, error) {
// NOTE(GODT-219) It would be possible to optimised the usage of godog with our context.
app := os.Getenv("TEST_APP")
ctx = testContext.New(app)
ctx = testContext.New()
return scenarioCtx, nil
}

View File

@ -27,7 +27,7 @@ import (
)
func benchTestContext() (*context.TestContext, *mocks.IMAPClient) {
ctx := context.New("bridge")
ctx := context.New()
username := "user"
account := ctx.GetTestAccount(username)

View File

@ -23,8 +23,6 @@ import (
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/ProtonMail/proton-bridge/internal/users"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
@ -55,11 +53,10 @@ type TestContext struct {
clientManager pmapi.Manager
// Core related variables.
bridge *bridge.Bridge
importExport *importexport.ImportExport
users *users.Users
credStore users.CredentialsStorer
lastError error
bridge *bridge.Bridge
users *users.Users
credStore users.CredentialsStorer
lastError error
// IMAP related variables.
imapAddr string
@ -75,13 +72,6 @@ type TestContext struct {
smtpLastResponses map[string]*mocks.SMTPResponse
smtpResponseLocker sync.Locker
// Transfer related variables.
transferLocalRootForImport string
transferLocalRootForExport string
transferRemoteIMAPServer *mocks.IMAPServer
transferProgress *transfer.Progress
transferSkipEncryptedMessages bool
// Store releated variables.
bddMessageIDsToAPIIDs map[string]string
@ -93,9 +83,11 @@ type TestContext struct {
}
// New returns a new test TestContext.
func New(app string) *TestContext {
func New() *TestContext {
setLogrusVerbosityFromEnv()
listener := listener.New()
pmapiController, clientManager := newPMAPIController(app, listener)
pmapiController, clientManager := newPMAPIController(listener)
ctx := &TestContext{
t: &bddT{},
@ -121,15 +113,8 @@ func New(app string) *TestContext {
// Ensure that the config is cleaned up after the test is over.
ctx.addCleanupChecked(ctx.locations.Clear, "Cleaning bridge config data")
// Create bridge or import-export instance under test.
switch app {
case "bridge":
ctx.withBridgeInstance()
case "ie":
ctx.withImportExportInstance()
default:
panic("unknown app: " + app)
}
// Create bridge instance under test.
ctx.withBridgeInstance()
return ctx
}

View File

@ -1,50 +0,0 @@
// Copyright (c) 2021 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package context
import (
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/users"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
)
// GetImportExport returns import-export instance.
func (ctx *TestContext) GetImportExport() *importexport.ImportExport {
return ctx.importExport
}
// withImportExportInstance creates a import-export instance for use in the test.
// TestContext has this by default once called with env variable TEST_APP=ie.
func (ctx *TestContext) withImportExportInstance() {
ctx.importExport = newImportExportInstance(ctx.t, ctx.locations, ctx.cache, ctx.credStore, ctx.listener, ctx.clientManager)
ctx.users = ctx.importExport.Users
}
// newImportExportInstance creates a new import-export instance configured to use the given config/credstore.
func newImportExportInstance(
t *bddT,
locations importexport.Locator,
cache importexport.Cacher,
credStore users.CredentialsStorer,
eventListener listener.Listener,
clientManager pmapi.Manager,
) *importexport.ImportExport {
panicHandler := &panicHandler{t: t}
return importexport.New(locations, cache, panicHandler, eventListener, clientManager, credStore)
}

View File

@ -46,7 +46,7 @@ type PMAPIController interface {
UnlockEvents()
}
func newPMAPIController(app string, listener listener.Listener) (PMAPIController, pmapi.Manager) {
func newPMAPIController(listener listener.Listener) (PMAPIController, pmapi.Manager) {
switch os.Getenv(EnvName) {
case EnvFake:
cntl, cm := fakeapi.NewController()
@ -54,7 +54,7 @@ func newPMAPIController(app string, listener listener.Listener) (PMAPIController
return cntl, cm
case EnvLive:
cntl, cm := liveapi.NewController(app)
cntl, cm := liveapi.NewController()
addConnectionObserver(cm, listener)
return cntl, cm

View File

@ -1,101 +0,0 @@
// Copyright (c) 2021 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package context
import (
"io/ioutil"
"math/rand"
"os"
"strconv"
"github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/ProtonMail/proton-bridge/test/mocks"
)
// SetTransferProgress sets transfer progress.
func (ctx *TestContext) SetTransferProgress(progress *transfer.Progress) {
ctx.transferProgress = progress
}
// GetTransferProgress returns transfer progress.
func (ctx *TestContext) GetTransferProgress() *transfer.Progress {
return ctx.transferProgress
}
// SetTransferSkipEncryptedMessages sets whether encrypted messages will be skipped.
func (ctx *TestContext) SetTransferSkipEncryptedMessages(value bool) {
ctx.transferSkipEncryptedMessages = value
}
// GetTransferSkipEncryptedMessages gets whether encrypted messages will be skipped.
func (ctx *TestContext) GetTransferSkipEncryptedMessages() bool {
return ctx.transferSkipEncryptedMessages
}
// GetTransferLocalRootForImport creates temporary root for importing
// if it not exists yet, and returns its path.
func (ctx *TestContext) GetTransferLocalRootForImport() string {
if ctx.transferLocalRootForImport != "" {
return ctx.transferLocalRootForImport
}
root := ctx.createLocalRoot()
ctx.transferLocalRootForImport = root
return root
}
// GetTransferLocalRootForExport creates temporary root for exporting
// if it not exists yet, and returns its path.
func (ctx *TestContext) GetTransferLocalRootForExport() string {
if ctx.transferLocalRootForExport != "" {
return ctx.transferLocalRootForExport
}
root := ctx.createLocalRoot()
ctx.transferLocalRootForExport = root
return root
}
func (ctx *TestContext) createLocalRoot() string {
root, err := ioutil.TempDir("", "transfer")
if err != nil {
panic("failed to create temp transfer root: " + err.Error())
}
ctx.addCleanupChecked(func() error {
return os.RemoveAll(root)
}, "Cleaning transfer data")
return root
}
// GetTransferRemoteIMAPServer creates mocked IMAP server if it not created yet, and returns it.
func (ctx *TestContext) GetTransferRemoteIMAPServer() *mocks.IMAPServer {
if ctx.transferRemoteIMAPServer != nil {
return ctx.transferRemoteIMAPServer
}
port := 21300 + rand.Intn(100) //nolint[gosec] It is OK to use weaker rand generator here
ctx.transferRemoteIMAPServer = mocks.NewIMAPServer("user", "pass", "127.0.0.1", strconv.Itoa(port))
ctx.transferRemoteIMAPServer.Start()
ctx.addCleanupChecked(func() error {
ctx.transferRemoteIMAPServer.Stop()
return nil
}, "Cleaning transfer IMAP server")
return ctx.transferRemoteIMAPServer
}

View File

@ -1,43 +0,0 @@
Feature: Export to EML files
Background:
Given there is connected user "user"
And there is "user" with mailbox "Folders/Foo"
And there are messages in mailbox "INBOX" for "user"
| from | to | subject | time |
| bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
And there are messages in mailbox "Folders/Foo" for "user"
| from | to | subject | time |
| foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
Scenario: Export all
When user "user" exports to EML files
Then progress result is "OK"
# Every message is also in All Mail.
And transfer exported 8 messages
And transfer imported 8 messages
And transfer failed for 0 messages
And transfer exported messages
| folder | from | to | subject | time |
| Inbox | bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
| Foo | foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| Foo | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| Foo | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
| All Mail | bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
| All Mail | foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| All Mail | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| All Mail | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
Scenario: Export only Foo with time limit
When user "user" exports to EML files with rules
| source | target | from | to |
| Foo | | 2020-01-01T12:10:00 | 2020-01-01T13:00:00 |
Then progress result is "OK"
And transfer exported 2 messages
And transfer imported 2 messages
And transfer failed for 0 messages
And transfer exported messages
| folder | from | to | subject | time |
| Foo | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| Foo | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |

View File

@ -1,43 +0,0 @@
Feature: Export to MBOX files
Background:
Given there is connected user "user"
And there is "user" with mailbox "Folders/Foo"
And there are messages in mailbox "INBOX" for "user"
| from | to | subject | time |
| bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
And there are messages in mailbox "Folders/Foo" for "user"
| from | to | subject | time |
| foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
Scenario: Export all
When user "user" exports to MBOX files
Then progress result is "OK"
# Every message is also in All Mail.
And transfer exported 8 messages
And transfer imported 8 messages
And transfer failed for 0 messages
And transfer exported messages
| folder | from | to | subject | time |
| Inbox | bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
| Foo | foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| Foo | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| Foo | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
| All Mail | bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
| All Mail | foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| All Mail | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| All Mail | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
Scenario: Export only Foo with time limit
When user "user" exports to MBOX files with rules
| source | target | from | to |
| Foo | | 2020-01-01T12:10:00 | 2020-01-01T13:00:00 |
Then progress result is "OK"
And transfer exported 2 messages
And transfer imported 2 messages
And transfer failed for 0 messages
And transfer exported messages
| folder | from | to | subject | time |
| Foo | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| Foo | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |

View File

@ -1,24 +0,0 @@
Feature: Import from EML files
Background:
Given there is connected user "user"
Scenario: Import draft without from fallbacks to primary address
Given there is EML file "Drafts/one.eml"
"""
Subject: no from yet
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
hello
"""
When user "user" imports local files with rules
| source | target |
| Drafts | Drafts |
Then progress result is "OK"
And transfer exported 1 messages
And transfer imported 1 messages
And transfer failed for 0 messages
And API mailbox "Drafts" for "user" has messages
| from | to | subject |
| [userAddress] | test@protonmail.com | no from yet |

View File

@ -1,43 +0,0 @@
Feature: Import embedded message
Background:
Given there is connected user "user"
And there is EML file "Inbox/hello.eml"
"""
From: Foo <foo@example.com>
To: Bridge Test <bridgetest@pm.test>
Subject: Embedded message
Content-Type: multipart/mixed; boundary="boundary"
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
This is a multi-part message in MIME format.
--boundary
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
--boundary
Content-Type: message/rfc822; name="embedded.eml"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="embedded.eml"
From: Bar <bar@example.com>
To: Bridge Test <bridgetest@pm.test>
Subject: (No Subject)
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
hello
--boundary--
"""
Scenario: Import it
When user "user" imports local files
Then progress result is "OK"
And transfer exported 1 messages
And transfer imported 1 messages
And transfer failed for 0 messages
And API mailbox "INBOX" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@pm.test | Embedded message |

View File

@ -1,61 +0,0 @@
Feature: Import from EML files
Background:
Given there is connected user "user"
And there is "user" with mailbox "Folders/Foo"
And there is "user" with mailbox "Folders/Bar"
And there are EML files
| file | from | to | subject | time |
| Foo/one.eml | foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| Foo/two.eml | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| Sub/Foo/three.eml | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
And there is EML file "Inbox/hello.eml"
"""
Subject: hello
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
hello
"""
Scenario: Import all
When user "user" imports local files
Then progress result is "OK"
And transfer exported 4 messages
And transfer imported 4 messages
And transfer failed for 0 messages
And API mailbox "INBOX" for "user" has messages
| from | to | subject |
| bridgetest@pm.test | test@protonmail.com | hello |
And API mailbox "Folders/Foo" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@protonmail.com | one |
| bar@example.com | bridgetest@protonmail.com | two |
| bar@example.com | bridgetest@protonmail.com | three |
Scenario: Import only Foo to Bar with time limit
When user "user" imports local files with rules
| source | target | from | to |
| Foo | Bar | 2020-01-01T12:10:00 | 2020-01-01T13:00:00 |
Then progress result is "OK"
And transfer exported 2 messages
And transfer imported 2 messages
And transfer failed for 0 messages
And API mailbox "Folders/Bar" for "user" has messages
| from | to | subject |
| bar@example.com | bridgetest@protonmail.com | two |
| bar@example.com | bridgetest@protonmail.com | three |
Scenario: Import broken EML message
Given there is EML file "Broken/broken.eml"
"""
Content-type: multipart/mixed
"""
When user "user" imports local files with rules
| source | target |
| Broken | Foo |
Then progress result is "OK"
And transfer exported 1 messages
And transfer imported 0 messages
And transfer failed for 1 messages

View File

@ -1,188 +0,0 @@
Feature: Import from EML files
Background:
Given there is connected user "user"
And there is EML file "Inbox/clear.eml"
"""
Subject: clear
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
secret
"""
And there is EML file "Inbox/encrypted.eml"
"""
Subject: encrypted
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
-----BEGIN PGP MESSAGE-----
hQEMA7hGUUsYs0fEAQgA10NwJSNTLm3vpxVtoYBaA9AjFI5H4hKuV3/f2NHbWb2s
k5enK3tEIOYdFdrO+NLrV6weHq3Dgu4er3URTQ62tFwjSJyeXxmY0d9J+JdxibJg
wqZubuSHYzQHpFqJgoUUWEVEsv0Ao8yQo8BE9iybCKoZf6KojROUuE748oxlxJnV
m1XuaVIzgw4xN0GUA5sLLuWeL94b2dZe5SDDQE5POzDgueZ7faefX8U1pGErCRJ0
sO6FSw3SF4NpvrxVESWgCmsG5pcuxE2JqB0UoHnNDcqsW8w1Q+GabAPo6UqHhgIg
56MRCWeou2djHIIj9TMUIVFzSG/HvTYQWVS+i4Z7AdJJAXr53GgbZQznO80Qxwcb
FFdlwOXHuaXqhqCb338jlQWnbcnUsuJWxBAxkHrlP/nluFqPdIBOglC38kdYSBed
3YwuEB9sXV/fcw==
=B05V
-----END PGP MESSAGE-----
"""
And there is EML file "Inbox/encrypted-mime.eml"
"""
Subject: encrypted mime
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
MIME-Version: 1.0
Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="WLjzd46aUAiOcuNXjWTJItBZonI56MuAk"
--WLjzd46aUAiOcuNXjWTJItBZonI56MuAk
Content-Type: application/pgp-encrypted
Content-Description: PGP/MIME version identification
--WLjzd46aUAiOcuNXjWTJItBZonI56MuAk
Content-Type: application/octet-stream; name="encrypted.asc"
Content-Description: OpenPGP encrypted message
Content-Disposition: inline; filename="encrypted.asc"
-----BEGIN PGP MESSAGE-----
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
=4M3l
-----END PGP MESSAGE-----
--WLjzd46aUAiOcuNXjWTJItBZonI56MuAk--
"""
And there is EML file "Inbox/signed.eml"
"""
Subject: signed
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
secret
-----BEGIN PGP SIGNATURE-----
iQEzBAEBCAAdFiEENaE2ZPemenI4pZah/SJcGo7SJWIFAl+cCAgACgkQ/SJcGo7S
JWKsOQf/YakNXkMNjZIu8Hf1WflxtiDXVzTugOicC05k5W64oIqSHt0xNaFKE37k
//3eDMWbHvqHKFVdg7qcLsVPeVBaW3bdZUiexGM24OiGgyEitufnHQLOtEDTound
JyH5nUeHpvpBKIIOJZNBDM0HsRYnwKwrOWk3N2VRwog4J8J3cmJ/f9bPWNI/0OPT
qmtVGRVg6Ge83nZn51Vof//jFzkO4wGYCsE0aF0Ywc7nISZuyKQzmu/qgmwzDG50
PjpvIQ/ygisRPNdRlylXEqyoIDCQ+v0AnxhhwX/5dbt6xMuMMOxBrFSC94Zce1Vj
x2ssXlT4ONPnkI/YWwhtQPLU628IMg==
=GiS3
-----END PGP SIGNATURE-----
"""
And there is EML file "Inbox/encrypted-signed.eml"
"""
Subject: encrypted and signed
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
-----BEGIN PGP MESSAGE-----
hQEMA7hGUUsYs0fEAQf/dppHciWIf+o4l0gEfHeyHV/HVhG4es0aVQYrwFQlSWVx
estMuyLBSMfrsQXLago7Q9ZNo/XnKszzprCXxxYH52hAg64oAsjKB3jgRmVizs8b
8lj0BRf003wUluS/0msV9SiEZBGeL8jGq6Te9vaM8OHHhIVzVjGnRdTSC0jBE6cS
vy8IBHXYe0LfdZiPojPDPGQdSej+H3uu7eZGBvVHTDeQLPDel4k7Ykdr0qlNXs6O
5XpM5YG4w+t0aG+YROPH+BUj8PpPojQ/lrv/yFISTRbHlEd8N50w8BNTnBet+9Vm
oPcyvN+RQxBlvRuPpDjUmREvmtObKZV6+m6gocemx9LAzQEeVLcpjO/hJhl8gX72
MNz3McU7aXf5sSoOPdHDNx8T2NON/2bwG5FE+PRMuVywTKhCB7o8VAsJpGMQ8xRM
5WCNhow0AI7kni8yZA+GbvspnJWfit9tCTR5MIFHCSH9J3kJJnWkxQSN04GGpBcd
n43GWn7O7ufA4lMMZiGXMdi/J1iV9waAsIfMPk29BMq6xK0/jJYdHqQS+vNsSnF5
xL/Ir4RYq4SFFA06A/E7HpXr2ruZhBQCkzaIIdrVJR/Lp2VLJIVulTBQK8y2AFtj
JeeKS0kIuC/7UPF2O624kwNr8dmIhDJYusFs6ZeED/nAKwDO/vP2CSwVC3sUjn3N
u+sWqQUTxSmjhRVf9b0+VyTh0mXCovJQXomL6Zz6lxXuJqqzELIOfCxYD1z9GwTG
cT08Aa2eEpf3agdLCTxvjO3iq9FksMHvIN+LSCQ6Pw+aTByjrk0oMmvGbANAogTk
yrplG/iRVlmq0p/Cfl5UEjKqT/nt5j9zbpeuYXmhjiBT9SBE07oUVLY1VT7ihcY=
=qYnL
-----END PGP MESSAGE-----
"""
Scenario: Import encrypted
Given there is skip encrypted messages set to "false"
When user "user" imports local files
Then progress result is "OK"
And transfer failed for 0 messages
And transfer exported 5 messages
And transfer imported 5 messages
And transfer skipped 0 messages
And API mailbox "INBOX" for "user" has messages
| from | to | subject |
| bridgetest@pm.test | test@protonmail.com | clear |
| bridgetest@pm.test | test@protonmail.com | encrypted |
| bridgetest@pm.test | test@protonmail.com | encrypted mime |
| bridgetest@pm.test | test@protonmail.com | signed |
| bridgetest@pm.test | test@protonmail.com | encrypted and signed |
Scenario: Skip encrypted
Given there is skip encrypted messages set to "true"
When user "user" imports local files
Then progress result is "OK"
And transfer failed for 0 messages
And transfer exported 5 messages
And transfer imported 2 messages
And transfer skipped 3 messages
And API mailbox "INBOX" for "user" has messages
| from | to | subject |
| bridgetest@pm.test | test@protonmail.com | clear |
| bridgetest@pm.test | test@protonmail.com | signed |

View File

@ -1,49 +0,0 @@
Feature: Import-Export app
Background:
Given there is connected user "user"
And there is "user" with mailbox "Folders/Foo"
And there is "user" with mailbox "Folders/Bar"
Scenario: EML -> PM -> EML
Given there are EML files
| file | from | to | subject | time |
| Inbox/hello.eml | bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
| Foo/one.eml | foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| Foo/two.eml | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| Sub/Foo/three.eml | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
When user "user" imports local files
Then progress result is "OK"
And transfer failed for 0 messages
And transfer imported 4 messages
When user "user" exports to EML files
Then progress result is "OK"
And transfer failed for 0 messages
# Every message is also in All Mail.
And transfer imported 8 messages
And exported messages match the original ones
Scenario: MBOX -> PM -> MBOX
Given there is MBOX file "Inbox.mbox" with messages
| from | to | subject | time |
| bridgetest@pm.test | test@protonmail.com | hello | 2020-01-01T12:00:00 |
And there is MBOX file "Foo.mbox" with messages
| from | to | subject | time |
| foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
When user "user" imports local files
Then progress result is "OK"
And transfer failed for 0 messages
And transfer imported 4 messages
When user "user" exports to MBOX files
Then progress result is "OK"
And transfer failed for 0 messages
# Every message is also in All Mail.
And transfer imported 8 messages
And exported messages match the original ones

View File

@ -1,80 +0,0 @@
Feature: Import from IMAP server
Background:
Given there is connected user "user"
And there is "user" with mailbox "Folders/Foo"
And there is "user" with mailbox "Folders/Bar"
And there are IMAP mailboxes
| name |
| Inbox |
| Foo |
| Broken |
And there are IMAP messages
| mailbox | seqnum | uid | from | to | subject | time |
| Foo | 1 | 12 | foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| Foo | 2 | 14 | bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
| Foo | 3 | 15 | bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
And there is IMAP message in mailbox "Inbox" with seq 1, uid 42, time "2020-01-01T12:34:56" and subject "hello"
"""
Subject: hello
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
hello
"""
Scenario: Import all
When user "user" imports remote messages
Then progress result is "OK"
And transfer exported 4 messages
And transfer imported 4 messages
And transfer failed for 0 messages
And API mailbox "INBOX" for "user" has messages
| from | to | subject |
| bridgetest@pm.test | test@protonmail.com | hello |
And API mailbox "Folders/Foo" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@protonmail.com | one |
| bar@example.com | bridgetest@protonmail.com | two |
| bar@example.com | bridgetest@protonmail.com | three |
Scenario: Import only Foo to Bar with time limit
When user "user" imports remote messages with rules
| source | target | from | to |
| Foo | Bar | 2020-01-01T12:10:00 | 2020-01-01T13:00:00 |
Then progress result is "OK"
And transfer exported 2 messages
And transfer imported 2 messages
And transfer failed for 0 messages
And API mailbox "Folders/Bar" for "user" has messages
| from | to | subject |
| bar@example.com | bridgetest@protonmail.com | two |
| bar@example.com | bridgetest@protonmail.com | three |
# Note we need to have message which we can parse and use in go-imap
# but which has problem on our side. Used example with missing boundary
# is real example which we want to solve one day. Probabl this test
# can be removed once we import any time of message or switch is to
# something we will never allow.
Scenario: Import broken message
Given there is IMAP message in mailbox "Broken" with seq 1, uid 42, time "2020-01-01T12:34:56" and subject "broken"
"""
Subject: missing boundary end
Content-Type: multipart/related; boundary=boundary
--boundary
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=utf-8
body
"""
When user "user" imports remote messages with rules
| source | target |
| Broken | Foo |
Then progress result is "OK"
And transfer exported 1 messages
And transfer imported 0 messages
And transfer failed for 1 messages

View File

@ -1,65 +0,0 @@
Feature: Import from MBOX files
Background:
Given there is connected user "user"
And there is "user" with mailbox "Folders/Foo"
And there is "user" with mailbox "Folders/Bar"
And there is MBOX file "Foo.mbox" with messages
| from | to | subject | time |
| foo@example.com | bridgetest@protonmail.com | one | 2020-01-01T12:00:00 |
| bar@example.com | bridgetest@protonmail.com | two | 2020-01-01T13:00:00 |
And there is MBOX file "Sub/Foo.mbox" with messages
| from | to | subject | time |
| bar@example.com | bridgetest@protonmail.com | three | 2020-01-01T12:30:00 |
And there is MBOX file "Inbox.mbox"
"""
From bridgetest@pm.test Thu Feb 20 20:20:20 2020
Subject: hello
From: Bridge Test <bridgetest@pm.test>
To: Internal Bridge <test@protonmail.com>
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
hello
"""
Scenario: Import all
When user "user" imports local files
Then progress result is "OK"
And transfer exported 4 messages
And transfer imported 4 messages
And transfer failed for 0 messages
And API mailbox "INBOX" for "user" has messages
| from | to | subject |
| bridgetest@pm.test | test@protonmail.com | hello |
And API mailbox "Folders/Foo" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@protonmail.com | one |
| bar@example.com | bridgetest@protonmail.com | two |
| bar@example.com | bridgetest@protonmail.com | three |
Scenario: Import only Foo to Bar with time limit
When user "user" imports local files with rules
| source | target | from | to |
| Foo | Bar | 2020-01-01T12:10:00 | 2020-01-01T13:00:00 |
Then progress result is "OK"
And transfer exported 2 messages
And transfer imported 2 messages
And transfer failed for 0 messages
And API mailbox "Folders/Bar" for "user" has messages
| from | to | subject |
| bar@example.com | bridgetest@protonmail.com | two |
| bar@example.com | bridgetest@protonmail.com | three |
Scenario: Import broken message
Given there is MBOX file "Broken.mbox"
"""
From bridgetest@pm.test Thu Feb 20 20:20:20 2020
Content-type: multipart/mixed
"""
When user "user" imports local files with rules
| source | target |
| Broken | Foo |
Then progress result is "OK"
And transfer exported 1 messages
And transfer imported 0 messages
And transfer failed for 1 messages

View File

@ -1,98 +0,0 @@
Feature: Import to sent
Background:
Given there is connected user "user"
And there is "user" with mailbox "Labels/label"
And there is EML file "Sent/one.eml"
"""
Subject: one
From: Foo <foo@example.com>
To: Bridge Test <bridgetest@pm.test>
Message-ID: one.integrationtest
one
"""
And there is EML file "Sent/two.eml"
"""
Subject: two
From: Bar <bar@example.com>
To: Bridge Test <bridgetest@pm.test>
Message-ID: two.integrationtest
two
"""
Scenario: Import sent only
When user "user" imports local files
Then progress result is "OK"
And transfer exported 2 messages
And transfer imported 2 messages
And transfer failed for 0 messages
And API mailbox "INBOX" for "user" has 0 message
And API mailbox "Sent" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@pm.test | one |
| bar@example.com | bridgetest@pm.test | two |
# Messages imported to label only are added automatically to Archive folder.
# Then it depends on the order: if the message is first imported to Sent
# folder and later to that label with importing to Archive, message will not
# be in Sent but Archive. The order is semi-random for the big messages,
# e.g., it will do alphabetical order of mailboxes, but for under ten small
# messages the order is random every time (because we are importing in
# batches of up to ten messages and iterating through map we use to collect
# messages is random). So we cannot for this test ensure the same output
# every time.
@ignore-live
Scenario: Import to sent and custom label
And there is EML file "Label/one.eml"
"""
Subject: one
From: Foo <foo@example.com>
To: Bridge Test <bridgetest@pm.test>
Message-ID: one.integrationtest
one
"""
When user "user" imports local files
Then progress result is "OK"
And transfer exported 3 messages
And transfer imported 3 messages
And transfer failed for 0 messages
# We had an issue that moving message to Sent automatically added
# the message also into Inbox if the message was in some custom label.
And API mailbox "INBOX" for "user" has 0 message
And API mailbox "Labels/label" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@pm.test | one |
And API mailbox "Sent" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@pm.test | one |
| bar@example.com | bridgetest@pm.test | two |
Scenario: Import to sent and inbox is in both mailboxes
And there is EML file "Inbox/one.eml"
"""
Subject: one
From: Foo <foo@example.com>
To: Bridge Test <bridgetest@pm.test>
Message-ID: one.integrationtest
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
one
"""
When user "user" imports local files
Then progress result is "OK"
And transfer exported 3 messages
And transfer imported 3 messages
And transfer failed for 0 messages
And API mailbox "INBOX" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@pm.test | one |
And API mailbox "Sent" for "user" has messages
| from | to | subject |
| foo@example.com | bridgetest@pm.test | one |
| bar@example.com | bridgetest@pm.test | two |

View File

@ -1,20 +0,0 @@
Feature: Delete user
Scenario: Deleting connected user
Given there is connected user "user"
When user deletes "user"
Then last response is "OK"
Scenario: Deleting connected user with cache
Given there is connected user "user"
When user deletes "user" with cache
Then last response is "OK"
Scenario: Deleting disconnected user
Given there is disconnected user "user"
When user deletes "user"
Then last response is "OK"
Scenario: Deleting disconnected user with cache
Given there is disconnected user "user"
When user deletes "user" with cache
Then last response is "OK"

View File

@ -1,58 +0,0 @@
Feature: Login for the first time
Scenario: Normal login
Given there is user "user"
When "user" logs in
Then last response is "OK"
And "user" is connected
@ignore-live
Scenario: Login with bad username
When "user" logs in with bad password
Then last response is "failed to login: Incorrect login credentials. Please try again"
@ignore-live
Scenario: Login with bad password
Given there is user "user"
When "user" logs in with bad password
Then last response is "failed to login: Incorrect login credentials. Please try again"
Scenario: Login without internet connection
Given there is no internet connection
When "user" logs in
Then last response is "failed to login: no internet connection"
@ignore-live
Scenario: Login user with 2FA
Given there is user "user2fa"
When "user2fa" logs in
Then last response is "OK"
And "user2fa" is connected
Scenario: Login user with capital letters in address
Given there is user "userAddressWithCapitalLetter"
When "userAddressWithCapitalLetter" logs in
Then last response is "OK"
And "userAddressWithCapitalLetter" is connected
Scenario: Login user with more addresses
Given there is user "userMoreAddresses"
When "userMoreAddresses" logs in
Then last response is "OK"
And "userMoreAddresses" is connected
@ignore-live
Scenario: Login user with disabled primary address
Given there is user "userDisabledPrimaryAddress"
When "userDisabledPrimaryAddress" logs in
Then last response is "OK"
And "userDisabledPrimaryAddress" is connected
Scenario: Login two users
Given there is user "user"
And there is user "userMoreAddresses"
When "user" logs in
Then last response is "OK"
And "user" is connected
When "userMoreAddresses" logs in
Then last response is "OK"
And "userMoreAddresses" is connected

View File

@ -1,12 +0,0 @@
Feature: Re-login
Scenario: Re-login with connected user
Given there is connected user "user"
When "user" logs in
Then last response is "failed to finish login: user is already connected"
And "user" is connected
Scenario: Re-login with disconnected user
Given there is disconnected user "user"
When "user" logs in
Then last response is "OK"
And "user" is connected

View File

@ -0,0 +1,30 @@
Feature: Servers are closed when no internet
Scenario: All connection are closed and then restored multiple times
Given there is connected user "user"
And there is IMAP client "i1" logged in as "user"
And there is SMTP client "s1" logged in as "user"
When there is no internet connection
And 1 second pass
Then IMAP client "i1" is logged out
And SMTP client "s1" is logged out
Given the internet connection is restored
And 1 second pass
And there is IMAP client "i2" logged in as "user"
And there is SMTP client "s2" logged in as "user"
When IMAP client "i2" gets info of "INBOX"
When SMTP client "s2" sends "HELO example.com"
Then IMAP response to "i2" is "OK"
Then SMTP response to "s2" is "OK"
When there is no internet connection
And 1 second pass
Then IMAP client "i2" is logged out
And SMTP client "s2" is logged out
Given the internet connection is restored
And 1 second pass
And there is IMAP client "i3" logged in as "user"
And there is SMTP client "s3" logged in as "user"
When IMAP client "i3" gets info of "INBOX"
When SMTP client "s3" sends "HELO example.com"
Then IMAP response to "i3" is "OK"
Then SMTP response to "s3" is "OK"

View File

@ -38,7 +38,7 @@ type Controller struct {
noInternetConnection bool
}
func NewController(_ string) (*Controller, pmapi.Manager) {
func NewController() (*Controller, pmapi.Manager) {
controller := &Controller{
log: logrus.WithField("pkg", "live-controller"),
lock: &sync.RWMutex{},

View File

@ -1,227 +0,0 @@
// Copyright (c) 2021 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package tests
import (
"fmt"
"time"
"github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/cucumber/godog"
)
func TransferActionsFeatureContext(s *godog.ScenarioContext) {
s.Step(`^user "([^"]*)" imports local files$`, userImportsLocalFiles)
s.Step(`^user "([^"]*)" imports local files with rules$`, userImportsLocalFilesWithRules)
s.Step(`^user "([^"]*)" imports local files to address "([^"]*)"$`, userImportsLocalFilesToAddress)
s.Step(`^user "([^"]*)" imports local files to address "([^"]*)" with rules$`, userImportsLocalFilesToAddressWithRules)
s.Step(`^user "([^"]*)" imports remote messages$`, userImportsRemoteMessages)
s.Step(`^user "([^"]*)" imports remote messages with rules$`, userImportsRemoteMessagesWithRules)
s.Step(`^user "([^"]*)" imports remote messages to address "([^"]*)"$`, userImportsRemoteMessagesToAddress)
s.Step(`^user "([^"]*)" imports remote messages to address "([^"]*)" with rules$`, userImportsRemoteMessagesToAddressWithRules)
s.Step(`^user "([^"]*)" exports to EML files$`, userExportsToEMLFiles)
s.Step(`^user "([^"]*)" exports to EML files with rules$`, userExportsToEMLFilesWithRules)
s.Step(`^user "([^"]*)" exports address "([^"]*)" to EML files$`, userExportsAddressToEMLFiles)
s.Step(`^user "([^"]*)" exports address "([^"]*)" to EML files with rules$`, userExportsAddressToEMLFilesWithRules)
s.Step(`^user "([^"]*)" exports to MBOX files$`, userExportsToMBOXFiles)
s.Step(`^user "([^"]*)" exports to MBOX files with rules$`, userExportsToMBOXFilesWithRules)
s.Step(`^user "([^"]*)" exports address "([^"]*)" to MBOX files$`, userExportsAddressToMBOXFiles)
s.Step(`^user "([^"]*)" exports address "([^"]*)" to MBOX files with rules$`, userExportsAddressToMBOXFilesWithRules)
}
// Local import.
func userImportsLocalFiles(bddUserID string) error {
return userImportsLocalFilesToAddressWithRules(bddUserID, "", nil)
}
func userImportsLocalFilesWithRules(bddUserID string, rules *godog.Table) error {
return userImportsLocalFilesToAddressWithRules(bddUserID, "", rules)
}
func userImportsLocalFilesToAddress(bddUserID, bddAddressID string) error {
return userImportsLocalFilesToAddressWithRules(bddUserID, bddAddressID, nil)
}
func userImportsLocalFilesToAddressWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
path := ctx.GetTransferLocalRootForImport()
return ctx.GetImportExport().GetLocalImporter(username, address, path)
})
}
// Remote import.
func userImportsRemoteMessages(bddUserID string) error {
return userImportsRemoteMessagesToAddressWithRules(bddUserID, "", nil)
}
func userImportsRemoteMessagesWithRules(bddUserID string, rules *godog.Table) error {
return userImportsRemoteMessagesToAddressWithRules(bddUserID, "", rules)
}
func userImportsRemoteMessagesToAddress(bddUserID, bddAddressID string) error {
return userImportsRemoteMessagesToAddressWithRules(bddUserID, bddAddressID, nil)
}
func userImportsRemoteMessagesToAddressWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
imapServer := ctx.GetTransferRemoteIMAPServer()
return ctx.GetImportExport().GetRemoteImporter(username, address, imapServer.Username, imapServer.Password, imapServer.Host, imapServer.Port)
})
}
// EML export.
func userExportsToEMLFiles(bddUserID string) error {
return userExportsAddressToEMLFilesWithRules(bddUserID, "", nil)
}
func userExportsToEMLFilesWithRules(bddUserID string, rules *godog.Table) error {
return userExportsAddressToEMLFilesWithRules(bddUserID, "", rules)
}
func userExportsAddressToEMLFiles(bddUserID, bddAddressID string) error {
return userExportsAddressToEMLFilesWithRules(bddUserID, bddAddressID, nil)
}
func userExportsAddressToEMLFilesWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
path := ctx.GetTransferLocalRootForExport()
return ctx.GetImportExport().GetEMLExporter(username, address, path)
})
}
// MBOX export.
func userExportsToMBOXFiles(bddUserID string) error {
return userExportsAddressToMBOXFilesWithRules(bddUserID, "", nil)
}
func userExportsToMBOXFilesWithRules(bddUserID string, rules *godog.Table) error {
return userExportsAddressToMBOXFilesWithRules(bddUserID, "", rules)
}
func userExportsAddressToMBOXFiles(bddUserID, bddAddressID string) error {
return userExportsAddressToMBOXFilesWithRules(bddUserID, bddAddressID, nil)
}
func userExportsAddressToMBOXFilesWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
path := ctx.GetTransferLocalRootForExport()
return ctx.GetImportExport().GetMBOXExporter(username, address, path)
})
}
// Helpers.
func doTransfer(bddUserID, bddAddressID string, rules *godog.Table, getTransferrer func(string, string) (*transfer.Transfer, error)) error {
account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID)
if account == nil {
return godog.ErrPending
}
transferrer, err := getTransferrer(account.Username(), account.Address())
if err != nil {
return internalError(err, "failed to init transfer")
}
if err := setRules(transferrer, rules); err != nil {
return internalError(err, "failed to set rules")
}
transferrer.SetSkipEncryptedMessages(ctx.GetTransferSkipEncryptedMessages())
progress := transferrer.Start()
ctx.SetTransferProgress(progress)
return nil
}
func setRules(transferrer *transfer.Transfer, rules *godog.Table) error {
if rules == nil {
return nil
}
transferrer.ResetRules()
allSourceMailboxes, err := transferrer.SourceMailboxes()
if err != nil {
return internalError(err, "failed to get source mailboxes")
}
allTargetMailboxes, err := transferrer.TargetMailboxes()
if err != nil {
return internalError(err, "failed to get target mailboxes")
}
head := rules.Rows[0].Cells
for _, row := range rules.Rows[1:] {
source := ""
target := ""
fromTime := int64(0)
toTime := int64(0)
for n, cell := range row.Cells {
switch head[n].Value {
case "source":
source = cell.Value
case "target":
target = cell.Value
case "from":
date, err := time.Parse(timeFormat, cell.Value)
if err != nil {
return internalError(err, "failed to parse from time")
}
fromTime = date.Unix()
case "to":
date, err := time.Parse(timeFormat, cell.Value)
if err != nil {
return internalError(err, "failed to parse to time")
}
toTime = date.Unix()
default:
return fmt.Errorf("unexpected column name: %s", head[n].Value)
}
}
sourceMailbox, err := getMailboxByName(allSourceMailboxes, source)
if err != nil {
return internalError(err, "failed to match source mailboxes")
}
// Empty target means the same as source. Useful for exports.
targetMailboxes := []transfer.Mailbox{}
if target == "" {
targetMailboxes = append(targetMailboxes, sourceMailbox)
} else {
targetMailbox, err := getMailboxByName(allTargetMailboxes, target)
if err != nil {
return internalError(err, "failed to match target mailboxes")
}
targetMailboxes = append(targetMailboxes, targetMailbox)
}
if err := transferrer.SetRule(sourceMailbox, targetMailboxes, fromTime, toTime); err != nil {
return internalError(err, "failed to set rule")
}
}
return nil
}
func getMailboxByName(mailboxes []transfer.Mailbox, name string) (transfer.Mailbox, error) {
for _, mailbox := range mailboxes {
if mailbox.Name == name {
return mailbox, nil
}
}
return transfer.Mailbox{}, fmt.Errorf("mailbox %s not found", name)
}

View File

@ -1,277 +0,0 @@
// Copyright (c) 2021 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package tests
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/ProtonMail/go-rfc5322"
"github.com/cucumber/godog"
"github.com/emersion/go-mbox"
"github.com/emersion/go-message"
"github.com/pkg/errors"
a "github.com/stretchr/testify/assert"
)
func TransferChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^progress result is "([^"]*)"$`, progressFinishedWith)
s.Step(`^transfer exported (\d+) messages$`, transferExportedNumberOfMessages)
s.Step(`^transfer imported (\d+) messages$`, transferImportedNumberOfMessages)
s.Step(`^transfer skipped (\d+) messages$`, transferSkippedNumberOfMessages)
s.Step(`^transfer failed for (\d+) messages$`, transferFailedForNumberOfMessages)
s.Step(`^transfer exported messages$`, transferExportedMessages)
s.Step(`^exported messages match the original ones$`, exportedMessagesMatchTheOriginalOnes)
}
func progressFinishedWith(wantResponse string) error {
progress := ctx.GetTransferProgress()
// Wait till transport is finished.
updateCh := progress.GetUpdateChannel()
if updateCh != nil {
for range updateCh {
}
}
err := progress.GetFatalError()
if wantResponse == "OK" {
a.NoError(ctx.GetTestingT(), err)
} else {
a.EqualError(ctx.GetTestingT(), err, wantResponse)
}
return ctx.GetTestingError()
}
func transferExportedNumberOfMessages(wantCount int) error {
progress := ctx.GetTransferProgress()
counts := progress.GetCounts()
a.Equal(ctx.GetTestingT(), uint(wantCount), counts.Exported)
return ctx.GetTestingError()
}
func transferImportedNumberOfMessages(wantCount int) error {
progress := ctx.GetTransferProgress()
counts := progress.GetCounts()
a.Equal(ctx.GetTestingT(), uint(wantCount), counts.Imported)
return ctx.GetTestingError()
}
func transferSkippedNumberOfMessages(wantCount int) error {
progress := ctx.GetTransferProgress()
counts := progress.GetCounts()
a.Equal(ctx.GetTestingT(), uint(wantCount), counts.Skipped)
return ctx.GetTestingError()
}
func transferFailedForNumberOfMessages(wantCount int) error {
progress := ctx.GetTransferProgress()
failedMessages := progress.GetFailedMessages()
a.Equal(ctx.GetTestingT(), wantCount, len(failedMessages), "failed messages: %v", failedMessages)
return ctx.GetTestingError()
}
func transferExportedMessages(messages *godog.Table) error {
expectedMessages := map[string][]MessageAttributes{}
head := messages.Rows[0].Cells
for _, row := range messages.Rows[1:] {
folder := ""
msg := MessageAttributes{}
for n, cell := range row.Cells {
switch head[n].Value {
case "folder":
folder = cell.Value
case "subject":
msg.subject = cell.Value
case "from":
msg.from = cell.Value
case "to":
msg.to = []string{cell.Value}
case "time":
date, err := time.Parse(timeFormat, cell.Value)
if err != nil {
return internalError(err, "failed to parse time")
}
msg.date = date.Unix()
default:
return fmt.Errorf("unexpected column name: %s", head[n].Value)
}
}
expectedMessages[folder] = append(expectedMessages[folder], msg)
sort.Sort(BySubject(expectedMessages[folder]))
}
exportRoot := ctx.GetTransferLocalRootForExport()
exportedMessages, err := readMessages(exportRoot)
if err != nil {
return errors.Wrap(err, "scanning exported messages")
}
a.Equal(ctx.GetTestingT(), expectedMessages, exportedMessages)
return ctx.GetTestingError()
}
func exportedMessagesMatchTheOriginalOnes() error {
importRoot := ctx.GetTransferLocalRootForImport()
exportRoot := ctx.GetTransferLocalRootForExport()
importMessages, err := readMessages(importRoot)
if err != nil {
return errors.Wrap(err, "scanning messages for import")
}
exportMessages, err := readMessages(exportRoot)
if err != nil {
return errors.Wrap(err, "scanning exported messages")
}
delete(exportMessages, "All Mail") // Ignore All Mail.
a.Equal(ctx.GetTestingT(), importMessages, exportMessages)
return ctx.GetTestingError()
}
func readMessages(root string) (map[string][]MessageAttributes, error) {
files, err := ioutil.ReadDir(root)
if err != nil {
return nil, err
}
messagesPerLabel := map[string][]MessageAttributes{}
for _, file := range files {
if !file.IsDir() {
fileReader, err := os.Open(filepath.Join(root, file.Name()))
if err != nil {
return nil, errors.Wrap(err, "opening file")
}
if filepath.Ext(file.Name()) == ".eml" {
label := filepath.Base(root)
msg, err := readMessageAttributes(fileReader)
if err != nil {
return nil, err
}
messagesPerLabel[label] = append(messagesPerLabel[label], msg)
sort.Sort(BySubject(messagesPerLabel[label]))
} else if filepath.Ext(file.Name()) == ".mbox" {
label := strings.TrimSuffix(file.Name(), ".mbox")
mboxReader := mbox.NewReader(fileReader)
for {
msgReader, err := mboxReader.NextMessage()
if err == io.EOF {
break
} else if err != nil {
return nil, errors.Wrap(err, "reading next message")
}
msg, err := readMessageAttributes(msgReader)
if err != nil {
return nil, err
}
messagesPerLabel[label] = append(messagesPerLabel[label], msg)
}
sort.Sort(BySubject(messagesPerLabel[label]))
}
} else {
subfolderRoot := filepath.Join(root, file.Name())
subfolderMessagesPerLabel, err := readMessages(subfolderRoot)
if err != nil {
return nil, err
}
for key, value := range subfolderMessagesPerLabel {
messagesPerLabel[key] = append(messagesPerLabel[key], value...)
sort.Sort(BySubject(messagesPerLabel[key]))
}
}
}
return messagesPerLabel, nil
}
type MessageAttributes struct {
subject string
from string
to []string
date int64
}
func readMessageAttributes(fileReader io.Reader) (MessageAttributes, error) {
entity, err := message.Read(fileReader)
if err != nil {
return MessageAttributes{}, errors.Wrap(err, "reading file")
}
date, err := parseTime(entity.Header.Get("date"))
if err != nil {
return MessageAttributes{}, errors.Wrap(err, "parsing date")
}
from, err := parseAddress(entity.Header.Get("from"))
if err != nil {
return MessageAttributes{}, errors.Wrap(err, "parsing from")
}
to, err := parseAddresses(entity.Header.Get("to"))
if err != nil {
return MessageAttributes{}, errors.Wrap(err, "parsing to")
}
return MessageAttributes{
subject: entity.Header.Get("subject"),
from: from,
to: to,
date: date.Unix(),
}, nil
}
func parseTime(input string) (time.Time, error) {
for _, format := range []string{time.RFC1123, time.RFC1123Z} {
t, err := time.Parse(format, input)
if err == nil {
return t, nil
}
}
return time.Time{}, errors.New("Unrecognized time format")
}
func parseAddresses(input string) ([]string, error) {
addresses, err := rfc5322.ParseAddressList(input)
if err != nil {
return nil, err
}
result := []string{}
for _, address := range addresses {
result = append(result, address.Address)
}
return result, nil
}
func parseAddress(input string) (string, error) {
address, err := rfc5322.ParseAddressList(input)
if err != nil {
return "", err
}
return address[0].Address, nil
}
// BySubject implements sort.Interface based on the subject field.
type BySubject []MessageAttributes
func (a BySubject) Len() int { return len(a) }
func (a BySubject) Less(i, j int) bool { return a[i].subject < a[j].subject }
func (a BySubject) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

View File

@ -1,275 +0,0 @@
// Copyright (c) 2021 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package tests
import (
"bytes"
"fmt"
"net/textproto"
"os"
"path/filepath"
"strconv"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
"github.com/emersion/go-imap"
"github.com/emersion/go-mbox"
)
func TransferSetupFeatureContext(s *godog.ScenarioContext) {
s.Step(`^there are EML files$`, thereAreEMLFiles)
s.Step(`^there is EML file "([^"]*)"$`, thereIsEMLFile)
s.Step(`^there is MBOX file "([^"]*)" with messages$`, thereIsMBOXFileWithMessages)
s.Step(`^there is MBOX file "([^"]*)"$`, thereIsMBOXFile)
s.Step(`^there are IMAP mailboxes$`, thereAreIMAPMailboxes)
s.Step(`^there are IMAP messages$`, thereAreIMAPMessages)
s.Step(`^there is IMAP message in mailbox "([^"]*)" with seq (\d+), uid (\d+), time "([^"]*)" and subject "([^"]*)"$`, thereIsIMAPMessage)
s.Step(`^there is skip encrypted messages set to "([^"]*)"$`, thereIsSkipEncryptedMessagesSetTo)
}
func thereAreEMLFiles(messages *godog.Table) error {
head := messages.Rows[0].Cells
for _, row := range messages.Rows[1:] {
fileName := ""
for n, cell := range row.Cells {
switch head[n].Value {
case "file":
fileName = cell.Value
case "from", "to", "subject", "time", "body":
default:
return fmt.Errorf("unexpected column name: %s", head[n].Value)
}
}
body := getBodyFromDataRow(head, row)
if err := createFile(fileName, body); err != nil {
return err
}
}
return nil
}
func thereIsEMLFile(fileName string, message *godog.DocString) error {
return createFile(fileName, message.Content)
}
func thereIsMBOXFileWithMessages(fileName string, messages *godog.Table) error {
mboxBuffer := &bytes.Buffer{}
mboxWriter := mbox.NewWriter(mboxBuffer)
head := messages.Rows[0].Cells
for _, row := range messages.Rows[1:] {
from := ""
for n, cell := range row.Cells {
switch head[n].Value {
case "from":
from = cell.Value
case "to", "subject", "time", "body":
default:
return fmt.Errorf("unexpected column name: %s", head[n].Value)
}
}
body := getBodyFromDataRow(head, row)
messageWriter, err := mboxWriter.CreateMessage(from, time.Now())
if err != nil {
return err
}
_, err = messageWriter.Write([]byte(body))
if err != nil {
return err
}
}
return createFile(fileName, mboxBuffer.String())
}
func thereIsMBOXFile(fileName string, messages *godog.DocString) error {
return createFile(fileName, messages.Content)
}
func thereAreIMAPMailboxes(mailboxes *godog.Table) error {
imapServer := ctx.GetTransferRemoteIMAPServer()
head := mailboxes.Rows[0].Cells
for _, row := range mailboxes.Rows[1:] {
mailboxName := ""
for n, cell := range row.Cells {
switch head[n].Value {
case "name":
mailboxName = cell.Value
default:
return fmt.Errorf("unexpected column name: %s", head[n].Value)
}
}
imapServer.AddMailbox(mailboxName)
}
return nil
}
func thereAreIMAPMessages(messages *godog.Table) (err error) {
imapServer := ctx.GetTransferRemoteIMAPServer()
head := messages.Rows[0].Cells
for _, row := range messages.Rows[1:] {
mailboxName := ""
date := time.Now()
subject := ""
seqNum := 0
uid := 0
for n, cell := range row.Cells {
switch head[n].Value {
case "mailbox":
mailboxName = cell.Value
case "uid":
uid, err = strconv.Atoi(cell.Value)
if err != nil {
return internalError(err, "failed to parse uid")
}
case "seqnum":
seqNum, err = strconv.Atoi(cell.Value)
if err != nil {
return internalError(err, "failed to parse seqnum")
}
case "time":
date, err = time.Parse(timeFormat, cell.Value)
if err != nil {
return internalError(err, "failed to parse time")
}
case "subject":
subject = cell.Value
case "from", "to", "body":
default:
return fmt.Errorf("unexpected column name: %s", head[n].Value)
}
}
body := getBodyFromDataRow(head, row)
imapMessage, err := getIMAPMessage(seqNum, uid, date, subject, body)
if err != nil {
return err
}
imapServer.AddMessage(mailboxName, imapMessage)
}
return nil
}
func thereIsIMAPMessage(mailboxName string, seqNum, uid int, dateValue, subject string, message *godog.DocString) error {
imapServer := ctx.GetTransferRemoteIMAPServer()
date, err := time.Parse(timeFormat, dateValue)
if err != nil {
return internalError(err, "failed to parse time")
}
imapMessage, err := getIMAPMessage(seqNum, uid, date, subject, message.Content)
if err != nil {
return err
}
imapServer.AddMessage(mailboxName, imapMessage)
return nil
}
func getBodyFromDataRow(head []*messages.PickleTableCell, row *messages.PickleTableRow) string {
body := "hello"
headers := textproto.MIMEHeader{}
headers.Set("Received", "by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000")
for n, cell := range row.Cells {
switch head[n].Value {
case "from":
headers.Set("from", cell.Value)
case "to":
headers.Set("to", cell.Value)
case "subject":
headers.Set("subject", cell.Value)
case "time":
date, err := time.Parse(timeFormat, cell.Value)
if err != nil {
panic(err)
}
headers.Set("date", date.Format(time.RFC1123))
case "body":
body = cell.Value
}
}
buffer := &bytes.Buffer{}
_ = message.WriteHeader(buffer, headers)
return buffer.String() + body + "\n\n"
}
func getIMAPMessage(seqNum, uid int, date time.Time, subject, body string) (*imap.Message, error) {
reader := bytes.NewBufferString(body)
bodyStructure, err := message.NewBodyStructure(reader)
if err != nil {
return nil, internalError(err, "failed to parse body structure")
}
imapBodyStructure, err := bodyStructure.IMAPBodyStructure([]int{})
if err != nil {
return nil, internalError(err, "failed to parse body structure")
}
bodySection, _ := imap.ParseBodySectionName("BODY[]")
return &imap.Message{
SeqNum: uint32(seqNum),
Uid: uint32(uid),
Size: uint32(len(body)),
Envelope: &imap.Envelope{
Date: date,
Subject: subject,
},
BodyStructure: imapBodyStructure,
Body: map[*imap.BodySectionName]imap.Literal{
bodySection: bytes.NewBufferString(body),
},
}, nil
}
func createFile(fileName, body string) error {
root := ctx.GetTransferLocalRootForImport()
filePath := filepath.Join(root, fileName)
dirPath := filepath.Dir(filePath)
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return internalError(err, "failed to create dir")
}
f, err := os.Create(filePath)
if err != nil {
return internalError(err, "failed to create file")
}
defer f.Close() //nolint
_, err = f.WriteString(body)
return internalError(err, "failed to write to file")
}
func thereIsSkipEncryptedMessagesSetTo(value string) error {
switch value {
case "true":
ctx.SetTransferSkipEncryptedMessages(true)
case "false":
ctx.SetTransferSkipEncryptedMessages(false)
default:
return fmt.Errorf("expected either true or false, was %v", value)
}
return nil
}