Compare commits

...

33 Commits

Author SHA1 Message Date
b0ce46ca8a Other: Bridge James v1.8.12 2021-11-29 06:37:57 +01:00
6435f7b09a GODT-1432: Check if keys are active before unlocking 2021-11-23 18:48:49 +01:00
59075f2e26 GODT-1409 Wait in GetEvents during message preparation for live API. 2021-11-19 10:21:14 +01:00
1d9855a190 Other: Bridge James 1.8.11 2021-11-12 14:06:04 +01:00
cea33bebe2 GODT-1415: Only messages which are in Spam should be moved to INBOX once they are marked as not-a-spam.
This is regression of GODT-963
2021-11-09 10:32:58 +00:00
9d405a1549 GODT-1397: Update bbolt to v1.3.6 2021-11-09 10:01:08 +00:00
47f468e4b7 GODT-1410: Remove event ID from sentry report description 2021-11-08 16:05:34 +00:00
b9c6c00709 GODT-1395: CI should fail on go.sum changed. 2021-11-05 06:12:38 +01:00
5ce9cb8eec GODT-1405: Integration test fix: Prevent unilateral update in FETCH when copying message by append. 2021-11-01 16:32:28 +01:00
bc7133e401 Merge branch 'master' into devel 2021-10-21 13:26:29 +02:00
a219ecf3cb Other: fix go.sum 2021-10-21 13:25:44 +02:00
8061b1e6fa GODT-1392: added unit test for get message 2021-10-15 14:01:04 +02:00
6509df523f GODT-1360: added extra check.
This should help diagnostic test failure next time it happens.
2021-10-08 09:01:02 +00:00
0d1abaec0d Other: fixed new copy test feature. 2021-10-07 11:31:56 +00:00
4d1ace5de7 Other: Copy from All Mail allowed again.
Creates duplicate.
Added test scenario.
2021-10-07 11:31:56 +00:00
1250621a4d GODT-963 STORE removing junk or adding nojunk should move message to inbox 2021-10-07 11:31:56 +00:00
8d6e55ba54 GODT-965 MOVE command should end with error for All Mail 2021-10-07 11:31:56 +00:00
a4a29cbf82 Other Improved tests in move_without_support 2021-10-07 11:31:56 +00:00
39bccc2747 GODT-968 Messages in All Mail should not be able to mark as deleted
Feature already implemented.
A test already existed, but lacked the final expunge check.
2021-10-07 11:31:56 +00:00
0cf1b38c2b GODT-967 Append external message to All Mail should be APPEND to Archive instead 2021-10-07 11:31:56 +00:00
6b7e706100 GODT-966 Do nothing if message APPEND to All Mail is already in Archive. 2021-10-07 11:31:56 +00:00
bb90ed5446 GODT-966: Fixed comment. 2021-10-07 11:31:56 +00:00
107843d58f GODT-966: return correct UID in response to APPEND to All Mail. 2021-10-07 11:31:56 +00:00
63f089540e GODT-966 Append internal message to AllMail should be no action
GODT-966 Fixed CI issues (WIP)

WIP GODT-966 modified behavior of APPEND.

WIP GODT-966 implemented test for possible order of operations.

WIP GODT-966: code cleanup and refactoring.

WIP GODT-966 fix for linter.

Other: Minor refactoring.
2021-10-07 11:31:56 +00:00
c35ff4f913 Other: James v1.8.10 2021-09-30 22:19:14 +02:00
cd6b5cdcc3 GODT-1348: max 100 conn per host 2021-09-30 21:19:11 +02:00
8f1ca00c9d GODT-1318 go-srp version identifier in go.sum underlying commit is identical 2021-09-30 20:19:07 +02:00
f21f583d05 GODT-1202: Do not update package if it's version older than launcher 2021-09-23 16:24:11 +00:00
e940d9f6fe GODT-1204: Handle importing too big messages 2021-09-23 11:09:56 +00:00
1ed8e939b8 Other: typo 2021-09-20 14:28:57 +02:00
b5d63783f7 GODT-1318: Bump gopenpgp to 2.2.2, go-srp to a843a0b9, go-crypto to 52430bf6 2021-09-17 11:54:55 +02:00
e0113ec267 GODT-219: Update to godog v0.12.1 2021-09-10 13:38:05 +02:00
22d2bcc21d GODT-1205: "RCPT TO" does not contain all addressed from "CC" 2021-09-06 21:13:37 +00:00
78 changed files with 1231 additions and 327 deletions

View File

@ -85,11 +85,17 @@ dependency-updates:
stage: build stage: build
only: only:
- branches - branches
before_script:
- mkdir -p .cache/bin
- export PATH=$(pwd)/.cache/bin:$PATH
- export GOPATH="$CI_PROJECT_DIR/.cache"
script: script:
- make build - make build
- git diff && git diff-index --quiet HEAD
artifacts: artifacts:
# Note: The latest artifacts for refs are locked against deletion, and kept regardless of the expiry time. # Note: The latest artifacts for refs are locked against deletion, and kept
# Introduced in GitLab 13.0 behind a disabled feature flag, and made the default behavior in GitLab 13.4. # regardless of the expiry time. Introduced in GitLab 13.0 behind a
# disabled feature flag, and made the default behavior in GitLab 13.4.
expire_in: 1 day expire_in: 1 day
tags: tags:
- large - large

View File

@ -2,6 +2,43 @@
Changelog [format](http://keepachangelog.com/en/1.0.0/) Changelog [format](http://keepachangelog.com/en/1.0.0/)
## [Bridge 1.8.12] James
### Fixed
* GODT-1432: Check if keys are active before unlocking.
## [Bridge 1.8.11] James
### Fixed
* GODT-1415: Only messages which are in Spam should be moved to INBOX once they are marked as not-a-spam.
* GODT-1405: Integration test fix: Prevent unilateral update in FETCH when copying message by append.
* GODT-1392: Fix broken header fields for attachments.
* GODT-1360: Fix live integration test.
* GODT-968: Messages in All Mail should not be able to mark as deleted.
* GODT-967: Append external message to All Mail should be APPEND to Archive instead.
* GODT-966: Append internal message to AllMail should be no action.
* GODT-965: MOVE command should end with error for All Mail.
* GODT-963: STORE removing junk or adding nojunk should move message to inbox.
### Changed
* GODT-1397: Update bbolt to v1.3.6.
* GODT-1410: Remove event ID from sentry report description.
* GODT-1395: CI should fail on go.sum changed.
## [Bridge 1.8.10] James
### Fixed
* GODT-1348: Max 100 conn per host.
* GODT-1204: Handle importing too big messages.
* GODT-1202: Do not update package if it's version older than launcher.
* GODT-1318: Bump gopenpgp to v2.2.2, go-srp to v0.0.1, go-crypto to 52430bf6.
* GODT-219: Update to godog v0.12.1.
* GODT-1205: "RCPT TO" does not contain all addressed from "CC".
* GODT-1103: Cleanup on windows when uninstalling Bridge.
## [Bridge 1.8.9] James ## [Bridge 1.8.9] James
### Fixed ### Fixed

View File

@ -10,7 +10,7 @@ TARGET_OS?=${GOOS}
.PHONY: build build-ie build-nogui build-ie-nogui build-launcher build-launcher-ie versioner hasher .PHONY: build build-ie build-nogui build-ie-nogui build-launcher build-launcher-ie versioner hasher
# Keep version hardcoded so app build works also without Git repository. # Keep version hardcoded so app build works also without Git repository.
BRIDGE_APP_VERSION?=1.8.9+git BRIDGE_APP_VERSION?=1.8.12+git
IE_APP_VERSION?=1.3.3+git IE_APP_VERSION?=1.3.3+git
APP_VERSION:=${BRIDGE_APP_VERSION} APP_VERSION:=${BRIDGE_APP_VERSION}
SRC_ICO:=logo.ico SRC_ICO:=logo.ico

View File

@ -24,6 +24,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"github.com/Masterminds/semver/v3"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/proton-bridge/internal/config/useragent" "github.com/ProtonMail/proton-bridge/internal/config/useragent"
"github.com/ProtonMail/proton-bridge/internal/constants" "github.com/ProtonMail/proton-bridge/internal/constants"
@ -86,7 +87,7 @@ func main() { // nolint[funlen]
versioner := versioner.New(updatesPath) versioner := versioner.New(updatesPath)
exe, err := getPathToExecutable(ExeName, versioner, kr, reporter) exe, err := getPathToUpdatedExecutable(ExeName, versioner, kr, reporter)
if err != nil { if err != nil {
if exe, err = getFallbackExecutable(ExeName, versioner); err != nil { if exe, err = getFallbackExecutable(ExeName, versioner); err != nil {
logrus.WithError(err).Fatal("Failed to find any launchable executable") logrus.WithError(err).Fatal("Failed to find any launchable executable")
@ -142,7 +143,7 @@ func appendLauncherPath(path string, args []string) []string {
return res return res
} }
func getPathToExecutable( func getPathToUpdatedExecutable(
name string, name string,
versioner *versioner.Versioner, versioner *versioner.Versioner,
kr *crypto.KeyRing, kr *crypto.KeyRing,
@ -153,6 +154,11 @@ func getPathToExecutable(
return "", errors.Wrap(err, "failed to list available versions") return "", errors.Wrap(err, "failed to list available versions")
} }
currentVersion, err := semver.StrictNewVersion(constants.Version)
if err != nil {
logrus.WithField("version", constants.Version).WithError(err).Error("Failed to parse current version")
}
for _, version := range versions { for _, version := range versions {
vlog := logrus.WithField("version", version) vlog := logrus.WithField("version", version)
@ -170,6 +176,11 @@ func getPathToExecutable(
continue continue
} }
// Skip versions that are less or equal to launcher version.
if currentVersion != nil && !version.SemVer().GreaterThan(currentVersion) {
continue
}
exe, err := version.GetExecutable(name) exe, err := version.GetExecutable(name)
if err != nil { if err != nil {
vlog.WithError(err).Error("Failed to get executable") vlog.WithError(err).Error("Failed to get executable")
@ -179,7 +190,7 @@ func getPathToExecutable(
return exe, nil return exe, nil
} }
return "", errors.New("no available versions") return "", errors.New("no available newer versions")
} }
func getFallbackExecutable(name string, versioner *versioner.Versioner) (string, error) { func getFallbackExecutable(name string, versioner *versioner.Versioner) (string, error) {

View File

@ -1,12 +1,12 @@
# Encryption # Encryption
Encryption is done in PMAPI, bridge utils and bridge itself. The best would be to keep encryption Encryption is done in PMAPI, bridge utils and bridge itself. The best would be to keep encryption
in PMAPI and bridge utils (in pacakge such as messages). All packages are using our high-level in PMAPI and bridge utils (in package such as messages). All packages are using our high-level
GopenPGP library on top of openpgp. GopenPGP library on top of OpenPGP.
## `gopenpgp.KeyRing` ## `gopenpgp.KeyRing`
We use one `KeyRing` per address. Our usage then contains all keys for specific address. Primary We use one `KeyRing` per address. Our usage then contains all keys for specific address. Primary
key is always on the first position, then there old ones to be able to decrypt last e-mail. key is always on the first position, then there old ones to be able to decrypt last e-mail.
Openpgp encrypts given message with all available keys, so we need to first get first (primary) OpenPGP encrypts given message with all available keys, so we need to first get first (primary)
key for encryption to have message encrypted only once with primary key. key for encryption to have message encrypted only once with primary key.

14
go.mod
View File

@ -14,19 +14,20 @@ require (
github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1 github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1
github.com/Masterminds/semver/v3 v3.1.0 github.com/Masterminds/semver/v3 v3.1.0
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde
github.com/ProtonMail/go-rfc5322 v0.8.0 github.com/ProtonMail/go-rfc5322 v0.8.0
github.com/ProtonMail/go-srp v0.0.0-20210514134713-bd9454f3fa01 github.com/ProtonMail/go-srp v0.0.1
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
github.com/ProtonMail/gopenpgp/v2 v2.1.10 github.com/ProtonMail/gopenpgp/v2 v2.2.2
github.com/PuerkitoBio/goquery v1.5.1 github.com/PuerkitoBio/goquery v1.5.1
github.com/abiosoft/ishell v2.0.0+incompatible github.com/abiosoft/ishell v2.0.0+incompatible
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc
github.com/chzyer/logex v1.1.10 // indirect github.com/chzyer/logex v1.1.10 // indirect
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
github.com/cucumber/godog v0.8.1 github.com/cucumber/godog v0.12.1
github.com/cucumber/messages-go/v16 v16.0.1
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342 github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342
github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c
@ -57,17 +58,16 @@ require (
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.7.0
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e
github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200904063919-c0c124a5770d // indirect github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200904063919-c0c124a5770d // indirect
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200904063919-c0c124a5770d // indirect github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200904063919-c0c124a5770d // indirect
github.com/urfave/cli/v2 v2.2.0 github.com/urfave/cli/v2 v2.2.0
github.com/vmihailenco/msgpack/v5 v5.1.3 github.com/vmihailenco/msgpack/v5 v5.1.3
go.etcd.io/bbolt v1.3.5 go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec
) )
replace ( replace (

280
go.sum
View File

@ -1,3 +1,16 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1 h1:j9HaafapDbPbGRDku6e/HRs6KBMcKHiWcm1/9Sbxnl4= github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1 h1:j9HaafapDbPbGRDku6e/HRs6KBMcKHiWcm1/9Sbxnl4=
github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1/go.mod h1:NtXa9WwQsukMHZpjNakTTz0LArxvGYdPA9CjIcUSZ6s= github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1/go.mod h1:NtXa9WwQsukMHZpjNakTTz0LArxvGYdPA9CjIcUSZ6s=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
@ -8,14 +21,17 @@ github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMd
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57 h1:pHA4K54ifoogVLunGGHi3xyF5Nz4x+Uh3dJuy3NwGQQ= github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57 h1:pHA4K54ifoogVLunGGHi3xyF5Nz4x+Uh3dJuy3NwGQQ=
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I= github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk= github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a h1:fXK2KsfnkBV9Nh+9SKzHchYjuE9s0vI20JG1mbtEAcc= github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a h1:fXK2KsfnkBV9Nh+9SKzHchYjuE9s0vI20JG1mbtEAcc=
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c h1:FP7mMdsXy0ybzar1sJeIcZtaJka0U/ZmLTW4wRpolYk=
github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac h1:2xU3QncAiS/W3UlWZTkbNKW5WkLzk6Egl1T0xX+sbjs= github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac h1:2xU3QncAiS/W3UlWZTkbNKW5WkLzk6Egl1T0xX+sbjs=
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU= github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde h1:5koQozTDELymYOyFbQ/VSubexAEXzDR8qGM5mO8GRdw= github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde h1:5koQozTDELymYOyFbQ/VSubexAEXzDR8qGM5mO8GRdw=
@ -26,12 +42,14 @@ github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
github.com/ProtonMail/go-rfc5322 v0.8.0 h1:7emrf75n3CDIduQflx7aT1nJa5h/kGsiFKUYX/+IAkU= github.com/ProtonMail/go-rfc5322 v0.8.0 h1:7emrf75n3CDIduQflx7aT1nJa5h/kGsiFKUYX/+IAkU=
github.com/ProtonMail/go-rfc5322 v0.8.0/go.mod h1:BwpTbkJxkMGkc+pC84AXZnwuWOisEULBpfPIyIKS/Us= github.com/ProtonMail/go-rfc5322 v0.8.0/go.mod h1:BwpTbkJxkMGkc+pC84AXZnwuWOisEULBpfPIyIKS/Us=
github.com/ProtonMail/go-srp v0.0.0-20210514134713-bd9454f3fa01 h1:sRxNvPGnJFh6yWlSr9BpGsSrshFkZLClSm5oIi++a0I= github.com/ProtonMail/go-srp v0.0.0-20210910093455-a843a0b9adff h1:eiue56XAPSkOpsy5Fwnyz4+Vd7i2cN5D4orc++Irt1g=
github.com/ProtonMail/go-srp v0.0.0-20210514134713-bd9454f3fa01/go.mod h1:jOXzdvWTILIJzl83yzi/EZcnnhpI+A/5EyflaeVfi/0= github.com/ProtonMail/go-srp v0.0.0-20210910093455-a843a0b9adff/go.mod h1:Uvv5cqSGCs8MTZ8sbKiCkBnaB6/OA3eq2mc77tl2VVA=
github.com/ProtonMail/go-srp v0.0.1 h1:J0O9Zb5XTC6iDrB7feH41cu+TUEB+l7uHctXIK6oS2o=
github.com/ProtonMail/go-srp v0.0.1/go.mod h1:Uvv5cqSGCs8MTZ8sbKiCkBnaB6/OA3eq2mc77tl2VVA=
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ= github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA= github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA=
github.com/ProtonMail/gopenpgp/v2 v2.1.10 h1:WPwpzVQFvzzFIzHCsJ1RzuVkN4JsMIydl/0vc05yU0E= github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
github.com/ProtonMail/gopenpgp/v2 v2.1.10/go.mod h1:CHIXesUdnPxIxtJTg2P/cxoA0cvUwIBpZIS8SsY82QA= github.com/ProtonMail/gopenpgp/v2 v2.2.2/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6kK9XlZYGiDCvpM=
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
@ -40,29 +58,53 @@ github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzg
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8= github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc h1:mZca0/HZ/XWXP9txkfdl2GH6mUzBqAlyJz3u5Lg8fuA= github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc h1:mZca0/HZ/XWXP9txkfdl2GH6mUzBqAlyJz3u5Lg8fuA=
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc/go.mod h1:qqsTQiwdyqxU05iDCsi0oN3P4nrVxAmn8xCtODDSf/U= github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc/go.mod h1:qqsTQiwdyqxU05iDCsi0oN3P4nrVxAmn8xCtODDSf/U=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antlr/antlr4 v0.0.0-20201029161626-9a95f0cc3d7c h1:j/C2kxPfyE0d87/ggAjIsCV5Cdkqmjb+O0W8W+1J+IY= github.com/antlr/antlr4 v0.0.0-20201029161626-9a95f0cc3d7c h1:j/C2kxPfyE0d87/ggAjIsCV5Cdkqmjb+O0W8W+1J+IY=
github.com/antlr/antlr4 v0.0.0-20201029161626-9a95f0cc3d7c/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= github.com/antlr/antlr4 v0.0.0-20201029161626-9a95f0cc3d7c/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= github.com/cronokirby/saferith v0.31.0 h1:TIlhldetKLeGAb19bZvWiuwQEzfzwSPthDEyJ9Ah8xs=
github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= github.com/cronokirby/saferith v0.31.0/go.mod h1:QKJhjoqUtBsXCAVEjw38mFqoi7DebT7kthcD7UzbnoA=
github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE=
github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw=
github.com/cucumber/godog v0.12.1 h1:IhWVYFKDReM5WsuA9AuRLRPWOyvFNO9UBUKrNfLPais=
github.com/cucumber/godog v0.12.1/go.mod h1:u6SD7IXC49dLpPN35kal0oYEjsXZWee4pW6Tm9t5pIc=
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY=
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g= github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g=
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -71,6 +113,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a h1:bMdSPm6sssuOFpIaveu3XGAijMS3Tq2S3EqFZmZxidc= github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a h1:bMdSPm6sssuOFpIaveu3XGAijMS3Tq2S3EqFZmZxidc=
@ -94,6 +137,7 @@ github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5 h1:n9qx98xiS5V4x
github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5/go.mod h1:WIi9g8OKJQHXtQbx7GExlo6UAFaui9WDMYabJ+Be4WI= github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5/go.mod h1:WIi9g8OKJQHXtQbx7GExlo6UAFaui9WDMYabJ+Be4WI=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
@ -103,40 +147,96 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/getsentry/sentry-go v0.8.0 h1:F52cjBVLuiTfdW6p4JFuxlt3pOjKfWYT/aka7cdJ7v0= github.com/getsentry/sentry-go v0.8.0 h1:F52cjBVLuiTfdW6p4JFuxlt3pOjKfWYT/aka7cdJ7v0=
github.com/getsentry/sentry-go v0.8.0/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws= github.com/getsentry/sentry-go v0.8.0/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKYS4= github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKYS4=
github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q= github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE=
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.3.0 h1:xdXq34gBOMEloa9rlGStLxmfX/dyIK8htOv36dQUwHU=
github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@ -147,9 +247,13 @@ github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0Gqw
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc= github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
@ -159,12 +263,17 @@ github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubc
github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d h1:gVjhBCfVGl32RIBooOANzfw+0UqX8HU+yPlMv8vypcg= github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d h1:gVjhBCfVGl32RIBooOANzfw+0UqX8HU+yPlMv8vypcg=
github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d/go.mod h1:W6EbaYmb4RldPn0N3gvVHjY1wmU59kbymhW9NATWhwY= github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d/go.mod h1:W6EbaYmb4RldPn0N3gvVHjY1wmU59kbymhW9NATWhwY=
github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE= github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -174,9 +283,12 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
@ -186,61 +298,95 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758= github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -251,14 +397,17 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e h1:G0DQ/TRQyrEZjtLlLwevFjaRiG8eeCMlq9WXQ2OO2bk= github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e h1:G0DQ/TRQyrEZjtLlLwevFjaRiG8eeCMlq9WXQ2OO2bk=
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us= github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200904063919-c0c124a5770d h1:hAZyEG2swPRWjF0kqqdGERXUazYnRJdAk4a58f14z7Y= github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200904063919-c0c124a5770d h1:hAZyEG2swPRWjF0kqqdGERXUazYnRJdAk4a58f14z7Y=
github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200904063919-c0c124a5770d/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc= github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200904063919-c0c124a5770d/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc=
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200904063919-c0c124a5770d h1:AJRoBel/g9cDS+yE8BcN3E+TDD/xNAguG21aoR8DAIE= github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200904063919-c0c124a5770d h1:AJRoBel/g9cDS+yE8BcN3E+TDD/xNAguG21aoR8DAIE=
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200904063919-c0c124a5770d/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4= github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200904063919-c0c124a5770d/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@ -277,40 +426,72 @@ github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190910184405-b558ed863381/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -318,42 +499,74 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec h1:A1qYjneJuzBZZ2gIB8rd6zrfq6l7SoEMJ8EsSilNK/U= golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec h1:A1qYjneJuzBZZ2gIB8rd6zrfq6l7SoEMJ8EsSilNK/U=
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@ -361,18 +574,53 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@ -20,6 +20,7 @@ package imap
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"fmt"
"io/ioutil" "io/ioutil"
"net/mail" "net/mail"
"strings" "strings"
@ -116,11 +117,10 @@ func (im *imapMailbox) createMessage(imapFlags []string, date time.Time, r imap.
if internalID != "" { if internalID != "" {
if msg, err := im.storeMailbox.GetMessage(internalID); err == nil { if msg, err := im.storeMailbox.GetMessage(internalID); err == nil {
if im.user.user.IsCombinedAddressMode() || im.storeAddress.AddressID() == msg.Message().AddressID { if im.user.user.IsCombinedAddressMode() || im.storeAddress.AddressID() == msg.Message().AddressID {
return im.labelExistingMessage(msg.ID(), msg.IsMarkedDeleted()) return im.labelExistingMessage(msg)
} }
} }
} }
return im.importMessage(kr, hdr, body, imapFlags, date) return im.importMessage(kr, hdr, body, imapFlags, date)
} }
@ -146,7 +146,17 @@ func (im *imapMailbox) createDraftMessage(kr *crypto.KeyRing, email string, body
return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{draft.ID})) return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{draft.ID}))
} }
func (im *imapMailbox) labelExistingMessage(messageID string, isDeleted bool) error { func findMailboxForAddress(address storeAddressProvider, labelID string) (storeMailboxProvider, error) {
for _, mailBox := range address.ListMailboxes() {
if mailBox.LabelID() == labelID {
return mailBox, nil
}
}
return nil, fmt.Errorf("could not find %v label in mailbox for user %v", labelID,
address.AddressString())
}
func (im *imapMailbox) labelExistingMessage(msg storeMessageProvider) error { //nolint[funlen]
im.log.Info("Labelling existing message") im.log.Info("Labelling existing message")
// IMAP clients can move message to local folder (setting \Deleted flag) // IMAP clients can move message to local folder (setting \Deleted flag)
@ -156,20 +166,37 @@ func (im *imapMailbox) labelExistingMessage(messageID string, isDeleted bool) er
// not delete the message (EXPUNGE would delete the original message and // not delete the message (EXPUNGE would delete the original message and
// the new duplicate one would stay). API detects duplicates; therefore // the new duplicate one would stay). API detects duplicates; therefore
// we need to remove \Deleted flag if IMAP client re-imports. // we need to remove \Deleted flag if IMAP client re-imports.
if isDeleted { if msg.IsMarkedDeleted() {
if err := im.storeMailbox.MarkMessagesUndeleted([]string{messageID}); err != nil { if err := im.storeMailbox.MarkMessagesUndeleted([]string{msg.ID()}); err != nil {
log.WithError(err).Error("Failed to undelete re-imported message") log.WithError(err).Error("Failed to undelete re-imported message")
} }
} }
if err := im.storeMailbox.LabelMessages([]string{messageID}); err != nil { // Outlook Uses APPEND instead of COPY. There is no need to copy to All Mail because messages are already there.
// If the message is copied from Spam or Trash, it must be moved otherwise we will have data loss.
// If the message is moved from any folder, the moment when expunge happens on source we will move message trash unless we move it to archive.
// If the message is already in Archive we should not call API at all.
// Otherwise the message is already in All mail, Return OK.
var storeMBox = im.storeMailbox
if pmapi.AllMailLabel == storeMBox.LabelID() {
if msg.Message().HasLabelID(pmapi.ArchiveLabel) {
return uidplus.AppendResponse(storeMBox.UIDValidity(), storeMBox.GetUIDList([]string{msg.ID()}))
}
var err error
storeMBox, err = findMailboxForAddress(im.storeAddress, pmapi.ArchiveLabel)
if err != nil {
return err
}
}
if err := storeMBox.LabelMessages([]string{msg.ID()}); err != nil {
return err return err
} }
return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{messageID})) return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{msg.ID()}))
} }
func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, body []byte, imapFlags []string, date time.Time) error { func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, body []byte, imapFlags []string, date time.Time) error { //nolint[funlen]
im.log.Info("Importing external message") im.log.Info("Importing external message")
var ( var (
@ -211,18 +238,29 @@ func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, b
return err return err
} }
messageID, err := im.storeMailbox.ImportMessage(enc, seen, labelIDs, flags, time) var targetMailbox = im.storeMailbox
if targetMailbox.LabelID() == pmapi.AllMailLabel {
// Importing mail in directly into All Mail is not allowed. Instead we redirect the import to Archive
// The mail will automatically appear in All mail. The appends response still reports that the mail was
// successfully APPEND to All Mail.
targetMailbox, err = findMailboxForAddress(im.storeAddress, pmapi.ArchiveLabel)
if err != nil {
return err
}
}
messageID, err := targetMailbox.ImportMessage(enc, seen, labelIDs, flags, time)
if err != nil { if err != nil {
return err return err
} }
msg, err := im.storeMailbox.GetMessage(messageID) msg, err := targetMailbox.GetMessage(messageID)
if err != nil { if err != nil {
return err return err
} }
if msg.IsMarkedDeleted() { if msg.IsMarkedDeleted() {
if err := im.storeMailbox.MarkMessagesUndeleted([]string{messageID}); err != nil { if err := targetMailbox.MarkMessagesUndeleted([]string{messageID}); err != nil {
log.WithError(err).Error("Failed to undelete re-imported message") log.WithError(err).Error("Failed to undelete re-imported message")
} }
} }

View File

@ -137,8 +137,14 @@ func (im *imapMailbox) setFlags(messageIDs, flags []string) error { //nolint
return nil return nil
} }
func (im *imapMailbox) addOrRemoveFlags(operation imap.FlagsOp, messageIDs, flags []string) error { func (im *imapMailbox) addOrRemoveFlags(operation imap.FlagsOp, messageIDs, flags []string) error { //nolint[funlen]
for _, f := range flags { for _, f := range flags {
// Adding flag 'nojunk' is equivalent to removing flag 'junk'
if (operation == imap.AddFlags) && (f == "nojunk") {
operation = imap.RemoveFlags
f = "junk"
}
switch f { switch f {
case imap.SeenFlag: case imap.SeenFlag:
switch operation { //nolint[exhaustive] imap.SetFlags is processed by im.setFlags switch operation { //nolint[exhaustive] imap.SetFlags is processed by im.setFlags
@ -175,23 +181,37 @@ func (im *imapMailbox) addOrRemoveFlags(operation imap.FlagsOp, messageIDs, flag
} }
case imap.AnsweredFlag, imap.DraftFlag, imap.RecentFlag: case imap.AnsweredFlag, imap.DraftFlag, imap.RecentFlag:
// Not supported. // Not supported.
case message.AppleMailJunkFlag, message.ThunderbirdJunkFlag: case strings.ToLower(message.AppleMailJunkFlag), strings.ToLower(message.ThunderbirdJunkFlag):
storeMailbox, err := im.storeAddress.GetMailbox("Spam") spamMailbox, err := im.storeAddress.GetMailbox("Spam")
if err != nil { if err != nil {
return err return err
} }
// Handle custom junk flags for Apple Mail and Thunderbird. // Handle custom junk flags for Apple Mail and Thunderbird.
switch operation { //nolint[exhaustive] imap.SetFlag is processed by im.setFlags switch operation { //nolint[exhaustive] imap.SetFlag is processed by im.setFlags
// No label removal is necessary because Spam and Inbox are both exclusive labels so the backend // No label removal is necessary because Spam and Inbox are both exclusive labels so the backend
// will automatically take care of label removal. // will automatically take care of label removal.
case imap.AddFlags: case imap.AddFlags:
if err := storeMailbox.LabelMessages(messageIDs); err != nil { if err := spamMailbox.LabelMessages(messageIDs); err != nil {
return err return err
} }
case imap.RemoveFlags: case imap.RemoveFlags:
if err := storeMailbox.UnlabelMessages(messageIDs); err != nil { // During spam flag removal only messages which
return err // are in Spam folder should be moved to Inbox.
// For other messages it is NOOP.
messagesInSpam := []string{}
for _, mID := range messageIDs {
if uid := spamMailbox.GetUIDList([]string{mID}); len(*uid) != 0 {
messagesInSpam = append(messagesInSpam, mID)
}
}
if len(messagesInSpam) != 0 {
inboxMailbox, err := im.storeAddress.GetMailbox("INBOX")
if err != nil {
return err
}
if err := inboxMailbox.LabelMessages(messagesInSpam); err != nil {
return err
}
} }
} }
} }
@ -230,6 +250,10 @@ func (im *imapMailbox) moveMessages(uid bool, seqSet *imap.SeqSet, targetLabel s
// Called from go-imap in goroutines - we need to handle panics for each function. // Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic() defer im.panicHandler.HandlePanic()
// Moving from All Mail is not allowed.
if im.storeMailbox.LabelID() == pmapi.AllMailLabel {
return errors.New("move from All Mail is not allowed")
}
return im.labelMessages(uid, seqSet, targetLabel, true) return im.labelMessages(uid, seqSet, targetLabel, true)
} }

View File

@ -61,9 +61,17 @@ func NewReporter(appName, appVersion string, userAgent fmt.Stringer) *Reporter {
} }
func (r *Reporter) ReportException(i interface{}) error { func (r *Reporter) ReportException(i interface{}) error {
return r.ReportExceptionWithContext(i, make(map[string]interface{}))
}
func (r *Reporter) ReportMessage(msg string) error {
return r.ReportMessageWithContext(msg, make(map[string]interface{}))
}
func (r *Reporter) ReportExceptionWithContext(i interface{}, context map[string]interface{}) error {
err := fmt.Errorf("recover: %v", i) err := fmt.Errorf("recover: %v", i)
return r.scopedReport(func() { return r.scopedReport(context, func() {
if eventID := sentry.CaptureException(err); eventID != nil { if eventID := sentry.CaptureException(err); eventID != nil {
logrus.WithError(err). logrus.WithError(err).
WithField("reportID", *eventID). WithField("reportID", *eventID).
@ -72,8 +80,8 @@ func (r *Reporter) ReportException(i interface{}) error {
}) })
} }
func (r *Reporter) ReportMessage(msg string) error { func (r *Reporter) ReportMessageWithContext(msg string, context map[string]interface{}) error {
return r.scopedReport(func() { return r.scopedReport(context, func() {
if eventID := sentry.CaptureMessage(msg); eventID != nil { if eventID := sentry.CaptureMessage(msg); eventID != nil {
logrus.WithField("message", msg). logrus.WithField("message", msg).
WithField("reportID", *eventID). WithField("reportID", *eventID).
@ -83,7 +91,7 @@ func (r *Reporter) ReportMessage(msg string) error {
} }
// Report reports a sentry crash with stacktrace from all goroutines. // Report reports a sentry crash with stacktrace from all goroutines.
func (r *Reporter) scopedReport(doReport func()) error { func (r *Reporter) scopedReport(context map[string]interface{}, doReport func()) error {
SkipDuringUnwind() SkipDuringUnwind()
if os.Getenv("PROTONMAIL_ENV") == "dev" { if os.Getenv("PROTONMAIL_ENV") == "dev" {
@ -101,6 +109,7 @@ func (r *Reporter) scopedReport(doReport func()) error {
sentry.WithScope(func(scope *sentry.Scope) { sentry.WithScope(func(scope *sentry.Scope) {
SkipDuringUnwind() SkipDuringUnwind()
scope.SetTags(tags) scope.SetTags(tags)
scope.SetContexts(context)
doReport() doReport()
}) })

View File

@ -362,7 +362,8 @@ func (su *smtpUser) Send(returnPath string, to []string, messageReader io.Reader
req := pmapi.NewSendMessageReq(kr, mimeBody, plainBody, richBody, attkeys) req := pmapi.NewSendMessageReq(kr, mimeBody, plainBody, richBody, attkeys)
containsUnencryptedRecipients := false containsUnencryptedRecipients := false
for _, email := range to { for _, recipient := range message.Recipients() {
email := recipient.Address
if !looksLikeEmail(email) { if !looksLikeEmail(email) {
return errors.New(`"` + email + `" is not a valid recipient.`) return errors.New(`"` + email + `" is not a valid recipient.`)
} }

View File

@ -247,7 +247,12 @@ func (loop *eventLoop) processNextEvent() (more bool, err error) { // nolint[fun
l.WithError(err).WithField("errors", loop.errCounter).Error("Error skipped") l.WithError(err).WithField("errors", loop.errCounter).Error("Error skipped")
loop.errCounter++ loop.errCounter++
if loop.errCounter == errMaxSentry { if loop.errCounter == errMaxSentry {
if sentryErr := loop.store.sentryReporter.ReportMessage("Warning: event loop issues: " + err.Error() + ", " + loop.currentEventID); sentryErr != nil { context := map[string]interface{}{
"EventLoop": map[string]interface{}{
"EventID": loop.currentEventID,
},
}
if sentryErr := loop.store.sentryReporter.ReportMessageWithContext("Warning: event loop issues: "+err.Error(), context); sentryErr != nil {
l.WithError(sentryErr).Error("Failed to report error to sentry") l.WithError(sentryErr).Error("Failed to report error to sentry")
} }
} }
@ -302,7 +307,12 @@ func (loop *eventLoop) processEvent(event *pmapi.Event) (err error) {
eventLog.Info("Processing refresh event") eventLog.Info("Processing refresh event")
loop.store.triggerSync() loop.store.triggerSync()
if sentryErr := loop.store.sentryReporter.ReportMessage("Warning: refresh occurred, " + loop.currentEventID); sentryErr != nil { context := map[string]interface{}{
"EventLoop": map[string]interface{}{
"EventID": loop.currentEventID,
},
}
if sentryErr := loop.store.sentryReporter.ReportMessageWithContext("Warning: refresh occurred", context); sentryErr != nil {
loop.log.WithError(sentryErr).Error("Failed to report refresh to sentry") loop.log.WithError(sentryErr).Error("Failed to report refresh to sentry")
} }

View File

@ -59,6 +59,10 @@ func (v *Version) Equal(version *semver.Version) bool {
return v.version.Equal(version) return v.version.Equal(version)
} }
func (v *Version) SemVer() *semver.Version {
return v.version
}
// VerifyFiles verifies all files in the version directory. // VerifyFiles verifies all files in the version directory.
func (v *Version) VerifyFiles(kr *crypto.KeyRing) error { func (v *Version) VerifyFiles(kr *crypto.KeyRing) error {
fileBytes, err := ioutil.ReadFile(filepath.Join(v.path, sumFile)) // nolint[gosec] fileBytes, err := ioutil.ReadFile(filepath.Join(v.path, sumFile)) // nolint[gosec]

View File

@ -33,9 +33,11 @@ func CreateTransportWithDialer(dialer TLSDialer) *http.Transport {
return &http.Transport{ return &http.Transport{
DialTLS: dialer.DialTLS, DialTLS: dialer.DialTLS,
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
MaxIdleConns: 100, MaxIdleConns: 100,
IdleConnTimeout: 5 * time.Minute, MaxIdleConnsPerHost: 100,
IdleConnTimeout: 5 * time.Minute,
ExpectContinueTimeout: 500 * time.Millisecond, ExpectContinueTimeout: 500 * time.Millisecond,
// GODT-126: this was initially 10s but logs from users showed a significant number // GODT-126: this was initially 10s but logs from users showed a significant number

View File

@ -27,7 +27,10 @@ import (
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
const MaxImportMessageRequestLength = 10 const (
MaxImportMessageRequestLength = 10
MaxImportMessageRequestSize = 25 * 1024 * 1024 // 25 MB total limit
)
type ImportMsgReq struct { type ImportMsgReq struct {
Metadata *ImportMetadata // Metadata about the message to import. Metadata *ImportMetadata // Metadata about the message to import.
@ -91,6 +94,14 @@ func (c *client) Import(ctx context.Context, reqs ImportMsgReqs) ([]*ImportMsgRe
return nil, errors.New("request is too long") return nil, errors.New("request is too long")
} }
remainingSize := MaxImportMessageRequestSize
for _, req := range reqs {
remainingSize -= len(req.Message)
if remainingSize < 0 {
return nil, errors.New("request size is too big")
}
}
fields, err := reqs.buildMultipartFormData() fields, err := reqs.buildMultipartFormData()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"testing" "testing"
@ -115,3 +116,32 @@ func TestClient_Import(t *testing.T) { // nolint[funlen]
r.Equal(t, 1, len(imported)) r.Equal(t, 1, len(imported))
r.Equal(t, testImportRes, imported[0]) r.Equal(t, testImportRes, imported[0])
} }
func TestClientImportBigSize(t *testing.T) {
s, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r.FailNow(t, "request is not dropped")
}))
defer s.Close()
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const size = MaxImportMessageRequestSize + 1
msg := make([]byte, size)
for i := 0; i < size; i++ {
msg[i] = letterBytes[rand.Intn(len(letterBytes))]
}
importRequest := []*ImportMsgReq{
{
Metadata: &ImportMetadata{
AddressID: "addressID",
Unread: Boolean(false),
Flags: FlagReceived | FlagImported,
LabelIDs: []string{ArchiveLabel},
},
Message: msg,
},
}
_, err := c.Import(context.Background(), importRequest)
r.EqualError(t, err, "request size is too big")
}

View File

@ -37,6 +37,7 @@ type PMKey struct {
PrivateKey *crypto.Key PrivateKey *crypto.Key
Primary int Primary int
Token string Token string
Active Boolean
Signature string Signature string
} }
@ -135,6 +136,11 @@ func (keys *PMKeys) UnlockAll(passphrase []byte, userKey *crypto.KeyRing) (kr *c
} }
for _, key := range *keys { for _, key := range *keys {
if !key.Active {
logrus.WithField("fingerprint", key.Fingerprint).Warn("Skipping inactive key")
continue
}
var secret []byte var secret []byte
if key.Token == "" || key.Signature == "" { if key.Token == "" || key.Signature == "" {

View File

@ -43,7 +43,7 @@ func New(cfg Config) Manager {
func newManager(cfg Config) *manager { func newManager(cfg Config) *manager {
m := &manager{ m := &manager{
cfg: cfg, cfg: cfg,
rc: resty.New(), rc: resty.New().EnableTrace(),
locker: &sync.Mutex{}, locker: &sync.Mutex{},
} }
@ -59,6 +59,7 @@ func newManager(cfg Config) *manager {
// wrapped in JSON. If error is returned, `handleRequestFailure` is called, // wrapped in JSON. If error is returned, `handleRequestFailure` is called,
// otherwise `handleRequestSuccess` is called. // otherwise `handleRequestSuccess` is called.
m.rc.SetError(&Error{}) m.rc.SetError(&Error{})
m.rc.OnAfterResponse(logConnReuse)
m.rc.OnAfterResponse(updateTime) m.rc.OnAfterResponse(updateTime)
m.rc.OnAfterResponse(m.catchAPIError) m.rc.OnAfterResponse(m.catchAPIError)
m.rc.OnAfterResponse(m.handleRequestSuccess) m.rc.OnAfterResponse(m.handleRequestSuccess)

View File

@ -429,6 +429,14 @@ func (m *Message) Has(flag int64) bool {
return (m.Flags & flag) == flag return (m.Flags & flag) == flag
} }
func (m *Message) Recipients() []*mail.Address {
var recipients []*mail.Address
recipients = append(recipients, m.ToList...)
recipients = append(recipients, m.CCList...)
recipients = append(recipients, m.BCCList...)
return recipients
}
// MessagesCount contains message counts for one label. // MessagesCount contains message counts for one label.
type MessagesCount struct { type MessagesCount struct {
LabelID string LabelID string

View File

@ -24,8 +24,7 @@ import (
"testing" "testing"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
a "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require"
r "github.com/stretchr/testify/require"
) )
const testMessageCleartext = `<div>jeej saas<br></div><div><br></div><div class="protonmail_signature_block"><div>Sent from <a href="https://protonmail.ch">ProtonMail</a>, encrypted email based in Switzerland.<br></div><div><br></div></div>` const testMessageCleartext = `<div>jeej saas<br></div><div><br></div><div class="protonmail_signature_block"><div>Sent from <a href="https://protonmail.ch">ProtonMail</a>, encrypted email based in Switzerland.<br></div><div><br></div></div>`
@ -127,70 +126,78 @@ ClW54lp9eeOfYTsdTSbn9VaSO0E6m2/Q4Tk=
-----END PGP PUBLIC KEY BLOCK-----` -----END PGP PUBLIC KEY BLOCK-----`
func TestMessage_IsBodyEncrypted(t *testing.T) { func TestMessage_IsBodyEncrypted(t *testing.T) {
r := require.New(t)
msg := &Message{Body: testMessageEncrypted} msg := &Message{Body: testMessageEncrypted}
r.True(t, msg.IsBodyEncrypted(), "the body should be encrypted") r.True(msg.IsBodyEncrypted(), "the body should be encrypted")
msg.Body = testMessageCleartext msg.Body = testMessageCleartext
r.True(t, !msg.IsBodyEncrypted(), "the body should not be encrypted") r.True(!msg.IsBodyEncrypted(), "the body should not be encrypted")
} }
func TestMessage_Decrypt(t *testing.T) { func TestMessage_Decrypt(t *testing.T) {
r := require.New(t)
msg := &Message{Body: testMessageEncrypted} msg := &Message{Body: testMessageEncrypted}
dec, err := msg.Decrypt(testPrivateKeyRing) dec, err := msg.Decrypt(testPrivateKeyRing)
r.NoError(t, err) r.NoError(err)
r.Equal(t, testMessageCleartext, string(dec)) r.Equal(testMessageCleartext, string(dec))
} }
func TestMessage_Decrypt_Legacy(t *testing.T) { func TestMessage_Decrypt_Legacy(t *testing.T) {
r := require.New(t)
testPrivateKeyLegacy := readTestFile("testPrivateKeyLegacy", false) testPrivateKeyLegacy := readTestFile("testPrivateKeyLegacy", false)
key, err := crypto.NewKeyFromArmored(testPrivateKeyLegacy) key, err := crypto.NewKeyFromArmored(testPrivateKeyLegacy)
r.NoError(t, err) r.NoError(err)
unlockedKey, err := key.Unlock([]byte(testMailboxPasswordLegacy)) unlockedKey, err := key.Unlock([]byte(testMailboxPasswordLegacy))
r.NoError(t, err) r.NoError(err)
testPrivateKeyRingLegacy, err := crypto.NewKeyRing(unlockedKey) testPrivateKeyRingLegacy, err := crypto.NewKeyRing(unlockedKey)
r.NoError(t, err) r.NoError(err)
msg := &Message{Body: testMessageEncryptedLegacy} msg := &Message{Body: testMessageEncryptedLegacy}
dec, err := msg.Decrypt(testPrivateKeyRingLegacy) dec, err := msg.Decrypt(testPrivateKeyRingLegacy)
r.NoError(t, err) r.NoError(err)
r.Equal(t, testMessageCleartextLegacy, string(dec)) r.Equal(testMessageCleartextLegacy, string(dec))
} }
func TestMessage_Decrypt_signed(t *testing.T) { func TestMessage_Decrypt_signed(t *testing.T) {
r := require.New(t)
msg := &Message{Body: testMessageSigned} msg := &Message{Body: testMessageSigned}
dec, err := msg.Decrypt(testPrivateKeyRing) dec, err := msg.Decrypt(testPrivateKeyRing)
r.NoError(t, err) r.NoError(err)
r.Equal(t, testMessageCleartext, string(dec)) r.Equal(testMessageCleartext, string(dec))
} }
func TestMessage_Encrypt(t *testing.T) { func TestMessage_Encrypt(t *testing.T) {
r := require.New(t)
key, err := crypto.NewKeyFromArmored(testMessageSigner) key, err := crypto.NewKeyFromArmored(testMessageSigner)
r.NoError(t, err) r.NoError(err)
signer, err := crypto.NewKeyRing(key) signer, err := crypto.NewKeyRing(key)
r.NoError(t, err) r.NoError(err)
msg := &Message{Body: testMessageCleartext} msg := &Message{Body: testMessageCleartext}
r.NoError(t, msg.Encrypt(testPrivateKeyRing, testPrivateKeyRing)) r.NoError(msg.Encrypt(testPrivateKeyRing, testPrivateKeyRing))
dec, err := msg.Decrypt(testPrivateKeyRing) dec, err := msg.Decrypt(testPrivateKeyRing)
r.NoError(t, err) r.NoError(err)
r.Equal(t, testMessageCleartext, string(dec)) r.Equal(testMessageCleartext, string(dec))
r.Equal(t, testIdentity, signer.GetIdentities()[0]) r.Equal(testIdentity, signer.GetIdentities()[0])
} }
func routeLabelMessages(tb testing.TB, w http.ResponseWriter, req *http.Request) string { func routeLabelMessages(tb testing.TB, w http.ResponseWriter, req *http.Request) string {
r.NoError(tb, checkMethodAndPath(req, "PUT", "/mail/v4/messages/label")) require.NoError(tb, checkMethodAndPath(req, "PUT", "/mail/v4/messages/label"))
return "messages/label/put_response.json" return "messages/label/put_response.json"
} }
func TestMessage_LabelMessages_NoPaging(t *testing.T) { func TestMessage_LabelMessages_NoPaging(t *testing.T) {
r := require.New(t)
// This should be only enough IDs to produce one page. // This should be only enough IDs to produce one page.
testIDs := []string{} testIDs := []string{}
for i := 0; i < messageIDPageSize-1; i++ { for i := 0; i < messageIDPageSize-1; i++ {
@ -203,10 +210,12 @@ func TestMessage_LabelMessages_NoPaging(t *testing.T) {
) )
defer finish() defer finish()
a.NoError(t, c.LabelMessages(context.Background(), testIDs, "mylabel")) r.NoError(c.LabelMessages(context.Background(), testIDs, "mylabel"))
} }
func TestMessage_LabelMessages_Paging(t *testing.T) { func TestMessage_LabelMessages_Paging(t *testing.T) {
r := require.New(t)
// This should be enough IDs to produce three pages. // This should be enough IDs to produce three pages.
testIDs := []string{} testIDs := []string{}
for i := 0; i < 3*messageIDPageSize; i++ { for i := 0; i < 3*messageIDPageSize; i++ {
@ -221,5 +230,26 @@ func TestMessage_LabelMessages_Paging(t *testing.T) {
) )
defer finish() defer finish()
a.NoError(t, c.LabelMessages(context.Background(), testIDs, "mylabel")) r.NoError(c.LabelMessages(context.Background(), testIDs, "mylabel"))
}
// TestClient_GetMessage might look like no actual functionality is tested
// here. But there was case when API was responding with bad payload and it was
// useful to have this to quickly test it.
func TestClient_GetMessage(t *testing.T) {
r := require.New(t)
testID := "AeUizgtA3H44qRgcr-HdBApwLiUhlQg5kB81mg_QalWotmQJIHep9OScWIo7Wu9pnYxM4RqQxJnr3BE4kh4y_Q=="
finish, c := newTestClientCallbacks(t,
func(tb testing.TB, w http.ResponseWriter, req *http.Request) string {
r.NoError(checkMethodAndPath(req, "GET", "/mail/v4/messages/"+testID))
return "/messages/get_response.json"
},
)
defer finish()
msg, err := c.GetMessage(context.Background(), testID)
r.NoError(err)
r.Equal(testID, msg.ID)
} }

View File

@ -82,6 +82,14 @@ func updateTime(_ *resty.Client, res *resty.Response) error {
return nil return nil
} }
func logConnReuse(_ *resty.Client, res *resty.Response) error {
if !res.Request.TraceInfo().IsConnReused {
logrus.WithField("host", res.Request.URL).Trace("Connection was NOT reused")
}
return nil
}
func catchRetryAfter(_ *resty.Client, res *resty.Response) (time.Duration, error) { func catchRetryAfter(_ *resty.Client, res *resty.Response) (time.Duration, error) {
if res.StatusCode() == http.StatusTooManyRequests { if res.StatusCode() == http.StatusTooManyRequests {
if after := res.Header().Get("Retry-After"); after != "" { if after := res.Header().Get("Retry-After"); after != "" {

View File

@ -2,6 +2,7 @@
{ {
"ID": "hKRtZeTDhvzfAaycb5BOVx6Y3hc3gs4QvET8H_YZBTwAQBPp3h6FI4nnkJePCYuM9CG0zf7TQzOJeB2rPi0YmQ==", "ID": "hKRtZeTDhvzfAaycb5BOVx6Y3hc3gs4QvET8H_YZBTwAQBPp3h6FI4nnkJePCYuM9CG0zf7TQzOJeB2rPi0YmQ==",
"Primary": 1, "Primary": 1,
"Active": 1,
"Flags": 3, "Flags": 3,
"Version": 3, "Version": 3,
"Activation": null, "Activation": null,
@ -12,6 +13,7 @@
{ {
"ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==", "ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==",
"Primary": 0, "Primary": 0,
"Active": 1,
"Flags": 3, "Flags": 3,
"Version": 3, "Version": 3,
"Activation": null, "Activation": null,
@ -19,4 +21,4 @@
"Token": null, "Token": null,
"Signature": null "Signature": null
} }
] ]

View File

@ -2,6 +2,7 @@
{ {
"ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==", "ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==",
"Primary": 1, "Primary": 1,
"Active": 1,
"Flags": 3, "Flags": 3,
"Version": 3, "Version": 3,
"Activation": null, "Activation": null,
@ -12,6 +13,7 @@
{ {
"ID": "hKRtZeTDhvzfAaycb5BOVx6Y3hc3gs4QvET8H_YZBTwAQBPp3h6FI4nnkJePCYuM9CG0zf7TQzOJeB2rPi0YmQ==", "ID": "hKRtZeTDhvzfAaycb5BOVx6Y3hc3gs4QvET8H_YZBTwAQBPp3h6FI4nnkJePCYuM9CG0zf7TQzOJeB2rPi0YmQ==",
"Primary": 0, "Primary": 0,
"Active": 1,
"Flags": 3, "Flags": 3,
"Version": 3, "Version": 3,
"Activation": null, "Activation": null,
@ -19,4 +21,4 @@
"Token": "-----BEGIN PGP MESSAGE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwcBMAxW/uQhm1PKxAQf8D4BAoXqnYgoTr7jEiabP9NkeuD6Z88fZDgUzXte5\n6aYMmCtPr0Vef1eDl2x0S0q/YJgR+A5Icmjxk+jG9nMSnRPlAozJfTCXu/Oy\nfkI4CcClGQv7U1EzfknBVNEKmuPO8XlEkGZ5lW/TzWk8ZtQqJsVuqZlZ5qQE\n/p8FNYEZ5RHPhhKlotA+T6XKh23z9mcN/JNEsSNcx+guERbivtTwnd5JpxQJ\nMNWzUYh+K0W7LgxoAo2jevDd4CZ/0sAAViIl+QrShodseV9agbldGUUoIMzn\nHUuIV6VYi7X82eKQXDInrtPc9IHekbDDFoncLnsGGrEqD/8O/qOHHOPwNwU+\n+cHATAOUTDv/ccWq1QEIALl8SNOQBmCuanAceUTSwCkM1fC3Ddqa8ZmnMMyG\nnDWDI7XkFU31CO10lN20/kAWjdN2073B/NT+17cp8fCqbQ1pAFJHpabdqmbI\ntm3pCC5M6otTN+MhjgFYcBuxo0rq9qtuEzz5j4Ub9MIIJTurUHMEPMI462Dc\ndK/d8BvDkU7q67Lkp65vpe9e/pv0lMMrQjdohnTHNgbZhbI/Z5LU0ApD//Ye\ncSC26BRUMJITiGKb9pKGAi0/ig0jJfzykgEarzOsY/v0W7016AMka+NsHuNc\n7Qfyg2LApF5s9Z6aK+Uy851haUk+p+abjBdcWACziAmimVGjlRYm49ra4UJ7\nc6LSeAElM584e4Z3VnqajAJWbmWt2atgBQmcwEBBsdNAtzIagNydMbBBIELA\n5wGUbMXfQxrMOo/Mdac/5EMTHT0/kVQNIBHtpX5SknWvbc5DjIHoH3+BbL87\neaxhKW94hMubKNKT3dbm4PKHtsMiS3TGkFZ3GjvA0Cy/Kw==\n=b1N8\n-----END PGP MESSAGE-----\n", "Token": "-----BEGIN PGP MESSAGE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwcBMAxW/uQhm1PKxAQf8D4BAoXqnYgoTr7jEiabP9NkeuD6Z88fZDgUzXte5\n6aYMmCtPr0Vef1eDl2x0S0q/YJgR+A5Icmjxk+jG9nMSnRPlAozJfTCXu/Oy\nfkI4CcClGQv7U1EzfknBVNEKmuPO8XlEkGZ5lW/TzWk8ZtQqJsVuqZlZ5qQE\n/p8FNYEZ5RHPhhKlotA+T6XKh23z9mcN/JNEsSNcx+guERbivtTwnd5JpxQJ\nMNWzUYh+K0W7LgxoAo2jevDd4CZ/0sAAViIl+QrShodseV9agbldGUUoIMzn\nHUuIV6VYi7X82eKQXDInrtPc9IHekbDDFoncLnsGGrEqD/8O/qOHHOPwNwU+\n+cHATAOUTDv/ccWq1QEIALl8SNOQBmCuanAceUTSwCkM1fC3Ddqa8ZmnMMyG\nnDWDI7XkFU31CO10lN20/kAWjdN2073B/NT+17cp8fCqbQ1pAFJHpabdqmbI\ntm3pCC5M6otTN+MhjgFYcBuxo0rq9qtuEzz5j4Ub9MIIJTurUHMEPMI462Dc\ndK/d8BvDkU7q67Lkp65vpe9e/pv0lMMrQjdohnTHNgbZhbI/Z5LU0ApD//Ye\ncSC26BRUMJITiGKb9pKGAi0/ig0jJfzykgEarzOsY/v0W7016AMka+NsHuNc\n7Qfyg2LApF5s9Z6aK+Uy851haUk+p+abjBdcWACziAmimVGjlRYm49ra4UJ7\nc6LSeAElM584e4Z3VnqajAJWbmWt2atgBQmcwEBBsdNAtzIagNydMbBBIELA\n5wGUbMXfQxrMOo/Mdac/5EMTHT0/kVQNIBHtpX5SknWvbc5DjIHoH3+BbL87\neaxhKW94hMubKNKT3dbm4PKHtsMiS3TGkFZ3GjvA0Cy/Kw==\n=b1N8\n-----END PGP MESSAGE-----\n",
"Signature": "-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwsBcBAEBCAAGBQJcxtSkAAoJEGfweL9m9sQefz8IAJhpEOvG7d+PVgq3bEL+\nXzY8yTUKB1ZXEMbcR/uHUWSfu0F8zy4CNtSG5HUVjNxx1xzzJVJcxWB/ljO+\n/bJSFOFexTyNh8i/xU+CiBfm5RhAFTYF9xFwfD3LKp5gaalJAhWhArk1/Wuh\nWTDpFpk39uzBRKNwcnSgiJYPxOjAZxj+w/hhHPwmco5cUwMiMR5MNrfzKf+x\nwX3Cfs9fsiiCDzohBzbK0FFsMnJ8aXNVsDBjEA1KrB7sdyaf8FnaM0RsFRDb\njxtIgfcFBbyNJh4414Unt4AYTIrIWhK4OOXI3AfsJy8p6KRBKQUcUkKcDxab\nPTXOPZsZ+UgQ5MevyVnP1zfCwFwEAQEIAAYFAlzG1KQACgkQm1DMMBzxetx/\nPwf9F04uHtix0zDpP1IvG4VYlor4rjYTdfXqxxiFXHO6MZXJoigS1E71E8r4\nsqZ6PoQ5/xCj2A01KRhuF1Bon3mEZEwaIUuBqTV91sLsVWfoxgyPAYpr6gK/\n1W9JhVNNrVRMGox42LQUjyiq0ESrfWmqC8SuMfZMoUoBZycHicA50RbyOUnT\nLO57ArL3JIVmYtyosaXM3idzmNHmaXSkcGt4cvTVysJZQrneaxmikfm5CH2O\n1z2goLBNnzsbRionoV6gCukZOiM/d14yiyeYtsFJ7u/vodkI5y0M0sF8VnN/\njL9keP4ZpHiJ4MBd6tyIH0pLueDRlurFL3fcHsEzD5SrOA==\n=wGCk\n-----END PGP SIGNATURE-----\n" "Signature": "-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwsBcBAEBCAAGBQJcxtSkAAoJEGfweL9m9sQefz8IAJhpEOvG7d+PVgq3bEL+\nXzY8yTUKB1ZXEMbcR/uHUWSfu0F8zy4CNtSG5HUVjNxx1xzzJVJcxWB/ljO+\n/bJSFOFexTyNh8i/xU+CiBfm5RhAFTYF9xFwfD3LKp5gaalJAhWhArk1/Wuh\nWTDpFpk39uzBRKNwcnSgiJYPxOjAZxj+w/hhHPwmco5cUwMiMR5MNrfzKf+x\nwX3Cfs9fsiiCDzohBzbK0FFsMnJ8aXNVsDBjEA1KrB7sdyaf8FnaM0RsFRDb\njxtIgfcFBbyNJh4414Unt4AYTIrIWhK4OOXI3AfsJy8p6KRBKQUcUkKcDxab\nPTXOPZsZ+UgQ5MevyVnP1zfCwFwEAQEIAAYFAlzG1KQACgkQm1DMMBzxetx/\nPwf9F04uHtix0zDpP1IvG4VYlor4rjYTdfXqxxiFXHO6MZXJoigS1E71E8r4\nsqZ6PoQ5/xCj2A01KRhuF1Bon3mEZEwaIUuBqTV91sLsVWfoxgyPAYpr6gK/\n1W9JhVNNrVRMGox42LQUjyiq0ESrfWmqC8SuMfZMoUoBZycHicA50RbyOUnT\nLO57ArL3JIVmYtyosaXM3idzmNHmaXSkcGt4cvTVysJZQrneaxmikfm5CH2O\n1z2goLBNnzsbRionoV6gCukZOiM/d14yiyeYtsFJ7u/vodkI5y0M0sF8VnN/\njL9keP4ZpHiJ4MBd6tyIH0pLueDRlurFL3fcHsEzD5SrOA==\n=wGCk\n-----END PGP SIGNATURE-----\n"
} }
] ]

View File

@ -2,6 +2,7 @@
{ {
"ID": "hKRtZeTDhvzfAaycb5BOVx6Y3hc3gs4QvET8H_YZBTwAQBPp3h6FI4nnkJePCYuM9CG0zf7TQzOJeB2rPi0YmQ==", "ID": "hKRtZeTDhvzfAaycb5BOVx6Y3hc3gs4QvET8H_YZBTwAQBPp3h6FI4nnkJePCYuM9CG0zf7TQzOJeB2rPi0YmQ==",
"Primary": 1, "Primary": 1,
"Active": 1,
"Flags": 3, "Flags": 3,
"Version": 3, "Version": 3,
"Activation": null, "Activation": null,
@ -9,4 +10,4 @@
"Token": "-----BEGIN PGP MESSAGE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwcBMAxW/uQhm1PKxAQf8D4BAoXqnYgoTr7jEiabP9NkeuD6Z88fZDgUzXte5\n6aYMmCtPr0Vef1eDl2x0S0q/YJgR+A5Icmjxk+jG9nMSnRPlAozJfTCXu/Oy\nfkI4CcClGQv7U1EzfknBVNEKmuPO8XlEkGZ5lW/TzWk8ZtQqJsVuqZlZ5qQE\n/p8FNYEZ5RHPhhKlotA+T6XKh23z9mcN/JNEsSNcx+guERbivtTwnd5JpxQJ\nMNWzUYh+K0W7LgxoAo2jevDd4CZ/0sAAViIl+QrShodseV9agbldGUUoIMzn\nHUuIV6VYi7X82eKQXDInrtPc9IHekbDDFoncLnsGGrEqD/8O/qOHHOPwNwU+\n+cHATAOUTDv/ccWq1QEIALl8SNOQBmCuanAceUTSwCkM1fC3Ddqa8ZmnMMyG\nnDWDI7XkFU31CO10lN20/kAWjdN2073B/NT+17cp8fCqbQ1pAFJHpabdqmbI\ntm3pCC5M6otTN+MhjgFYcBuxo0rq9qtuEzz5j4Ub9MIIJTurUHMEPMI462Dc\ndK/d8BvDkU7q67Lkp65vpe9e/pv0lMMrQjdohnTHNgbZhbI/Z5LU0ApD//Ye\ncSC26BRUMJITiGKb9pKGAi0/ig0jJfzykgEarzOsY/v0W7016AMka+NsHuNc\n7Qfyg2LApF5s9Z6aK+Uy851haUk+p+abjBdcWACziAmimVGjlRYm49ra4UJ7\nc6LSeAElM584e4Z3VnqajAJWbmWt2atgBQmcwEBBsdNAtzIagNydMbBBIELA\n5wGUbMXfQxrMOo/Mdac/5EMTHT0/kVQNIBHtpX5SknWvbc5DjIHoH3+BbL87\neaxhKW94hMubKNKT3dbm4PKHtsMiS3TGkFZ3GjvA0Cy/Kw==\n=b1N8\n-----END PGP MESSAGE-----\n", "Token": "-----BEGIN PGP MESSAGE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwcBMAxW/uQhm1PKxAQf8D4BAoXqnYgoTr7jEiabP9NkeuD6Z88fZDgUzXte5\n6aYMmCtPr0Vef1eDl2x0S0q/YJgR+A5Icmjxk+jG9nMSnRPlAozJfTCXu/Oy\nfkI4CcClGQv7U1EzfknBVNEKmuPO8XlEkGZ5lW/TzWk8ZtQqJsVuqZlZ5qQE\n/p8FNYEZ5RHPhhKlotA+T6XKh23z9mcN/JNEsSNcx+guERbivtTwnd5JpxQJ\nMNWzUYh+K0W7LgxoAo2jevDd4CZ/0sAAViIl+QrShodseV9agbldGUUoIMzn\nHUuIV6VYi7X82eKQXDInrtPc9IHekbDDFoncLnsGGrEqD/8O/qOHHOPwNwU+\n+cHATAOUTDv/ccWq1QEIALl8SNOQBmCuanAceUTSwCkM1fC3Ddqa8ZmnMMyG\nnDWDI7XkFU31CO10lN20/kAWjdN2073B/NT+17cp8fCqbQ1pAFJHpabdqmbI\ntm3pCC5M6otTN+MhjgFYcBuxo0rq9qtuEzz5j4Ub9MIIJTurUHMEPMI462Dc\ndK/d8BvDkU7q67Lkp65vpe9e/pv0lMMrQjdohnTHNgbZhbI/Z5LU0ApD//Ye\ncSC26BRUMJITiGKb9pKGAi0/ig0jJfzykgEarzOsY/v0W7016AMka+NsHuNc\n7Qfyg2LApF5s9Z6aK+Uy851haUk+p+abjBdcWACziAmimVGjlRYm49ra4UJ7\nc6LSeAElM584e4Z3VnqajAJWbmWt2atgBQmcwEBBsdNAtzIagNydMbBBIELA\n5wGUbMXfQxrMOo/Mdac/5EMTHT0/kVQNIBHtpX5SknWvbc5DjIHoH3+BbL87\neaxhKW94hMubKNKT3dbm4PKHtsMiS3TGkFZ3GjvA0Cy/Kw==\n=b1N8\n-----END PGP MESSAGE-----\n",
"Signature": "-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwsBcBAEBCAAGBQJcxtSkAAoJEGfweL9m9sQefz8IAJhpEOvG7d+PVgq3bEL+\nXzY8yTUKB1ZXEMbcR/uHUWSfu0F8zy4CNtSG5HUVjNxx1xzzJVJcxWB/ljO+\n/bJSFOFexTyNh8i/xU+CiBfm5RhAFTYF9xFwfD3LKp5gaalJAhWhArk1/Wuh\nWTDpFpk39uzBRKNwcnSgiJYPxOjAZxj+w/hhHPwmco5cUwMiMR5MNrfzKf+x\nwX3Cfs9fsiiCDzohBzbK0FFsMnJ8aXNVsDBjEA1KrB7sdyaf8FnaM0RsFRDb\njxtIgfcFBbyNJh4414Unt4AYTIrIWhK4OOXI3AfsJy8p6KRBKQUcUkKcDxab\nPTXOPZsZ+UgQ5MevyVnP1zfCwFwEAQEIAAYFAlzG1KQACgkQm1DMMBzxetx/\nPwf9F04uHtix0zDpP1IvG4VYlor4rjYTdfXqxxiFXHO6MZXJoigS1E71E8r4\nsqZ6PoQ5/xCj2A01KRhuF1Bon3mEZEwaIUuBqTV91sLsVWfoxgyPAYpr6gK/\n1W9JhVNNrVRMGox42LQUjyiq0ESrfWmqC8SuMfZMoUoBZycHicA50RbyOUnT\nLO57ArL3JIVmYtyosaXM3idzmNHmaXSkcGt4cvTVysJZQrneaxmikfm5CH2O\n1z2goLBNnzsbRionoV6gCukZOiM/d14yiyeYtsFJ7u/vodkI5y0M0sF8VnN/\njL9keP4ZpHiJ4MBd6tyIH0pLueDRlurFL3fcHsEzD5SrOA==\n=wGCk\n-----END PGP SIGNATURE-----\n" "Signature": "-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwsBcBAEBCAAGBQJcxtSkAAoJEGfweL9m9sQefz8IAJhpEOvG7d+PVgq3bEL+\nXzY8yTUKB1ZXEMbcR/uHUWSfu0F8zy4CNtSG5HUVjNxx1xzzJVJcxWB/ljO+\n/bJSFOFexTyNh8i/xU+CiBfm5RhAFTYF9xFwfD3LKp5gaalJAhWhArk1/Wuh\nWTDpFpk39uzBRKNwcnSgiJYPxOjAZxj+w/hhHPwmco5cUwMiMR5MNrfzKf+x\nwX3Cfs9fsiiCDzohBzbK0FFsMnJ8aXNVsDBjEA1KrB7sdyaf8FnaM0RsFRDb\njxtIgfcFBbyNJh4414Unt4AYTIrIWhK4OOXI3AfsJy8p6KRBKQUcUkKcDxab\nPTXOPZsZ+UgQ5MevyVnP1zfCwFwEAQEIAAYFAlzG1KQACgkQm1DMMBzxetx/\nPwf9F04uHtix0zDpP1IvG4VYlor4rjYTdfXqxxiFXHO6MZXJoigS1E71E8r4\nsqZ6PoQ5/xCj2A01KRhuF1Bon3mEZEwaIUuBqTV91sLsVWfoxgyPAYpr6gK/\n1W9JhVNNrVRMGox42LQUjyiq0ESrfWmqC8SuMfZMoUoBZycHicA50RbyOUnT\nLO57ArL3JIVmYtyosaXM3idzmNHmaXSkcGt4cvTVysJZQrneaxmikfm5CH2O\n1z2goLBNnzsbRionoV6gCukZOiM/d14yiyeYtsFJ7u/vodkI5y0M0sF8VnN/\njL9keP4ZpHiJ4MBd6tyIH0pLueDRlurFL3fcHsEzD5SrOA==\n=wGCk\n-----END PGP SIGNATURE-----\n"
} }
] ]

View File

@ -2,6 +2,7 @@
{ {
"ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==", "ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==",
"Primary": 1, "Primary": 1,
"Active": 1,
"Flags": 3, "Flags": 3,
"Version": 3, "Version": 3,
"Activation": null, "Activation": null,

View File

@ -5,6 +5,7 @@
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: OpenPGP.js v0.7.1\r\nComment: http://openpgpjs.org\r\n\r\n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\nqqGRQm3MxoTdgQUShAwbNwNNQR9cInfMnA==\r\n=2wIY\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n", "PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: OpenPGP.js v0.7.1\r\nComment: http://openpgpjs.org\r\n\r\n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\nqqGRQm3MxoTdgQUShAwbNwNNQR9cInfMnA==\r\n=2wIY\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n",
"Fingerprint": "c93f767df53b0ca8395cfde90483475164ec6353", "Fingerprint": "c93f767df53b0ca8395cfde90483475164ec6353",
"Activation": null, "Activation": null,
"Primary": 1 "Primary": 1,
"Active": 1
} }
] ]

View File

@ -21,7 +21,8 @@
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: OpenPGP.js v0.7.1\r\nComment: http://openpgpjs.org\r\n\r\n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\nqqGRQm3MxoTdgQUShAwbNwNNQR9cInfMnA==\r\n=2wIY\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n", "PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: OpenPGP.js v0.7.1\r\nComment: http://openpgpjs.org\r\n\r\nxcMGBFRJbc0BCAC0mMLZPDBbtSCWvxwmOfXfJkE2+ssM3ux21LhD/bPiWefE\nWSHlCjJ8PqPHy7snSiUuxuj3f9AvXPvg+mjGLBwu1/QsnSP24sl3qD2onl39\nvPiLJXUqZs20ZRgnvX70gjkgEzMFBxINiy2MTIG+4RU8QA7y8KzWev0btqKi\nMeVa+GLEHhgZ2KPOn4Jv1q4bI9hV0C9NUe2tTXS6/Vv3vbCY7lRR0kbJ65T5\nc8CmpqJuASIJNrSXM/Q3NnnsY4kBYH0s5d2FgbASQvzrjuC2rngUg0EoPsrb\nDEVRA2/BCJonw7aASiNCrSP92lkZdtYlax/pcoE/mQ4WSwySFmcFT7yFABEB\nAAH+CQMIvzcDReuJkc9gnxAkfgmnkBFwRQrqT/4UAPOF8WGVo0uNvDo7Snlk\nqWsJS+54+/Xx6Jur/PdBWeEu+6+6GnppYuvsaT0D0nFdFhF6pjng+02IOxfG\nqlYXYcW4hRru3BfvJlSvU2LL/Z/ooBnw3T5vqd0eFHKrvabUuwf0x3+K/sru\nFp24rl2PU+bzQlUgKpWzKDmO+0RdKQ6KVCyCDMIXaAkALwNffAvYxI0wnb2y\nWAV/bGn1ODnszOYPk3pEMR6kKSxLLaO69kYx4eTERFyJ+1puAxEPCk3Cfeif\nyDWi4rU03YB16XH7hQLSFl61SKeIYlkKmkO5Hk1ybi/BhvOGBPVeGGbxWnwI\n46G8DfBHW0+uvD5cAQtk2d/q3Ge1I+DIyvuRCcSu0XSBNv/Bkpp4IbAUPBaW\nTIvf5p9oxw+AjrMtTtcdSiee1S6CvMMaHhVD7SI6qGA8GqwaXueeLuEXa0Ok\nBWlehx8wibMi4a9fLcQZtzJkmGhR1WzXcJfiEg32srILwIzPQYxuFdZZ2elb\ngYp/bMEIp4LKhi43IyM6peCDHDzEba8NuOSd0heEqFIm0vlXujMhkyMUvDBv\nH0V5On4aMuw/aSEKcAdbazppOru/W1ndyFa5ZHQIC19g72ZaDVyYjPyvNgOV\nAFqO4o3IbC5z31zMlTtMbAq2RG9svwUVejn0tmF6UPluTe0U1NuXFpLK6TCH\nwqocLz4ecptfJQulpYjClVLgzaYGDuKwQpIwPWg5G/DtKSCGNtEkfqB3aemH\nV5xmoYm1v5CQZAEvvsrLA6jxCk9lzqYV8QMivWNXUG+mneIEM35G0HOPzXca\nLLyB+N8Zxioc9DPGfdbcxXuVgOKRepbkq4xv1pUpMQ4BUmlkejDRSP+5SIR3\niEthg+FU6GRSQbORE6nhrKjGBk8fpNpozQZVc2VySUTCwHIEEAEIACYFAlRJ\nbc8GCwkIBwMCCRA+tiWe3yHfJAQVCAIKAxYCAQIbAwIeAQAA9J0H/RLR/Uwt\nCakrPKtfeGaNuOI45SRTNxM8TklC6tM28sJSzkX8qKPzvI1PxyLhs/i0/fCQ\n7Z5bU6n41oLuqUt2S9vy+ABlChKAeziOqCHUcMzHOtbKiPkKW88aO687nx+A\nol2XOnMTkVIC+edMUgnKp6tKtZnbO4ea6Cg88TFuli4hLHNXTfCECswuxHOc\nAO1OKDRrCd08iPI5CLNCIV60QnduitE1vF6ehgrH25Vl6LEdd8vPVlTYAvsa\n6ySk2RIrHNLUZZ3iII3MBFL8HyINp/XA1BQP+QbH801uSLq8agxM4iFT9C+O\nD147SawUGhjD5RG7T+YtqItzgA1V9l277EXHwwYEVEltzwEIAJD57uX6bOc4\nTgf3utfL/4hdyoqIMVHkYQOvE27wPsZxX08QsdlaNeGji9Ap2ifIDuckUqn6\nJi9jtZDKtOzdTBm6rnG5nPmkn6BJXPhnecQRP8N0XBISnAGmE4t+bxtts5Wb\nqeMdxJYqMiGqzrLBRJEIDTcg3+QF2Y3RywOqlcXqgG/xX++PsvR1Jiz0rEVP\nTcBc7ytyb/Av7mx1S802HRYGJHOFtVLoPTrtPCvv+DRDK8JzxQW2XSQLlI0M\n9s1tmYhCogYIIqKx9qOTd5mFJ1hJlL6i9xDkvE21qPFASFtww5tiYmUfFaxI\nLwbXPZlQ1I/8fuaUdOxctQ+g40ZgHPcAEQEAAf4JAwgdUg8ubE2BT2DITBD+\nXFgjrnUlQBilbN8/do/36KHuImSPO/GGLzKh4+oXxrvLc5fQLjeO+bzeen4u\nCOCBRO0hG7KpJPhQ6+T02uEF6LegE1sEz5hp6BpKUdPZ1+8799Rylb5kubC5\nIKnLqqpGDbH3hIsmSV3CG/ESkaGMLc/K0ZPt1JRWtUQ9GesXT0v6fdM5GB/L\ncZWFdDoYgZAw5BtymE44knIodfDAYJ4DHnPCh/oilWe1qVTQcNMdtkpBgkuo\nTHecqEmiODQz5EX8pVmS596XsnPO299Lo3TbaHUQo7EC6Au1Au9+b5hC1pDa\nFVCLcproi/Cgch0B/NOCFkVLYmp6BEljRj2dSZRWbO0vgl9kFmJEeiiH41+k\nEAI6PASSKZs3BYLFc2I8mBkcvt90kg4MTBjreuk0uWf1hdH2Rv8zprH4h5Uh\ngjx5nUDX8WXyeLxTU5EBKry+A2DIe0Gm0/waxp6lBlUl+7ra28KYEoHm8Nq/\nN9FCuEhFkFgw6EwUp7jsrFcqBKvmni6jyplm+mJXi3CK+IiNcqub4XPnBI97\nlR19fupB/Y6M7yEaxIM8fTQXmP+x/fe8zRphdo+7o+pJQ3hk5LrrNPK8GEZ6\nDLDOHjZzROhOgBvWtbxRktHk+f5YpuQL+xWd33IV1xYSSHuoAm0Zwt0QJxBs\noFBwJEq1NWM4FxXJBogvzV7KFhl/hXgtvx+GaMv3y8gucj+gE89xVv0XBXjl\n5dy5/PgCI0Id+KAFHyKpJA0N0h8O4xdJoNyIBAwDZ8LHt0vlnLGwcJFR9X7/\nPfWe0PFtC3d7cYY3RopDhnRP7MZs1Wo9nZ4IvlXoEsE2nPkWcns+Wv5Yaewr\ns2ra9ZIK7IIJhqKKgmQtCeiXyFwTq+kfunDnxeCavuWL3HuLKIOZf7P9vXXt\nXgEir9rCwF8EGAEIABMFAlRJbdIJED62JZ7fId8kAhsMAAD+LAf+KT1EpkwH\n0ivTHmYako+6qG6DCtzd3TibWw51cmbY20Ph13NIS/MfBo828S9SXm/sVUzN\n/r7qZgZYfI0/j57tG3BguVGm53qya4bINKyi1RjK6aKo/rrzRkh5ZVD5rVNO\nE2zzvyYAnLUWG9AV1OYDxcgLrXqEMWlqZAo+Wmg7VrTBmdCGs/BPvscNgQRr\n6Gpjgmv9ru6LjRL7vFhEcov/tkBLj+CtaWWFTd1s2vBLOs4rCsD9TT/23vfw\nCnokvvVjKYN5oviy61yhpqF1rWlOsxZ4+2sKW3Pq7JLBtmzsZegTONfcQAf7\nqqGRQm3MxoTdgQUShAwbNwNNQR9cInfMnA==\r\n=2wIY\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n",
"Activation": null, "Activation": null,
"Primary": 1, "Primary": 1,
"Token": null "Token": null,
"Active": 1
} }
] ]
}, },
@ -40,4 +41,4 @@
"Keys": [] "Keys": []
} }
] ]
} }

View File

@ -42,9 +42,11 @@
"MIMEType": "text/plain", "MIMEType": "text/plain",
"KeyPackets": "wcBMA0fcZ7XLgmf2AQgAiRsOlnm1kSB4/lr7tYe6pBsRGn10GqwUhrwU5PMKOHdCgnO12jO3y3CzP0Yl/jGhAYja9wLDqH8X0sk3tY32u4Sb1Qe5IuzggAiCa4dwOJj5gEFMTHMzjIMPHR7A70XqUxMhmILye8V4KRm/j4c1sxbzA1rM3lYBumQuB5l/ck0Kgt4ZqxHVXHK5Q1l65FHhSXRj8qnunasHa30TYNzP8nmBA8BinnJxpiQ7FGc2umnUhgkFtjm5ixu9vyjr9ukwDTbwAXXfmY+o7tK7kqIXJcmTL6k2UeC6Mz1AagQtRCRtU+bv/3zGojq/trZo9lom3naIeQYa36Ketmcpj2Qwjg==", "KeyPackets": "wcBMA0fcZ7XLgmf2AQgAiRsOlnm1kSB4/lr7tYe6pBsRGn10GqwUhrwU5PMKOHdCgnO12jO3y3CzP0Yl/jGhAYja9wLDqH8X0sk3tY32u4Sb1Qe5IuzggAiCa4dwOJj5gEFMTHMzjIMPHR7A70XqUxMhmILye8V4KRm/j4c1sxbzA1rM3lYBumQuB5l/ck0Kgt4ZqxHVXHK5Q1l65FHhSXRj8qnunasHa30TYNzP8nmBA8BinnJxpiQ7FGc2umnUhgkFtjm5ixu9vyjr9ukwDTbwAXXfmY+o7tK7kqIXJcmTL6k2UeC6Mz1AagQtRCRtU+bv/3zGojq/trZo9lom3naIeQYa36Ketmcpj2Qwjg==",
"Headers": { "Headers": {
"content-description": "You'll never believe what's in this text file" "content-description": "attachment",
"x-pm-incorporated":"1",
"x-pm-notes": ["You'll never believe", "what's in this text file"]
}, },
"MessageID": "h3CD-DT7rLoAw1vmpcajvIPAl-wwDfXR2MHtWID3wuQURDBKTiGUAwd6E2WBbS44QQKeXImW-axm6X0hAfcVCA==" "MessageID": "AeUizgtA3H44qRgcr-HdBApwLiUhlQg5kB81mg_QalWotmQJIHep9OScWIo7Wu9pnYxM4RqQxJnr3BE4kh4y_Q=="
} }
], ],
"LabelIDs": [ "LabelIDs": [
@ -52,4 +54,4 @@
"0" "0"
] ]
} }
} }

View File

@ -20,7 +20,8 @@
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: OpenPGP.js v0.7.1\r\nComment: http://openpgpjs.org\r\n\r\n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\nqqGRQm3MxoTdgQUShAwbNwNNQR9cInfMnA==\r\n=2wIY\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n", "PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: OpenPGP.js v0.7.1\r\nComment: http://openpgpjs.org\r\n\r\nxcMGBFRJbc0BCAC0mMLZPDBbtSCWvxwmOfXfJkE2+ssM3ux21LhD/bPiWefE\nWSHlCjJ8PqPHy7snSiUuxuj3f9AvXPvg+mjGLBwu1/QsnSP24sl3qD2onl39\nvPiLJXUqZs20ZRgnvX70gjkgEzMFBxINiy2MTIG+4RU8QA7y8KzWev0btqKi\nMeVa+GLEHhgZ2KPOn4Jv1q4bI9hV0C9NUe2tTXS6/Vv3vbCY7lRR0kbJ65T5\nc8CmpqJuASIJNrSXM/Q3NnnsY4kBYH0s5d2FgbASQvzrjuC2rngUg0EoPsrb\nDEVRA2/BCJonw7aASiNCrSP92lkZdtYlax/pcoE/mQ4WSwySFmcFT7yFABEB\nAAH+CQMIvzcDReuJkc9gnxAkfgmnkBFwRQrqT/4UAPOF8WGVo0uNvDo7Snlk\nqWsJS+54+/Xx6Jur/PdBWeEu+6+6GnppYuvsaT0D0nFdFhF6pjng+02IOxfG\nqlYXYcW4hRru3BfvJlSvU2LL/Z/ooBnw3T5vqd0eFHKrvabUuwf0x3+K/sru\nFp24rl2PU+bzQlUgKpWzKDmO+0RdKQ6KVCyCDMIXaAkALwNffAvYxI0wnb2y\nWAV/bGn1ODnszOYPk3pEMR6kKSxLLaO69kYx4eTERFyJ+1puAxEPCk3Cfeif\nyDWi4rU03YB16XH7hQLSFl61SKeIYlkKmkO5Hk1ybi/BhvOGBPVeGGbxWnwI\n46G8DfBHW0+uvD5cAQtk2d/q3Ge1I+DIyvuRCcSu0XSBNv/Bkpp4IbAUPBaW\nTIvf5p9oxw+AjrMtTtcdSiee1S6CvMMaHhVD7SI6qGA8GqwaXueeLuEXa0Ok\nBWlehx8wibMi4a9fLcQZtzJkmGhR1WzXcJfiEg32srILwIzPQYxuFdZZ2elb\ngYp/bMEIp4LKhi43IyM6peCDHDzEba8NuOSd0heEqFIm0vlXujMhkyMUvDBv\nH0V5On4aMuw/aSEKcAdbazppOru/W1ndyFa5ZHQIC19g72ZaDVyYjPyvNgOV\nAFqO4o3IbC5z31zMlTtMbAq2RG9svwUVejn0tmF6UPluTe0U1NuXFpLK6TCH\nwqocLz4ecptfJQulpYjClVLgzaYGDuKwQpIwPWg5G/DtKSCGNtEkfqB3aemH\nV5xmoYm1v5CQZAEvvsrLA6jxCk9lzqYV8QMivWNXUG+mneIEM35G0HOPzXca\nLLyB+N8Zxioc9DPGfdbcxXuVgOKRepbkq4xv1pUpMQ4BUmlkejDRSP+5SIR3\niEthg+FU6GRSQbORE6nhrKjGBk8fpNpozQZVc2VySUTCwHIEEAEIACYFAlRJ\nbc8GCwkIBwMCCRA+tiWe3yHfJAQVCAIKAxYCAQIbAwIeAQAA9J0H/RLR/Uwt\nCakrPKtfeGaNuOI45SRTNxM8TklC6tM28sJSzkX8qKPzvI1PxyLhs/i0/fCQ\n7Z5bU6n41oLuqUt2S9vy+ABlChKAeziOqCHUcMzHOtbKiPkKW88aO687nx+A\nol2XOnMTkVIC+edMUgnKp6tKtZnbO4ea6Cg88TFuli4hLHNXTfCECswuxHOc\nAO1OKDRrCd08iPI5CLNCIV60QnduitE1vF6ehgrH25Vl6LEdd8vPVlTYAvsa\n6ySk2RIrHNLUZZ3iII3MBFL8HyINp/XA1BQP+QbH801uSLq8agxM4iFT9C+O\nD147SawUGhjD5RG7T+YtqItzgA1V9l277EXHwwYEVEltzwEIAJD57uX6bOc4\nTgf3utfL/4hdyoqIMVHkYQOvE27wPsZxX08QsdlaNeGji9Ap2ifIDuckUqn6\nJi9jtZDKtOzdTBm6rnG5nPmkn6BJXPhnecQRP8N0XBISnAGmE4t+bxtts5Wb\nqeMdxJYqMiGqzrLBRJEIDTcg3+QF2Y3RywOqlcXqgG/xX++PsvR1Jiz0rEVP\nTcBc7ytyb/Av7mx1S802HRYGJHOFtVLoPTrtPCvv+DRDK8JzxQW2XSQLlI0M\n9s1tmYhCogYIIqKx9qOTd5mFJ1hJlL6i9xDkvE21qPFASFtww5tiYmUfFaxI\nLwbXPZlQ1I/8fuaUdOxctQ+g40ZgHPcAEQEAAf4JAwgdUg8ubE2BT2DITBD+\nXFgjrnUlQBilbN8/do/36KHuImSPO/GGLzKh4+oXxrvLc5fQLjeO+bzeen4u\nCOCBRO0hG7KpJPhQ6+T02uEF6LegE1sEz5hp6BpKUdPZ1+8799Rylb5kubC5\nIKnLqqpGDbH3hIsmSV3CG/ESkaGMLc/K0ZPt1JRWtUQ9GesXT0v6fdM5GB/L\ncZWFdDoYgZAw5BtymE44knIodfDAYJ4DHnPCh/oilWe1qVTQcNMdtkpBgkuo\nTHecqEmiODQz5EX8pVmS596XsnPO299Lo3TbaHUQo7EC6Au1Au9+b5hC1pDa\nFVCLcproi/Cgch0B/NOCFkVLYmp6BEljRj2dSZRWbO0vgl9kFmJEeiiH41+k\nEAI6PASSKZs3BYLFc2I8mBkcvt90kg4MTBjreuk0uWf1hdH2Rv8zprH4h5Uh\ngjx5nUDX8WXyeLxTU5EBKry+A2DIe0Gm0/waxp6lBlUl+7ra28KYEoHm8Nq/\nN9FCuEhFkFgw6EwUp7jsrFcqBKvmni6jyplm+mJXi3CK+IiNcqub4XPnBI97\nlR19fupB/Y6M7yEaxIM8fTQXmP+x/fe8zRphdo+7o+pJQ3hk5LrrNPK8GEZ6\nDLDOHjZzROhOgBvWtbxRktHk+f5YpuQL+xWd33IV1xYSSHuoAm0Zwt0QJxBs\noFBwJEq1NWM4FxXJBogvzV7KFhl/hXgtvx+GaMv3y8gucj+gE89xVv0XBXjl\n5dy5/PgCI0Id+KAFHyKpJA0N0h8O4xdJoNyIBAwDZ8LHt0vlnLGwcJFR9X7/\nPfWe0PFtC3d7cYY3RopDhnRP7MZs1Wo9nZ4IvlXoEsE2nPkWcns+Wv5Yaewr\ns2ra9ZIK7IIJhqKKgmQtCeiXyFwTq+kfunDnxeCavuWL3HuLKIOZf7P9vXXt\nXgEir9rCwF8EGAEIABMFAlRJbdIJED62JZ7fId8kAhsMAAD+LAf+KT1EpkwH\n0ivTHmYako+6qG6DCtzd3TibWw51cmbY20Ph13NIS/MfBo828S9SXm/sVUzN\n/r7qZgZYfI0/j57tG3BguVGm53qya4bINKyi1RjK6aKo/rrzRkh5ZVD5rVNO\nE2zzvyYAnLUWG9AV1OYDxcgLrXqEMWlqZAo+Wmg7VrTBmdCGs/BPvscNgQRr\n6Gpjgmv9ru6LjRL7vFhEcov/tkBLj+CtaWWFTd1s2vBLOs4rCsD9TT/23vfw\nCnokvvVjKYN5oviy61yhpqF1rWlOsxZ4+2sKW3Pq7JLBtmzsZegTONfcQAf7\nqqGRQm3MxoTdgQUShAwbNwNNQR9cInfMnA==\r\n=2wIY\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n",
"Fingerprint": "c93f767df53b0ca8395cfde90483475164ec6353", "Fingerprint": "c93f767df53b0ca8395cfde90483475164ec6353",
"Activation": null, "Activation": null,
"Primary": 1 "Primary": 1,
"Active": 1
} }
] ]
} }

View File

@ -61,5 +61,5 @@ func TestClient_CurrentUser(t *testing.T) {
// Ignore KeyRings during the check because they have unexported fields and cannot be compared // Ignore KeyRings during the check because they have unexported fields and cannot be compared
r.True(t, cmp.Equal(user, testCurrentUser, cmpopts.IgnoreTypes(&crypto.Key{}))) r.True(t, cmp.Equal(user, testCurrentUser, cmpopts.IgnoreTypes(&crypto.Key{})))
r.Nil(t, c.Unlock(context.Background(), []byte(testMailboxPassword))) r.NoError(t, c.Unlock(context.Background(), []byte(testMailboxPassword)))
} }

View File

@ -1,7 +1,7 @@
.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-ie test-live test-live-bridge test-live-ie test-stage test-debug test-live-debug bench
export GO111MODULE=on export GO111MODULE=on
export BRIDGE_VERSION:=1.8.9+integrationtests export BRIDGE_VERSION:=1.8.12+integrationtests
export VERBOSITY?=fatal export VERBOSITY?=fatal
export TEST_DATA=testdata export TEST_DATA=testdata
export TEST_APP?=bridge export TEST_APP?=bridge
@ -14,7 +14,7 @@ check-go:
check-godog: check-godog:
@which godog || $(MAKE) install-godog @which godog || $(MAKE) install-godog
install-godog: check-go install-godog: check-go
go get github.com/cucumber/godog/cmd/godog@v0.8.1 go get github.com/cucumber/godog/cmd/godog@v0.12.1
test: test-bridge test-ie test: test-bridge test-ie
test-bridge: FEATURES ?= features/bridge test-bridge: FEATURES ?= features/bridge

View File

@ -23,7 +23,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func APIActionsFeatureContext(s *godog.Suite) { func APIActionsFeatureContext(s *godog.ScenarioContext) {
s.Step(`^the internet connection is lost$`, theInternetConnectionIsLost) s.Step(`^the internet connection is lost$`, theInternetConnectionIsLost)
s.Step(`^the internet connection is restored$`, theInternetConnectionIsRestored) s.Step(`^the internet connection is restored$`, theInternetConnectionIsRestored)
s.Step(`^(\d+) second[s]? pass$`, secondsPass) s.Step(`^(\d+) second[s]? pass$`, secondsPass)

View File

@ -27,16 +27,16 @@ import (
"github.com/ProtonMail/proton-bridge/pkg/pmapi" "github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/test/accounts" "github.com/ProtonMail/proton-bridge/test/accounts"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func APIChecksFeatureContext(s *godog.Suite) { func APIChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^API endpoint "([^"]*)" is called$`, apiIsCalled) s.Step(`^API endpoint "([^"]*)" is called$`, apiIsCalled)
s.Step(`^API endpoint "([^"]*)" is called with$`, apiIsCalledWith) s.Step(`^API endpoint "([^"]*)" is called with$`, apiIsCalledWith)
s.Step(`^API endpoint "([^"]*)" is not called$`, apiIsNotCalled) s.Step(`^API endpoint "([^"]*)" is not called$`, apiIsNotCalled)
s.Step(`^API endpoint "([^"]*)" is not called with$`, apiIsNotCalledWith) s.Step(`^API endpoint "([^"]*)" is not called with$`, apiIsNotCalledWith)
s.Step(`^message is sent with API call$`, messageIsSentWithAPICall) s.Step(`^message is sent with API call$`, messageIsSentWithAPICall)
s.Step(`^packages are sent with API call$`, packagesAreSentWithAPICall)
s.Step(`^API mailbox "([^"]*)" for "([^"]*)" has (\d+) message(?:s)?$`, apiMailboxForUserHasNumberOfMessages) s.Step(`^API mailbox "([^"]*)" for "([^"]*)" has (\d+) message(?:s)?$`, apiMailboxForUserHasNumberOfMessages)
s.Step(`^API mailbox "([^"]*)" for address "([^"]*)" of "([^"]*)" has (\d+) message(?:s)?$`, apiMailboxForAddressOfUserHasNumberOfMessages) s.Step(`^API mailbox "([^"]*)" for address "([^"]*)" of "([^"]*)" has (\d+) message(?:s)?$`, apiMailboxForAddressOfUserHasNumberOfMessages)
s.Step(`^API mailbox "([^"]*)" for "([^"]*)" has messages$`, apiMailboxForUserHasMessages) s.Step(`^API mailbox "([^"]*)" for "([^"]*)" has messages$`, apiMailboxForUserHasMessages)
@ -51,13 +51,24 @@ func apiIsCalled(endpoint string) error {
return nil return nil
} }
func apiIsCalledWith(endpoint string, data *gherkin.DocString) error { func apiIsCalledWith(endpoint string, data *godog.DocString) error {
if !apiIsCalledWithHelper(endpoint, data.Content) { if !apiIsCalledWithHelper(endpoint, data.Content) {
return fmt.Errorf("%s was not called with %s", endpoint, data.Content) return fmt.Errorf("%s was not called with %s", endpoint, data.Content)
} }
return nil return nil
} }
func apiIsCalledWithRegex(endpoint string, data *godog.DocString) error {
match, err := apiIsCalledWithHelperRegex(endpoint, data.Content)
if err != nil {
return err
}
if !match {
return fmt.Errorf("%s was not called with %s", endpoint, data.Content)
}
return nil
}
func apiIsNotCalled(endpoint string) error { func apiIsNotCalled(endpoint string) error {
if apiIsCalledWithHelper(endpoint, "") { if apiIsCalledWithHelper(endpoint, "") {
return fmt.Errorf("%s was called", endpoint) return fmt.Errorf("%s was called", endpoint)
@ -65,7 +76,7 @@ func apiIsNotCalled(endpoint string) error {
return nil return nil
} }
func apiIsNotCalledWith(endpoint string, data *gherkin.DocString) error { func apiIsNotCalledWith(endpoint string, data *godog.DocString) error {
if apiIsCalledWithHelper(endpoint, data.Content) { if apiIsCalledWithHelper(endpoint, data.Content) {
return fmt.Errorf("%s was called with %s", endpoint, data.Content) return fmt.Errorf("%s was called with %s", endpoint, data.Content)
} }
@ -80,7 +91,15 @@ func apiIsCalledWithHelper(endpoint string, content string) bool {
return ctx.GetPMAPIController().WasCalled(method, path, request) return ctx.GetPMAPIController().WasCalled(method, path, request)
} }
func messageIsSentWithAPICall(data *gherkin.DocString) error { func apiIsCalledWithHelperRegex(endpoint string, content string) (bool, error) {
split := strings.Split(endpoint, " ")
method := split[0]
path := split[1]
request := []byte(content)
return ctx.GetPMAPIController().WasCalledRegex(method, path, request)
}
func messageIsSentWithAPICall(data *godog.DocString) error {
endpoint := "POST /mail/v4/messages" endpoint := "POST /mail/v4/messages"
if err := apiIsCalledWith(endpoint, data); err != nil { if err := apiIsCalledWith(endpoint, data); err != nil {
return err return err
@ -94,6 +113,20 @@ func messageIsSentWithAPICall(data *gherkin.DocString) error {
return nil return nil
} }
func packagesAreSentWithAPICall(data *godog.DocString) error {
endpoint := "POST /mail/v4/messages/.+$"
if err := apiIsCalledWithRegex(endpoint, data); err != nil {
return err
}
for _, request := range ctx.GetPMAPIController().GetCalls("POST", "/mail/v4/messages") {
if !checkAllRequiredFieldsForSendingMessage(request) {
return fmt.Errorf("%s was not called with all required fields: %s", endpoint, request)
}
}
return nil
}
func checkAllRequiredFieldsForSendingMessage(request []byte) bool { func checkAllRequiredFieldsForSendingMessage(request []byte) bool {
if matches := regexp.MustCompile(`"Subject":`).Match(request); !matches { if matches := regexp.MustCompile(`"Subject":`).Match(request); !matches {
return false return false
@ -145,11 +178,11 @@ func apiMailboxForAddressOfUserHasNumberOfMessages(mailboxName, bddAddressID, bd
return nil return nil
} }
func apiMailboxForUserHasMessages(mailboxName, bddUserID string, messages *gherkin.DataTable) error { func apiMailboxForUserHasMessages(mailboxName, bddUserID string, messages *godog.Table) error {
return apiMailboxForAddressOfUserHasMessages(mailboxName, "", bddUserID, messages) return apiMailboxForAddressOfUserHasMessages(mailboxName, "", bddUserID, messages)
} }
func apiMailboxForAddressOfUserHasMessages(mailboxName, bddAddressID, bddUserID string, messages *gherkin.DataTable) error { func apiMailboxForAddressOfUserHasMessages(mailboxName, bddAddressID, bddUserID string, messages *godog.Table) error {
account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID) account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID)
if account == nil { if account == nil {
return godog.ErrPending return godog.ErrPending

View File

@ -21,7 +21,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func APISetupFeatureContext(s *godog.Suite) { func APISetupFeatureContext(s *godog.ScenarioContext) {
s.Step(`^there is no internet connection$`, thereIsNoInternetConnection) s.Step(`^there is no internet connection$`, thereIsNoInternetConnection)
} }

View File

@ -18,9 +18,10 @@
package tests package tests
import ( import (
"context"
"os" "os"
"github.com/ProtonMail/proton-bridge/test/context" testContext "github.com/ProtonMail/proton-bridge/test/context"
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
@ -28,12 +29,14 @@ const (
timeFormat = "2006-01-02T15:04:05" timeFormat = "2006-01-02T15:04:05"
) )
func FeatureContext(s *godog.Suite) { func SuiteInitializer(s *godog.TestSuiteContext) {
s.BeforeSuite(context.BeforeRun) s.BeforeSuite(testContext.BeforeRun)
s.AfterSuite(context.AfterRun) s.AfterSuite(testContext.AfterRun)
}
s.BeforeScenario(beforeScenario) func ScenarioInitializer(s *godog.ScenarioContext) {
s.AfterScenario(afterScenario) s.Before(beforeScenario)
s.After(afterScenario)
APIActionsFeatureContext(s) APIActionsFeatureContext(s)
APIChecksFeatureContext(s) APIChecksFeatureContext(s)
@ -66,16 +69,16 @@ func FeatureContext(s *godog.Suite) {
UsersChecksFeatureContext(s) UsersChecksFeatureContext(s)
} }
var ctx *context.TestContext //nolint[gochecknoglobals] var ctx *testContext.TestContext //nolint[gochecknoglobals]
func beforeScenario(scenario interface{}) { func beforeScenario(scenarioCtx context.Context, _ *godog.Scenario) (context.Context, error) {
// bridge or ie. With godog 0.10.x and later it can be determined from // NOTE(GODT-219) It would be possible to optimised the usage of godog with our context.
// scenario.Uri and its file location.
app := os.Getenv("TEST_APP") app := os.Getenv("TEST_APP")
ctx = context.New(app) ctx = testContext.New(app)
return scenarioCtx, nil
} }
func afterScenario(scenario interface{}, err error) { func afterScenario(scenarioCtx context.Context, _ *godog.Scenario, err error) (context.Context, error) {
if err != nil { if err != nil {
for _, user := range ctx.GetUsers().GetUsers() { for _, user := range ctx.GetUsers().GetUsers() {
store := user.GetStore() store := user.GetStore()
@ -88,4 +91,6 @@ func afterScenario(scenario interface{}, err error) {
if err != nil { if err != nil {
ctx.GetPMAPIController().PrintCalls() ctx.GetPMAPIController().PrintCalls()
} }
return scenarioCtx, err
} }

View File

@ -21,7 +21,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func BridgeActionsFeatureContext(s *godog.Suite) { func BridgeActionsFeatureContext(s *godog.ScenarioContext) {
s.Step(`^bridge starts$`, bridgeStarts) s.Step(`^bridge starts$`, bridgeStarts)
s.Step(`^bridge syncs "([^"]*)"$`, bridgeSyncsUser) s.Step(`^bridge syncs "([^"]*)"$`, bridgeSyncsUser)
} }

View File

@ -22,7 +22,7 @@ import (
a "github.com/stretchr/testify/assert" a "github.com/stretchr/testify/assert"
) )
func CommonChecksFeatureContext(s *godog.Suite) { func CommonChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^last response is "([^"]*)"$`, lastResponseIs) s.Step(`^last response is "([^"]*)"$`, lastResponseIs)
} }

View File

@ -0,0 +1,99 @@
// 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 calls
import (
"fmt"
"regexp"
"github.com/nsf/jsondiff"
)
type CallRecord struct {
method string
path string
request []byte
}
type Calls []CallRecord
func (c *Calls) Register(method, path string, request []byte) {
*c = append(*c, CallRecord{
method: method,
path: path,
request: request,
})
}
func (c *Calls) PrintCalls() {
fmt.Println("API calls:")
for idx, call := range *c {
fmt.Printf("%02d: [%s] %s\n", idx+1, call.method, call.path)
if call.request != nil && string(call.request) != "null" {
fmt.Printf("\t%s\n", call.request)
}
}
}
func (c *Calls) WasCalled(method, path string, expectedRequest []byte) bool {
res, err := c.WasCalledRegex("^"+regexp.QuoteMeta(method)+"$", "^"+regexp.QuoteMeta(path)+"$", expectedRequest)
if err != nil {
panic(err)
}
return res
}
func (c *Calls) WasCalledRegex(methodRegex, pathRegex string, expectedRequest []byte) (bool, error) {
for _, call := range *c {
matched, err := regexp.Match(methodRegex, []byte(call.method))
if err != nil {
return false, err
}
if !matched {
continue
}
matched, err = regexp.Match(pathRegex, []byte(call.path))
if err != nil {
return false, err
}
if !matched {
continue
}
if string(expectedRequest) == "" {
return true, nil
}
diff, _ := jsondiff.Compare(call.request, expectedRequest, &jsondiff.Options{})
isSuperset := diff == jsondiff.FullMatch || diff == jsondiff.SupersetMatch
if isSuperset {
return true, nil
}
}
return false, nil
}
func (c *Calls) GetCalls(method, path string) [][]byte {
requests := [][]byte{}
for _, call := range *c {
if call.method == method && call.path == path {
requests = append(requests, call.request)
}
}
return requests
}

View File

@ -177,3 +177,6 @@ func (ctx *TestContext) SetLastError(err error) {
func (ctx *TestContext) GetLastError() error { func (ctx *TestContext) GetLastError() error {
return ctx.lastError return ctx.lastError
} }
func (ctx *TestContext) MessagePreparationStarted() { ctx.pmapiController.LockEvents() }
func (ctx *TestContext) MessagePreparationFinished() { ctx.pmapiController.UnlockEvents() }

View File

@ -40,7 +40,10 @@ type PMAPIController interface {
ReorderAddresses(user *pmapi.User, addressIDs []string) error ReorderAddresses(user *pmapi.User, addressIDs []string) error
PrintCalls() PrintCalls()
WasCalled(method, path string, expectedRequest []byte) bool WasCalled(method, path string, expectedRequest []byte) bool
WasCalledRegex(methodRegex, pathRegex string, expectedRequest []byte) (bool, error)
GetCalls(method, path string) [][]byte GetCalls(method, path string) [][]byte
LockEvents()
UnlockEvents()
} }
func newPMAPIController(app string, listener listener.Listener) (PMAPIController, pmapi.Manager) { func newPMAPIController(app string, listener listener.Listener) (PMAPIController, pmapi.Manager) {

View File

@ -21,6 +21,7 @@ import (
"sync" "sync"
"github.com/ProtonMail/proton-bridge/pkg/pmapi" "github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/test/context/calls"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -30,7 +31,7 @@ type Controller struct {
// Internal states. // Internal states.
lock *sync.RWMutex lock *sync.RWMutex
fakeAPIs []*FakePMAPI fakeAPIs []*FakePMAPI
calls []*fakeCall calls calls.Calls
labelIDGenerator idGenerator labelIDGenerator idGenerator
messageIDGenerator idGenerator messageIDGenerator idGenerator
tokenGenerator idGenerator tokenGenerator idGenerator
@ -52,7 +53,7 @@ func NewController() (*Controller, pmapi.Manager) {
controller := &Controller{ controller := &Controller{
lock: &sync.RWMutex{}, lock: &sync.RWMutex{},
fakeAPIs: []*FakePMAPI{}, fakeAPIs: []*FakePMAPI{},
calls: []*fakeCall{}, calls: calls.Calls{},
labelIDGenerator: 100, // We cannot use system label IDs. labelIDGenerator: 100, // We cannot use system label IDs.
messageIDGenerator: 0, messageIDGenerator: 0,
tokenGenerator: 1000, // No specific reason; 1000 simply feels right. tokenGenerator: 1000, // No specific reason; 1000 simply feels right.

View File

@ -19,10 +19,8 @@ package fakeapi
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/ProtonMail/proton-bridge/pkg/pmapi" "github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/nsf/jsondiff"
) )
type method string type method string
@ -34,12 +32,6 @@ const (
DELETE method = "DELETE" DELETE method = "DELETE"
) )
type fakeCall struct {
method method
path string
request []byte
}
func (ctl *Controller) checkAndRecordCall(method method, path string, req interface{}) error { func (ctl *Controller) checkAndRecordCall(method method, path string, req interface{}) error {
ctl.lock.Lock() ctl.lock.Lock()
defer ctl.lock.Unlock() defer ctl.lock.Unlock()
@ -54,11 +46,7 @@ func (ctl *Controller) checkAndRecordCall(method method, path string, req interf
} }
} }
ctl.calls = append(ctl.calls, &fakeCall{ ctl.calls.Register(string(method), path, request)
method: method,
path: path,
request: request,
})
if ctl.noInternetConnection { if ctl.noInternetConnection {
return pmapi.ErrNoConnection return pmapi.ErrNoConnection
@ -68,38 +56,17 @@ func (ctl *Controller) checkAndRecordCall(method method, path string, req interf
} }
func (ctl *Controller) PrintCalls() { func (ctl *Controller) PrintCalls() {
fmt.Println("API calls:") ctl.calls.PrintCalls()
for idx, call := range ctl.calls {
fmt.Printf("%02d: [%s] %s\n", idx+1, call.method, call.path)
if call.request != nil && string(call.request) != "null" {
fmt.Printf("\t%s\n", call.request)
}
}
} }
func (ctl *Controller) WasCalled(method, path string, expectedRequest []byte) bool { func (ctl *Controller) WasCalled(method, path string, expectedRequest []byte) bool {
for _, call := range ctl.calls { return ctl.calls.WasCalled(method, path, expectedRequest)
if string(call.method) != method || call.path != path { }
continue
} func (ctl *Controller) WasCalledRegex(methodRegex, pathRegex string, expectedRequest []byte) (bool, error) {
if string(expectedRequest) == "" { return ctl.calls.WasCalledRegex(methodRegex, pathRegex, expectedRequest)
return true
}
diff, _ := jsondiff.Compare(call.request, expectedRequest, &jsondiff.Options{})
isSuperset := diff == jsondiff.FullMatch || diff == jsondiff.SupersetMatch
if isSuperset {
return true
}
}
return false
} }
func (ctl *Controller) GetCalls(method, path string) [][]byte { func (ctl *Controller) GetCalls(method, path string) [][]byte {
requests := [][]byte{} return ctl.calls.GetCalls(method, path)
for _, call := range ctl.calls {
if string(call.method) == method && call.path == path {
requests = append(requests, call.request)
}
}
return requests
} }

View File

@ -197,3 +197,9 @@ func (ctl *Controller) GetAuthClient(username string) pmapi.Client {
return nil return nil
} }
// LockEvents doesn't needs to be implemented for fakeAPI.
func (ctl *Controller) LockEvents() {}
// UnlockEvents doesn't needs to be implemented for fakeAPI.
func (ctl *Controller) UnlockEvents() {}

View File

@ -30,7 +30,16 @@ func (api *FakePMAPI) isLabelFolder(labelID string) bool {
return bool(label.Exclusive) return bool(label.Exclusive)
} }
} }
return labelID == pmapi.InboxLabel || labelID == pmapi.ArchiveLabel || labelID == pmapi.SentLabel switch labelID {
case pmapi.InboxLabel,
pmapi.TrashLabel,
pmapi.SpamLabel,
pmapi.ArchiveLabel,
pmapi.SentLabel,
pmapi.DraftLabel:
return true
}
return false
} }
func (api *FakePMAPI) ListLabels(context.Context) ([]*pmapi.Label, error) { func (api *FakePMAPI) ListLabels(context.Context) ([]*pmapi.Label, error) {

View File

@ -48,6 +48,26 @@ Feature: IMAP copy messages
| from | to | subject | body | read | deleted | | from | to | subject | body | read | deleted |
| john.doe@mail.com | user@pm.me | foo | hello | true | false | | john.doe@mail.com | user@pm.me | foo | hello | true | false |
Scenario: Copy message from All mail moves from the original location
Given there is IMAP client selected in "All Mail"
When IMAP client copies message seq "1" to "Folders/mbox"
Then IMAP response is "OK"
And mailbox "INBOX" for "user" has 2 messages
And mailbox "INBOX" for "user" has messages
| from | to | subject | body | read | deleted |
| jane.doe@mail.com | name@pm.me | bar | world | false | true |
| john.doe@mail.com | user@pm.me | response | hello | true | false |
And mailbox "All Mail" for "user" has 3 messages
And mailbox "All Mail" for "user" has messages
| from | to | subject | body | read | deleted |
| john.doe@mail.com | user@pm.me | foo | hello | true | false |
| jane.doe@mail.com | name@pm.me | bar | world | false | false |
| john.doe@mail.com | user@pm.me | response | hello | true | false |
And mailbox "Folders/mbox" for "user" has 1 messages
And mailbox "Folders/mbox" for "user" has messages
| from | to | subject | body | read | deleted |
| john.doe@mail.com | user@pm.me | foo | hello | true | false |
Scenario: Copy all messages to folder does move Scenario: Copy all messages to folder does move
Given there is IMAP client selected in "INBOX" Given there is IMAP client selected in "INBOX"
When IMAP client copies message seq "1:*" to "Folders/mbox" When IMAP client copies message seq "1:*" to "Folders/mbox"

View File

@ -65,6 +65,7 @@ Feature: IMAP create messages
| from | to | subject | body | | from | to | subject | body |
| [primary] | chosen@one.com | Meet the Twins | Hello, Mr. Anderson | | [primary] | chosen@one.com | Meet the Twins | Hello, Mr. Anderson |
And there is IMAP client selected in "Sent" And there is IMAP client selected in "Sent"
Then mailbox "Sent" for "userMoreAddresses" has 1 messages
When IMAP client creates message "Meet the Twins" from address "primary" of "userMoreAddresses" to "chosen@one.com" with body "Hello, Mr. Anderson" in "Sent" When IMAP client creates message "Meet the Twins" from address "primary" of "userMoreAddresses" to "chosen@one.com" with body "Hello, Mr. Anderson" in "Sent"
Then IMAP response is "OK" Then IMAP response is "OK"
And mailbox "Sent" for "userMoreAddresses" has 2 messages And mailbox "Sent" for "userMoreAddresses" has 2 messages

View File

@ -96,12 +96,22 @@ Feature: IMAP remove messages from mailbox
| LOGOUT | 9 | | LOGOUT | 9 |
| UNSELECT | 10 | | UNSELECT | 10 |
Scenario: Not possible to delete from All Mail Scenario: Not possible to delete from All Mail and expunge does nothing
Given there are 1 messages in mailbox "INBOX" for "user" Given there are messages in mailbox "INBOX" for "user"
| id | from | to | subject | body |
| 1 | john.doe@mail.com | user@pm.me | subj1 | body1 |
And there is IMAP client logged in as "user" And there is IMAP client logged in as "user"
And there is IMAP client selected in "All Mail" And there is IMAP client selected in "All Mail"
When IMAP client marks message seq "1" as deleted When IMAP client marks message seq "1" as deleted
Then IMAP response is "IMAP error: NO operation not allowed for 'All Mail' folder" Then IMAP response is "IMAP error: NO operation not allowed for 'All Mail' folder"
And mailbox "All Mail" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | subj1 |
When IMAP client sends expunge
Then IMAP response is "OK"
And mailbox "All Mail" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | subj1 |
Scenario: Expunge specific message only Scenario: Expunge specific message only
Given there are 5 messages in mailbox "INBOX" for "user" Given there are 5 messages in mailbox "INBOX" for "user"

View File

@ -198,3 +198,23 @@ Feature: IMAP import messages
""" """
Then IMAP response is "OK \[APPENDUID \d 1\] APPEND completed" Then IMAP response is "OK \[APPENDUID \d 1\] APPEND completed"
Scenario: Import message to All Mail
When IMAP client imports message to "All Mail"
"""
From: Foo <from1@pm.me>
To: Bridge Test <to1@pm.me>
Subject: subj1
Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000
body1
"""
Then IMAP response is "OK \[APPENDUID \d 1\] APPEND completed"
Then mailbox "Archive" for "user" has messages
| from | to | subject | body
| from1@pm.me | to1@pm.me | subj1 | body1
And API mailbox "Archive" for "user" has 1 message
And mailbox "All Mail" for "user" has messages
| from | to | subject | body
| from1@pm.me | to1@pm.me | subj1 | body1
And API mailbox "All Mail" for "user" has 1 message

View File

@ -80,16 +80,12 @@ Feature: IMAP move messages
Scenario: Move message from All Mail is not possible Scenario: Move message from All Mail is not possible
Given there is IMAP client selected in "All Mail" Given there is IMAP client selected in "All Mail"
When IMAP client moves message seq "1" to "Folders/folder" When IMAP client moves message seq "1" to "Folders/folder"
Then IMAP response is "OK" Then IMAP response is "NO move from All Mail is not allowed"
And mailbox "All Mail" for "user" has messages And mailbox "All Mail" for "user" has messages
| from | to | subject | | from | to | subject |
| john.doe@mail.com | user@pm.me | foo | | john.doe@mail.com | user@pm.me | foo |
| jane.doe@mail.com | name@pm.me | bar | | jane.doe@mail.com | name@pm.me | bar |
And mailbox "Folders/folder" for "user" has messages And mailbox "Folders/folder" for "user" has 0 messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | baz |
And API endpoint "PUT /mail/v4/messages/label" is called
And API endpoint "PUT /mail/v4/messages/unlabel" is not called
Scenario: Move message from Inbox to Sent is not possible Scenario: Move message from Inbox to Sent is not possible
Given there is IMAP client selected in "INBOX" Given there is IMAP client selected in "INBOX"

View File

@ -5,52 +5,73 @@ Feature: IMAP move messages by append and delete (without MOVE support, e.g., Ou
And there is IMAP client "source" logged in as "user" And there is IMAP client "source" logged in as "user"
And there is IMAP client "target" logged in as "user" And there is IMAP client "target" logged in as "user"
Scenario Outline: Move message from INBOX to mailbox by append and delete Scenario Outline: Move message from <srcMailbox> to <dstMailbox> by <order>
Given there are messages in mailbox "INBOX" for "user" Given there are messages in mailbox "<srcMailbox>" for "user"
| id | from | to | subject | body | | id | from | to | subject | body |
| 1 | john.doe@mail.com | user@pm.me | foo | hello | | 1 | sndr1@pm.me | rcvr1@pm.me | subj1 | body1 |
| 2 | jane.doe@mail.com | name@pm.me | bar | world | | 2 | sndr2@pm.me | rcvr2@pm.me | subj2 | body2 |
And there is IMAP client "source" selected in "INBOX" And there is IMAP client "source" selected in "<srcMailbox>"
And there is IMAP client "target" selected in "<mailbox>" And there is IMAP client "target" selected in "<dstMailbox>"
When IMAP clients "source" and "target" move message seq "2" of "user" from "INBOX" to "<mailbox>" by append and delete When IMAP clients "source" and "target" move message seq "2" of "user" to "<dstMailbox>" by <order>
Then IMAP response to "source" is "OK" Then IMAP response to "source" is "OK"
Then IMAP response to "target" is "OK" Then IMAP response to "target" is "OK"
When IMAP client "source" sends expunge And mailbox "<dstMailbox>" for "user" has 1 messages
Then IMAP response to "source" is "OK" And mailbox "<dstMailbox>" for "user" has messages
And mailbox "INBOX" for "user" has messages | from | to | subject |
| from | to | subject | | sndr2@pm.me | rcvr2@pm.me | subj2 |
| john.doe@mail.com | user@pm.me | foo | And mailbox "<srcMailbox>" for "user" has 1 messages
And mailbox "<mailbox>" for "user" has messages And mailbox "<srcMailbox>" for "user" has messages
| from | to | subject | | from | to | subject |
| jane.doe@mail.com | name@pm.me | bar | | sndr1@pm.me | rcvr1@pm.me | subj1 |
Examples: Examples:
| mailbox | | srcMailbox | dstMailbox | order |
| Archive | | Trash | INBOX | APPEND DELETE EXPUNGE |
| Folders/mbox | | Spam | INBOX | APPEND DELETE EXPUNGE |
| Spam | | INBOX | Archive | APPEND DELETE EXPUNGE |
| Trash | | INBOX | Folders/mbox | APPEND DELETE EXPUNGE |
| INBOX | Spam | APPEND DELETE EXPUNGE |
| INBOX | Trash | APPEND DELETE EXPUNGE |
| Trash | INBOX | DELETE APPEND EXPUNGE |
| Spam | INBOX | DELETE APPEND EXPUNGE |
| INBOX | Archive | DELETE APPEND EXPUNGE |
| 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 |
Scenario Outline: Move message from Trash/Spam to INBOX by append and delete Scenario Outline: Move message from <mailbox> to All Mail by <order>
Given there are messages in mailbox "<mailbox>" for "user" Given there are messages in mailbox "<mailbox>" for "user"
| id | from | to | subject | body | | id | from | to | subject | body |
| 1 | john.doe@mail.com | user@pm.me | foo | hello | | 1 | john.doe@mail.com | user@pm.me | subj1 | body1 |
| 2 | jane.doe@mail.com | name@pm.me | bar | world | | 2 | john.doe@mail.com | name@pm.me | subj2 | body2 |
And there is IMAP client "source" selected in "<mailbox>" And there is IMAP client "source" selected in "<mailbox>"
And there is IMAP client "target" selected in "INBOX" And there is IMAP client "target" selected in "All Mail"
When IMAP clients "source" and "target" move message seq "2" of "user" from "<mailbox>" to "INBOX" by append and delete When IMAP clients "source" and "target" move message seq "2" of "user" to "All Mail" by <order>
Then IMAP response to "source" is "OK" Then IMAP response to "source" is "OK"
Then IMAP response to "target" is "OK" Then IMAP response to "target" is "OK"
When IMAP client "source" sends expunge
Then IMAP response to "source" is "OK"
And mailbox "INBOX" for "user" has messages
| from | to | subject |
| jane.doe@mail.com | name@pm.me | bar |
And mailbox "<mailbox>" for "user" has messages And mailbox "<mailbox>" for "user" has messages
| from | to | subject | | from | to | subject |
| john.doe@mail.com | user@pm.me | foo | | john.doe@mail.com | user@pm.me | subj1 |
And mailbox "All Mail" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | subj1 |
| john.doe@mail.com | name@pm.me | subj2 |
Examples: Examples:
| mailbox | | mailbox | order |
| Spam | | INBOX | APPEND DELETE EXPUNGE |
| Trash | | Archive | APPEND DELETE EXPUNGE |
| Trash | APPEND DELETE EXPUNGE |
| Spam | APPEND DELETE EXPUNGE |
| INBOX | DELETE APPEND EXPUNGE |
| Archive | DELETE APPEND EXPUNGE |
| Trash | DELETE APPEND EXPUNGE |
| Spam | DELETE APPEND EXPUNGE |
| INBOX | DELETE EXPUNGE APPEND |
| Archive | DELETE EXPUNGE APPEND |
| Trash | DELETE EXPUNGE APPEND |
| Spam | DELETE EXPUNGE APPEND |

View File

@ -19,3 +19,44 @@ Feature: IMAP update messages in Spam folder
| from | to | subject | | from | to | subject |
| john.doe@mail.com | user@pm.me | foo | | john.doe@mail.com | user@pm.me | foo |
| jane.doe@mail.com | name@pm.me | bar | | jane.doe@mail.com | name@pm.me | bar |
Scenario Outline: Move from Spam to INBOX when client <operation> <flag>
When IMAP client <operation> flags "<flag>" <suffix> message seq "1"
Then IMAP response is "OK"
And mailbox "INBOX" for "user" has 1 messages
And mailbox "INBOX" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | foo |
And mailbox "Spam" for "user" has 1 messages
And mailbox "Spam" for "user" has messages
| from | to | subject |
| jane.doe@mail.com | name@pm.me | bar |
Examples:
| operation | suffix | flag |
| adds | to | nojunk |
| adds | to | NoJunk |
| removes | from | junk |
| removes | from | Junk |
| removes | from | $Junk |
Scenario Outline: Do not move from Archive to INBOX when client <operation> <flag>
Given there are messages in mailbox "Archive" for "user"
| id | from | to | subject | body | read | starred | deleted |
| 1 | john.doe@mail.com | user@pm.me | Archived | hello | false | false | false |
And there is IMAP client selected in "Archive"
When IMAP client <operation> flags "<flag>" <suffix> message seq "1"
Then IMAP response is "OK"
And mailbox "INBOX" for "user" has 0 messages
And mailbox "Archive" for "user" has 1 messages
And mailbox "Archive" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | Archived |
Examples:
| operation | suffix | flag |
| adds | to | nojunk |
| adds | to | NoJunk |
| removes | from | junk |
| removes | from | Junk |
| removes | from | $Junk |

View File

@ -256,3 +256,69 @@ Feature: SMTP sending of plain messages
} }
} }
""" """
Scenario: RCPT does not contain all CC
When SMTP client sends "MAIL FROM:<[userAddress]>"
Then SMTP response is "OK"
When SMTP client sends "RCPT TO:<bridgetest@protonmail.com>"
Then SMTP response is "OK"
When SMTP client sends "DATA"
Then SMTP response is "OK"
When SMTP client sends
"""
From: Bridge Test <[userAddress]>
To: Internal Bridge <bridgetest@protonmail.com>
CC: Internal Bridge 2 <bridgetest2@protonmail.com>
Content-Type: text/plain
Subject: RCPT-CC test
This is CC missing in RCPT test. Have a nice day!
.
"""
Then SMTP response is "OK"
And mailbox "Sent" for "user" has messages
| from | to | cc | subject |
| [userAddress] | bridgetest@protonmail.com | bridgetest2@protonmail.com | RCPT-CC test |
And message is sent with API call
"""
{
"Message": {
"Subject": "RCPT-CC test",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "bridgetest@protonmail.com",
"Name": "Internal Bridge"
}
],
"CCList": [
{
"Address": "bridgetest2@protonmail.com",
"Name": "Internal Bridge 2"
}
],
"BCCList": []
}
}
"""
And packages are sent with API call
"""
{
"Packages":[
{
"Addresses":{
"bridgetest@protonmail.com":{
"Type":1
},
"bridgetest2@protonmail.com":{
"Type":1
}
},
"Type":1,
"MIMEType":"text/plain"
}
]
}
"""

View File

@ -21,7 +21,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func IMAPActionsAuthFeatureContext(s *godog.Suite) { func IMAPActionsAuthFeatureContext(s *godog.ScenarioContext) {
s.Step(`^IMAP client authenticates "([^"]*)"$`, imapClientAuthenticates) s.Step(`^IMAP client authenticates "([^"]*)"$`, imapClientAuthenticates)
s.Step(`^IMAP client "([^"]*)" authenticates "([^"]*)"$`, imapClientNamedAuthenticates) s.Step(`^IMAP client "([^"]*)" authenticates "([^"]*)"$`, imapClientNamedAuthenticates)
s.Step(`^IMAP client authenticates "([^"]*)" with address "([^"]*)"$`, imapClientAuthenticatesWithAddress) s.Step(`^IMAP client authenticates "([^"]*)" with address "([^"]*)"$`, imapClientAuthenticatesWithAddress)

View File

@ -23,7 +23,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func IMAPActionsMailboxFeatureContext(s *godog.Suite) { func IMAPActionsMailboxFeatureContext(s *godog.ScenarioContext) {
s.Step(`^IMAP client creates mailbox "([^"]*)"$`, imapClientCreatesMailbox) s.Step(`^IMAP client creates mailbox "([^"]*)"$`, imapClientCreatesMailbox)
s.Step(`^IMAP client renames mailbox "([^"]*)" to "([^"]*)"$`, imapClientRenamesMailboxTo) s.Step(`^IMAP client renames mailbox "([^"]*)" to "([^"]*)"$`, imapClientRenamesMailboxTo)
s.Step(`^IMAP client deletes mailbox "([^"]*)"$`, imapClientDeletesMailbox) s.Step(`^IMAP client deletes mailbox "([^"]*)"$`, imapClientDeletesMailbox)

View File

@ -18,16 +18,19 @@
package tests package tests
import ( import (
"errors"
"fmt" "fmt"
"strconv" "strconv"
"strings"
"sync" "sync"
"time"
"github.com/ProtonMail/proton-bridge/test/mocks"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin"
"golang.org/x/net/html/charset" "golang.org/x/net/html/charset"
) )
func IMAPActionsMessagesFeatureContext(s *godog.Suite) { func IMAPActionsMessagesFeatureContext(s *godog.ScenarioContext) {
s.Step(`^IMAP client sends command "([^"]*)"$`, imapClientSendsCommand) s.Step(`^IMAP client sends command "([^"]*)"$`, imapClientSendsCommand)
s.Step(`^IMAP client fetches "([^"]*)"$`, imapClientFetches) s.Step(`^IMAP client fetches "([^"]*)"$`, imapClientFetches)
s.Step(`^IMAP client fetches header(?:s)? of "([^"]*)"$`, imapClientFetchesHeader) s.Step(`^IMAP client fetches header(?:s)? of "([^"]*)"$`, imapClientFetchesHeader)
@ -36,7 +39,8 @@ func IMAPActionsMessagesFeatureContext(s *godog.Suite) {
s.Step(`^IMAP client searches for "([^"]*)"$`, imapClientSearchesFor) s.Step(`^IMAP client searches for "([^"]*)"$`, imapClientSearchesFor)
s.Step(`^IMAP client copies message seq "([^"]*)" to "([^"]*)"$`, imapClientCopiesMessagesTo) s.Step(`^IMAP client copies message seq "([^"]*)" to "([^"]*)"$`, imapClientCopiesMessagesTo)
s.Step(`^IMAP client moves message seq "([^"]*)" to "([^"]*)"$`, imapClientMovesMessagesTo) s.Step(`^IMAP client moves message seq "([^"]*)" to "([^"]*)"$`, imapClientMovesMessagesTo)
s.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message seq "([^"]*)" of "([^"]*)" from "([^"]*)" to "([^"]*)" by append and delete$`, imapClientsMoveMessageSeqOfUserFromToByAppendAndDelete) s.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message seq "([^"]*)" of "([^"]*)" from "([^"]*)" to "([^"]*)"$`, imapClientsMoveMessageSeqOfUserFromTo)
s.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message seq "([^"]*)" of "([^"]*)" to "([^"]*)" by ([^"]*) ([^"]*) ([^"]*)`, imapClientsMoveMessageSeqOfUserFromToByOrederedOperations)
s.Step(`^IMAP client imports message to "([^"]*)"$`, imapClientCreatesMessage) s.Step(`^IMAP client imports message to "([^"]*)"$`, imapClientCreatesMessage)
s.Step(`^IMAP client imports message to "([^"]*)" with encoding "([^"]*)"$`, imapClientCreatesMessageWithEncoding) s.Step(`^IMAP client imports message to "([^"]*)" with encoding "([^"]*)"$`, imapClientCreatesMessageWithEncoding)
s.Step(`^IMAP client creates message "([^"]*)" from "([^"]*)" to "([^"]*)" with body "([^"]*)" in "([^"]*)"$`, imapClientCreatesMessageFromToWithBody) s.Step(`^IMAP client creates message "([^"]*)" from "([^"]*)" to "([^"]*)" with body "([^"]*)" in "([^"]*)"$`, imapClientCreatesMessageFromToWithBody)
@ -44,6 +48,10 @@ func IMAPActionsMessagesFeatureContext(s *godog.Suite) {
s.Step(`^IMAP client creates message "([^"]*)" from address "([^"]*)" of "([^"]*)" to "([^"]*)" with body "([^"]*)" in "([^"]*)"$`, imapClientCreatesMessageFromAddressOfUserToWithBody) s.Step(`^IMAP client creates message "([^"]*)" from address "([^"]*)" of "([^"]*)" to "([^"]*)" with body "([^"]*)" in "([^"]*)"$`, imapClientCreatesMessageFromAddressOfUserToWithBody)
s.Step(`^IMAP client marks message seq "([^"]*)" with "([^"]*)"$`, imapClientMarksMessageSeqWithFlags) s.Step(`^IMAP client marks message seq "([^"]*)" with "([^"]*)"$`, imapClientMarksMessageSeqWithFlags)
s.Step(`^IMAP client "([^"]*)" marks message seq "([^"]*)" with "([^"]*)"$`, imapClientNamedMarksMessageSeqWithFlags) s.Step(`^IMAP client "([^"]*)" marks message seq "([^"]*)" with "([^"]*)"$`, imapClientNamedMarksMessageSeqWithFlags)
s.Step(`^IMAP client adds flags "([^"]*)" to message seq "([^"]*)"$`, imapClientAddsFlagsToMessageSeq)
s.Step(`^IMAP client "([^"]*)" adds flags "([^"]*)" to message seq "([^"]*)"$`, imapClientNamedAddsFlagsToMessageSeq)
s.Step(`^IMAP client removes flags "([^"]*)" from message seq "([^"]*)"$`, imapClientRemovesFlagsFromMessageSeq)
s.Step(`^IMAP client "([^"]*)" removes flags "([^"]*)" from message seq "([^"]*)"$`, imapClientNamedRemovesFlagsFromMessageSeq)
s.Step(`^IMAP client marks message seq "([^"]*)" as read$`, imapClientMarksMessageSeqAsRead) s.Step(`^IMAP client marks message seq "([^"]*)" as read$`, imapClientMarksMessageSeqAsRead)
s.Step(`^IMAP client "([^"]*)" marks message seq "([^"]*)" as read$`, imapClientNamedMarksMessageSeqAsRead) s.Step(`^IMAP client "([^"]*)" marks message seq "([^"]*)" as read$`, imapClientNamedMarksMessageSeqAsRead)
s.Step(`^IMAP client marks message seq "([^"]*)" as unread$`, imapClientMarksMessageSeqAsUnread) s.Step(`^IMAP client marks message seq "([^"]*)" as unread$`, imapClientMarksMessageSeqAsUnread)
@ -114,7 +122,7 @@ func imapClientMovesMessagesTo(messageSeq, newMailboxName string) error {
return nil return nil
} }
func imapClientsMoveMessageSeqOfUserFromToByAppendAndDelete(sourceIMAPClient, targetIMAPClient, messageSeq, bddUserID, sourceMailboxName, targetMailboxName string) error { func imapClientsMoveMessageSeqOfUserFromTo(sourceIMAPClient, targetIMAPClient, messageSeq, bddUserID, sourceMailboxName, targetMailboxName string) error {
account := ctx.GetTestAccount(bddUserID) account := ctx.GetTestAccount(bddUserID)
if account == nil { if account == nil {
return godog.ErrPending return godog.ErrPending
@ -161,11 +169,54 @@ func imapClientsMoveMessageSeqOfUserFromToByAppendAndDelete(sourceIMAPClient, ta
return nil return nil
} }
func imapClientCreatesMessage(mailboxName string, message *gherkin.DocString) error { func extractMessageBodyFromImapResponse(response *mocks.IMAPResponse) (string, error) {
sections := response.Sections()
if len(sections) != 1 {
return "", internalError(errors.New("unexpected result from FETCH"), "retrieving message body using FETCH")
}
sections = strings.Split(sections[0], "\n")
if len(sections) < 2 {
return "", internalError(errors.New("failed to parse FETCH result"), "extraction body from FETCH reply")
}
return strings.Join(sections[1:], "\n"), nil
}
func imapClientsMoveMessageSeqOfUserFromToByOrederedOperations(sourceIMAPClient, targetIMAPClient, messageSeq, bddUserID, targetMailboxName, op1, op2, op3 string) error {
account := ctx.GetTestAccount(bddUserID)
if account == nil {
return godog.ErrPending
}
// call NOOP to prevent unilateral updates in following FETCH
ctx.GetIMAPClient(sourceIMAPClient).Noop().AssertOK()
msgStr, err := extractMessageBodyFromImapResponse(ctx.GetIMAPClient(sourceIMAPClient).Fetch(messageSeq, "BODY.PEEK[]").AssertOK())
if err != nil {
return err
}
for _, op := range []string{op1, op2, op3} {
switch op {
case "APPEND":
res := ctx.GetIMAPClient(targetIMAPClient).Append(targetMailboxName, msgStr)
ctx.SetIMAPLastResponse(targetIMAPClient, res)
case "DELETE":
_ = imapClientNamedMarksMessageSeqAsDeleted(sourceIMAPClient, messageSeq)
case "EXPUNGE":
_ = imapClientNamedExpunge(sourceIMAPClient)
default:
return errors.New("unknow IMAP operation " + op)
}
time.Sleep(100 * time.Millisecond)
}
return nil
}
func imapClientCreatesMessage(mailboxName string, message *godog.DocString) error {
return imapClientCreatesMessageWithEncoding(mailboxName, "utf8", message) return imapClientCreatesMessageWithEncoding(mailboxName, "utf8", message)
} }
func imapClientCreatesMessageWithEncoding(mailboxName, encodingName string, message *gherkin.DocString) error { func imapClientCreatesMessageWithEncoding(mailboxName, encodingName string, message *godog.DocString) error {
encoding, _ := charset.Lookup(encodingName) encoding, _ := charset.Lookup(encodingName)
msg := message.Content msg := message.Content
@ -223,6 +274,26 @@ func imapClientNamedMarksMessageSeqWithFlags(imapClient, messageSeq, flags strin
return nil return nil
} }
func imapClientAddsFlagsToMessageSeq(flags, messageSeq string) error {
return imapClientNamedAddsFlagsToMessageSeq("imap", flags, messageSeq)
}
func imapClientNamedAddsFlagsToMessageSeq(imapClient, flags, messageSeq string) error {
res := ctx.GetIMAPClient(imapClient).AddFlags(messageSeq, flags)
ctx.SetIMAPLastResponse(imapClient, res)
return nil
}
func imapClientRemovesFlagsFromMessageSeq(flags, messageSeq string) error {
return imapClientNamedRemovesFlagsFromMessageSeq("imap", flags, messageSeq)
}
func imapClientNamedRemovesFlagsFromMessageSeq(imapClient, flags, messageSeq string) error {
res := ctx.GetIMAPClient(imapClient).RemoveFlags(messageSeq, flags)
ctx.SetIMAPLastResponse(imapClient, res)
return nil
}
func imapClientMarksMessageSeqAsRead(messageSeq string) error { func imapClientMarksMessageSeqAsRead(messageSeq string) error {
return imapClientNamedMarksMessageSeqAsRead("imap", messageSeq) return imapClientNamedMarksMessageSeqAsRead("imap", messageSeq)
} }
@ -313,11 +384,11 @@ func imapClientNamedExpungeByUID(imapClient, uids string) error {
return nil return nil
} }
func imapClientSendsID(data *gherkin.DocString) error { func imapClientSendsID(data *godog.DocString) error {
return imapClientNamedSendsID("imap", data) return imapClientNamedSendsID("imap", data)
} }
func imapClientNamedSendsID(imapClient string, data *gherkin.DocString) error { func imapClientNamedSendsID(imapClient string, data *godog.DocString) error {
res := ctx.GetIMAPClient(imapClient).ID(data.Content) res := ctx.GetIMAPClient(imapClient).ID(data.Content)
ctx.SetIMAPLastResponse(imapClient, res) ctx.SetIMAPLastResponse(imapClient, res)
return nil return nil

View File

@ -26,7 +26,7 @@ import (
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
) )
func IMAPChecksFeatureContext(s *godog.Suite) { func IMAPChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^IMAP response is "([^"]*)"$`, imapResponseIs) s.Step(`^IMAP response is "([^"]*)"$`, imapResponseIs)
s.Step(`^IMAP response to "([^"]*)" is "([^"]*)"$`, imapResponseNamedIs) s.Step(`^IMAP response to "([^"]*)" is "([^"]*)"$`, imapResponseNamedIs)
s.Step(`^IMAP response contains "([^"]*)"$`, imapResponseContains) s.Step(`^IMAP response contains "([^"]*)"$`, imapResponseContains)

View File

@ -21,7 +21,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func IMAPSetupFeatureContext(s *godog.Suite) { func IMAPSetupFeatureContext(s *godog.ScenarioContext) {
s.Step(`^there is IMAP client logged in as "([^"]*)"$`, thereIsIMAPClientLoggedInAs) s.Step(`^there is IMAP client logged in as "([^"]*)"$`, thereIsIMAPClientLoggedInAs)
s.Step(`^there is IMAP client "([^"]*)" logged in as "([^"]*)"$`, thereIsIMAPClientNamedLoggedInAs) s.Step(`^there is IMAP client "([^"]*)" logged in as "([^"]*)"$`, thereIsIMAPClientNamedLoggedInAs)
s.Step(`^there is IMAP client logged in as "([^"]*)" with address "([^"]*)"$`, thereIsIMAPClientLoggedInAsWithAddress) s.Step(`^there is IMAP client logged in as "([^"]*)" with address "([^"]*)"$`, thereIsIMAPClientLoggedInAsWithAddress)

View File

@ -17,62 +17,25 @@
package liveapi package liveapi
import (
"fmt"
"github.com/nsf/jsondiff"
)
type fakeCall struct {
method string
path string
request []byte
}
func (ctl *Controller) recordCall(method, path string, request []byte) { func (ctl *Controller) recordCall(method, path string, request []byte) {
ctl.lock.Lock() ctl.lock.Lock()
defer ctl.lock.Unlock() defer ctl.lock.Unlock()
ctl.calls = append(ctl.calls, &fakeCall{ ctl.calls.Register(method, path, request)
method: method,
path: path,
request: request,
})
} }
func (ctl *Controller) PrintCalls() { func (ctl *Controller) PrintCalls() {
fmt.Println("API calls:") ctl.calls.PrintCalls()
for idx, call := range ctl.calls {
fmt.Printf("%02d: [%s] %s\n", idx+1, call.method, call.path)
if call.request != nil && string(call.request) != "null" {
fmt.Printf("\t%s\n", call.request)
}
}
} }
func (ctl *Controller) WasCalled(method, path string, expectedRequest []byte) bool { func (ctl *Controller) WasCalled(method, path string, expectedRequest []byte) bool {
for _, call := range ctl.calls { return ctl.calls.WasCalled(method, path, expectedRequest)
if call.method != method || call.path != path { }
continue
} func (ctl *Controller) WasCalledRegex(methodRegex, pathRegex string, expectedRequest []byte) (bool, error) {
if string(expectedRequest) == "" { return ctl.calls.WasCalledRegex(methodRegex, pathRegex, expectedRequest)
return true
}
diff, _ := jsondiff.Compare(call.request, expectedRequest, &jsondiff.Options{})
isSuperset := diff == jsondiff.FullMatch || diff == jsondiff.SupersetMatch
if isSuperset {
return true
}
}
return false
} }
func (ctl *Controller) GetCalls(method, path string) [][]byte { func (ctl *Controller) GetCalls(method, path string) [][]byte {
requests := [][]byte{} return ctl.calls.GetCalls(method, path)
for _, call := range ctl.calls {
if call.method == method && call.path == path {
requests = append(requests, call.request)
}
}
return requests
} }

View File

@ -22,6 +22,7 @@ import (
"sync" "sync"
"github.com/ProtonMail/proton-bridge/pkg/pmapi" "github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/test/context/calls"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -30,7 +31,7 @@ type Controller struct {
log *logrus.Entry log *logrus.Entry
// Internal states. // Internal states.
lock *sync.RWMutex lock *sync.RWMutex
calls []*fakeCall calls calls.Calls
messageIDsByUsername map[string][]string messageIDsByUsername map[string][]string
// State controlled by test. // State controlled by test.
@ -41,7 +42,7 @@ func NewController(_ string) (*Controller, pmapi.Manager) {
controller := &Controller{ controller := &Controller{
log: logrus.WithField("pkg", "live-controller"), log: logrus.WithField("pkg", "live-controller"),
lock: &sync.RWMutex{}, lock: &sync.RWMutex{},
calls: []*fakeCall{}, calls: calls.Calls{},
messageIDsByUsername: map[string][]string{}, messageIDsByUsername: map[string][]string{},
noInternetConnection: false, noInternetConnection: false,

26
test/liveapi/events.go Normal file
View File

@ -0,0 +1,26 @@
// 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 liveapi
func (ctl *Controller) LockEvents() {
persistentClients.eventsPaused.Add(1)
}
func (ctl *Controller) UnlockEvents() {
persistentClients.eventsPaused.Done()
}

View File

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"os" "os"
"sync"
"github.com/ProtonMail/go-srp" "github.com/ProtonMail/go-srp"
"github.com/ProtonMail/proton-bridge/internal/constants" "github.com/ProtonMail/proton-bridge/internal/constants"
@ -47,6 +48,8 @@ var persistentClients = struct {
manager pmapi.Manager manager pmapi.Manager
byName map[string]clientAuthGetter byName map[string]clientAuthGetter
saltByName map[string]string saltByName map[string]string
eventsPaused sync.WaitGroup
}{} }{}
type persistentClient struct { type persistentClient struct {
@ -69,6 +72,17 @@ func (pc *persistentClient) AuthSalt(_ context.Context) (string, error) {
return persistentClients.saltByName[pc.username], nil return persistentClients.saltByName[pc.username], nil
} }
// GetEvent needs to wait for preparation to finish. Otherwise messages will be
// in wrong order and test will fail.
func (pc *persistentClient) GetEvent(ctx context.Context, eventID string) (*pmapi.Event, error) {
persistentClients.eventsPaused.Wait()
normalClient, ok := persistentClients.byName[pc.username].(pmapi.Client)
if !ok {
return nil, errors.New("cannot convert to normal client")
}
return normalClient.GetEvent(ctx, eventID)
}
func SetupPersistentClients() { func SetupPersistentClients() {
app := os.Getenv("TEST_APP") app := os.Getenv("TEST_APP")

View File

@ -33,7 +33,7 @@ var opt = godog.Options{ //nolint[gochecknoglobals]
} }
func init() { //nolint[gochecknoinits] func init() { //nolint[gochecknoinits]
godog.BindFlags("godog.", flag.CommandLine, &opt) godog.BindCommandLineFlags("godog.", &opt)
// This would normally be done using ldflags but `godog` command doesn't support that. // This would normally be done using ldflags but `godog` command doesn't support that.
constants.Version = os.Getenv("BRIDGE_VERSION") constants.Version = os.Getenv("BRIDGE_VERSION")
@ -43,9 +43,12 @@ func TestMain(m *testing.M) {
flag.Parse() flag.Parse()
opt.Paths = flag.Args() opt.Paths = flag.Args()
status := godog.RunWithOptions("godogs", func(s *godog.Suite) { status := godog.TestSuite{
FeatureContext(s) Name: "bridge-integration-tests",
}, opt) TestSuiteInitializer: SuiteInitializer,
ScenarioInitializer: ScenarioInitializer,
Options: &opt,
}.Run()
if st := m.Run(); st > status { if st := m.Run(); st > status {
status = st status = st

View File

@ -253,6 +253,10 @@ func (c *IMAPClient) ExpungeUID(ids string) *IMAPResponse {
return c.SendCommand(fmt.Sprintf("UID EXPUNGE %s", ids)) return c.SendCommand(fmt.Sprintf("UID EXPUNGE %s", ids))
} }
func (c *IMAPClient) Noop() *IMAPResponse {
return c.SendCommand("NOOP")
}
// Extennsions // Extennsions
// Extennsions: IDLE // Extennsions: IDLE

View File

@ -107,6 +107,11 @@ func (ir *IMAPResponse) AssertOK() *IMAPResponse {
return ir return ir
} }
func (ir *IMAPResponse) Sections() []string {
ir.wait()
return ir.sections
}
func (ir *IMAPResponse) AssertResult(wantResult string) *IMAPResponse { func (ir *IMAPResponse) AssertResult(wantResult string) *IMAPResponse {
ir.wait() ir.wait()
a.NoError(ir.t, ir.err) a.NoError(ir.t, ir.err)

View File

@ -21,10 +21,9 @@ import (
"strings" "strings"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin"
) )
func SMTPActionsAuthFeatureContext(s *godog.Suite) { func SMTPActionsAuthFeatureContext(s *godog.ScenarioContext) {
s.Step(`^SMTP client authenticates "([^"]*)"$`, smtpClientAuthenticates) s.Step(`^SMTP client authenticates "([^"]*)"$`, smtpClientAuthenticates)
s.Step(`^SMTP client "([^"]*)" authenticates "([^"]*)"$`, smtpClientNamedAuthenticates) s.Step(`^SMTP client "([^"]*)" authenticates "([^"]*)"$`, smtpClientNamedAuthenticates)
s.Step(`^SMTP client authenticates "([^"]*)" with address "([^"]*)"$`, smtpClientAuthenticatesWithAddress) s.Step(`^SMTP client authenticates "([^"]*)" with address "([^"]*)"$`, smtpClientAuthenticatesWithAddress)
@ -37,6 +36,7 @@ func SMTPActionsAuthFeatureContext(s *godog.Suite) {
s.Step(`^SMTP client sends message with bcc "([^"]*)"$`, smtpClientSendsMessageWithBCC) s.Step(`^SMTP client sends message with bcc "([^"]*)"$`, smtpClientSendsMessageWithBCC)
s.Step(`^SMTP client "([^"]*)" sends message with bcc "([^"]*)"$`, smtpClientNamedSendsMessageWithBCC) s.Step(`^SMTP client "([^"]*)" sends message with bcc "([^"]*)"$`, smtpClientNamedSendsMessageWithBCC)
s.Step(`^SMTP client sends "([^"]*)"$`, smtpClientSendsCommand) s.Step(`^SMTP client sends "([^"]*)"$`, smtpClientSendsCommand)
s.Step(`^SMTP client sends$`, smtpClientSendsCommandMultiline)
s.Step(`^SMTP client "([^"]*)" sends "([^"]*)"$`, smtpClientNamedSendsCommand) s.Step(`^SMTP client "([^"]*)" sends "([^"]*)"$`, smtpClientNamedSendsCommand)
} }
@ -90,19 +90,19 @@ func smtpClientLogsOut() error {
return nil return nil
} }
func smtpClientSendsMessage(message *gherkin.DocString) error { func smtpClientSendsMessage(message *godog.DocString) error {
return smtpClientNamedSendsMessage("smtp", message) return smtpClientNamedSendsMessage("smtp", message)
} }
func smtpClientNamedSendsMessage(clientID string, message *gherkin.DocString) error { func smtpClientNamedSendsMessage(clientID string, message *godog.DocString) error {
return smtpClientNamedSendsMessageWithBCC(clientID, "", message) return smtpClientNamedSendsMessageWithBCC(clientID, "", message)
} }
func smtpClientSendsMessageWithBCC(bcc string, message *gherkin.DocString) error { func smtpClientSendsMessageWithBCC(bcc string, message *godog.DocString) error {
return smtpClientNamedSendsMessageWithBCC("smtp", bcc, message) return smtpClientNamedSendsMessageWithBCC("smtp", bcc, message)
} }
func smtpClientNamedSendsMessageWithBCC(clientID, bcc string, message *gherkin.DocString) error { func smtpClientNamedSendsMessageWithBCC(clientID, bcc string, message *godog.DocString) error {
res := ctx.GetSMTPClient(clientID).SendMail(strings.NewReader(message.Content), bcc) res := ctx.GetSMTPClient(clientID).SendMail(strings.NewReader(message.Content), bcc)
ctx.SetSMTPLastResponse(clientID, res) ctx.SetSMTPLastResponse(clientID, res)
return nil return nil
@ -111,6 +111,9 @@ func smtpClientNamedSendsMessageWithBCC(clientID, bcc string, message *gherkin.D
func smtpClientSendsCommand(command string) error { func smtpClientSendsCommand(command string) error {
return smtpClientNamedSendsCommand("smtp", command) return smtpClientNamedSendsCommand("smtp", command)
} }
func smtpClientSendsCommandMultiline(command *godog.DocString) error {
return smtpClientNamedSendsCommand("smtp", command.Content)
}
func smtpClientNamedSendsCommand(clientName, command string) error { func smtpClientNamedSendsCommand(clientName, command string) error {
command = strings.ReplaceAll(command, "\\r", "\r") command = strings.ReplaceAll(command, "\\r", "\r")
command = strings.ReplaceAll(command, "\\n", "\n") command = strings.ReplaceAll(command, "\\n", "\n")

View File

@ -21,7 +21,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func SMTPChecksFeatureContext(s *godog.Suite) { func SMTPChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^SMTP response is "([^"]*)"$`, smtpResponseIs) s.Step(`^SMTP response is "([^"]*)"$`, smtpResponseIs)
s.Step(`^SMTP response to "([^"]*)" is "([^"]*)"$`, smtpResponseNamedIs) s.Step(`^SMTP response to "([^"]*)" is "([^"]*)"$`, smtpResponseNamedIs)
s.Step(`^SMTP client is logged out`, smtpClientIsLoggedOut) s.Step(`^SMTP client is logged out`, smtpClientIsLoggedOut)

View File

@ -21,7 +21,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func SMTPSetupFeatureContext(s *godog.Suite) { func SMTPSetupFeatureContext(s *godog.ScenarioContext) {
s.Step(`^there is SMTP client logged in as "([^"]*)"$`, thereIsSMTPClientLoggedInAs) s.Step(`^there is SMTP client logged in as "([^"]*)"$`, thereIsSMTPClientLoggedInAs)
s.Step(`^there is SMTP client "([^"]*)" logged in as "([^"]*)"$`, thereIsSMTPClientNamedLoggedInAs) s.Step(`^there is SMTP client "([^"]*)" logged in as "([^"]*)"$`, thereIsSMTPClientNamedLoggedInAs)
s.Step(`^there is SMTP client logged in as "([^"]*)" with address "([^"]*)"$`, thereIsSMTPClientLoggedInAsWithAddress) s.Step(`^there is SMTP client logged in as "([^"]*)" with address "([^"]*)"$`, thereIsSMTPClientLoggedInAsWithAddress)

View File

@ -24,7 +24,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func StoreActionsFeatureContext(s *godog.Suite) { func StoreActionsFeatureContext(s *godog.ScenarioContext) {
s.Step(`^the event loop of "([^"]*)" loops once$`, theEventLoopLoops) s.Step(`^the event loop of "([^"]*)" loops once$`, theEventLoopLoops)
s.Step(`^"([^"]*)" receives an address event$`, receivesAnAddressEvent) s.Step(`^"([^"]*)" receives an address event$`, receivesAnAddressEvent)
} }

View File

@ -27,11 +27,11 @@ import (
"github.com/ProtonMail/proton-bridge/pkg/pmapi" "github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/test/accounts" "github.com/ProtonMail/proton-bridge/test/accounts"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin" "github.com/cucumber/messages-go/v16"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
) )
func StoreChecksFeatureContext(s *godog.Suite) { func StoreChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^"([^"]*)" has mailbox "([^"]*)"$`, userHasMailbox) s.Step(`^"([^"]*)" has mailbox "([^"]*)"$`, userHasMailbox)
s.Step(`^"([^"]*)" does not have mailbox "([^"]*)"$`, userDoesNotHaveMailbox) s.Step(`^"([^"]*)" does not have mailbox "([^"]*)"$`, userDoesNotHaveMailbox)
s.Step(`^"([^"]*)" has the following messages$`, userHasFollowingMessages) s.Step(`^"([^"]*)" has the following messages$`, userHasFollowingMessages)
@ -72,7 +72,7 @@ func userDoesNotHaveMailbox(bddUserID, mailboxName string) error {
return nil return nil
} }
func userHasFollowingMessages(bddUserID string, structure *gherkin.DataTable) error { func userHasFollowingMessages(bddUserID string, structure *godog.Table) error {
return processMailboxStructureDataTable(structure, func(bddAddressID string, mailboxNames string, numberOfMessages int) error { return processMailboxStructureDataTable(structure, func(bddAddressID string, mailboxNames string, numberOfMessages int) error {
for _, mailboxName := range strings.Split(mailboxNames, ",") { for _, mailboxName := range strings.Split(mailboxNames, ",") {
if err := mailboxForAddressOfUserHasNumberOfMessages(mailboxName, bddAddressID, bddUserID, numberOfMessages); err != nil { if err := mailboxForAddressOfUserHasNumberOfMessages(mailboxName, bddAddressID, bddUserID, numberOfMessages); err != nil {
@ -111,7 +111,7 @@ func mailboxForAddressOfUserHasNumberOfMessages(mailboxName, bddAddressID, bddUs
return nil return nil
} }
func mailboxForUserHasMessages(mailboxName, bddUserID string, messages *gherkin.DataTable) error { func mailboxForUserHasMessages(mailboxName, bddUserID string, messages *godog.Table) error {
return mailboxForAddressOfUserHasMessages(mailboxName, "", bddUserID, messages) return mailboxForAddressOfUserHasMessages(mailboxName, "", bddUserID, messages)
} }
@ -119,7 +119,7 @@ func mailboxForUserHasNoMessages(mailboxName, bddUserID string) error {
return mailboxForUserHasNumberOfMessages(mailboxName, bddUserID, 0) return mailboxForUserHasNumberOfMessages(mailboxName, bddUserID, 0)
} }
func mailboxForAddressOfUserHasMessages(mailboxName, bddAddressID, bddUserID string, messages *gherkin.DataTable) error { func mailboxForAddressOfUserHasMessages(mailboxName, bddAddressID, bddUserID string, messages *godog.Table) error {
account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID) account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID)
if account == nil { if account == nil {
return godog.ErrPending return godog.ErrPending
@ -172,7 +172,7 @@ func mailboxForAddressOfUserHasMessages(mailboxName, bddAddressID, bddUserID str
return nil return nil
} }
func pmapiMessagesContainsMessageRow(account *accounts.TestAccount, pmapiMessages []*pmapi.Message, head []*gherkin.TableCell, row *gherkin.TableRow) (bool, error) { func pmapiMessagesContainsMessageRow(account *accounts.TestAccount, pmapiMessages []*pmapi.Message, head []*messages.PickleTableCell, row *messages.PickleTableRow) (bool, error) {
messages := make([]interface{}, len(pmapiMessages)) messages := make([]interface{}, len(pmapiMessages))
for i := range pmapiMessages { for i := range pmapiMessages {
messages[i] = pmapiMessages[i] messages[i] = pmapiMessages[i]
@ -180,7 +180,7 @@ func pmapiMessagesContainsMessageRow(account *accounts.TestAccount, pmapiMessage
return messagesContainsMessageRow(account, messages, head, row) return messagesContainsMessageRow(account, messages, head, row)
} }
func storeMessagesContainsMessageRow(account *accounts.TestAccount, storeMessages []*store.Message, head []*gherkin.TableCell, row *gherkin.TableRow) (bool, error) { func storeMessagesContainsMessageRow(account *accounts.TestAccount, storeMessages []*store.Message, head []*messages.PickleTableCell, row *messages.PickleTableRow) (bool, error) {
messages := make([]interface{}, len(storeMessages)) messages := make([]interface{}, len(storeMessages))
for i := range storeMessages { for i := range storeMessages {
messages[i] = storeMessages[i] messages[i] = storeMessages[i]
@ -188,7 +188,7 @@ func storeMessagesContainsMessageRow(account *accounts.TestAccount, storeMessage
return messagesContainsMessageRow(account, messages, head, row) return messagesContainsMessageRow(account, messages, head, row)
} }
func messagesContainsMessageRow(account *accounts.TestAccount, allMessages []interface{}, head []*gherkin.TableCell, row *gherkin.TableRow) (bool, error) { //nolint[funlen] func messagesContainsMessageRow(account *accounts.TestAccount, allMessages []interface{}, head []*messages.PickleTableCell, row *messages.PickleTableRow) (bool, error) { //nolint[funlen]
found := false found := false
for _, someMessage := range allMessages { for _, someMessage := range allMessages {
var message *pmapi.Message var message *pmapi.Message

View File

@ -27,10 +27,9 @@ import (
"github.com/ProtonMail/proton-bridge/pkg/pmapi" "github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin"
) )
func StoreSetupFeatureContext(s *godog.Suite) { func StoreSetupFeatureContext(s *godog.ScenarioContext) {
s.Step(`^there is "([^"]*)" with mailboxes`, thereIsUserWithMailboxes) s.Step(`^there is "([^"]*)" with mailboxes`, thereIsUserWithMailboxes)
s.Step(`^there is "([^"]*)" with mailbox "([^"]*)"$`, thereIsUserWithMailbox) s.Step(`^there is "([^"]*)" with mailbox "([^"]*)"$`, thereIsUserWithMailbox)
s.Step(`^there are messages in mailbox(?:es)? "([^"]*)" for "([^"]*)"$`, thereAreMessagesInMailboxesForUser) s.Step(`^there are messages in mailbox(?:es)? "([^"]*)" for "([^"]*)"$`, thereAreMessagesInMailboxesForUser)
@ -40,7 +39,7 @@ func StoreSetupFeatureContext(s *godog.Suite) {
s.Step(`^there are (\d+) messages in mailbox(?:es)? "([^"]*)" for address "([^"]*)" of "([^"]*)"$`, thereAreSomeMessagesInMailboxesForAddressOfUser) s.Step(`^there are (\d+) messages in mailbox(?:es)? "([^"]*)" for address "([^"]*)" of "([^"]*)"$`, thereAreSomeMessagesInMailboxesForAddressOfUser)
} }
func thereIsUserWithMailboxes(bddUserID string, mailboxes *gherkin.DataTable) error { func thereIsUserWithMailboxes(bddUserID string, mailboxes *godog.Table) error {
for _, row := range mailboxes.Rows { for _, row := range mailboxes.Rows {
if err := thereIsUserWithMailbox(bddUserID, row.Cells[0].Value); err != nil { if err := thereIsUserWithMailbox(bddUserID, row.Cells[0].Value); err != nil {
return err return err
@ -71,11 +70,18 @@ func thereIsUserWithMailbox(bddUserID, mailboxName string) error {
return internalError(store.RebuildMailboxes(), "rebuilding mailboxes") return internalError(store.RebuildMailboxes(), "rebuilding mailboxes")
} }
func thereAreMessagesInMailboxesForUser(mailboxNames, bddUserID string, messages *gherkin.DataTable) error { func thereAreMessagesInMailboxesForUser(mailboxNames, bddUserID string, messages *godog.Table) error {
return thereAreMessagesInMailboxesForAddressOfUser(mailboxNames, "", bddUserID, messages) return thereAreMessagesInMailboxesForAddressOfUser(mailboxNames, "", bddUserID, messages)
} }
func thereAreMessagesInMailboxesForAddressOfUser(mailboxNames, bddAddressID, bddUserID string, messages *gherkin.DataTable) error { func thereAreMessagesInMailboxesForAddressOfUser(mailboxNames, bddAddressID, bddUserID string, messages *godog.Table) error {
// It is needed to prevent event processing before syncing these message
// otherwise the seqID and UID will be in reverse order. The
// synchronization add newest message first, the eventloop adds the oldest
// message first.
ctx.MessagePreparationStarted()
defer ctx.MessagePreparationFinished()
account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID) account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID)
if account == nil { if account == nil {
return godog.ErrPending return godog.ErrPending
@ -102,6 +108,11 @@ func thereAreMessagesInMailboxesForAddressOfUser(mailboxNames, bddAddressID, bdd
if message.HasLabelID(pmapi.SentLabel) { if message.HasLabelID(pmapi.SentLabel) {
message.Flags |= pmapi.FlagSent message.Flags |= pmapi.FlagSent
} else {
// some tests (Outlook move by DELETE EXPUNGE APPEND) imply creating hard copies of emails,
// and the importMessage() function flags the email as Sent if the 'Received' key in not present in the
// header.
header.Add("Received", "from dummy.protonmail.com")
} }
bddMessageID := "" bddMessageID := ""
@ -222,13 +233,13 @@ func thereAreSomeMessagesInMailboxesForUser(numberOfMessages int, mailboxNames,
return thereAreSomeMessagesInMailboxesForAddressOfUser(numberOfMessages, mailboxNames, "", bddUserID) return thereAreSomeMessagesInMailboxesForAddressOfUser(numberOfMessages, mailboxNames, "", bddUserID)
} }
func thereAreSomeMessagesForUserAsFollows(bddUserID string, structure *gherkin.DataTable) error { func thereAreSomeMessagesForUserAsFollows(bddUserID string, structure *godog.Table) error {
return processMailboxStructureDataTable(structure, func(bddAddressID string, mailboxNames string, numberOfMessages int) error { return processMailboxStructureDataTable(structure, func(bddAddressID string, mailboxNames string, numberOfMessages int) error {
return thereAreSomeMessagesInMailboxesForAddressOfUser(numberOfMessages, mailboxNames, bddAddressID, bddUserID) return thereAreSomeMessagesInMailboxesForAddressOfUser(numberOfMessages, mailboxNames, bddAddressID, bddUserID)
}) })
} }
func processMailboxStructureDataTable(structure *gherkin.DataTable, callback func(string, string, int) error) error { func processMailboxStructureDataTable(structure *godog.Table, callback func(string, string, int) error) error {
head := structure.Rows[0].Cells head := structure.Rows[0].Cells
for i := 1; i < len(structure.Rows); i++ { for i := 1; i < len(structure.Rows); i++ {
bddAddressID := "" bddAddressID := ""
@ -259,6 +270,13 @@ func processMailboxStructureDataTable(structure *gherkin.DataTable, callback fun
} }
func thereAreSomeMessagesInMailboxesForAddressOfUser(numberOfMessages int, mailboxNames, bddAddressID, bddUserID string) error { func thereAreSomeMessagesInMailboxesForAddressOfUser(numberOfMessages int, mailboxNames, bddAddressID, bddUserID string) error {
// It is needed to prevent event processing before syncing these message
// otherwise the seqID and UID will be in reverse order. The
// synchronization add newest message first, the eventloop adds the oldest
// message first.
ctx.MessagePreparationStarted()
defer ctx.MessagePreparationFinished()
account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID) account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID)
if account == nil { if account == nil {
return godog.ErrPending return godog.ErrPending

View File

@ -2,6 +2,7 @@
{ {
"ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==", "ID": "MhF8dxtN5Lz_GruskN3L9kTKWusZHBdhWaxc7w1tgze2qB6uM9AyC6mWRQg1B7WGZ_r-9gn-XC95-IkpNJr0jg==",
"Primary": 1, "Primary": 1,
"Active": 1,
"Flags": 3, "Flags": 3,
"Version": 3, "Version": 3,
"Activation": null, "Activation": null,
@ -9,4 +10,4 @@
"Token": null, "Token": null,
"Signature": null "Signature": null
} }
] ]

View File

@ -5,6 +5,7 @@
"PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n\r\n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\r\na7RmMPBR30nWAMrvteun5z2n8rizgEZHcmE=\r\n=2e5K\r\n-----END PGP PRIVATE KEY BLOCK-----", "PrivateKey": "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n\r\n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\r\na7RmMPBR30nWAMrvteun5z2n8rizgEZHcmE=\r\n=2e5K\r\n-----END PGP PRIVATE KEY BLOCK-----",
"Fingerprint": "c93f767df53b0ca8395cfde90483475164ec6353", "Fingerprint": "c93f767df53b0ca8395cfde90483475164ec6353",
"Activation": null, "Activation": null,
"Primary": 1 "Primary": 1,
"Active": 1
} }
] ]

View File

@ -23,10 +23,9 @@ import (
"github.com/ProtonMail/proton-bridge/internal/transfer" "github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin"
) )
func TransferActionsFeatureContext(s *godog.Suite) { func TransferActionsFeatureContext(s *godog.ScenarioContext) {
s.Step(`^user "([^"]*)" imports local files$`, userImportsLocalFiles) s.Step(`^user "([^"]*)" imports local files$`, userImportsLocalFiles)
s.Step(`^user "([^"]*)" imports local files with rules$`, userImportsLocalFilesWithRules) 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 "([^"]*)"$`, userImportsLocalFilesToAddress)
@ -51,7 +50,7 @@ func userImportsLocalFiles(bddUserID string) error {
return userImportsLocalFilesToAddressWithRules(bddUserID, "", nil) return userImportsLocalFilesToAddressWithRules(bddUserID, "", nil)
} }
func userImportsLocalFilesWithRules(bddUserID string, rules *gherkin.DataTable) error { func userImportsLocalFilesWithRules(bddUserID string, rules *godog.Table) error {
return userImportsLocalFilesToAddressWithRules(bddUserID, "", rules) return userImportsLocalFilesToAddressWithRules(bddUserID, "", rules)
} }
@ -59,7 +58,7 @@ func userImportsLocalFilesToAddress(bddUserID, bddAddressID string) error {
return userImportsLocalFilesToAddressWithRules(bddUserID, bddAddressID, nil) return userImportsLocalFilesToAddressWithRules(bddUserID, bddAddressID, nil)
} }
func userImportsLocalFilesToAddressWithRules(bddUserID, bddAddressID string, rules *gherkin.DataTable) error { func userImportsLocalFilesToAddressWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) { return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
path := ctx.GetTransferLocalRootForImport() path := ctx.GetTransferLocalRootForImport()
return ctx.GetImportExport().GetLocalImporter(username, address, path) return ctx.GetImportExport().GetLocalImporter(username, address, path)
@ -72,7 +71,7 @@ func userImportsRemoteMessages(bddUserID string) error {
return userImportsRemoteMessagesToAddressWithRules(bddUserID, "", nil) return userImportsRemoteMessagesToAddressWithRules(bddUserID, "", nil)
} }
func userImportsRemoteMessagesWithRules(bddUserID string, rules *gherkin.DataTable) error { func userImportsRemoteMessagesWithRules(bddUserID string, rules *godog.Table) error {
return userImportsRemoteMessagesToAddressWithRules(bddUserID, "", rules) return userImportsRemoteMessagesToAddressWithRules(bddUserID, "", rules)
} }
@ -80,7 +79,7 @@ func userImportsRemoteMessagesToAddress(bddUserID, bddAddressID string) error {
return userImportsRemoteMessagesToAddressWithRules(bddUserID, bddAddressID, nil) return userImportsRemoteMessagesToAddressWithRules(bddUserID, bddAddressID, nil)
} }
func userImportsRemoteMessagesToAddressWithRules(bddUserID, bddAddressID string, rules *gherkin.DataTable) error { func userImportsRemoteMessagesToAddressWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) { return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
imapServer := ctx.GetTransferRemoteIMAPServer() imapServer := ctx.GetTransferRemoteIMAPServer()
return ctx.GetImportExport().GetRemoteImporter(username, address, imapServer.Username, imapServer.Password, imapServer.Host, imapServer.Port) return ctx.GetImportExport().GetRemoteImporter(username, address, imapServer.Username, imapServer.Password, imapServer.Host, imapServer.Port)
@ -93,7 +92,7 @@ func userExportsToEMLFiles(bddUserID string) error {
return userExportsAddressToEMLFilesWithRules(bddUserID, "", nil) return userExportsAddressToEMLFilesWithRules(bddUserID, "", nil)
} }
func userExportsToEMLFilesWithRules(bddUserID string, rules *gherkin.DataTable) error { func userExportsToEMLFilesWithRules(bddUserID string, rules *godog.Table) error {
return userExportsAddressToEMLFilesWithRules(bddUserID, "", rules) return userExportsAddressToEMLFilesWithRules(bddUserID, "", rules)
} }
@ -101,7 +100,7 @@ func userExportsAddressToEMLFiles(bddUserID, bddAddressID string) error {
return userExportsAddressToEMLFilesWithRules(bddUserID, bddAddressID, nil) return userExportsAddressToEMLFilesWithRules(bddUserID, bddAddressID, nil)
} }
func userExportsAddressToEMLFilesWithRules(bddUserID, bddAddressID string, rules *gherkin.DataTable) error { func userExportsAddressToEMLFilesWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) { return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
path := ctx.GetTransferLocalRootForExport() path := ctx.GetTransferLocalRootForExport()
return ctx.GetImportExport().GetEMLExporter(username, address, path) return ctx.GetImportExport().GetEMLExporter(username, address, path)
@ -114,7 +113,7 @@ func userExportsToMBOXFiles(bddUserID string) error {
return userExportsAddressToMBOXFilesWithRules(bddUserID, "", nil) return userExportsAddressToMBOXFilesWithRules(bddUserID, "", nil)
} }
func userExportsToMBOXFilesWithRules(bddUserID string, rules *gherkin.DataTable) error { func userExportsToMBOXFilesWithRules(bddUserID string, rules *godog.Table) error {
return userExportsAddressToMBOXFilesWithRules(bddUserID, "", rules) return userExportsAddressToMBOXFilesWithRules(bddUserID, "", rules)
} }
@ -122,7 +121,7 @@ func userExportsAddressToMBOXFiles(bddUserID, bddAddressID string) error {
return userExportsAddressToMBOXFilesWithRules(bddUserID, bddAddressID, nil) return userExportsAddressToMBOXFilesWithRules(bddUserID, bddAddressID, nil)
} }
func userExportsAddressToMBOXFilesWithRules(bddUserID, bddAddressID string, rules *gherkin.DataTable) error { func userExportsAddressToMBOXFilesWithRules(bddUserID, bddAddressID string, rules *godog.Table) error {
return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) { return doTransfer(bddUserID, bddAddressID, rules, func(username, address string) (*transfer.Transfer, error) {
path := ctx.GetTransferLocalRootForExport() path := ctx.GetTransferLocalRootForExport()
return ctx.GetImportExport().GetMBOXExporter(username, address, path) return ctx.GetImportExport().GetMBOXExporter(username, address, path)
@ -131,7 +130,7 @@ func userExportsAddressToMBOXFilesWithRules(bddUserID, bddAddressID string, rule
// Helpers. // Helpers.
func doTransfer(bddUserID, bddAddressID string, rules *gherkin.DataTable, getTransferrer func(string, string) (*transfer.Transfer, error)) error { func doTransfer(bddUserID, bddAddressID string, rules *godog.Table, getTransferrer func(string, string) (*transfer.Transfer, error)) error {
account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID) account := ctx.GetTestAccountWithAddress(bddUserID, bddAddressID)
if account == nil { if account == nil {
return godog.ErrPending return godog.ErrPending
@ -149,7 +148,7 @@ func doTransfer(bddUserID, bddAddressID string, rules *gherkin.DataTable, getTra
return nil return nil
} }
func setRules(transferrer *transfer.Transfer, rules *gherkin.DataTable) error { func setRules(transferrer *transfer.Transfer, rules *godog.Table) error {
if rules == nil { if rules == nil {
return nil return nil
} }

View File

@ -29,14 +29,13 @@ import (
"github.com/ProtonMail/go-rfc5322" "github.com/ProtonMail/go-rfc5322"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin"
"github.com/emersion/go-mbox" "github.com/emersion/go-mbox"
"github.com/emersion/go-message" "github.com/emersion/go-message"
"github.com/pkg/errors" "github.com/pkg/errors"
a "github.com/stretchr/testify/assert" a "github.com/stretchr/testify/assert"
) )
func TransferChecksFeatureContext(s *godog.Suite) { func TransferChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^progress result is "([^"]*)"$`, progressFinishedWith) s.Step(`^progress result is "([^"]*)"$`, progressFinishedWith)
s.Step(`^transfer exported (\d+) messages$`, transferExportedNumberOfMessages) s.Step(`^transfer exported (\d+) messages$`, transferExportedNumberOfMessages)
s.Step(`^transfer imported (\d+) messages$`, transferImportedNumberOfMessages) s.Step(`^transfer imported (\d+) messages$`, transferImportedNumberOfMessages)
@ -92,7 +91,7 @@ func transferFailedForNumberOfMessages(wantCount int) error {
return ctx.GetTestingError() return ctx.GetTestingError()
} }
func transferExportedMessages(messages *gherkin.DataTable) error { func transferExportedMessages(messages *godog.Table) error {
expectedMessages := map[string][]MessageAttributes{} expectedMessages := map[string][]MessageAttributes{}
head := messages.Rows[0].Cells head := messages.Rows[0].Cells

View File

@ -28,12 +28,12 @@ import (
"github.com/ProtonMail/proton-bridge/pkg/message" "github.com/ProtonMail/proton-bridge/pkg/message"
"github.com/cucumber/godog" "github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin" "github.com/cucumber/messages-go/v16"
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
"github.com/emersion/go-mbox" "github.com/emersion/go-mbox"
) )
func TransferSetupFeatureContext(s *godog.Suite) { func TransferSetupFeatureContext(s *godog.ScenarioContext) {
s.Step(`^there are EML files$`, thereAreEMLFiles) s.Step(`^there are EML files$`, thereAreEMLFiles)
s.Step(`^there is EML file "([^"]*)"$`, thereIsEMLFile) s.Step(`^there is EML file "([^"]*)"$`, thereIsEMLFile)
s.Step(`^there is MBOX file "([^"]*)" with messages$`, thereIsMBOXFileWithMessages) s.Step(`^there is MBOX file "([^"]*)" with messages$`, thereIsMBOXFileWithMessages)
@ -44,7 +44,7 @@ func TransferSetupFeatureContext(s *godog.Suite) {
s.Step(`^there is skip encrypted messages set to "([^"]*)"$`, thereIsSkipEncryptedMessagesSetTo) s.Step(`^there is skip encrypted messages set to "([^"]*)"$`, thereIsSkipEncryptedMessagesSetTo)
} }
func thereAreEMLFiles(messages *gherkin.DataTable) error { func thereAreEMLFiles(messages *godog.Table) error {
head := messages.Rows[0].Cells head := messages.Rows[0].Cells
for _, row := range messages.Rows[1:] { for _, row := range messages.Rows[1:] {
fileName := "" fileName := ""
@ -66,11 +66,11 @@ func thereAreEMLFiles(messages *gherkin.DataTable) error {
return nil return nil
} }
func thereIsEMLFile(fileName string, message *gherkin.DocString) error { func thereIsEMLFile(fileName string, message *godog.DocString) error {
return createFile(fileName, message.Content) return createFile(fileName, message.Content)
} }
func thereIsMBOXFileWithMessages(fileName string, messages *gherkin.DataTable) error { func thereIsMBOXFileWithMessages(fileName string, messages *godog.Table) error {
mboxBuffer := &bytes.Buffer{} mboxBuffer := &bytes.Buffer{}
mboxWriter := mbox.NewWriter(mboxBuffer) mboxWriter := mbox.NewWriter(mboxBuffer)
@ -102,11 +102,11 @@ func thereIsMBOXFileWithMessages(fileName string, messages *gherkin.DataTable) e
return createFile(fileName, mboxBuffer.String()) return createFile(fileName, mboxBuffer.String())
} }
func thereIsMBOXFile(fileName string, messages *gherkin.DocString) error { func thereIsMBOXFile(fileName string, messages *godog.DocString) error {
return createFile(fileName, messages.Content) return createFile(fileName, messages.Content)
} }
func thereAreIMAPMailboxes(mailboxes *gherkin.DataTable) error { func thereAreIMAPMailboxes(mailboxes *godog.Table) error {
imapServer := ctx.GetTransferRemoteIMAPServer() imapServer := ctx.GetTransferRemoteIMAPServer()
head := mailboxes.Rows[0].Cells head := mailboxes.Rows[0].Cells
for _, row := range mailboxes.Rows[1:] { for _, row := range mailboxes.Rows[1:] {
@ -124,7 +124,7 @@ func thereAreIMAPMailboxes(mailboxes *gherkin.DataTable) error {
return nil return nil
} }
func thereAreIMAPMessages(messages *gherkin.DataTable) (err error) { func thereAreIMAPMessages(messages *godog.Table) (err error) {
imapServer := ctx.GetTransferRemoteIMAPServer() imapServer := ctx.GetTransferRemoteIMAPServer()
head := messages.Rows[0].Cells head := messages.Rows[0].Cells
for _, row := range messages.Rows[1:] { for _, row := range messages.Rows[1:] {
@ -170,7 +170,7 @@ func thereAreIMAPMessages(messages *gherkin.DataTable) (err error) {
return nil return nil
} }
func thereIsIMAPMessage(mailboxName string, seqNum, uid int, dateValue, subject string, message *gherkin.DocString) error { func thereIsIMAPMessage(mailboxName string, seqNum, uid int, dateValue, subject string, message *godog.DocString) error {
imapServer := ctx.GetTransferRemoteIMAPServer() imapServer := ctx.GetTransferRemoteIMAPServer()
date, err := time.Parse(timeFormat, dateValue) date, err := time.Parse(timeFormat, dateValue)
@ -187,7 +187,7 @@ func thereIsIMAPMessage(mailboxName string, seqNum, uid int, dateValue, subject
return nil return nil
} }
func getBodyFromDataRow(head []*gherkin.TableCell, row *gherkin.TableRow) string { func getBodyFromDataRow(head []*messages.PickleTableCell, row *messages.PickleTableRow) string {
body := "hello" body := "hello"
headers := textproto.MIMEHeader{} 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") headers.Set("Received", "by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000")

View File

@ -21,7 +21,7 @@ import (
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
func UsersActionsFeatureContext(s *godog.Suite) { func UsersActionsFeatureContext(s *godog.ScenarioContext) {
s.Step(`^"([^"]*)" logs in$`, userLogsIn) s.Step(`^"([^"]*)" logs in$`, userLogsIn)
s.Step(`^"([^"]*)" logs in with bad password$`, userLogsInWithBadPassword) s.Step(`^"([^"]*)" logs in with bad password$`, userLogsInWithBadPassword)
s.Step(`^"([^"]*)" logs out$`, userLogsOut) s.Step(`^"([^"]*)" logs out$`, userLogsOut)

View File

@ -24,7 +24,7 @@ import (
a "github.com/stretchr/testify/assert" a "github.com/stretchr/testify/assert"
) )
func UsersChecksFeatureContext(s *godog.Suite) { func UsersChecksFeatureContext(s *godog.ScenarioContext) {
s.Step(`^"([^"]*)" has address mode in "([^"]*)" mode$`, userHasAddressModeInMode) s.Step(`^"([^"]*)" has address mode in "([^"]*)" mode$`, userHasAddressModeInMode)
s.Step(`^"([^"]*)" is disconnected$`, userIsDisconnected) s.Step(`^"([^"]*)" is disconnected$`, userIsDisconnected)
s.Step(`^"([^"]*)" is connected$`, userIsConnected) s.Step(`^"([^"]*)" is connected$`, userIsConnected)

View File

@ -25,7 +25,7 @@ import (
a "github.com/stretchr/testify/assert" a "github.com/stretchr/testify/assert"
) )
func UsersSetupFeatureContext(s *godog.Suite) { func UsersSetupFeatureContext(s *godog.ScenarioContext) {
s.Step(`^there is user "([^"]*)"$`, thereIsUser) s.Step(`^there is user "([^"]*)"$`, thereIsUser)
s.Step(`^there is connected user "([^"]*)"$`, thereIsConnectedUser) s.Step(`^there is connected user "([^"]*)"$`, thereIsConnectedUser)
s.Step(`^there is user "([^"]*)" which just logged in$`, thereIsUserWhichJustLoggedIn) s.Step(`^there is user "([^"]*)" which just logged in$`, thereIsUserWhichJustLoggedIn)