Compare commits

..

30 Commits

Author SHA1 Message Date
072ce54fe1 Bridge 1.5.2 Golden Gate
Release Notes
* Improved package creation logic
* Refactor of sending functions to simplify code maintenance
* Added tests for package creation

Fixed
* Bridge crashes related to labels handling
* GUI popup related to TLS connection error
* An issue where a random session key is included in the data payload
* Error handling (including improved detection)
2020-11-24 10:38:36 +01:00
1f31df3a94 Bridge 1.5.1 Golden Gate
Release Notes
* Improved package creation logic
* Refactor of sending functions to simplify code maintenance
* Added tests for package creation

Fixed
* Bridge crashes related to labels handling
* GUI popup related to TLS connection error
* An issue where a random session key is included in the data payload
* Error handling (including improved detection)
2020-11-23 07:43:43 +01:00
9ee30e4923 Add sentry fingerprint 2020-11-20 14:44:42 +00:00
7b44f12ab1 Update sentry client 2020-11-20 14:44:42 +00:00
874882b554 Logic change to follow old code. 2020-11-20 13:39:13 +00:00
945bdf4c60 Custom types for flags and encrypted outside test 2020-11-20 13:39:13 +00:00
6e1e5a2afe re-organise test definitions 2020-11-20 13:39:13 +00:00
b709b51790 Simplify test cases 2020-11-20 13:39:13 +00:00
d380485bb6 Fixing lint and integration tests, changelog, GODT-880, and typos 2020-11-20 13:39:13 +00:00
87c8228cd0 rename 2020-11-20 13:39:13 +00:00
152046bf97 refactor smtp sending
* [x] move package creation logic to `pmapi.SendMessageReq`
* [ ] write test of package creation logic
    * [x] internal
    * [x] plain
    * [x] external encrypted
    * [ ] signature ???
    * [x] attachments
2020-11-20 13:39:13 +00:00
a0fbed5859 use unreleased for changes 2020-11-20 14:35:24 +01:00
89e9e17d26 Fix typos in InlineLabelSelect.qml 2020-11-20 07:43:43 +00:00
b595247392 chore: add version info to github issue template 2020-11-19 16:57:21 +01:00
9d50a8cef2 Add OS to app version 2020-11-18 09:46:01 +00:00
f888176485 Build creates proper binary names 2020-11-18 08:56:38 +00:00
2f9876ad74 Remove unnecessary semicolon 2020-11-13 13:18:16 +00:00
53404122cc Integration test of sending and manual appending to Sent mailbox 2020-11-13 13:18:16 +00:00
ba65494fce Try load messages one-by-one 2020-11-13 09:43:04 +00:00
70645c1732 Import-Export Elbe 1.2.1
• Further improvements to address and date parsing
• Better handling and displaying of skipped messages
• Improved error reporting
2020-11-11 14:03:00 +01:00
1055e60d27 Fixing time order in changelog. 2020-11-11 12:02:56 +01:00
e04196f8a0 feat: switch to public go-rfc5322 parser 2020-11-10 09:27:07 +00:00
11a0dec047 Using atomic bool 2020-11-10 07:50:29 +00:00
b9740e1b7d Close connection before deleting labels to prevent panics accessing deleted bucket 2020-11-10 07:50:29 +00:00
f0695eb870 add test gui 2020-11-09 11:58:32 +00:00
a40018cdf9 Percentage available on progress count struct 2020-11-09 11:58:32 +00:00
5b7eabe21a Skipped messages do not change total counts but shows as separate number 2020-11-09 11:58:32 +00:00
d5d60aa11b feat: remove tls upgrade error notification 2020-11-09 10:59:42 +00:00
a62fa132e6 rename build tag 2020-11-06 16:02:30 +01:00
052395f917 test: add benchmarks for rfc5322 address/date parser 2020-11-04 15:00:18 +01:00
139 changed files with 2072 additions and 21240 deletions

2
.gitattributes vendored
View File

@ -1 +1 @@
Changelog.md merge=union
unreleased.md merge=union

View File

@ -27,6 +27,9 @@ Issue tracker is ONLY used for reporting bugs with technical details. "It doesn'
3.
4.
## Version Information
<!--- Which version of the app(s) were you using when you experienced this issue? -->
## Context (Environment)
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->

View File

@ -20,9 +20,6 @@ issues:
- gochecknoglobals
- gochecknoinits
- gosec
- path: pkg/message/rfc5322
linters:
- dupl
linters-settings:
godox:

View File

@ -1,8 +1,45 @@
# ProtonMail Bridge Changelog
# ProtonMail Bridge and Import-Export app Changelog
Changelog [format](http://keepachangelog.com/en/1.0.0/)
## Unreleased
## [Bridge 1.5.2] Golden Gate
### Changed
* GODT-883 Use `ClearPacket` for `text/plain` with signature
## [Bridge 1.5.1] Golden Gate
### Added
* GODT-701 Try load messages one-by-one if IMAP server errors with batch load
and not interrupt the transfer.
* GODT-878 Tests for send packet creation logic.
### Changed
* GODT-180 Updated Sentry client.
* GODT-651 Build creates proper binary names.
* GODT-878 Fix an issue where the random session key is inadvertently sent to
the Proton server. The data payload is always encrypted within TLS, but this
is still a potential privacy problem. Discovered by Proton's internal
security audit team.
* GODT-878 Refactor and move the send packet creation logic to `pmapi.SendMessageReq`.
* GODT-878 Encryption of session keys moved to pmapi.
## [IE 1.2.1] Elbe
### Added
* GODT-799 Skipped messages do not change total counts but shows as separate number.
## Fixed
* GODT-799 Fix skipping unwanted folders importing from mbox files.
* GODT-769 Close connection before deleting labels to prevent panics accessing deleted bucket.
### Removed
* GODT-766 Remove GUI popup for IMAP TLS error.
## [Bridge 1.5.0] Golden Gate
### Changed
* Updated go-mbox dependency back to upstream.
@ -11,6 +48,8 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
* GODT-847 Waiting for unilateral update during deleting the message.
* GODT-849 Show in error counts in the end also lost messages.
* GODT-835 Do not include conversation ID in references to show properly conversation threads in clients.
* GODT-685 Improve deb packaging regarding dejavu font
## [IE 1.2.0] Elbe

View File

@ -10,19 +10,21 @@ TARGET_OS?=${GOOS}
.PHONY: build build-ie build-nogui build-ie-nogui check-has-go
# Keep version hardcoded so app build works also without Git repository.
BRIDGE_APP_VERSION?=1.5.0-git
IE_APP_VERSION?=1.2.0-git
BRIDGE_APP_VERSION?=1.5.2-git
IE_APP_VERSION?=1.2.1-git
APP_VERSION:=${BRIDGE_APP_VERSION}
SRC_ICO:=logo.ico
SRC_ICNS:=Bridge.icns
SRC_SVG:=logo.svg
TGT_ICNS:=Bridge.icns
EXE_NAME:=proton-bridge
ifeq "${TARGET_CMD}" "Import-Export"
APP_VERSION:=${IE_APP_VERSION}
SRC_ICO:=ie.ico
SRC_ICNS:=ie.icns
SRC_SVG:=ie.svg
TGT_ICNS:=ImportExport.icns
EXE_NAME:=proton-ie
endif
REVISION:=$(shell git rev-parse --short=10 HEAD)
BUILD_TIME:=$(shell date +%FT%T%z)
@ -40,17 +42,22 @@ BUILD_FLAGS_NOGUI+= ${GO_LDFLAGS}
DEPLOY_DIR:=cmd/${TARGET_CMD}/deploy
ICO_FILES:=
EXE:=$(shell basename ${CURDIR})
DIRNAME:=$(shell basename ${CURDIR})
EXE:=${EXE_NAME}
EXE_QT:=${DIRNAME}
ifeq "${TARGET_OS}" "windows"
EXE:=${EXE}.exe
EXE_QT:=${EXE_QT}.exe
ICO_FILES:=${SRC_ICO} icon.rc icon_windows.syso
endif
ifeq "${TARGET_OS}" "darwin"
DARWINAPP_CONTENTS:=${DEPLOY_DIR}/darwin/${EXE}.app/Contents
EXE:=${EXE}.app/Contents/MacOS/${EXE}
EXE:=${EXE}.app
EXE_QT:=${EXE_QT}.app
EXE_BINARY_DARWIN:=/Contents/MacOS/${EXE_NAME}
endif
EXE_TARGET:=${DEPLOY_DIR}/${TARGET_OS}/${EXE}
EXE_QT_TARGET:=${DEPLOY_DIR}/${TARGET_OS}/${EXE_QT}
TGZ_TARGET:=bridge_${TARGET_OS}_${REVISION}.tgz
ifeq "${TARGET_CMD}" "Import-Export"
@ -62,7 +69,7 @@ build-ie:
TARGET_CMD=Import-Export $(MAKE) build
build-nogui:
go build ${BUILD_FLAGS_NOGUI} -o ${TARGET_CMD} cmd/${TARGET_CMD}/main.go
go build ${BUILD_FLAGS_NOGUI} -o ${EXE_NAME} cmd/${TARGET_CMD}/main.go
build-ie-nogui:
TARGET_CMD=Import-Export $(MAKE) build-nogui
@ -77,12 +84,16 @@ ${DEPLOY_DIR}/linux: ${EXE_TARGET}
cp -pf ./Changelog.md ${DEPLOY_DIR}/linux/
${DEPLOY_DIR}/darwin: ${EXE_TARGET}
if [ "${DIRNAME}" != "${EXE_NAME}" ]; then \
mv ${EXE_TARGET}/Contents/MacOS/{${DIRNAME},${EXE_NAME}}; \
perl -i -pe"s/>${DIRNAME}/>${EXE_NAME}/g" ${EXE_TARGET}/Contents/Info.plist; \
fi
cp ./internal/frontend/share/icons/${SRC_ICNS} ${DARWINAPP_CONTENTS}/Resources/${TGT_ICNS}
cp LICENSE ${DARWINAPP_CONTENTS}/Resources/
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebEngine.framework"
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebView.framework"
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebEngineCore.framework"
./utils/remove_non_relative_links_darwin.sh "${EXE_TARGET}"
./utils/remove_non_relative_links_darwin.sh "${EXE_TARGET}${EXE_BINARY_DARWIN}"
${DEPLOY_DIR}/windows: ${EXE_TARGET}
cp ./internal/frontend/share/icons/${SRC_ICO} ${DEPLOY_DIR}/windows/logo.ico
@ -100,6 +111,7 @@ ${EXE_TARGET}: check-has-go gofiles ${ICO_FILES} update-vendor
cp cmd/${TARGET_CMD}/main.go .
qtdeploy ${BUILD_FLAGS} ${QT_BUILD_TARGET}
mv deploy cmd/${TARGET_CMD}
if [ "${EXE_QT_TARGET}" != "${EXE_TARGET}" ]; then mv ${EXE_QT_TARGET} ${EXE_TARGET}; fi
rm -rf ${TARGET_OS} main.go
logo.ico ie.ico: ./internal/frontend/share/icons/${SRC_ICO}
@ -196,7 +208,7 @@ coverage: test
mocks:
mockgen --package mocks github.com/ProtonMail/proton-bridge/internal/users Configer,PanicHandler,ClientManager,CredentialsStorer,StoreMaker > internal/users/mocks/mocks.go
mockgen --package mocks github.com/ProtonMail/proton-bridge/internal/transfer PanicHandler,ClientManager > internal/transfer/mocks/mocks.go
mockgen --package mocks github.com/ProtonMail/proton-bridge/internal/transfer PanicHandler,ClientManager,IMAPClientProvider > internal/transfer/mocks/mocks.go
mockgen --package mocks github.com/ProtonMail/proton-bridge/internal/store PanicHandler,ClientManager,BridgeUser > internal/store/mocks/mocks.go
mockgen --package mocks github.com/ProtonMail/proton-bridge/pkg/listener Listener > internal/store/mocks/utils_mocks.go
mockgen --package mocks github.com/ProtonMail/proton-bridge/pkg/pmapi Client > pkg/pmapi/mocks/mocks.go
@ -280,8 +292,3 @@ clean: clean-vendor
rm -rf cmd/Import-Export/deploy
rm -f build last.log mem.pprof main.go
rm -rf logo.ico icon.rc icon_windows.syso internal/frontend/qt/icon_windows.syso
.PHONY: generate
generate:
go generate ./...
$(MAKE) add-license

9
go.mod
View File

@ -18,14 +18,13 @@ require (
github.com/ProtonMail/go-apple-mobileconfig v0.0.0-20160701194735-7ea9927a11f6
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde
github.com/ProtonMail/go-rfc5322 v0.2.0
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
github.com/ProtonMail/gopenpgp/v2 v2.0.1
github.com/PuerkitoBio/goquery v1.5.1
github.com/abiosoft/ishell v2.0.0+incompatible
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc
github.com/antlr/antlr4 v0.0.0-20201020194047-0a7eaede42b0
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect
github.com/chzyer/logex v1.1.10 // indirect
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
github.com/cucumber/godog v0.8.1
@ -43,7 +42,7 @@ require (
github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5 // indirect
github.com/fatih/color v1.9.0
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/getsentry/raven-go v0.2.0
github.com/getsentry/sentry-go v0.8.0
github.com/go-resty/resty/v2 v2.3.0
github.com/golang/mock v1.4.4
github.com/google/go-cmp v0.5.1
@ -60,7 +59,7 @@ require (
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.6.0
github.com/sirupsen/logrus v1.7.0
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/stretchr/testify v1.6.1
@ -75,7 +74,7 @@ require (
replace (
github.com/docker/docker-credential-helpers => github.com/ProtonMail/docker-credential-helpers v1.1.0
github.com/emersion/go-imap => github.com/ProtonMail/go-imap v0.0.0-20201016095853-a7520cc904d3
github.com/emersion/go-imap => github.com/ProtonMail/go-imap v0.0.0-20201102134601-418cd74e9474
github.com/emersion/go-smtp => github.com/ProtonMail/go-smtp v0.0.0-20181206232543-8261df20d309
github.com/jameskeane/bcrypt => github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998
golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20200818122824-ed5d25e28db8

165
go.sum
View File

@ -1,6 +1,10 @@
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/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
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/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998 h1:YT2uVwQiRQZxCaaahwfcgTq2j3j66w00n/27gb/zubs=
@ -15,12 +19,14 @@ github.com/ProtonMail/go-apple-mobileconfig v0.0.0-20160701194735-7ea9927a11f6 h
github.com/ProtonMail/go-apple-mobileconfig v0.0.0-20160701194735-7ea9927a11f6/go.mod h1:EtDfBMIDWmVe4viZCuBTEfe3OIIo0ghbpOaAZVO+hVg=
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-imap v0.0.0-20201016095853-a7520cc904d3 h1:Jvv9t3rSg/ID3Fh+uYsxgmvNI9fYnlab4vtBsbPtmq8=
github.com/ProtonMail/go-imap v0.0.0-20201016095853-a7520cc904d3/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
github.com/ProtonMail/go-imap v0.0.0-20201102134601-418cd74e9474 h1:D0RwDtkBw0Gt7hmbb1ivdEulplJAwu1i2jzh4HM45fo=
github.com/ProtonMail/go-imap v0.0.0-20201102134601-418cd74e9474/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/go.mod h1:795VPXcRUIQ9JyMNHP4el582VokQfippgjkQP3Gk0r0=
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4=
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
github.com/ProtonMail/go-rfc5322 v0.2.0 h1:tndoDGFtiCvESta9KLUeMksojz8qf76PefnkoQ+fqeg=
github.com/ProtonMail/go-rfc5322 v0.2.0/go.mod h1:mzZWlMWnQJuYLL7JpzuPF5+FimV2lZ9f0jeq24kJjpU=
github.com/ProtonMail/go-smtp v0.0.0-20181206232543-8261df20d309 h1:2pzfKjhBjSnw3BgmfTYRFQr1rFGxhfhUY0KKkg+RYxE=
github.com/ProtonMail/go-smtp v0.0.0-20181206232543-8261df20d309/go.mod h1:6UoBvDAMA/cTBwS3Y7tGpKnY5RH1F1uYHschT6eqAkI=
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
@ -29,22 +35,30 @@ github.com/ProtonMail/gopenpgp/v2 v2.0.1 h1:x0uvDhry5WzoHeJO4J3dgMLhG4Z9PeBJ2O+s
github.com/ProtonMail/gopenpgp/v2 v2.0.1/go.mod h1:wQQCJo7DURO6S9VwH+kSDEYs/B63yZnAEfGlOg8YNBY=
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/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
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/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
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/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antlr/antlr4 v0.0.0-20201020194047-0a7eaede42b0 h1:7RW94Pqb4Twsfpz42ALQ+sD0cUUpN8HF4uzKyQf2D8Y=
github.com/antlr/antlr4 v0.0.0-20201020194047-0a7eaede42b0/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA=
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
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/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
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/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/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
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/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/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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -55,6 +69,11 @@ github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7h
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
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/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a h1:bMdSPm6sssuOFpIaveu3XGAijMS3Tq2S3EqFZmZxidc=
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ=
github.com/emersion/go-imap-idle v0.0.0-20200601154248-f05f54664cc4 h1:/JIALzmCduf5o8TWJSiOBzTb9+R0SChwElUrJLlp2po=
@ -79,79 +98,161 @@ github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe h1:40SWqY0
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5 h1:n9qx98xiS5V4x2WIpPC2rr9mUM5ri9r/YhCEKbhCHro=
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/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
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/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws=
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/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/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
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/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
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/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/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
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/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/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-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
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/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
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/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
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.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE=
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/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.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQxB52A=
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
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/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
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.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
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/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
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/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
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-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 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/myesui/uuid v1.0.0 h1:xCBmH4l5KuvLYc5L7AS7SZg9/jKdIFubM7OVoLqaQUI=
github.com/myesui/uuid v1.0.0/go.mod h1:2CDfNgU0LR8mIdO8vdWd8i9gWWxLlcoIGGpSNgafq84=
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/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
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/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/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/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/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
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/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/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/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/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/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/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
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/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
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/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
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/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -168,29 +269,58 @@ github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e h1:G0DQ/TRQyrEZjtLlLw
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
github.com/twinj/uuid v1.0.0 h1:fzz7COZnDrXGTAOHGuUGYd6sG+JMq+AoE7+Jlu0przk=
github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY=
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/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
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/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
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/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/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.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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-20181220203305-927f97764cc3/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-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-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20190923162816-aa69164e4478/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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
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/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-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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190422165155-953cdadca894/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-20190924154521-2837fb4f24fe/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=
@ -201,6 +331,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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-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-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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@ -210,9 +343,17 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
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/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M=
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
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.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-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -15,8 +15,8 @@
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated by ./credits.sh at Wed Nov 4 12:24:36 PM CET 2020. DO NOT EDIT.
// Code generated by ./credits.sh at Tue Nov 24 08:56:01 AM CET 2020. DO NOT EDIT.
package bridge
const Credits = "github.com/0xAX/notificator;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/antlr/antlr4;github.com/certifi/gocertifi;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-mbox;github.com/emersion/go-message;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/raven-go;github.com/golang/mock;github.com/google/go-cmp;github.com/google/uuid;github.com/gopherjs/gopherjs;github.com/go-resty/resty/v2;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/Masterminds/semver/v3;github.com/mattn/go-runewidth;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/olekukonko/tablewriter;github.com/pkg/errors;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/gopenpgp/v2;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/PuerkitoBio/goquery;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/ssor/bom;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"
const Credits = "github.com/0xAX/notificator;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-mbox;github.com/emersion/go-message;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/sentry-go;github.com/golang/mock;github.com/google/go-cmp;github.com/google/uuid;github.com/gopherjs/gopherjs;github.com/go-resty/resty/v2;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/Masterminds/semver/v3;github.com/mattn/go-runewidth;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/olekukonko/tablewriter;github.com/pkg/errors;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/gopenpgp/v2;github.com/ProtonMail/go-rfc5322;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/PuerkitoBio/goquery;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/ssor/bom;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"

View File

@ -15,17 +15,18 @@
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated by ./release-notes.sh at 'Wed Nov 4 12:24:35 PM CET 2020'. DO NOT EDIT.
// Code generated by ./release-notes.sh at 'Mon Nov 23 07:38:53 AM CET 2020'. DO NOT EDIT.
package bridge
const ReleaseNotes = `Ensured better message flow by refactoring both address and date parsing
Improved secure connectivity checks
Better deb packaging
More robust error handling
const ReleaseNotes = `Improved package creation logic
Refactor of sending functions to simplify code maintenance
Added tests for package creation
For more detailed summary of the changes see https://github.com/ProtonMail/proton-bridge/blob/master/Changelog.md
`
const ReleaseFixedBugs = `Ensured that conversations are properly threaded
Fixed Linux font issues (Fedora)
Better handling of Mime encrypted messages
const ReleaseFixedBugs = `Bridge crashes related to labels handling
GUI popup related to TLS connection error
An issue where a random session key is included in the data payload
• Error handling (including improved detection)
`

View File

@ -22,7 +22,7 @@ import (
"runtime"
"github.com/ProtonMail/proton-bridge/pkg/constants"
"github.com/getsentry/raven-go"
"github.com/getsentry/sentry-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@ -51,10 +51,18 @@ var (
// Main sets up Sentry, filters out unwanted args, creates app and runs it.
func Main(appName, usage string, extraFlags []cli.Flag, run func(*cli.Context) error) {
if err := raven.SetDSN(constants.DSNSentry); err != nil {
err := sentry.Init(sentry.ClientOptions{
Dsn: constants.DSNSentry,
Release: constants.Revision,
})
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetFingerprint([]string{"{{ default }}"})
})
if err != nil {
log.WithError(err).Errorln("Can not setup sentry DSN")
}
raven.SetRelease(constants.Revision)
filterProcessSerialNumberFromArgs()
filterRestartNumberFromArgs()

View File

@ -40,7 +40,6 @@ const (
NoActiveKeyForRecipientEvent = "noActiveKeyForRecipient"
UpgradeApplicationEvent = "upgradeApplication"
TLSCertIssue = "tlsCertPinningIssue"
IMAPTLSBadCert = "imapTLSBadCert"
// LogoutEventTimeout is the minimum time to permit between logout events being sent.
LogoutEventTimeout = 3 * time.Minute

View File

@ -184,9 +184,17 @@ func (f *frontendCLI) setTransferRules(t *transfer.Transfer) bool {
}
func (f *frontendCLI) printTransferProgress(progress *transfer.Progress) {
failed, imported, exported, added, total := progress.GetCounts()
if total != 0 {
f.Println(fmt.Sprintf("Progress update: %d (%d / %d) / %d, failed: %d", imported, exported, added, total, failed))
counts := progress.GetCounts()
if counts.Total != 0 {
f.Println(fmt.Sprintf(
"Progress update: %d (%d / %d) / %d, skipped: %d, failed: %d",
counts.Imported,
counts.Exported,
counts.Added,
counts.Total,
counts.Skipped,
counts.Failed,
))
}
if progress.IsPaused() {

View File

@ -237,13 +237,6 @@ Item {
winMain.tlsBarState="notOK"
}
onShowIMAPCertTroubleshoot : {
go.notifyBubble(1, qsTr(
"Bridge was unable to establish a connection with your Email client. <br> <a href=\"https://protonmail.com/support/knowledge-base/bridge-ssl-connection-issue\">Learn more</a> <br>",
"notification message"
))
}
}

View File

@ -599,7 +599,7 @@ Dialog {
}
Text {
text: qsTr("<b>Import summary:</b><br>Total number of emails: %1<br>Imported emails: %2<br>Errors: %3").arg(go.total).arg(finalReport.imported).arg(go.progressFails)
text: qsTr("<b>Import summary:</b><br>Total number of emails: %1<br>Imported emails: %2<br>Filtered out emails: %3<br>Errors: %4").arg(go.total).arg(go.progressImported).arg(go.progressSkipped).arg(go.progressFails)
anchors.horizontalCenter: parent.horizontalCenter
textFormat: Text.RichText
horizontalAlignment: Text.AlignHCenter

View File

@ -45,7 +45,7 @@ Row {
}
InfoToolTip {
info: qsTr( "When master import lablel is selected then all imported email will have this label.", "Tooltip text for master import label")
info: qsTr( "When master import label is selected then all imported emails will have this label.", "Tooltip text for master import label")
anchors.verticalCenter: parent.verticalCenter
}

View File

@ -122,7 +122,6 @@ Window {
ListElement { title: "Minimize this" }
ListElement { title: "SendAlertPopup" }
ListElement { title: "TLSCertError" }
ListElement { title: "IMAPCertError" }
}
ListView {
@ -209,9 +208,6 @@ Window {
case "TLSCertError" :
go.showCertIssue()
break;
case "IMAPCertError" :
go.showIMAPCertTroubleshoot()
break;
default :
console.log("Not implemented " + data)
}
@ -314,7 +310,6 @@ Window {
signal failedAutostartCode(string code)
signal showCertIssue()
signal showIMAPCertTroubleshoot()
signal updateFinished(bool hasError)

View File

@ -840,6 +840,8 @@ Window {
property real progress: 0.0
property int progressFails: 0
property int progressImported: 0
property int progressSkipped: 0
property string progressDescription: "nothing"
property string progressInit: "init"
property int total: 42
@ -1011,6 +1013,8 @@ Window {
property SequentialAnimation animateProgressBar : SequentialAnimation {
id: apb
property real speedup : 1.0;
PropertyAnimation{ target: go; properties: "progressSkipped"; to: 0; duration: 1; }
PropertyAnimation{ target: go; properties: "progressImported"; to: 0; duration: 1; }
PropertyAnimation{ target: go; properties: "importLogFileName"; to: ""; duration: 1; }
PropertyAnimation{ target: go; properties: "progressDescription"; to: go.progressInit; duration: 1; }
PropertyAnimation{ duration: 2000/apb.speedup; }
@ -1024,6 +1028,8 @@ Window {
PropertyAnimation{ target: go; properties: "progress"; to: 0.01; duration: 1; }
PropertyAnimation{ duration: 1000/apb.speedup; }
PropertyAnimation{ target: go; properties: "progress"; to: 0.1; duration: 1; }
PropertyAnimation{ target: go; properties: "progressSkipped"; to: 12; duration: 1; }
PropertyAnimation{ target: go; properties: "progressImported"; to: 13.1; duration: 1; }
PropertyAnimation{ duration: 1000/apb.speedup; }
PropertyAnimation{ target: go; properties: "progress"; to: 0.3; duration: 1; }
PropertyAnimation{ target: go; properties: "progressFails"; to: 1; duration: 1; }

View File

@ -21,6 +21,7 @@ package qtcommon
import (
"fmt"
"github.com/therecipe/qt/core"
)

View File

@ -337,12 +337,14 @@ func (f *FrontendQt) setProgressManager(progress *transfer.Progress) {
if progress.IsStopped() {
break
}
failed, imported, _, _, total := progress.GetCounts()
f.Qml.SetTotal(int(total))
f.Qml.SetProgressFails(int(failed))
counts := progress.GetCounts()
f.Qml.SetTotal(int(counts.Total))
f.Qml.SetProgressImported(int(counts.Imported))
f.Qml.SetProgressSkipped(int(counts.Skipped))
f.Qml.SetProgressFails(int(counts.Failed))
f.Qml.SetProgressDescription(progress.PauseReason())
if total > 0 {
newProgress := float32(imported+failed) / float32(total)
if counts.Total > 0 {
newProgress := counts.Progress()
if newProgress >= 0 && newProgress != f.Qml.Progress() {
f.Qml.SetProgress(newProgress)
f.Qml.ProgressChanged(newProgress)
@ -350,8 +352,10 @@ func (f *FrontendQt) setProgressManager(progress *transfer.Progress) {
}
}
// Counts will add lost messages only once the progress is completeled.
failed, _, _, _, _ := progress.GetCounts()
f.Qml.SetProgressFails(int(failed))
counts := progress.GetCounts()
f.Qml.SetProgressImported(int(counts.Imported))
f.Qml.SetProgressSkipped(int(counts.Skipped))
f.Qml.SetProgressFails(int(counts.Failed))
if err := progress.GetFatalError(); err != nil {
f.Qml.SetProgressDescription(err.Error())

View File

@ -77,6 +77,8 @@ func (f *FrontendQt) StartImport(email string) { // TODO email not needed
log.Trace("Starting import")
f.Qml.SetProgressDescription("init") // TODO use const
f.Qml.SetProgressImported(0)
f.Qml.SetProgressSkipped(0)
f.Qml.SetProgressFails(0)
f.Qml.SetProgress(0.0)
f.Qml.SetTotal(1)

View File

@ -43,6 +43,8 @@ type GoQMLInterface struct {
_ string `property:lastError`
_ float32 `property:progress`
_ string `property:progressDescription`
_ int `property:progressImported`
_ int `property:progressSkipped`
_ int `property:progressFails`
_ int `property:total`
_ string `property:importLogFileName`

View File

@ -40,17 +40,17 @@ import (
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/internal/frontend/autoconfig"
"github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/preferences"
"github.com/ProtonMail/proton-bridge/internal/updates"
"github.com/ProtonMail/proton-bridge/pkg/config"
"github.com/ProtonMail/proton-bridge/pkg/ports"
"github.com/ProtonMail/proton-bridge/pkg/useragent"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/sirupsen/logrus"
"github.com/ProtonMail/proton-bridge/pkg/ports"
"github.com/ProtonMail/proton-bridge/pkg/useragent"
"github.com/kardianos/osext"
"github.com/sirupsen/logrus"
"github.com/skratchdot/open-golang/open"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
@ -187,7 +187,6 @@ func (s *FrontendQt) watchEvents() {
updateApplicationCh := s.getEventChannel(events.UpgradeApplicationEvent)
newUserCh := s.getEventChannel(events.UserRefreshEvent)
certIssue := s.getEventChannel(events.TLSCertIssue)
imapCertIssue := s.getEventChannel(events.IMAPTLSBadCert)
for {
select {
case errorDetails := <-errorCh:
@ -227,8 +226,6 @@ func (s *FrontendQt) watchEvents() {
s.Qml.LoadAccounts()
case <-certIssue:
s.Qml.ShowCertIssue()
case <-imapCertIssue:
s.Qml.ShowIMAPCertTroubleshoot()
}
}
}

View File

@ -135,7 +135,6 @@ type GoQMLInterface struct {
_ func(x, y float32) `slot:"saveOutgoingNoEncPopupCoord"`
_ func(recipient string) `signal:"showNoActiveKeyForRecipient"`
_ func() `signal:"showCertIssue"`
_ func() `signal:"ShowIMAPCertTroubleshoot"`
_ func() `slot:"startUpdate"`
_ func(hasError bool) `signal:"updateFinished"`

View File

@ -28,7 +28,6 @@ import (
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/emersion/go-imap"
goIMAPBackend "github.com/emersion/go-imap/backend"
"github.com/sirupsen/logrus"
)
type panicHandler interface {
@ -198,11 +197,3 @@ func (ib *imapBackend) monitorDisconnectedUsers() {
ib.deleteUser(address)
}
}
func (ib *imapBackend) upgradeError(err error) {
logrus.WithError(err).Error("IMAP connection couldn't be upgraded to TLS during STARTTLS")
if strings.Contains(err.Error(), "remote error: tls: bad certificate") {
ib.eventListener.Emit(events.IMAPTLSBadCert, err.Error())
}
}

View File

@ -58,7 +58,6 @@ func NewIMAPServer(debugClient, debugServer bool, port int, tls *tls.Config, ima
s.AllowInsecureAuth = true
s.ErrorLog = newServerErrorLogger("server-imap")
s.AutoLogout = 30 * time.Minute
s.UpgradeError = imapBackend.upgradeError
serverID := imapid.ID{
imapid.FieldName: "ProtonMail Bridge",

View File

@ -15,8 +15,8 @@
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated by ./credits.sh at Wed Nov 4 12:24:36 PM CET 2020. DO NOT EDIT.
// Code generated by ./credits.sh at Tue Nov 24 08:56:01 AM CET 2020. DO NOT EDIT.
package importexport
const Credits = "github.com/0xAX/notificator;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/antlr/antlr4;github.com/certifi/gocertifi;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-mbox;github.com/emersion/go-message;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/raven-go;github.com/golang/mock;github.com/google/go-cmp;github.com/google/uuid;github.com/gopherjs/gopherjs;github.com/go-resty/resty/v2;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/Masterminds/semver/v3;github.com/mattn/go-runewidth;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/olekukonko/tablewriter;github.com/pkg/errors;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/gopenpgp/v2;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/PuerkitoBio/goquery;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/ssor/bom;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"
const Credits = "github.com/0xAX/notificator;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-mbox;github.com/emersion/go-message;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/sentry-go;github.com/golang/mock;github.com/google/go-cmp;github.com/google/uuid;github.com/gopherjs/gopherjs;github.com/go-resty/resty/v2;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/Masterminds/semver/v3;github.com/mattn/go-runewidth;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/olekukonko/tablewriter;github.com/pkg/errors;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/gopenpgp/v2;github.com/ProtonMail/go-rfc5322;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/PuerkitoBio/goquery;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/ssor/bom;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"

View File

@ -15,18 +15,14 @@
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated by ./release-notes.sh at 'Wed Nov 4 12:24:35 PM CET 2020'. DO NOT EDIT.
// Code generated by ./release-notes.sh at 'Wed Nov 11 01:57:14 PM CET 2020'. DO NOT EDIT.
package importexport
const ReleaseNotes = `Improvements to the import from large mbox files with multiple labels
Not allow to run multiple instances of the app or transfers at the same time
Various enhancements of the import process related to parsing
• Cosmetic GUI changes
• Better error handling
const ReleaseNotes = `Further improvements to address and date parsing
Better handling and displaying of skipped messages
Improved error reporting
`
const ReleaseFixedBugs = `• Linux font issues - Fedora specific
• App response to the user pausing and canceling import or export
• Handling errors during update
const ReleaseFixedBugs = `
`

View File

@ -45,7 +45,7 @@ type SendPreferences struct {
// internal emails (including the so-called encrypted-to-outside emails,
// which even though meant for external users, they don't really get out of
// our platform). If the email is sent unencrypted, no PGP scheme is needed.
Scheme int
Scheme pmapi.PackageFlag
// MIMEType is the MIME type to use for formatting the body of the email
// (before encryption/after decryption). The standard possibilities are the
@ -191,8 +191,12 @@ func (b *sendPreferencesBuilder) build() (p SendPreferences) {
p.Scheme = pmapi.PGPMIMEPackage
}
case b.shouldSign() && !b.shouldEncrypt() && b.getScheme() == pgpMIME:
case b.shouldSign() && !b.shouldEncrypt():
if b.getScheme() == pgpInline {
p.Scheme = pmapi.ClearPackage
} else {
p.Scheme = pmapi.ClearMIMEPackage
}
default:
p.Scheme = pmapi.ClearPackage

View File

@ -41,7 +41,7 @@ func TestPreferencesBuilder(t *testing.T) {
wantEncrypt bool
wantSign bool
wantScheme int
wantScheme pmapi.PackageFlag
wantMIMEType string
wantPublicKey string
}{
@ -254,6 +254,20 @@ func TestPreferencesBuilder(t *testing.T) {
wantMIMEType: "multipart/mixed",
},
{
name: "external with contact sign enabled and plain text",
contactMeta: &ContactMetadata{MIMEType: "text/plain", Scheme: pgpInline, Sign: true, SignIsSet: true},
receivedKeys: []pmapi.PublicKey{},
isInternal: false,
mailSettings: pmapi.MailSettings{PGPScheme: pmapi.PGPMIMEPackage, DraftMIMEType: "text/html"},
wantEncrypt: false,
wantSign: true,
wantScheme: pmapi.ClearPackage,
wantMIMEType: "text/plain",
},
{
name: "external with sign enabled, sending plaintext, should still send as ClearMIME",

View File

@ -187,7 +187,7 @@ func (su *smtpUser) Send(from string, to []string, messageReader io.Reader) (err
log.WithError(err).Error("Failed to parse message")
return
}
clearBody := message.Body
richBody := message.Body
externalID := message.Header.Get("Message-Id")
externalID = strings.Trim(externalID, "<>")
@ -256,7 +256,6 @@ func (su *smtpUser) Send(from string, to []string, messageReader io.Reader) (err
atts = append(atts, message.Attachments...)
// Decrypt attachment keys, because we will need to re-encrypt them with the recipients' public keys.
attkeys := make(map[string]*crypto.SessionKey)
attkeysEncoded := make(map[string]pmapi.AlgoKey)
for _, att := range atts {
var keyPackets []byte
@ -266,23 +265,9 @@ func (su *smtpUser) Send(from string, to []string, messageReader io.Reader) (err
if attkeys[att.ID], err = kr.DecryptSessionKey(keyPackets); err != nil {
return errors.Wrap(err, "decrypting attachment session key")
}
attkeysEncoded[att.ID] = pmapi.AlgoKey{
Key: attkeys[att.ID].GetBase64Key(),
Algorithm: attkeys[att.ID].Algo,
}
}
plainSharedScheme := 0
htmlSharedScheme := 0
mimeSharedType := 0
plainAddressMap := make(map[string]*pmapi.MessageAddress)
htmlAddressMap := make(map[string]*pmapi.MessageAddress)
mimeAddressMap := make(map[string]*pmapi.MessageAddress)
var plainKey, htmlKey, mimeKey *crypto.SessionKey
var plainData, htmlData, mimeData []byte
req := pmapi.NewSendMessageReq(kr, mimeBody, plainBody, richBody, attkeys)
containsUnencryptedRecipients := false
for _, email := range to {
@ -298,61 +283,15 @@ func (su *smtpUser) Send(from string, to []string, messageReader io.Reader) (err
return err
}
var signature int
var signature pmapi.SignatureFlag
if sendPreferences.Sign {
signature = pmapi.YesSignature
signature = pmapi.SignatureDetached
} else {
signature = pmapi.NoSignature
}
if sendPreferences.Scheme == pmapi.PGPMIMEPackage || sendPreferences.Scheme == pmapi.ClearMIMEPackage {
if mimeKey == nil {
if mimeKey, mimeData, err = encryptSymmetric(kr, mimeBody, true); err != nil {
return err
}
}
if sendPreferences.Scheme == pmapi.PGPMIMEPackage {
mimeBodyPacket, _, err := createPackets(sendPreferences.PublicKey, mimeKey, map[string]*crypto.SessionKey{})
if err != nil {
return err
}
mimeAddressMap[email] = &pmapi.MessageAddress{Type: sendPreferences.Scheme, BodyKeyPacket: mimeBodyPacket, Signature: signature}
} else {
mimeAddressMap[email] = &pmapi.MessageAddress{Type: sendPreferences.Scheme, Signature: signature}
}
mimeSharedType |= sendPreferences.Scheme
} else {
switch sendPreferences.MIMEType {
case pmapi.ContentTypePlainText:
if plainKey == nil {
if plainKey, plainData, err = encryptSymmetric(kr, plainBody, true); err != nil {
return err
}
}
newAddress := &pmapi.MessageAddress{Type: sendPreferences.Scheme, Signature: signature}
if sendPreferences.Encrypt && sendPreferences.PublicKey != nil {
newAddress.BodyKeyPacket, newAddress.AttachmentKeyPackets, err = createPackets(sendPreferences.PublicKey, plainKey, attkeys)
if err != nil {
return err
}
}
plainAddressMap[email] = newAddress
plainSharedScheme |= sendPreferences.Scheme
case pmapi.ContentTypeHTML:
if htmlKey == nil {
if htmlKey, htmlData, err = encryptSymmetric(kr, clearBody, true); err != nil {
return err
}
}
newAddress := &pmapi.MessageAddress{Type: sendPreferences.Scheme, Signature: signature}
if sendPreferences.Encrypt && sendPreferences.PublicKey != nil {
newAddress.BodyKeyPacket, newAddress.AttachmentKeyPackets, err = createPackets(sendPreferences.PublicKey, htmlKey, attkeys)
if err != nil {
return err
}
}
htmlAddressMap[email] = newAddress
htmlSharedScheme |= sendPreferences.Scheme
signature = pmapi.SignatureNone
}
if err := req.AddRecipient(email, sendPreferences.Scheme, sendPreferences.PublicKey, signature, sendPreferences.MIMEType, sendPreferences.Encrypt); err != nil {
return errors.Wrap(err, "failed to add recipient")
}
}
@ -370,31 +309,7 @@ func (su *smtpUser) Send(from string, to []string, messageReader io.Reader) (err
}
}
req := &pmapi.SendMessageReq{}
plainPkg := buildPackage(plainAddressMap, plainSharedScheme, pmapi.ContentTypePlainText, plainData, plainKey, attkeysEncoded)
if plainPkg != nil {
req.Packages = append(req.Packages, plainPkg)
}
htmlPkg := buildPackage(htmlAddressMap, htmlSharedScheme, pmapi.ContentTypeHTML, htmlData, htmlKey, attkeysEncoded)
if htmlPkg != nil {
req.Packages = append(req.Packages, htmlPkg)
}
if len(mimeAddressMap) > 0 {
pkg := &pmapi.MessagePackage{
Body: base64.StdEncoding.EncodeToString(mimeData),
Addresses: mimeAddressMap,
MIMEType: pmapi.ContentTypeMultipartMixed,
Type: mimeSharedType,
BodyKey: pmapi.AlgoKey{
Key: mimeKey.GetBase64Key(),
Algorithm: mimeKey.Algo,
},
}
req.Packages = append(req.Packages, pkg)
}
req.PreparePackages()
return su.storeUser.SendMessage(message.ID, req)
}

View File

@ -18,11 +18,7 @@
package smtp
import (
"encoding/base64"
"regexp"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
)
//nolint:gochecknoglobals // Used like a constant
@ -35,85 +31,3 @@ var mailFormat = regexp.MustCompile(`.+@.+\..+`)
func looksLikeEmail(e string) bool {
return mailFormat.MatchString(e)
}
func createPackets(
pubkey *crypto.KeyRing,
bodyKey *crypto.SessionKey,
attkeys map[string]*crypto.SessionKey,
) (bodyPacket string, attachmentPackets map[string]string, err error) {
// Encrypt message body keys.
packetBytes, err := pubkey.EncryptSessionKey(bodyKey)
if err != nil {
return
}
bodyPacket = base64.StdEncoding.EncodeToString(packetBytes)
// Encrypt attachment keys.
attachmentPackets = make(map[string]string)
for id, attkey := range attkeys {
var packets []byte
if packets, err = pubkey.EncryptSessionKey(attkey); err != nil {
return
}
attachmentPackets[id] = base64.StdEncoding.EncodeToString(packets)
}
return
}
func encryptSymmetric(
kr *crypto.KeyRing,
textToEncrypt string,
canonicalizeText bool, // nolint[unparam]
) (key *crypto.SessionKey, symEncryptedData []byte, err error) {
// We use only primary key to encrypt the message. Our keyring contains all keys (primary, old and deacivated ones).
firstKey, err := kr.FirstKey()
if err != nil {
return
}
pgpMessage, err := firstKey.Encrypt(crypto.NewPlainMessageFromString(textToEncrypt), kr)
if err != nil {
return
}
pgpSplitMessage, err := pgpMessage.SeparateKeyAndData(len(textToEncrypt), 0)
if err != nil {
return
}
key, err = kr.DecryptSessionKey(pgpSplitMessage.GetBinaryKeyPacket())
if err != nil {
return
}
symEncryptedData = pgpSplitMessage.GetBinaryDataPacket()
return
}
func buildPackage(
addressMap map[string]*pmapi.MessageAddress,
sharedScheme int,
mimeType string,
bodyData []byte,
bodyKey *crypto.SessionKey,
attKeys map[string]pmapi.AlgoKey,
) (pkg *pmapi.MessagePackage) {
if len(addressMap) == 0 {
return nil
}
pkg = &pmapi.MessagePackage{
Body: base64.StdEncoding.EncodeToString(bodyData),
Addresses: addressMap,
MIMEType: mimeType,
Type: sharedScheme,
}
if sharedScheme|pmapi.ClearPackage > 0 {
pkg.BodyKey.Key = bodyKey.GetBase64Key()
pkg.BodyKey.Algorithm = bodyKey.Algo
pkg.AttachmentKeys = attKeys
}
return pkg
}

View File

@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"strings"
"sync/atomic"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/sirupsen/logrus"
@ -38,6 +39,8 @@ type Mailbox struct {
color string
log *logrus.Entry
isDeleting atomic.Value
}
func newMailbox(storeAddress *Address, labelID, labelPrefix, labelName, color string) (mb *Mailbox, err error) {
@ -59,6 +62,7 @@ func txNewMailbox(tx *bolt.Tx, storeAddress *Address, labelID, labelPrefix, labe
color: color,
log: l,
}
mb.isDeleting.Store(false)
err := initMailboxBucket(tx, mb.getBucketName())
if err != nil {
@ -215,6 +219,7 @@ func (storeMailbox *Mailbox) Rename(newName string) error {
// Deletion has to be propagated to all the same mailboxes in all addresses.
// The propagation is processed by the event loop.
func (storeMailbox *Mailbox) Delete() error {
storeMailbox.isDeleting.Store(true)
return storeMailbox.storeAddress.deleteMailbox(storeMailbox.labelID)
}
@ -226,6 +231,14 @@ func (storeMailbox *Mailbox) GetDelimiter() string {
// deleteMailboxEvent deletes the mailbox bucket.
// This is called from the event loop.
func (storeMailbox *Mailbox) deleteMailboxEvent() error {
if !storeMailbox.isDeleting.Load().(bool) {
// Deleting label removes bucket. Any ongoing connection selected
// in such mailbox then might panic because of non-existing bucket.
// Closing connetions prevents that panic but if the connection
// asked for deletion, it should not be closed so it can receive
// successful response.
storeMailbox.store.user.CloseAllConnections()
}
return storeMailbox.db().Update(func(tx *bolt.Tx) error {
return tx.Bucket(mailboxesBucket).DeleteBucket(storeMailbox.getBucketName())
})

View File

@ -5,9 +5,10 @@
package mocks
import (
reflect "reflect"
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockPanicHandler is a mock of PanicHandler interface
@ -105,6 +106,18 @@ func (m *MockBridgeUser) EXPECT() *MockBridgeUserMockRecorder {
return m.recorder
}
// CloseAllConnections mocks base method
func (m *MockBridgeUser) CloseAllConnections() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "CloseAllConnections")
}
// CloseAllConnections indicates an expected call of CloseAllConnections
func (mr *MockBridgeUserMockRecorder) CloseAllConnections() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseAllConnections", reflect.TypeOf((*MockBridgeUser)(nil).CloseAllConnections))
}
// CloseConnection mocks base method
func (m *MockBridgeUser) CloseConnection(arg0 string) {
m.ctrl.T.Helper()

View File

@ -5,9 +5,10 @@
package mocks
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
time "time"
gomock "github.com/golang/mock/gomock"
)
// MockListener is a mock of Listener interface

View File

@ -36,6 +36,7 @@ type BridgeUser interface {
GetPrimaryAddress() string
GetStoreAddresses() []string
UpdateUser() error
CloseAllConnections()
CloseConnection(string)
Logout() error
}

View File

@ -58,6 +58,7 @@ type MessageStatus struct {
targetID string // Message ID at the target (if any).
bodyHash string // Hash of the message body.
skipped bool
exported bool
imported bool
exportErr error
@ -96,7 +97,7 @@ func (status *MessageStatus) setDetailsFromHeader(header mail.Header) {
}
func (status *MessageStatus) hasError(includeMissing bool) bool {
return status.exportErr != nil || status.importErr != nil || (includeMissing && !status.imported)
return status.exportErr != nil || status.importErr != nil || (includeMissing && !status.skipped && !status.imported)
}
// GetErrorMessage returns error message.
@ -105,6 +106,9 @@ func (status *MessageStatus) GetErrorMessage() string {
}
func (status *MessageStatus) getErrorMessage(includeMissing bool) string {
if status.skipped {
return ""
}
if status.exportErr != nil {
return fmt.Sprintf("failed to export: %s", status.exportErr)
}

View File

@ -1,13 +1,16 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ProtonMail/proton-bridge/internal/transfer (interfaces: PanicHandler,ClientManager)
// Source: github.com/ProtonMail/proton-bridge/internal/transfer (interfaces: PanicHandler,ClientManager,IMAPClientProvider)
// Package mocks is a generated GoMock package.
package mocks
import (
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
imap "github.com/emersion/go-imap"
sasl "github.com/emersion/go-sasl"
gomock "github.com/golang/mock/gomock"
)
// MockPanicHandler is a mock of PanicHandler interface
@ -95,3 +98,170 @@ func (mr *MockClientManagerMockRecorder) GetClient(arg0 interface{}) *gomock.Cal
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClient", reflect.TypeOf((*MockClientManager)(nil).GetClient), arg0)
}
// MockIMAPClientProvider is a mock of IMAPClientProvider interface
type MockIMAPClientProvider struct {
ctrl *gomock.Controller
recorder *MockIMAPClientProviderMockRecorder
}
// MockIMAPClientProviderMockRecorder is the mock recorder for MockIMAPClientProvider
type MockIMAPClientProviderMockRecorder struct {
mock *MockIMAPClientProvider
}
// NewMockIMAPClientProvider creates a new mock instance
func NewMockIMAPClientProvider(ctrl *gomock.Controller) *MockIMAPClientProvider {
mock := &MockIMAPClientProvider{ctrl: ctrl}
mock.recorder = &MockIMAPClientProviderMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockIMAPClientProvider) EXPECT() *MockIMAPClientProviderMockRecorder {
return m.recorder
}
// Authenticate mocks base method
func (m *MockIMAPClientProvider) Authenticate(arg0 sasl.Client) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Authenticate", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Authenticate indicates an expected call of Authenticate
func (mr *MockIMAPClientProviderMockRecorder) Authenticate(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authenticate", reflect.TypeOf((*MockIMAPClientProvider)(nil).Authenticate), arg0)
}
// Capability mocks base method
func (m *MockIMAPClientProvider) Capability() (map[string]bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Capability")
ret0, _ := ret[0].(map[string]bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Capability indicates an expected call of Capability
func (mr *MockIMAPClientProviderMockRecorder) Capability() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capability", reflect.TypeOf((*MockIMAPClientProvider)(nil).Capability))
}
// Fetch mocks base method
func (m *MockIMAPClientProvider) Fetch(arg0 *imap.SeqSet, arg1 []imap.FetchItem, arg2 chan *imap.Message) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Fetch", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// Fetch indicates an expected call of Fetch
func (mr *MockIMAPClientProviderMockRecorder) Fetch(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fetch", reflect.TypeOf((*MockIMAPClientProvider)(nil).Fetch), arg0, arg1, arg2)
}
// List mocks base method
func (m *MockIMAPClientProvider) List(arg0, arg1 string, arg2 chan *imap.MailboxInfo) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "List", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// List indicates an expected call of List
func (mr *MockIMAPClientProviderMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockIMAPClientProvider)(nil).List), arg0, arg1, arg2)
}
// Login mocks base method
func (m *MockIMAPClientProvider) Login(arg0, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Login", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// Login indicates an expected call of Login
func (mr *MockIMAPClientProviderMockRecorder) Login(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Login", reflect.TypeOf((*MockIMAPClientProvider)(nil).Login), arg0, arg1)
}
// Select mocks base method
func (m *MockIMAPClientProvider) Select(arg0 string, arg1 bool) (*imap.MailboxStatus, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Select", arg0, arg1)
ret0, _ := ret[0].(*imap.MailboxStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Select indicates an expected call of Select
func (mr *MockIMAPClientProviderMockRecorder) Select(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockIMAPClientProvider)(nil).Select), arg0, arg1)
}
// State mocks base method
func (m *MockIMAPClientProvider) State() imap.ConnState {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "State")
ret0, _ := ret[0].(imap.ConnState)
return ret0
}
// State indicates an expected call of State
func (mr *MockIMAPClientProviderMockRecorder) State() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockIMAPClientProvider)(nil).State))
}
// Support mocks base method
func (m *MockIMAPClientProvider) Support(arg0 string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Support", arg0)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Support indicates an expected call of Support
func (mr *MockIMAPClientProviderMockRecorder) Support(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Support", reflect.TypeOf((*MockIMAPClientProvider)(nil).Support), arg0)
}
// SupportAuth mocks base method
func (m *MockIMAPClientProvider) SupportAuth(arg0 string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SupportAuth", arg0)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SupportAuth indicates an expected call of SupportAuth
func (mr *MockIMAPClientProviderMockRecorder) SupportAuth(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportAuth", reflect.TypeOf((*MockIMAPClientProvider)(nil).SupportAuth), arg0)
}
// UidFetch mocks base method
func (m *MockIMAPClientProvider) UidFetch(arg0 *imap.SeqSet, arg1 []imap.FetchItem, arg2 chan *imap.Message) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UidFetch", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// UidFetch indicates an expected call of UidFetch
func (mr *MockIMAPClientProviderMockRecorder) UidFetch(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UidFetch", reflect.TypeOf((*MockIMAPClientProvider)(nil).UidFetch), arg0, arg1, arg2)
}

View File

@ -140,6 +140,19 @@ func (p *Progress) addMessage(messageID string, sourceNames, targetNames []strin
}
}
// messageSkipped should be called once the message is skipped due to some
// filter such as time or folder and so on.
func (p *Progress) messageSkipped(messageID string) {
p.lock.Lock()
defer p.lock.Unlock()
defer p.update()
p.log.WithField("id", messageID).Debug("Message skipped")
p.messageStatuses[messageID].skipped = true
p.logMessage(messageID)
}
// messageExported should be called right before message is exported.
func (p *Progress) messageExported(messageID string, body []byte, err error) {
p.lock.Lock()
@ -330,35 +343,40 @@ func (p *Progress) GetFailedMessages() []*MessageStatus {
}
// GetCounts returns counts of exported and imported messages.
func (p *Progress) GetCounts() (failed, imported, exported, added, total uint) {
func (p *Progress) GetCounts() ProgressCounts {
p.lock.Lock()
defer p.lock.Unlock()
counts := ProgressCounts{}
// Return counts only once total is estimated or the process already
// ended (for a case when it ended quickly to report it correctly).
if p.updateCh != nil && !p.messageCounted {
return
return counts
}
// Include lost messages in the process only when transfer is done.
includeMissing := p.updateCh == nil
for _, mailboxCount := range p.messageCounts {
total += mailboxCount
counts.Total += mailboxCount
}
for _, status := range p.messageStatuses {
added++
counts.Added++
if status.skipped {
counts.Skipped++
}
if status.exported {
exported++
counts.Exported++
}
if status.imported {
imported++
counts.Imported++
}
if status.hasError(includeMissing) {
failed++
counts.Failed++
}
}
return
return counts
}
// GenerateBugReport generates similar file to import log except private information.

View File

@ -15,35 +15,21 @@
// 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 rfc5322
package transfer
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type atom struct {
value string
// ProgressCounts holds counts counted by Progress.
type ProgressCounts struct {
Failed,
Skipped,
Imported,
Exported,
Added,
Total uint
}
func (w *walker) EnterAtom(ctx *parser.AtomContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering atom")
w.enter(&atom{
value: ctx.GetText(),
})
}
func (w *walker) ExitAtom(ctx *parser.AtomContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting atom")
type withAtom interface {
withAtom(*atom)
}
res := w.exit().(*atom)
if parent, ok := w.parent().(withAtom); ok {
parent.withAtom(res)
}
// Progress returns ratio between processed messages (fully imported, skipped
// and failed ones) and total number of messages as percentage (0 - 1).
func (c *ProgressCounts) Progress() float32 {
progressed := c.Imported + c.Skipped + c.Failed
return float32(progressed) / float32(c.Total)
}

View File

@ -39,8 +39,8 @@ func TestProgressUpdateCount(t *testing.T) {
progress.finish()
_, _, _, _, total := progress.GetCounts() //nolint[dogsled]
r.Equal(t, uint(42), total)
counts := progress.GetCounts()
r.Equal(t, uint(42), counts.Total)
}
func TestProgressAddingMessages(t *testing.T) {
@ -66,13 +66,18 @@ func TestProgressAddingMessages(t *testing.T) {
progress.messageExported("msg4", []byte(""), errors.New("failed export"))
progress.messageImported("msg4", "", nil)
// msg5 is skipped.
progress.addMessage("msg5", []string{}, []string{})
progress.messageSkipped("msg5")
progress.finish()
failed, imported, exported, added, _ := progress.GetCounts()
a.Equal(t, uint(4), added)
a.Equal(t, uint(2), exported)
a.Equal(t, uint(2), imported)
a.Equal(t, uint(3), failed)
counts := progress.GetCounts()
a.Equal(t, uint(5), counts.Added)
a.Equal(t, uint(2), counts.Exported)
a.Equal(t, uint(2), counts.Imported)
a.Equal(t, uint(1), counts.Skipped)
a.Equal(t, uint(3), counts.Failed)
errorsMap := map[string]string{}
for _, status := range progress.GetFailedMessages() {

View File

@ -82,8 +82,6 @@ func (p *EMLProvider) getFilePathsPerFolder(rules transferRules) (map[string][]s
}
func (p *EMLProvider) exportMessages(rule *Rule, filePaths []string, progress *Progress, ch chan<- Message) {
count := uint(len(filePaths))
for _, filePath := range filePaths {
if progress.shouldStop() {
break
@ -91,6 +89,8 @@ func (p *EMLProvider) exportMessages(rule *Rule, filePaths []string, progress *P
msg, err := p.exportMessage(rule, filePath)
progress.addMessage(filePath, msg.sourceNames(), msg.targetNames())
// Read and check time in body only if the rule specifies it
// to not waste energy.
if err == nil && rule.HasTimeLimit() {
@ -99,17 +99,11 @@ func (p *EMLProvider) exportMessages(rule *Rule, filePaths []string, progress *P
err = msgTimeErr
} else if !rule.isTimeInRange(msgTime) {
log.WithField("msg", filePath).Debug("Message skipped due to time")
count--
progress.updateCount(rule.SourceMailbox.Name, count)
progress.messageSkipped(filePath)
continue
}
}
// addMessage is called after time check to not report message
// which should not be exported but any error from reading body
// or parsing time is reported as an error.
progress.addMessage(filePath, msg.sourceNames(), msg.targetNames())
progress.messageExported(filePath, msg.Body, err)
if err == nil {
ch <- msg

View File

@ -21,28 +21,49 @@ import (
"net"
"strings"
imapClient "github.com/emersion/go-imap/client"
"github.com/emersion/go-imap"
"github.com/emersion/go-sasl"
)
type IMAPClientProvider interface {
Capability() (map[string]bool, error)
Support(cap string) (bool, error)
State() imap.ConnState
SupportAuth(mech string) (bool, error)
Authenticate(auth sasl.Client) error
Login(username, password string) error
List(ref, name string, ch chan *imap.MailboxInfo) error
Select(name string, readOnly bool) (*imap.MailboxStatus, error)
Fetch(seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error
UidFetch(seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error
}
// IMAPProvider implements export from IMAP server.
type IMAPProvider struct {
username string
password string
addr string
client *imapClient.Client
clientDialer func(addr string) (IMAPClientProvider, error)
client IMAPClientProvider
timeIt *timeIt
}
// NewIMAPProvider returns new IMAPProvider.
func NewIMAPProvider(username, password, host, port string) (*IMAPProvider, error) {
return newIMAPProvider(imapClientDial, username, password, host, port)
}
func newIMAPProvider(clientDialer func(string) (IMAPClientProvider, error), username, password, host, port string) (*IMAPProvider, error) {
p := &IMAPProvider{
username: username,
password: password,
addr: net.JoinHostPort(host, port),
timeIt: newTimeIt("imap"),
clientDialer: clientDialer,
}
if err := p.auth(); err != nil {

View File

@ -84,31 +84,15 @@ func (p *IMAPProvider) loadMessagesInfo(rule *Rule, progress *Progress, uidValid
p.timeIt.start("load", rule.SourceMailbox.Name)
defer p.timeIt.stop("load", rule.SourceMailbox.Name)
log := log.WithField("mailbox", rule.SourceMailbox.Name)
messagesInfo := map[string]imapMessageInfo{}
pageStart := uint32(1)
pageEnd := imapPageSize
for {
if progress.shouldStop() {
break
}
// Some servers do not accept message sequence number higher than the total count.
if pageEnd > count {
pageEnd = count
}
seqSet := &imap.SeqSet{}
seqSet.AddRange(pageStart, pageEnd)
items := []imap.FetchItem{imap.FetchUid, imap.FetchRFC822Size}
fetchItems := []imap.FetchItem{imap.FetchUid, imap.FetchRFC822Size}
if rule.HasTimeLimit() {
items = append(items, imap.FetchEnvelope)
fetchItems = append(fetchItems, imap.FetchEnvelope)
}
pageMsgCount := uint32(0)
processMessageCallback := func(imapMessage *imap.Message) {
pageMsgCount++
if rule.HasTimeLimit() {
t := imapMessage.Envelope.Date.Unix()
if t != 0 && !rule.isTimeInRange(t) {
@ -127,18 +111,35 @@ func (p *IMAPProvider) loadMessagesInfo(rule *Rule, progress *Progress, uidValid
progress.addMessage(id, []string{rule.SourceMailbox.Name}, rule.TargetMailboxNames())
}
progress.callWrap(func() error {
return p.fetch(rule.SourceMailbox.Name, seqSet, items, processMessageCallback)
})
if pageMsgCount < imapPageSize {
pageStart := uint32(1)
pageEnd := imapPageSize
for {
if progress.shouldStop() || pageStart > count {
break
}
pageStart = pageEnd
pageEnd += imapPageSize
// Some servers do not accept message sequence number higher than the total count.
if pageEnd > count {
pageEnd = count
}
seqSet := &imap.SeqSet{}
seqSet.AddRange(pageStart, pageEnd)
err := p.fetch(rule.SourceMailbox.Name, seqSet, fetchItems, processMessageCallback)
if err != nil {
log.WithError(err).WithField("idx", seqSet).Warning("Load batch fetch failed, trying one by one")
for ; pageStart <= pageEnd; pageStart++ {
seqSet := &imap.SeqSet{}
seqSet.AddNum(pageStart)
if err := p.fetch(rule.SourceMailbox.Name, seqSet, fetchItems, processMessageCallback); err != nil {
log.WithError(err).WithField("idx", seqSet).Warning("Load fetch failed, skipping the message")
}
}
}
pageStart = pageEnd + 1
pageEnd += imapPageSize
}
return messagesInfo
}

View File

@ -0,0 +1,100 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package transfer
import (
"fmt"
"testing"
"github.com/emersion/go-imap"
gomock "github.com/golang/mock/gomock"
"github.com/pkg/errors"
r "github.com/stretchr/testify/require"
)
func newTestIMAPProvider(t *testing.T, m mocks) *IMAPProvider {
m.imapClientProvider.EXPECT().State().Return(imap.ConnectedState).AnyTimes()
m.imapClientProvider.EXPECT().Capability().Return(map[string]bool{
"AUTH": true,
}, nil).AnyTimes()
dialer := func(string) (IMAPClientProvider, error) {
return m.imapClientProvider, nil
}
provider, err := newIMAPProvider(dialer, "user", "pass", "host", "42")
r.NoError(t, err)
return provider
}
func TestProviderIMAPLoadMessagesInfo(t *testing.T) {
m := initMocks(t)
defer m.ctrl.Finish()
provider := newTestIMAPProvider(t, m)
progress := newProgress(log, nil)
drainProgressUpdateChannel(&progress)
rule := &Rule{SourceMailbox: Mailbox{Name: "Mailbox"}}
uidValidity := 1
count := 2200
failingIndex := 2100
m.imapClientProvider.EXPECT().Select(rule.SourceMailbox.Name, gomock.Any()).Return(&imap.MailboxStatus{}, nil).AnyTimes()
m.imapClientProvider.EXPECT().
Fetch(gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(seqSet *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error {
defer close(ch)
for _, seq := range seqSet.Set {
for i := seq.Start; i <= seq.Stop; i++ {
if int(i) == failingIndex {
return errors.New("internal server error")
}
ch <- &imap.Message{
SeqNum: i,
Uid: i * 10,
Size: i * 100,
}
}
}
return nil
}).
// 2200 messages is split into two batches (2000 and 200),
// the second one fails and makes 200 calls (one-by-one).
// Plus two failed requests are repeated `imapRetries` times.
Times(2 + 200 + (2 * (imapRetries - 1)))
messageInfo := provider.loadMessagesInfo(rule, &progress, uint32(uidValidity), uint32(count))
r.Equal(t, count-1, len(messageInfo)) // One message produces internal server error.
for index := 1; index <= count; index++ {
uid := index * 10
key := fmt.Sprintf("%s_%d:%d", rule.SourceMailbox.Name, uidValidity, uid)
if index == failingIndex {
r.Empty(t, messageInfo[key])
continue
}
r.Equal(t, imapMessageInfo{
id: key,
uid: uint32(uid),
size: uint32(index * 100),
}, messageInfo[key])
}
}

View File

@ -24,10 +24,11 @@ import (
"time"
imapID "github.com/ProtonMail/go-imap-id"
"github.com/ProtonMail/proton-bridge/pkg/constants"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/emersion/go-imap"
imapClient "github.com/emersion/go-imap/client"
sasl "github.com/emersion/go-sasl"
"github.com/emersion/go-sasl"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@ -51,6 +52,43 @@ func (l *imapErrorLogger) Println(v ...interface{}) {
l.log.Errorln(v...)
}
func imapClientDial(addr string) (IMAPClientProvider, error) {
if _, err := net.DialTimeout("tcp", addr, imapDialTimeout); err != nil {
return nil, errors.Wrap(err, "failed to dial server")
}
client, err := imapClientDialHelper(addr)
if err == nil {
client.ErrorLog = &imapErrorLogger{logrus.WithField("pkg", "imap-client")}
// Logrus `WriterLevel` fails for big messages because of bufio.MaxScanTokenSize limit.
// Also, this spams a lot, uncomment once needed during development.
//client.SetDebug(imap.NewDebugWriter(
// logrus.WithField("pkg", "imap/client").WriterLevel(logrus.TraceLevel),
// logrus.WithField("pkg", "imap/server").WriterLevel(logrus.TraceLevel),
//))
}
return client, err
}
func imapClientDialHelper(addr string) (*imapClient.Client, error) {
host, _, _ := net.SplitHostPort(addr)
if host == "127.0.0.1" {
return imapClient.Dial(addr)
}
// IMAP mail.yahoo.com has problem with golang TLS 1.3 implementation
// with weird behaviour, i.e., Yahoo does not return error during dial
// or handshake but server does logs out right after successful login
// leaving no time to perform any action.
// Limiting TLS to version 1.2 is working just fine.
var tlsConf *tls.Config
if strings.Contains(strings.ToLower(host), "yahoo") {
log.Warning("Yahoo server detected: limiting maximal TLS version to 1.2.")
tlsConf = &tls.Config{MaxVersion: tls.VersionTLS12}
}
return imapClient.DialTLS(addr, tlsConf)
}
func (p *IMAPProvider) ensureConnection(callback func() error) error {
return p.ensureConnectionAndSelection(callback, "")
}
@ -138,41 +176,10 @@ func (p *IMAPProvider) auth() error { //nolint[funlen]
log.Info("Connecting to server")
if _, err := net.DialTimeout("tcp", p.addr, imapDialTimeout); err != nil {
return ErrIMAPConnection{imapError{Err: err, Message: "failed to dial server"}}
}
var client *imapClient.Client
var err error
host, _, _ := net.SplitHostPort(p.addr)
if host == "127.0.0.1" {
client, err = imapClient.Dial(p.addr)
} else {
// IMAP.mail.yahoo.com have problem with golang TLS1.3
// implementation with weird behaviour i.e. Yahoo
// no error during dial or handshake but server logs out right
// after successful login leaving no time to perform any
// action. It was discovered that limiting to maximum TLS
// version 1.2 for yahoo servers is working solution.
var tlsConf *tls.Config
if strings.Contains(strings.ToLower(host), "yahoo") {
log.Warning("Yahoo server detected: limiting maximal TLS version to 1.2.")
tlsConf = &tls.Config{MaxVersion: tls.VersionTLS12}
}
client, err = imapClient.DialTLS(p.addr, tlsConf)
}
client, err := p.clientDialer(p.addr)
if err != nil {
return ErrIMAPConnection{imapError{Err: err, Message: "failed to connect to server"}}
}
client.ErrorLog = &imapErrorLogger{logrus.WithField("pkg", "imap-client")}
// Logrus `WriterLevel` fails for big messages because of bufio.MaxScanTokenSize limit.
// Also, this spams a lot, uncomment once needed during development.
//client.SetDebug(imap.NewDebugWriter(
// logrus.WithField("pkg", "imap/client").WriterLevel(logrus.TraceLevel),
// logrus.WithField("pkg", "imap/server").WriterLevel(logrus.TraceLevel),
//))
p.client = client
log.Info("Connected")
@ -210,14 +217,16 @@ func (p *IMAPProvider) auth() error { //nolint[funlen]
log.Info("Logged in")
idClient := imapID.NewClient(p.client)
if c, ok := p.client.(*imapClient.Client); ok {
idClient := imapID.NewClient(c)
if ok, err := idClient.SupportID(); err == nil && ok {
serverID, err := idClient.ID(imapID.ID{
imapID.FieldName: "ImportExport",
imapID.FieldVersion: "beta",
imapID.FieldVersion: constants.Version,
})
log.WithField("ID", serverID).WithError(err).Debug("Server info")
}
}
return err
}

View File

@ -114,7 +114,6 @@ func (p *MBOXProvider) transferTo(rules transferRules, progress *Progress, ch ch
}
index := 0
count := 0
for {
if progress.shouldStop() {
break
@ -133,24 +132,18 @@ func (p *MBOXProvider) transferTo(rules transferRules, progress *Progress, ch ch
msg, err := p.exportMessage(rules, folderName, id, msgReader)
progress.addMessage(id, msg.sourceNames(), msg.targetNames())
if err == nil && len(msg.Targets) == 0 {
// Here should be called progress.messageSkipped(id) once we have
// this feature, and following progress.updateCount can be removed.
progress.messageSkipped(id)
continue
}
count++
// addMessage is called after time check to not report message
// which should not be exported but any error from reading body
// or parsing time is reported as an error.
progress.addMessage(id, msg.sourceNames(), msg.targetNames())
progress.messageExported(id, msg.Body, err)
if err == nil {
ch <- msg
}
}
progress.updateCount(filePath, uint(count))
}
func (p *MBOXProvider) exportMessage(rules transferRules, folderName, id string, msgReader io.Reader) (Message, error) {
@ -177,7 +170,7 @@ func (p *MBOXProvider) getMessageRules(rules transferRules, folderName, id strin
folderRule, err := rules.getRuleBySourceMailboxName(folderName)
if err != nil {
log.WithField("msg", id).WithField("source", folderName).Debug("Message source doesn't have a rule")
} else {
} else if folderRule.Active {
msgRules = append(msgRules, folderRule)
}
@ -191,9 +184,11 @@ func (p *MBOXProvider) getMessageRules(rules transferRules, folderName, id strin
log.WithField("msg", id).WithField("source", label).Debug("Message source doesn't have a rule")
continue
}
if rule.Active {
msgRules = append(msgRules, rule)
}
}
}
return msgRules
}

View File

@ -155,6 +155,29 @@ func TestMBOXProviderTransferFromTo(t *testing.T) {
})
}
func TestMBOXProviderGetMessageRules(t *testing.T) {
provider := newTestMBOXProvider("")
body := []byte(`Subject: Test
X-Gmail-Labels: foo,bar
`)
rules := transferRules{
rules: map[string]*Rule{
"1": {Active: true, SourceMailbox: Mailbox{Name: "folder"}},
"2": {Active: false, SourceMailbox: Mailbox{Name: "foo"}},
"3": {Active: true, SourceMailbox: Mailbox{Name: "bar"}},
"4": {Active: false, SourceMailbox: Mailbox{Name: "baz"}},
"5": {Active: true, SourceMailbox: Mailbox{Name: "other"}},
},
}
gotRules := provider.getMessageRules(rules, "folder", "id", body)
r.Equal(t, 2, len(gotRules))
r.Equal(t, "folder", gotRules[0].SourceMailbox.Name)
r.Equal(t, "bar", gotRules[1].SourceMailbox.Name)
}
func TestMBOXProviderGetMessageTargetsReturnsOnlyOneFolder(t *testing.T) {
provider := newTestMBOXProvider("")

View File

@ -34,6 +34,7 @@ type mocks struct {
ctrl *gomock.Controller
panicHandler *transfermocks.MockPanicHandler
clientManager *transfermocks.MockClientManager
imapClientProvider *transfermocks.MockIMAPClientProvider
pmapiClient *pmapimocks.MockClient
pmapiConfig *pmapi.ClientConfig
@ -49,6 +50,7 @@ func initMocks(t *testing.T) mocks {
ctrl: mockCtrl,
panicHandler: transfermocks.NewMockPanicHandler(mockCtrl),
clientManager: transfermocks.NewMockClientManager(mockCtrl),
imapClientProvider: transfermocks.NewMockIMAPClientProvider(mockCtrl),
pmapiClient: pmapimocks.NewMockClient(mockCtrl),
pmapiConfig: &pmapi.ClientConfig{},
keyring: newTestKeyring(),

View File

@ -28,7 +28,7 @@ import (
"sort"
"strings"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322"
"github.com/ProtonMail/go-rfc5322"
"github.com/pkg/errors"
)

View File

@ -5,11 +5,12 @@
package mocks
import (
reflect "reflect"
store "github.com/ProtonMail/proton-bridge/internal/store"
credentials "github.com/ProtonMail/proton-bridge/internal/users/credentials"
pmapi "github.com/ProtonMail/proton-bridge/pkg/pmapi"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockConfiger is a mock of Configer interface

View File

@ -437,7 +437,7 @@ func (u *User) SwitchAddressMode() (err error) {
u.lock.Lock()
defer u.lock.Unlock()
u.closeAllConnections()
u.CloseAllConnections()
if u.store == nil {
err = errors.New("store is not initialised")
@ -509,7 +509,7 @@ func (u *User) Logout() (err error) {
// Do not close whole store, just event loop. Some information might be needed offline (e.g. addressID)
u.closeEventLoop()
u.closeAllConnections()
u.CloseAllConnections()
runtime.GC()
@ -532,8 +532,8 @@ func (u *User) closeEventLoop() {
u.store.CloseEventLoop()
}
// closeAllConnections calls CloseConnection for all users addresses.
func (u *User) closeAllConnections() {
// CloseAllConnections calls CloseConnection for all users addresses.
func (u *User) CloseAllConnections() {
for _, address := range u.creds.EmailList() {
u.CloseConnection(address)
}

View File

@ -186,7 +186,7 @@ func (u *Users) watchAPIAuths() {
func (u *Users) closeAllConnections() {
for _, user := range u.users {
user.closeAllConnections()
user.CloseAllConnections()
}
}

View File

@ -21,6 +21,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"github.com/ProtonMail/go-appdir"
"github.com/hashicorp/go-multierror"
@ -247,3 +248,17 @@ func (c *Config) GetDefaultIMAPPort() int {
func (c *Config) GetDefaultSMTPPort() int {
return 1025
}
// getAPIOS returns actual operating system.
func (c *Config) getAPIOS() string {
switch os := runtime.GOOS; os {
case "darwin": // nolint: goconst
return "macOS"
case "linux":
return "Linux"
case "windows":
return "Windows"
}
return "Linux"
}

View File

@ -29,7 +29,7 @@ import (
func (c *Config) GetAPIConfig() *pmapi.ClientConfig {
return &pmapi.ClientConfig{
AppVersion: strings.Title(c.appName) + "_" + c.version,
AppVersion: c.getAPIOS() + strings.Title(c.appName) + "_" + c.version,
ClientID: c.appName,
}
}

View File

@ -31,7 +31,7 @@ import (
func (c *Config) GetAPIConfig() *pmapi.ClientConfig {
return &pmapi.ClientConfig{
AppVersion: strings.Title(c.appName) + "_" + c.version,
AppVersion: c.getAPIOS() + strings.Title(c.appName) + "_" + c.version,
ClientID: c.appName,
Timeout: 25 * time.Minute, // Overall request timeout (~25MB / 25 mins => ~16kB/s, should be reasonable).
FirstReadTimeout: 30 * time.Second, // 30s to match 30s response header timeout.

View File

@ -15,6 +15,13 @@
// 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 rfc5322
package message
//go:generate make antlr
import (
"github.com/ProtonMail/go-rfc5322"
pmmime "github.com/ProtonMail/proton-bridge/pkg/mime"
)
func init() { // nolint[noinit]
rfc5322.CharsetReader = pmmime.CharsetReader
}

View File

@ -26,8 +26,8 @@ import (
"net/textproto"
"strings"
"github.com/ProtonMail/go-rfc5322"
"github.com/ProtonMail/proton-bridge/pkg/message/parser"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322"
pmmime "github.com/ProtonMail/proton-bridge/pkg/mime"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/emersion/go-message"

View File

@ -1,2 +0,0 @@
antlr: RFC5322Parser.g4 RFC5322Lexer.g4
antlr4 -Dlanguage=Go -o parser $^

View File

@ -1,160 +0,0 @@
# Outline
The `rfc5322` package implements a parser for `address-list` and `date-time` strings, as defined in RFC5322.
It also supports encoded words (RFC2047) and has international tokens (RFC6532).
# `rfc5322/parser` directory
The lexer and parser are generated using ANTLR4.
The grammar is defined in the g4 files:
- RFC5322Parser.g4 defines the parser grammar,
- RFC5322Lexer.g4 defines the lexer grammar.
These grammars are derived from the ABNF grammar provided in the RFCs above,
albeit with some relaxations added to support "nonstandard" (bad) input.
Running `antlr4` on these g4 files generates a parser which recognises strings conforming to the grammar:
- rfc5322_lexer.go
- rfc5322parser_base_listener.go
- rfc5322_parser.go
- rfc5322parser_listener.go
The generated parser can then be used to convert a valid address/date into an abstract syntax tree.
# `rfc5322` directory
Once we have an abstract syntax tree, we must turn it into something usable, namely a `mail.Address` or `time.Time`.
The generated code in the `rfc5322/parser` directory implements a walker.
This walker walks over the abstract syntax tree,
calling a callback when entering and another when when exiting each node.
By default, the callbacks are no-ops, unless they are overridden.
## `walker.go`
The `walker` type extends the base walker, overriding the default no-op callbacks
to do something specific when entering and exiting certain nodes.
The goal of the walker is to traverse the syntax tree, picking out relevant information from each node's text.
For example, when parsing a `mailbox` node, the relevant information to pick out from the parse tree is the
name and address of the mailbox. This information can appear in a number of different ways, e.g. it might be
RFC2047 word-encoded, it might be a string with escaped chars that need to be handled, it might have comments
that should be ignored, and so on.
So while walking the syntax tree, each node needs to ask its children what their "value" is.
The `mailbox` needs to ask its child nodes (either a `nameAddr` node or an `addrSpec` node)
what the name and address are.
If the child node is a `nameAddr`, it needs to ask its `displayName` child what the name is
and the `angleAddr` what the address is; these in turn ask `word` nodes, `addrSpec` nodes, etc.
Each child node is responsible for telling its parent what its own value is.
The parent is responsible for assembling the children into something useful.
Ideally, this would be done with the visitor pattern. But unfortunately, the generated parser only
provides a walker interface. So we need to make use of a stack, pushing on nodes when we enter them
and popping off nodes when we exit them, to turn the walker into a kind of visitor.
## `parser.go`
This file implements two methods,
`ParseAddressList(string) ([]*mail.Address, error)`
and
`ParseDateTime(string) (time.Time, error)`.
These methods set up a parser from the raw input, start the walker, and convert the walker result
into an object of the correct type.
# Example: Parsing `dateTime`
Parsing a date-time is rather simple. The implementation begins in `date_time.go`. The abridged code is below:
```
type dateTime struct {
year int
...
}
func (dt *dateTime) withYear(year *year) {
dt.year = year.value
}
...
func (w *walker) EnterDateTime(ctx *parser.DateTimeContext) {
w.enter(&dateTime{
loc: time.UTC,
})
}
func (w *walker) ExitDateTime(ctx *parser.DateTimeContext) {
dt := w.exit().(*dateTime)
w.res = time.Date(dt.year, ...)
}
```
As you can see, when the walker reaches a `dateTime` node, it pushes a `dateTime` object onto the stack:
```
w.enter(&dateTime{
loc: time.UTC,
})
```
and when it leaves a `dateTime` node, it pops it off the stack,
converting it from `interface{}` to the concrete type,
and uses the parsed `dateTime` values like day, month, year etc
to construct a go `time.Time` object to set the walker result:
```
dt := w.exit().(*dateTime)
w.res = time.Date(dt.year, ...)
```
These parsed values were discovered while the walker continued to walk across the date-time node.
Let's see how the walker discovers the `year`.
Here is the abridged code of what happens when the walker enters a `year` node:
```
type year struct {
value int
}
func (w *walker) EnterYear(ctx *parser.YearContext) {
var text string
for _, digit := range ctx.AllDigit() {
text += digit.GetText()
}
val, err := strconv.Atoi(text)
if err != nil {
w.err = err
}
w.enter(&year{
value: val,
})
}
```
When entering the `year` node, it collects all the raw digits, which are strings, then
converts them to an integer, and sets that as the year's integer value while pushing it onto the stack.
When exiting, it pops the year off the stack and gives itself to the parent (now on the top of the stack).
It doesn't know what type of object the parent is, it just checks to see if anything above it on the stack
is expecting a `year` node:
```
func (w *walker) ExitYear(ctx *parser.YearContext) {
type withYear interface {
withYear(*year)
}
res := w.exit().(*year)
if parent, ok := w.parent().(withYear); ok {
parent.withYear(res)
}
}
```
In our case, the `date` is expecting a `year` node because it implements `withYear`,
```
func (dt *dateTime) withYear(year *year) {
dt.year = year.value
}
```
and that is how the `dateTime` data members are collected.

View File

@ -1,98 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
lexer grammar RFC5322Lexer;
U_00: '\u0000';
U_01_08: '\u0001'..'\u0008';
TAB: '\t'; // \u0009
LF: '\n'; // \u000A
U_0B: '\u000B';
U_0C: '\u000C';
CR: '\r'; // \u000D
U_0E_1F: '\u000E'..'\u001F';
// Printable (0x20-0x7E)
SP: ' '; // \u0020
Exclamation: '!'; // \u0021
DQuote: '"'; // \u0022
Hash: '#'; // \u0023
Dollar: '$'; // \u0024
Percent: '%'; // \u0025
Ampersand: '&'; // \u0026
SQuote: '\''; // \u0027
LParens: '('; // \u0028
RParens: ')'; // \u0029
Asterisk: '*'; // \u002A
Plus: '+'; // \u002B
Comma: ','; // \u002C
Minus: '-'; // \u002D
Period: '.'; // \u002E
Slash: '/'; // \u002F
Digit: [0-9]; // \u0030 -- \u0039
Colon: ':'; // \u003A
Semicolon: ';'; // \u003B
Less: '<'; // \u003C
Equal: '='; // \u003D
Greater: '>'; // \u003E
Question: '?'; // \u003F
At: '@'; // \u0040
// alphaUpper
LBracket: '['; // \u005B
Backslash: '\\'; // \u005C
RBracket: ']'; // \u005D
Caret: '^'; // \u005E
Underscore: '_'; // \u005F
Backtick: '`'; // \u0060
// alphaLower
LCurly: '{'; // \u007B
Pipe: '|'; // \u007C
RCurly: '}'; // \u007D
Tilde: '~'; // \u007E
// Other
Delete: '\u007F';
// RFC6532 Extension
UTF8NonAscii: '\u0080'..'\uFFFF';
A: 'A'|'a';
B: 'B'|'b';
C: 'C'|'c';
D: 'D'|'d';
E: 'E'|'e';
F: 'F'|'f';
G: 'G'|'g';
H: 'H'|'h';
I: 'I'|'i';
J: 'J'|'j';
K: 'K'|'k';
L: 'L'|'l';
M: 'M'|'m';
N: 'N'|'n';
O: 'O'|'o';
P: 'P'|'p';
Q: 'Q'|'q';
R: 'R'|'r';
S: 'S'|'s';
T: 'T'|'t';
U: 'U'|'u';
V: 'V'|'v';
W: 'W'|'w';
X: 'X'|'x';
Y: 'Y'|'y';
Z: 'Z'|'z';

View File

@ -1,530 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
parser grammar RFC5322Parser;
options { tokenVocab=RFC5322Lexer; }
// -------------------
// 3.2. Lexical tokens
// -------------------
quotedChar: vchar | wsp;
quotedPair
: Backslash quotedChar
| obsQP
;
fws
: (wsp* crlf)? wsp+
| obsFWS
;
ctext
: alpha
| Exclamation
| DQuote
| Hash
| Dollar
| Percent
| Ampersand
| SQuote
| Asterisk
| Plus
| Comma
| Minus
| Period
| Slash
| Digit
| Colon
| Semicolon
| Less
| Equal
| Greater
| Question
| At
| LBracket
| RBracket
| Caret
| Underscore
| Backtick
| LCurly
| Pipe
| RCurly
| Tilde
| obsCtext
| UTF8NonAscii
;
ccontent
: ctext
| quotedPair
| comment
;
comment: LParens (fws? ccontent)* fws? RParens;
cfws
: (fws? comment)+ fws?
| fws
;
atext
: alpha
| Digit
| Exclamation
| Hash
| Dollar
| Percent
| Ampersand
| SQuote
| Asterisk
| Plus
| Minus
| Slash
| Equal
| Question
| Caret
| Underscore
| Backtick
| LCurly
| Pipe
| RCurly
| Tilde
| UTF8NonAscii
;
atom: atext+;
// Allow dotAtom to have a trailing period; some messages in the wild look like this.
dotAtom: atext+ (Period atext+)* Period?;
qtext
: alpha
| Exclamation
| Hash
| Dollar
| Percent
| Ampersand
| SQuote
| LParens
| RParens
| Asterisk
| Plus
| Comma
| Minus
| Period
| Slash
| Digit
| Colon
| Semicolon
| Less
| Equal
| Greater
| Question
| At
| LBracket
| RBracket
| Caret
| Underscore
| Backtick
| LCurly
| Pipe
| RCurly
| Tilde
| obsQtext
| UTF8NonAscii
;
quotedContent
: qtext
| quotedPair
;
quotedValue: (fws? quotedContent)*;
quotedString: DQuote quotedValue fws? DQuote;
// Allow word to consist of the @ token.
word
: cfws? encodedWord cfws?
| cfws? atom cfws?
| cfws? quotedString cfws?
| At
;
// --------------------------------
// 3.3. Date and Time Specification
// --------------------------------
dateTime: (dayOfweek Comma)? day month year hour Colon minute (Colon second)? zone? cfws? EOF;
dayOfweek
: fws? dayName
| cfws? dayName cfws?
;
dayName
: M O N
| T U E
| W E D
| T H U
| F R I
| S A T
| S U N
;
day
: fws? Digit Digit? fws
| cfws? Digit Digit? cfws?
;
month
: J A N
| F E B
| M A R
| A P R
| M A Y
| J U N
| J U L
| A U G
| S E P
| O C T
| N O V
| D E C
;
year
: fws Digit Digit Digit Digit fws
| cfws? Digit Digit cfws?
;
// NOTE: RFC5322 requires two digits for the hour, but we
// relax that requirement a bit, allowing single digits.
hour
: Digit? Digit
| cfws? Digit? Digit cfws?
;
minute
: Digit Digit
| cfws? Digit Digit cfws?
;
second
: Digit Digit
| cfws? Digit Digit cfws?
;
offset: (Plus | Minus)? Digit Digit Digit Digit;
zone
: fws offset
| obsZone
;
// --------------------------
// 3.4. Address Specification
// --------------------------
address
: mailbox
| group
;
mailbox
: nameAddr
| addrSpec
;
nameAddr: displayName? angleAddr;
angleAddr
: cfws? Less addrSpec? Greater cfws?
| obsAngleAddr
;
group: displayName Colon groupList? Semicolon cfws?;
displayName
: word+
| word (word | Period | cfws)*
;
mailboxList
: mailbox (Comma mailbox)*
| obsMboxList
;
addressList
: address (Comma address)* EOF
| obsAddrList EOF
;
groupList
: mailboxList
| cfws
| obsGroupList
;
// Allow addrSpec contain a port.
addrSpec: localPart At domain (Colon port)?;
localPart
: cfws? dotAtom cfws?
| cfws? quotedString cfws?
| obsLocalPart
;
port: Digit+;
domain
: cfws? dotAtom cfws?
| cfws? domainLiteral cfws?
| cfws? obsDomain cfws?
;
domainLiteral: LBracket (fws? dtext)* fws? RBracket;
dtext
: alpha
| Exclamation
| DQuote
| Hash
| Dollar
| Percent
| Ampersand
| SQuote
| LParens
| RParens
| Asterisk
| Plus
| Comma
| Minus
| Period
| Slash
| Digit
| Colon
| Semicolon
| Less
| Equal
| Greater
| Question
| At
| Caret
| Underscore
| Backtick
| LCurly
| Pipe
| RCurly
| Tilde
//| obsDtext
| UTF8NonAscii
;
// ----------------------------------
// 4.1. Miscellaneous Obsolete Tokens
// ----------------------------------
obsNoWSCTL
: U_01_08
| U_0B
| U_0C
| U_0E_1F
| Delete
;
obsCtext: obsNoWSCTL;
obsQtext: obsNoWSCTL;
obsQP: Backslash (U_00 | obsNoWSCTL | LF | CR);
// ---------------------------------
// 4.2. Obsolete Folding White Space
// ---------------------------------
obsFWS: wsp+ (crlf wsp+);
// ---------------------------
// 4.3. Obsolete Date and Time
// ---------------------------
obsZone
: U T
| U T C
| G M T
| E S T
| E D T
| C S T
| C D T
| M S T
| M D T
| P S T
| P D T
//| obsZoneMilitary
;
// ------------------------
// 4.4. Obsolete Addressing
// ------------------------
obsAngleAddr: cfws? Less obsRoute addrSpec Greater cfws?;
obsRoute: obsDomainList Colon;
obsDomainList: (cfws | Comma)* At domain (Comma cfws? (At domain)?)*;
obsMboxList: (cfws? Comma)* mailbox (Comma (mailbox | cfws)?)*;
obsAddrList: (cfws? Comma)* address (Comma (address | cfws)?)*;
obsGroupList: (cfws? Comma)+ cfws?;
obsLocalPart: word (Period word)*;
obsDomain: atom (Period atom)*;
// ------------------------------------
// 2. Syntax of encoded-words (RFC2047)
// ------------------------------------
encodedWord: Equal Question charset Question encoding Question encodedText Question Equal;
charset: token;
encoding: token;
token: tokenChar+;
tokenChar
: alpha
| Exclamation
| Hash
| Dollar
| Percent
| Ampersand
| SQuote
| Asterisk
| Plus
| Minus
| Digit
| Backslash
| Caret
| Underscore
| Backtick
| LCurly
| Pipe
| RCurly
| Tilde
;
encodedText: encodedChar+;
encodedChar
: alpha
| Exclamation
| DQuote
| Hash
| Dollar
| Percent
| Ampersand
| SQuote
| LParens
| RParens
| Asterisk
| Plus
| Comma
| Minus
| Period
| Slash
| Digit
| Colon
| Semicolon
| Less
| Equal
| Greater
| At
| LBracket
| Backslash
| RBracket
| Caret
| Underscore
| Backtick
| LCurly
| Pipe
| RCurly
| Tilde
;
// -------------------------
// B.1. Core Rules (RFC5234)
// -------------------------
crlf: CR LF;
wsp: SP | TAB;
vchar
: alpha
| Exclamation
| DQuote
| Hash
| Dollar
| Percent
| Ampersand
| SQuote
| LParens
| RParens
| Asterisk
| Plus
| Comma
| Minus
| Period
| Slash
| Digit
| Colon
| Semicolon
| Less
| Equal
| Greater
| Question
| At
| LBracket
| Backslash
| RBracket
| Caret
| Underscore
| Backtick
| LCurly
| Pipe
| RCurly
| Tilde
| UTF8NonAscii
;
alpha: A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z ;

View File

@ -1,58 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type addrSpec struct {
localPart, domain string
}
func (a *addrSpec) withLocalPart(localPart *localPart) {
a.localPart = localPart.value
}
func (a *addrSpec) withDomain(domain *domain) {
a.domain = domain.value
}
func (a *addrSpec) withPort(port *port) {
a.domain += ":" + port.value
}
func (w *walker) EnterAddrSpec(ctx *parser.AddrSpecContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering addrSpec")
w.enter(&addrSpec{})
}
func (w *walker) ExitAddrSpec(ctx *parser.AddrSpecContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting addrSpec")
type withAddrSpec interface {
withAddrSpec(*addrSpec)
}
res := w.exit().(*addrSpec)
if parent, ok := w.parent().(withAddrSpec); ok {
parent.withAddrSpec(res)
}
}

View File

@ -1,59 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"net/mail"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type address struct {
addresses []*mail.Address
}
func (a *address) withMailbox(mailbox *mailbox) {
a.addresses = append(a.addresses, &mail.Address{
Name: mailbox.name,
Address: mailbox.address,
})
}
func (a *address) withGroup(group *group) {
a.addresses = append(a.addresses, group.addresses...)
}
func (w *walker) EnterAddress(ctx *parser.AddressContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering address")
w.enter(&address{})
}
func (w *walker) ExitAddress(ctx *parser.AddressContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting address")
type withAddress interface {
withAddress(*address)
}
res := w.exit().(*address)
if parent, ok := w.parent().(withAddress); ok {
parent.withAddress(res)
}
}

View File

@ -1,43 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"net/mail"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type addressList struct {
addresses []*mail.Address
}
func (a *addressList) withAddress(address *address) {
a.addresses = append(a.addresses, address.addresses...)
}
func (w *walker) EnterAddressList(ctx *parser.AddressListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering addressList")
w.enter(&addressList{})
}
func (w *walker) ExitAddressList(ctx *parser.AddressListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting addressList")
w.res = w.exit().(*addressList).addresses
}

View File

@ -1,56 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"fmt"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type angleAddr struct {
address string
}
func (a *angleAddr) withAddrSpec(addrSpec *addrSpec) {
a.address = fmt.Sprintf("%v@%v", addrSpec.localPart, addrSpec.domain)
}
func (a *angleAddr) withObsAngleAddr(obsAngleAddr *obsAngleAddr) {
a.address = obsAngleAddr.address
}
func (w *walker) EnterAngleAddr(ctx *parser.AngleAddrContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering angleAddr")
w.enter(&angleAddr{})
}
func (w *walker) ExitAngleAddr(ctx *parser.AngleAddrContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting angleAddr")
type withAngleAddr interface {
withAngleAddr(*angleAddr)
}
res := w.exit().(*angleAddr)
if parent, ok := w.parent().(withAngleAddr); ok {
parent.withAngleAddr(res)
}
}

View File

@ -1,79 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type dateTime struct {
day int
month time.Month
year int
hour, min, sec int
loc *time.Location
}
func (dt *dateTime) withDay(day *day) {
dt.day = day.value
}
func (dt *dateTime) withMonth(month *month) {
dt.month = month.value
}
func (dt *dateTime) withYear(year *year) {
dt.year = year.value
}
func (dt *dateTime) withHour(hour *hour) {
dt.hour = hour.value
}
func (dt *dateTime) withMinute(minute *minute) {
dt.min = minute.value
}
func (dt *dateTime) withSecond(second *second) {
dt.sec = second.value
}
func (dt *dateTime) withZone(zone *zone) {
dt.loc = zone.location
}
func (w *walker) EnterDateTime(ctx *parser.DateTimeContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering dateTime")
w.enter(&dateTime{
loc: time.UTC,
})
}
func (w *walker) ExitDateTime(ctx *parser.DateTimeContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting dateTime")
dt := w.exit().(*dateTime)
w.res = time.Date(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec, 0, dt.loc)
}

View File

@ -1,62 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"strconv"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type day struct {
value int
}
func (w *walker) EnterDay(ctx *parser.DayContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering day")
var text string
for _, digit := range ctx.AllDigit() {
text += digit.GetText()
}
val, err := strconv.Atoi(text)
if err != nil {
w.err = err
}
w.enter(&day{
value: val,
})
}
func (w *walker) ExitDay(ctx *parser.DayContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting day")
type withDay interface {
withDay(*day)
}
res := w.exit().(*day)
if parent, ok := w.parent().(withDay); ok {
parent.withDay(res)
}
}

View File

@ -1,50 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type displayName struct {
words []string
}
func (n *displayName) withWord(word *word) {
n.words = append(n.words, word.value)
}
func (w *walker) EnterDisplayName(ctx *parser.DisplayNameContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering displayName")
w.enter(&displayName{})
}
func (w *walker) ExitDisplayName(ctx *parser.DisplayNameContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting displayName")
type withDisplayName interface {
withDisplayName(*displayName)
}
res := w.exit().(*displayName)
if parent, ok := w.parent().(withDisplayName); ok {
parent.withDisplayName(res)
}
}

View File

@ -1,60 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"strings"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type domain struct {
value string
}
func (d *domain) withDotAtom(dotAtom *dotAtom) {
d.value = dotAtom.value
}
func (d *domain) withDomainLiteral(domainLiteral *domainLiteral) {
d.value = domainLiteral.value
}
func (d *domain) withObsDomain(obsDomain *obsDomain) {
d.value = strings.Join(obsDomain.atoms, ".")
}
func (w *walker) EnterDomain(ctx *parser.DomainContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering domain")
w.enter(&domain{})
}
func (w *walker) ExitDomain(ctx *parser.DomainContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting domain")
type withDomain interface {
withDomain(*domain)
}
res := w.exit().(*domain)
if parent, ok := w.parent().(withDomain); ok {
parent.withDomain(res)
}
}

View File

@ -1,49 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type domainLiteral struct {
value string
}
func (w *walker) EnterDomainLiteral(ctx *parser.DomainLiteralContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering domainLiteral")
w.enter(&domainLiteral{
value: ctx.GetText(),
})
}
func (w *walker) ExitDomainLiteral(ctx *parser.DomainLiteralContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting domainLiteral")
type withDomainLiteral interface {
withDomainLiteral(*domainLiteral)
}
res := w.exit().(*domainLiteral)
if parent, ok := w.parent().(withDomainLiteral); ok {
parent.withDomainLiteral(res)
}
}

View File

@ -1,49 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type dotAtom struct {
value string
}
func (w *walker) EnterDotAtom(ctx *parser.DotAtomContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering dotAtom")
w.enter(&dotAtom{
value: ctx.GetText(),
})
}
func (w *walker) ExitDotAtom(ctx *parser.DotAtomContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting dotAtom")
type withDotAtom interface {
withDotAtom(*dotAtom)
}
res := w.exit().(*dotAtom)
if parent, ok := w.parent().(withDotAtom); ok {
parent.withDotAtom(res)
}
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
pmmime "github.com/ProtonMail/proton-bridge/pkg/mime"
"github.com/sirupsen/logrus"
)
type encodedWord struct {
value string
}
func (w *walker) EnterEncodedWord(ctx *parser.EncodedWordContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering encodedWord")
word, err := pmmime.WordDec.Decode(ctx.GetText())
if err != nil {
word = ctx.GetText()
}
w.enter(&encodedWord{
value: word,
})
}
func (w *walker) ExitEncodedWord(ctx *parser.EncodedWordContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting encodedWord")
type withEncodedWord interface {
withEncodedWord(*encodedWord)
}
res := w.exit().(*encodedWord)
if parent, ok := w.parent().(withEncodedWord); ok {
parent.withEncodedWord(res)
}
}

View File

@ -1,52 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"net/mail"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type group struct {
addresses []*mail.Address
}
func (g *group) withGroupList(groupList *groupList) {
g.addresses = append(g.addresses, groupList.addresses...)
}
func (w *walker) EnterGroup(ctx *parser.GroupContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering group")
w.enter(&group{})
}
func (w *walker) ExitGroup(ctx *parser.GroupContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting group")
type withGroup interface {
withGroup(*group)
}
res := w.exit().(*group)
if parent, ok := w.parent().(withGroup); ok {
parent.withGroup(res)
}
}

View File

@ -1,52 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"net/mail"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type groupList struct {
addresses []*mail.Address
}
func (gl *groupList) withMailboxList(mailboxList *mailboxList) {
gl.addresses = append(gl.addresses, mailboxList.addresses...)
}
func (w *walker) EnterGroupList(ctx *parser.GroupListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering groupList")
w.enter(&groupList{})
}
func (w *walker) ExitGroupList(ctx *parser.GroupListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting groupList")
type withGroupList interface {
withGroupList(*groupList)
}
res := w.exit().(*groupList)
if parent, ok := w.parent().(withGroupList); ok {
parent.withGroupList(res)
}
}

View File

@ -1,62 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"strconv"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type hour struct {
value int
}
func (w *walker) EnterHour(ctx *parser.HourContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering hour")
var text string
for _, digit := range ctx.AllDigit() {
text += digit.GetText()
}
val, err := strconv.Atoi(text)
if err != nil {
w.err = err
}
w.enter(&hour{
value: val,
})
}
func (w *walker) ExitHour(ctx *parser.HourContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting hour")
type withHour interface {
withHour(*hour)
}
res := w.exit().(*hour)
if parent, ok := w.parent().(withHour); ok {
parent.withHour(res)
}
}

View File

@ -1,60 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"strings"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type localPart struct {
value string
}
func (p *localPart) withDotAtom(dotAtom *dotAtom) {
p.value = dotAtom.value
}
func (p *localPart) withQuotedString(quotedString *quotedString) {
p.value = quotedString.value
}
func (p *localPart) withObsLocalPart(obsLocalPart *obsLocalPart) {
p.value = strings.Join(obsLocalPart.words, ".")
}
func (w *walker) EnterLocalPart(ctx *parser.LocalPartContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering localPart")
w.enter(&localPart{})
}
func (w *walker) ExitLocalPart(ctx *parser.LocalPartContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting localPart")
type withLocalPart interface {
withLocalPart(*localPart)
}
res := w.exit().(*localPart)
if parent, ok := w.parent().(withLocalPart); ok {
parent.withLocalPart(res)
}
}

View File

@ -1,57 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"fmt"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type mailbox struct {
name, address string
}
func (m *mailbox) withNameAddr(nameAddr *nameAddr) {
m.name = nameAddr.name
m.address = nameAddr.address
}
func (m *mailbox) withAddrSpec(addrSpec *addrSpec) {
m.address = fmt.Sprintf("%v@%v", addrSpec.localPart, addrSpec.domain)
}
func (w *walker) EnterMailbox(ctx *parser.MailboxContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering mailbox")
w.enter(&mailbox{})
}
func (w *walker) ExitMailbox(ctx *parser.MailboxContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting mailbox")
type withMailbox interface {
withMailbox(*mailbox)
}
res := w.exit().(*mailbox)
if parent, ok := w.parent().(withMailbox); ok {
parent.withMailbox(res)
}
}

View File

@ -1,59 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"net/mail"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type mailboxList struct {
addresses []*mail.Address
}
func (ml *mailboxList) withMailbox(mailbox *mailbox) {
ml.addresses = append(ml.addresses, &mail.Address{
Name: mailbox.name,
Address: mailbox.address,
})
}
func (ml *mailboxList) withObsMboxList(obsMboxList *obsMboxList) {
ml.addresses = append(ml.addresses, obsMboxList.addresses...)
}
func (w *walker) EnterMailboxList(ctx *parser.MailboxListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering mailboxList")
w.enter(&mailboxList{})
}
func (w *walker) ExitMailboxList(ctx *parser.MailboxListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting mailboxList")
type withMailboxList interface {
withMailboxList(*mailboxList)
}
res := w.exit().(*mailboxList)
if parent, ok := w.parent().(withMailboxList); ok {
parent.withMailboxList(res)
}
}

View File

@ -1,62 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"strconv"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type minute struct {
value int
}
func (w *walker) EnterMinute(ctx *parser.MinuteContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering minute")
var text string
for _, digit := range ctx.AllDigit() {
text += digit.GetText()
}
val, err := strconv.Atoi(text)
if err != nil {
w.err = err
}
w.enter(&minute{
value: val,
})
}
func (w *walker) ExitMinute(ctx *parser.MinuteContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting minute")
type withMinute interface {
withMinute(*minute)
}
res := w.exit().(*minute)
if parent, ok := w.parent().(withMinute); ok {
parent.withMinute(res)
}
}

View File

@ -1,84 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"errors"
"strings"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type month struct {
value time.Month
}
func (w *walker) EnterMonth(ctx *parser.MonthContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering month")
var m time.Month
switch strings.ToLower(ctx.GetText()) {
case "jan":
m = time.January
case "feb":
m = time.February
case "mar":
m = time.March
case "apr":
m = time.April
case "may":
m = time.May
case "jun":
m = time.June
case "jul":
m = time.July
case "aug":
m = time.August
case "sep":
m = time.September
case "oct":
m = time.October
case "nov":
m = time.November
case "dec":
m = time.December
default:
w.err = errors.New("no such month")
}
w.enter(&month{
value: m,
})
}
func (w *walker) ExitMonth(ctx *parser.MonthContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting month")
type withMonth interface {
withMonth(*month)
}
res := w.exit().(*month)
if parent, ok := w.parent().(withMonth); ok {
parent.withMonth(res)
}
}

View File

@ -1,56 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"strings"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type nameAddr struct {
name, address string
}
func (a *nameAddr) withDisplayName(displayName *displayName) {
a.name = strings.Join(displayName.words, " ")
}
func (a *nameAddr) withAngleAddr(angleAddr *angleAddr) {
a.address = angleAddr.address
}
func (w *walker) EnterNameAddr(ctx *parser.NameAddrContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering nameAddr")
w.enter(&nameAddr{})
}
func (w *walker) ExitNameAddr(ctx *parser.NameAddrContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting nameAddr")
type withNameAddr interface {
withNameAddr(*nameAddr)
}
res := w.exit().(*nameAddr)
if parent, ok := w.parent().(withNameAddr); ok {
parent.withNameAddr(res)
}
}

View File

@ -1,54 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"fmt"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
// When interpreting addresses, the route portion SHOULD be ignored.
type obsAngleAddr struct {
address string
}
func (a *obsAngleAddr) withAddrSpec(addrSpec *addrSpec) {
a.address = fmt.Sprintf("%v@%v", addrSpec.localPart, addrSpec.domain)
}
func (w *walker) EnterObsAngleAddr(ctx *parser.ObsAngleAddrContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering obsAngleAddr")
w.enter(&obsAngleAddr{})
}
func (w *walker) ExitObsAngleAddr(ctx *parser.ObsAngleAddrContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting obsAngleAddr")
type withObsAngleAddr interface {
withObsAngleAddr(*obsAngleAddr)
}
res := w.exit().(*obsAngleAddr)
if parent, ok := w.parent().(withObsAngleAddr); ok {
parent.withObsAngleAddr(res)
}
}

View File

@ -1,50 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type obsDomain struct {
atoms []string
}
func (p *obsDomain) withAtom(atom *atom) {
p.atoms = append(p.atoms, atom.value)
}
func (w *walker) EnterObsDomain(ctx *parser.ObsDomainContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering obsDomain")
w.enter(&obsDomain{})
}
func (w *walker) ExitObsDomain(ctx *parser.ObsDomainContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting obsDomain")
type withObsDomain interface {
withObsDomain(*obsDomain)
}
res := w.exit().(*obsDomain)
if parent, ok := w.parent().(withObsDomain); ok {
parent.withObsDomain(res)
}
}

View File

@ -1,50 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type obsLocalPart struct {
words []string
}
func (p *obsLocalPart) withWord(word *word) {
p.words = append(p.words, word.value)
}
func (w *walker) EnterObsLocalPart(ctx *parser.ObsLocalPartContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering obsLocalPart")
w.enter(&obsLocalPart{})
}
func (w *walker) ExitObsLocalPart(ctx *parser.ObsLocalPartContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting obsLocalPart")
type withObsLocalPart interface {
withObsLocalPart(*obsLocalPart)
}
res := w.exit().(*obsLocalPart)
if parent, ok := w.parent().(withObsLocalPart); ok {
parent.withObsLocalPart(res)
}
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"net/mail"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type obsMboxList struct {
addresses []*mail.Address
}
func (ml *obsMboxList) withMailbox(mailbox *mailbox) {
ml.addresses = append(ml.addresses, &mail.Address{
Name: mailbox.name,
Address: mailbox.address,
})
}
func (w *walker) EnterObsMboxList(ctx *parser.ObsMboxListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering obsMboxList")
w.enter(&obsMboxList{})
}
func (w *walker) ExitObsMboxList(ctx *parser.ObsMboxListContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting obsMboxList")
type withObsMboxList interface {
withObsMboxList(*obsMboxList)
}
res := w.exit().(*obsMboxList)
if parent, ok := w.parent().(withObsMboxList); ok {
parent.withObsMboxList(res)
}
}

View File

@ -1,82 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"errors"
"strings"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type obsZone struct {
location *time.Location
}
func (w *walker) EnterObsZone(ctx *parser.ObsZoneContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering obsZone")
loc := time.UTC
switch strings.ToLower(ctx.GetText()) {
case "ut":
loc = time.FixedZone(ctx.GetText(), 0)
case "utc":
loc = time.FixedZone(ctx.GetText(), 0)
case "gmt":
loc = time.FixedZone(ctx.GetText(), 0)
case "est":
loc = time.FixedZone(ctx.GetText(), -5*60*60)
case "edt":
loc = time.FixedZone(ctx.GetText(), -4*60*60)
case "cst":
loc = time.FixedZone(ctx.GetText(), -6*60*60)
case "cdt":
loc = time.FixedZone(ctx.GetText(), -5*60*60)
case "mst":
loc = time.FixedZone(ctx.GetText(), -7*60*60)
case "mdt":
loc = time.FixedZone(ctx.GetText(), -6*60*60)
case "pst":
loc = time.FixedZone(ctx.GetText(), -8*60*60)
case "pdt":
loc = time.FixedZone(ctx.GetText(), -7*60*60)
default:
w.err = errors.New("bad timezone")
}
w.enter(&obsZone{
location: loc,
})
}
func (w *walker) ExitObsZone(ctx *parser.ObsZoneContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting obsZone")
type withObsZone interface {
withObsZone(*obsZone)
}
res := w.exit().(*obsZone)
if parent, ok := w.parent().(withObsZone); ok {
parent.withObsZone(res)
}
}

View File

@ -1,73 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"fmt"
"strings"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type offset struct {
rep string
value int
}
func (w *walker) EnterOffset(ctx *parser.OffsetContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering offset")
text := ctx.GetText()
// NOTE: RFC5322 date-time should always begin with + or -
// but we relax that requirement a bit due to many messages
// in the wild that skip the +; we add the "+" if missing.
if !strings.HasPrefix(text, "+") && !strings.HasPrefix(text, "-") {
text = "+" + text
}
sgn := text[0:1]
hrs := text[1:3]
min := text[3:5]
dur, err := time.ParseDuration(fmt.Sprintf("%v%vh%vm", sgn, hrs, min))
if err != nil {
w.err = err
}
w.enter(&offset{
rep: text,
value: int(dur.Seconds()),
})
}
func (w *walker) ExitOffset(ctx *parser.OffsetContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting offset")
type withOffset interface {
withOffset(*offset)
}
res := w.exit().(*offset)
if parent, ok := w.parent().(withOffset); ok {
parent.withOffset(res)
}
}

View File

@ -1,701 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"encoding/xml"
"io"
"net/mail"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseSingleAddress(t *testing.T) {
tests := []struct {
input string
addrs []*mail.Address
}{
{
input: `user@example.com`,
addrs: []*mail.Address{{
Address: `user@example.com`,
}},
},
{
input: `John Doe <jdoe@machine.example>`,
addrs: []*mail.Address{{
Name: `John Doe`,
Address: `jdoe@machine.example`,
}},
},
{
input: `Mary Smith <mary@example.net>`,
addrs: []*mail.Address{{
Name: `Mary Smith`,
Address: `mary@example.net`,
}},
},
{
input: `"Joe Q. Public" <john.q.public@example.com>`,
addrs: []*mail.Address{{
Name: `Joe Q. Public`,
Address: `john.q.public@example.com`,
}},
},
{
input: `Mary Smith <mary@x.test>`,
addrs: []*mail.Address{{
Name: `Mary Smith`,
Address: `mary@x.test`,
}},
},
{
input: `jdoe@example.org`,
addrs: []*mail.Address{{
Address: `jdoe@example.org`,
}},
},
{
input: `Who? <one@y.test>`,
addrs: []*mail.Address{{
Name: `Who?`,
Address: `one@y.test`,
}},
},
{
input: `<boss@nil.test>`,
addrs: []*mail.Address{{
Address: `boss@nil.test`,
}},
},
{
input: `"Giant; \"Big\" Box" <sysservices@example.net>`,
addrs: []*mail.Address{{
Name: `Giant; "Big" Box`,
Address: `sysservices@example.net`,
}},
},
{
input: `Pete <pete@silly.example>`,
addrs: []*mail.Address{{
Name: `Pete`,
Address: `pete@silly.example`,
}},
},
{
input: `"Mary Smith: Personal Account" <smith@home.example>`,
addrs: []*mail.Address{{
Name: `Mary Smith: Personal Account`,
Address: `smith@home.example`,
}},
},
{
input: `Pete(A nice \) chap) <pete(his account)@silly.test(his host)>`,
addrs: []*mail.Address{{
Name: `Pete`,
Address: `pete@silly.test`,
}},
},
{
input: `Gogh Fir <gf@example.com>`,
addrs: []*mail.Address{{
Name: `Gogh Fir`,
Address: `gf@example.com`,
}},
},
{
input: `normal name <username@server.com>`,
addrs: []*mail.Address{{
Name: `normal name`,
Address: `username@server.com`,
}},
},
{
input: `"comma, name" <username@server.com>`,
addrs: []*mail.Address{{
Name: `comma, name`,
Address: `username@server.com`,
}},
},
{
input: `name <username@server.com> (ignore comment)`,
addrs: []*mail.Address{{
Name: `name`,
Address: `username@server.com`,
}},
},
{
input: `"Mail Robot" <>`,
addrs: []*mail.Address{{
Name: `Mail Robot`,
}},
},
{
input: `Michal Hořejšek <hořejšek@mail.com>`,
addrs: []*mail.Address{{
Name: `Michal Hořejšek`,
Address: `hořejšek@mail.com`, // Not his real address.
}},
},
{
input: `First Last <user@domain.com >`,
addrs: []*mail.Address{{
Name: `First Last`,
Address: `user@domain.com`,
}},
},
{
input: `First Last <user@domain.com. >`,
addrs: []*mail.Address{{
Name: `First Last`,
Address: `user@domain.com.`,
}},
},
{
input: `First Last <user@domain.com.>`,
addrs: []*mail.Address{{
Name: `First Last`,
Address: `user@domain.com.`,
}},
},
{
input: `First Last <user@domain.com:25>`,
addrs: []*mail.Address{{
Name: `First Last`,
Address: `user@domain.com:25`,
}},
},
{
input: `First Last <user@[10.0.0.1]>`,
addrs: []*mail.Address{{
Name: `First Last`,
Address: `user@[10.0.0.1]`,
}},
},
{
input: `<postmaster@[10.10.10.10]>`,
addrs: []*mail.Address{
{
Address: `postmaster@[10.10.10.10]`,
},
},
},
{
input: `user@domain <user@domain.com>`,
addrs: []*mail.Address{{
// Name: `user@domain`,
Name: `user @ domain`,
Address: `user@domain.com`,
}},
},
{
input: `First Last < user@domain.com>`,
addrs: []*mail.Address{{
Name: `First Last`,
Address: `user@domain.com`,
}},
},
{
input: `First Middle @ Last <user@domain.com>`,
addrs: []*mail.Address{{
Name: `First Middle @ Last`,
Address: `user@domain.com`,
}},
},
{
input: `user@domain.com,`,
addrs: []*mail.Address{
{
Address: `user@domain.com`,
},
},
},
{
input: `First Middle "Last" <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First Middle Last <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First Middle"Last" <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First Middle "Last"<user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First "Middle" "Last" <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First "Middle""Last" <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
addrs, err := ParseAddressList(test.input)
assert.NoError(t, err)
assert.ElementsMatch(t, test.addrs, addrs)
})
}
}
func TestParseSingleAddressEncodedWord(t *testing.T) {
tests := []struct {
input string
addrs []*mail.Address
}{
{
input: `=?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>`,
addrs: []*mail.Address{{
Name: `Keith Moore`,
Address: `moore@cs.utk.edu`,
}},
},
{
input: `=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>`,
addrs: []*mail.Address{{
Name: `Keld Jørn Simonsen`,
Address: `keld@dkuug.dk`,
}},
},
{
input: `=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`,
addrs: []*mail.Address{{
Name: `André Pirard`,
Address: `PIRARD@vm1.ulg.ac.be`,
}},
},
{
input: `=?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>`,
addrs: []*mail.Address{{
Name: `Olle Järnefors`,
Address: `ojarnef@admin.kth.se`,
}},
},
{
input: `=?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>`,
addrs: []*mail.Address{{
Name: `Patrik Fältström`,
Address: `paf@nada.kth.se`,
}},
},
{
input: `Nathaniel Borenstein <nsb@thumper.bellcore.com> (=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=)`,
addrs: []*mail.Address{{
Name: `Nathaniel Borenstein`,
Address: `nsb@thumper.bellcore.com`,
}},
},
{
input: `=?UTF-8?B?PEJlemUgam3DqW5hPg==?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `<Beze jména>`,
Address: `user@domain.com`,
},
},
},
{
input: `First Middle =?utf-8?Q?Last?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
/*
{
input: `First Middle=?utf-8?Q?Last?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
*/
{
input: `First Middle =?utf-8?Q?Last?=<user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First =?utf-8?Q?Middle?= =?utf-8?Q?Last?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First =?utf-8?Q?Middle?==?utf-8?Q?Last?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First "Middle"=?utf-8?Q?Last?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First "Middle" =?utf-8?Q?Last?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `First "Middle" =?utf-8?Q?Last?=<user@domain.com>`,
addrs: []*mail.Address{
{
Name: `First Middle Last`,
Address: `user@domain.com`,
},
},
},
{
input: `=?UTF-8?B?PEJlemUgam3DqW5hPg==?= <user@domain.com>`,
addrs: []*mail.Address{
{
Name: `<Beze jména>`,
Address: `user@domain.com`,
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
addrs, err := ParseAddressList(test.input)
assert.NoError(t, err)
assert.ElementsMatch(t, test.addrs, addrs)
})
}
}
func TestParseAddressList(t *testing.T) {
tests := []struct {
input string
addrs []*mail.Address
}{
{
input: `Alice <alice@example.com>, Bob <bob@example.com>, Eve <eve@example.com>`,
addrs: []*mail.Address{
{
Name: `Alice`,
Address: `alice@example.com`,
},
{
Name: `Bob`,
Address: `bob@example.com`,
},
{
Name: `Eve`,
Address: `eve@example.com`,
},
},
},
{
input: `Ed Jones <c@a.test>,joe@where.test,John <jdoe@one.test>`,
addrs: []*mail.Address{
{
Name: `Ed Jones`,
Address: `c@a.test`,
},
{
Address: `joe@where.test`,
},
{
Name: `John`,
Address: `jdoe@one.test`,
},
},
},
{
input: `name (ignore comment) <username@server.com>, (Comment as name) username2@server.com`,
addrs: []*mail.Address{
{
Name: `name`,
Address: `username@server.com`,
},
{
Address: `username2@server.com`,
},
},
},
{
input: `"normal name" <username@server.com>, "comma, name" <address@server.com>`,
addrs: []*mail.Address{
{
Name: `normal name`,
Address: `username@server.com`,
},
{
Name: `comma, name`,
Address: `address@server.com`,
},
},
},
{
input: `"comma, one" <username@server.com>, "comma, two" <address@server.com>`,
addrs: []*mail.Address{
{
Name: `comma, one`,
Address: `username@server.com`,
},
{
Name: `comma, two`,
Address: `address@server.com`,
},
},
},
{
input: `normal name <username@server.com>, (comment)All.(around)address@(the)server.com`,
addrs: []*mail.Address{
{
Name: `normal name`,
Address: `username@server.com`,
},
{
Address: `All.address@server.com`,
},
},
},
{
input: `normal name <username@server.com>, All.("comma, in comment")address@(the)server.com`,
addrs: []*mail.Address{
{
Name: `normal name`,
Address: `username@server.com`,
},
{
Address: `All.address@server.com`,
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
addrs, err := ParseAddressList(test.input)
assert.NoError(t, err)
assert.ElementsMatch(t, test.addrs, addrs)
})
}
}
func TestParseGroup(t *testing.T) {
tests := []struct {
input string
addrs []*mail.Address
}{
{
input: `A Group:Ed Jones <c@a.test>,joe@where.test,John <jdoe@one.test>;`,
addrs: []*mail.Address{
{
Name: `Ed Jones`,
Address: `c@a.test`,
},
{
Address: `joe@where.test`,
},
{
Name: `John`,
Address: `jdoe@one.test`,
},
},
},
{
input: `Undisclosed recipients:;`,
addrs: []*mail.Address{},
},
{
input: `(Empty list)(start)Hidden recipients :(nobody(that I know)) ;`,
addrs: []*mail.Address{},
},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
addrs, err := ParseAddressList(test.input)
assert.NoError(t, err)
assert.ElementsMatch(t, test.addrs, addrs)
})
}
}
// TestParseRejectedAddresses tests that weird addresses that are rejected by
// serverside are also rejected by us. If for some reason we end up being able
// to parse these malformed addresses, great! For now let's collect them here.
func TestParseRejectedAddresses(t *testing.T) {
tests := []struct {
input string
addrs []*mail.Address
}{
{input: `"comma, name" <username@server.com>, another, name <address@server.com>`},
{input: `username`},
{input: `undisclosed-recipients:`},
{input: `=?ISO-8859-2?Q?First_Last?= <user@domain.com>, <user@domain.com,First/AAA/BBB/CCC,>`},
{input: `user@domain...com`},
{input: `=?windows-1250?Q?Spr=E1vce_syst=E9mu?=`},
{input: `"'user@domain.com.'"`},
{input: `<this is not an email address>`},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
_, err := ParseAddressList(test.input)
assert.Error(t, err)
})
}
}
// TestIsEmailValidCategory runs over the "IsEmail" standard tests,
// ensuring it can at least recognize all emails in the "valid" category.
// In future, we should expand these tests to run over more categories.
func TestIsEmailValidCategory(t *testing.T) {
f, err := os.Open("tests.xml")
require.NoError(t, err)
defer func() { require.NoError(t, err) }()
for test := range readTestCases(f) {
test := test
if test.category != "ISEMAIL_VALID_CATEGORY" {
continue
}
t.Run(test.id, func(t *testing.T) {
_, err := ParseAddressList(test.address)
assert.NoError(t, err)
})
}
}
type testCase struct {
id string
address string
category string
diagnosis string
}
func readTestCases(r io.Reader) chan testCase {
ch := make(chan testCase)
var (
test testCase
data string
)
go func() {
decoder := xml.NewDecoder(r)
for token, err := decoder.Token(); err == nil; token, err = decoder.Token() {
switch t := token.(type) {
case xml.StartElement:
if t.Name.Local == "test" {
test = testCase{
id: t.Attr[0].Value,
}
}
case xml.EndElement:
switch t.Name.Local {
case "test":
ch <- test
case "address":
test.address = data
case "category":
test.category = data
case "diagnosis":
test.diagnosis = data
}
case xml.CharData:
data = string(t)
}
}
close(ch)
}()
return ch
}

View File

@ -1,248 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestParseDateTime(t *testing.T) {
tests := []struct {
input string
want string
}{
{
input: `Fri, 21 Nov 1997 09:55:06`,
want: `1997-11-21T09:55:06Z`,
},
{
input: `Fri, 21 Nov 1997 09:55:06 -0600`,
want: `1997-11-21T09:55:06-06:00`,
},
{
input: `Tue, 1 Jul 2003 10:52:37 +0200`,
want: `2003-07-01T10:52:37+02:00`,
},
{
input: `Thu, 13 Feb 1969 23:32:54 -0330`,
want: `1969-02-13T23:32:54-03:30`,
},
{
input: "Thu, 13 Feb 1969 23:32 -0330 (Newfoundland Time)",
want: `1969-02-13T23:32:00-03:30`,
},
{
input: `2 Jan 2006 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `2 Jan 2006 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `2 Jan 2006 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `2 Jan 2006 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `2 Jan 06 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `2 Jan 06 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `2 Jan 06 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `2 Jan 06 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `02 Jan 2006 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `02 Jan 2006 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `02 Jan 2006 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `02 Jan 2006 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `02 Jan 06 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `02 Jan 06 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `02 Jan 06 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `02 Jan 06 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 2 Jan 2006 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 2 Jan 2006 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 2 Jan 2006 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 2 Jan 2006 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 2 Jan 06 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 2 Jan 06 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 2 Jan 06 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 2 Jan 06 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 02 Jan 2006 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 02 Jan 2006 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 02 Jan 2006 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 02 Jan 2006 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 02 Jan 06 15:04:05 -0700`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 02 Jan 06 15:04:05 MST`,
want: `2006-01-02T15:04:05-07:00`,
},
{
input: `Mon, 02 Jan 06 15:04 -0700`,
want: `2006-01-02T15:04:00-07:00`,
},
{
input: `Mon, 02 Jan 06 15:04 MST`,
want: `2006-01-02T15:04:00-07:00`,
},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
got, err := ParseDateTime(test.input)
assert.NoError(t, err)
assert.Equal(t, test.want, got.Format(time.RFC3339))
})
}
}
func TestParseDateTimeObsolete(t *testing.T) {
tests := []struct {
input string
want string
}{
{
input: `21 Nov 97 09:55:06 GMT`,
want: `1997-11-21T09:55:06Z`,
},
{
input: `Wed, 01 Jan 2020 12:00:00 UTC`,
want: `2020-01-01T12:00:00Z`,
},
{
input: `Wed, 01 Jan 2020 13:00:00 UTC`,
want: `2020-01-01T13:00:00Z`,
},
{
input: `Wed, 01 Jan 2020 12:30:00 UTC`,
want: `2020-01-01T12:30:00Z`,
},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
got, err := ParseDateTime(test.input)
assert.NoError(t, err)
assert.Equal(t, test.want, got.Format(time.RFC3339))
})
}
}
func TestParseDateTimeRelaxed(t *testing.T) {
tests := []struct {
input string
want string
}{
{
input: `Mon, 28 Jan 2019 20:59:01 0000`,
want: `2019-01-28T20:59:01Z`,
},
{
input: `Mon, 25 Sep 2017 5:25:40 +0200`,
want: `2017-09-25T05:25:40+02:00`,
},
}
for _, test := range tests {
test := test
t.Run(test.input, func(t *testing.T) {
got, err := ParseDateTime(test.input)
assert.NoError(t, err)
assert.Equal(t, test.want, got.Format(time.RFC3339))
})
}
}

View File

@ -1,83 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"net/mail"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/antlr/antlr4/runtime/Go/antlr"
"github.com/sirupsen/logrus"
)
// ParseAddressList parses one or more valid RFC5322 (with RFC2047) addresses.
func ParseAddressList(input string) ([]*mail.Address, error) {
if len(input) == 0 {
return []*mail.Address{}, nil
}
l := parser.NewRFC5322Lexer(antlr.NewInputStream(input))
p := parser.NewRFC5322Parser(antlr.NewCommonTokenStream(l, antlr.TokenDefaultChannel))
w := &walker{}
p.AddErrorListener(w)
p.AddParseListener(&parseListener{rules: p.GetRuleNames()})
antlr.ParseTreeWalkerDefault.Walk(w, p.AddressList())
return w.res.([]*mail.Address), w.err
}
// ParseDateTime parses a valid RFC5322 date-time.
func ParseDateTime(input string) (time.Time, error) {
if len(input) == 0 {
return time.Time{}, nil
}
l := parser.NewRFC5322Lexer(antlr.NewInputStream(input))
p := parser.NewRFC5322Parser(antlr.NewCommonTokenStream(l, antlr.TokenDefaultChannel))
w := &walker{}
p.AddErrorListener(w)
p.AddParseListener(&parseListener{rules: p.GetRuleNames()})
antlr.ParseTreeWalkerDefault.Walk(w, p.DateTime())
return w.res.(time.Time), w.err
}
type parseListener struct {
antlr.BaseParseTreeListener
rules []string
}
func (l *parseListener) EnterEveryRule(ctx antlr.ParserRuleContext) {
logrus.
WithField("rule", l.rules[ctx.GetRuleIndex()]).
WithField("text", ctx.GetText()).
Trace("Entering rule")
}
func (l *parseListener) ExitEveryRule(ctx antlr.ParserRuleContext) {
logrus.
WithField("rule", l.rules[ctx.GetRuleIndex()]).
WithField("text", ctx.GetText()).
Trace("Exiting rule")
}

File diff suppressed because one or more lines are too long

View File

@ -1,110 +0,0 @@
U_00=1
U_01_08=2
TAB=3
LF=4
U_0B=5
U_0C=6
CR=7
U_0E_1F=8
SP=9
Exclamation=10
DQuote=11
Hash=12
Dollar=13
Percent=14
Ampersand=15
SQuote=16
LParens=17
RParens=18
Asterisk=19
Plus=20
Comma=21
Minus=22
Period=23
Slash=24
Digit=25
Colon=26
Semicolon=27
Less=28
Equal=29
Greater=30
Question=31
At=32
LBracket=33
Backslash=34
RBracket=35
Caret=36
Underscore=37
Backtick=38
LCurly=39
Pipe=40
RCurly=41
Tilde=42
Delete=43
UTF8NonAscii=44
A=45
B=46
C=47
D=48
E=49
F=50
G=51
H=52
I=53
J=54
K=55
L=56
M=57
N=58
O=59
P=60
Q=61
R=62
S=63
T=64
U=65
V=66
W=67
X=68
Y=69
Z=70
'\u0000'=1
'\t'=3
'\n'=4
'\u000B'=5
'\u000C'=6
'\r'=7
' '=9
'!'=10
'"'=11
'#'=12
'$'=13
'%'=14
'&'=15
'\''=16
'('=17
')'=18
'*'=19
'+'=20
','=21
'-'=22
'.'=23
'/'=24
':'=26
';'=27
'<'=28
'='=29
'>'=30
'?'=31
'@'=32
'['=33
'\\'=34
']'=35
'^'=36
'_'=37
'`'=38
'{'=39
'|'=40
'}'=41
'~'=42
'\u007F'=43

File diff suppressed because one or more lines are too long

View File

@ -1,110 +0,0 @@
U_00=1
U_01_08=2
TAB=3
LF=4
U_0B=5
U_0C=6
CR=7
U_0E_1F=8
SP=9
Exclamation=10
DQuote=11
Hash=12
Dollar=13
Percent=14
Ampersand=15
SQuote=16
LParens=17
RParens=18
Asterisk=19
Plus=20
Comma=21
Minus=22
Period=23
Slash=24
Digit=25
Colon=26
Semicolon=27
Less=28
Equal=29
Greater=30
Question=31
At=32
LBracket=33
Backslash=34
RBracket=35
Caret=36
Underscore=37
Backtick=38
LCurly=39
Pipe=40
RCurly=41
Tilde=42
Delete=43
UTF8NonAscii=44
A=45
B=46
C=47
D=48
E=49
F=50
G=51
H=52
I=53
J=54
K=55
L=56
M=57
N=58
O=59
P=60
Q=61
R=62
S=63
T=64
U=65
V=66
W=67
X=68
Y=69
Z=70
'\u0000'=1
'\t'=3
'\n'=4
'\u000B'=5
'\u000C'=6
'\r'=7
' '=9
'!'=10
'"'=11
'#'=12
'$'=13
'%'=14
'&'=15
'\''=16
'('=17
')'=18
'*'=19
'+'=20
','=21
'-'=22
'.'=23
'/'=24
':'=26
';'=27
'<'=28
'='=29
'>'=30
'?'=31
'@'=32
'['=33
'\\'=34
']'=35
'^'=36
'_'=37
'`'=38
'{'=39
'|'=40
'}'=41
'~'=42
'\u007F'=43

View File

@ -1,309 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated from RFC5322Lexer.g4 by ANTLR 4.8. DO NOT EDIT.
package parser
import (
"fmt"
"unicode"
"github.com/antlr/antlr4/runtime/Go/antlr"
)
// Suppress unused import error
var _ = fmt.Printf
var _ = unicode.IsLetter
var serializedLexerAtn = []uint16{
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 72, 283,
8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7,
9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12,
4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4,
18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23,
9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9,
28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33,
4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4,
39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44,
9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9,
49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54,
4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4,
60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65,
9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9,
70, 4, 71, 9, 71, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6,
3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3,
12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17,
3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3,
22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27,
3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 32, 3, 32, 3,
33, 3, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 37, 3, 37, 3, 38,
3, 38, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3,
43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 48, 3, 48,
3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 52, 3, 52, 3, 53, 3, 53, 3,
54, 3, 54, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59,
3, 59, 3, 60, 3, 60, 3, 61, 3, 61, 3, 62, 3, 62, 3, 63, 3, 63, 3, 64, 3,
64, 3, 65, 3, 65, 3, 66, 3, 66, 3, 67, 3, 67, 3, 68, 3, 68, 3, 69, 3, 69,
3, 70, 3, 70, 3, 71, 3, 71, 2, 2, 72, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13,
8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17,
33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47, 25, 49, 26,
51, 27, 53, 28, 55, 29, 57, 30, 59, 31, 61, 32, 63, 33, 65, 34, 67, 35,
69, 36, 71, 37, 73, 38, 75, 39, 77, 40, 79, 41, 81, 42, 83, 43, 85, 44,
87, 45, 89, 46, 91, 47, 93, 48, 95, 49, 97, 50, 99, 51, 101, 52, 103, 53,
105, 54, 107, 55, 109, 56, 111, 57, 113, 58, 115, 59, 117, 60, 119, 61,
121, 62, 123, 63, 125, 64, 127, 65, 129, 66, 131, 67, 133, 68, 135, 69,
137, 70, 139, 71, 141, 72, 3, 2, 29, 3, 2, 50, 59, 4, 2, 67, 67, 99, 99,
4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102,
4, 2, 71, 71, 103, 103, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105,
4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108,
4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111,
4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114,
4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117,
4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120,
4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123,
4, 2, 92, 92, 124, 124, 2, 282, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2,
7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2,
2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2,
2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2,
2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3,
2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45,
3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, 2, 2, 2, 51, 3, 2, 2, 2, 2,
53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, 3, 2, 2, 2, 2, 59, 3, 2, 2, 2,
2, 61, 3, 2, 2, 2, 2, 63, 3, 2, 2, 2, 2, 65, 3, 2, 2, 2, 2, 67, 3, 2, 2,
2, 2, 69, 3, 2, 2, 2, 2, 71, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2,
2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3,
2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91,
3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2,
99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2,
2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113,
3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2,
2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3,
2, 2, 2, 2, 129, 3, 2, 2, 2, 2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2,
135, 3, 2, 2, 2, 2, 137, 3, 2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2,
2, 2, 3, 143, 3, 2, 2, 2, 5, 145, 3, 2, 2, 2, 7, 147, 3, 2, 2, 2, 9, 149,
3, 2, 2, 2, 11, 151, 3, 2, 2, 2, 13, 153, 3, 2, 2, 2, 15, 155, 3, 2, 2,
2, 17, 157, 3, 2, 2, 2, 19, 159, 3, 2, 2, 2, 21, 161, 3, 2, 2, 2, 23, 163,
3, 2, 2, 2, 25, 165, 3, 2, 2, 2, 27, 167, 3, 2, 2, 2, 29, 169, 3, 2, 2,
2, 31, 171, 3, 2, 2, 2, 33, 173, 3, 2, 2, 2, 35, 175, 3, 2, 2, 2, 37, 177,
3, 2, 2, 2, 39, 179, 3, 2, 2, 2, 41, 181, 3, 2, 2, 2, 43, 183, 3, 2, 2,
2, 45, 185, 3, 2, 2, 2, 47, 187, 3, 2, 2, 2, 49, 189, 3, 2, 2, 2, 51, 191,
3, 2, 2, 2, 53, 193, 3, 2, 2, 2, 55, 195, 3, 2, 2, 2, 57, 197, 3, 2, 2,
2, 59, 199, 3, 2, 2, 2, 61, 201, 3, 2, 2, 2, 63, 203, 3, 2, 2, 2, 65, 205,
3, 2, 2, 2, 67, 207, 3, 2, 2, 2, 69, 209, 3, 2, 2, 2, 71, 211, 3, 2, 2,
2, 73, 213, 3, 2, 2, 2, 75, 215, 3, 2, 2, 2, 77, 217, 3, 2, 2, 2, 79, 219,
3, 2, 2, 2, 81, 221, 3, 2, 2, 2, 83, 223, 3, 2, 2, 2, 85, 225, 3, 2, 2,
2, 87, 227, 3, 2, 2, 2, 89, 229, 3, 2, 2, 2, 91, 231, 3, 2, 2, 2, 93, 233,
3, 2, 2, 2, 95, 235, 3, 2, 2, 2, 97, 237, 3, 2, 2, 2, 99, 239, 3, 2, 2,
2, 101, 241, 3, 2, 2, 2, 103, 243, 3, 2, 2, 2, 105, 245, 3, 2, 2, 2, 107,
247, 3, 2, 2, 2, 109, 249, 3, 2, 2, 2, 111, 251, 3, 2, 2, 2, 113, 253,
3, 2, 2, 2, 115, 255, 3, 2, 2, 2, 117, 257, 3, 2, 2, 2, 119, 259, 3, 2,
2, 2, 121, 261, 3, 2, 2, 2, 123, 263, 3, 2, 2, 2, 125, 265, 3, 2, 2, 2,
127, 267, 3, 2, 2, 2, 129, 269, 3, 2, 2, 2, 131, 271, 3, 2, 2, 2, 133,
273, 3, 2, 2, 2, 135, 275, 3, 2, 2, 2, 137, 277, 3, 2, 2, 2, 139, 279,
3, 2, 2, 2, 141, 281, 3, 2, 2, 2, 143, 144, 7, 2, 2, 2, 144, 4, 3, 2, 2,
2, 145, 146, 4, 3, 10, 2, 146, 6, 3, 2, 2, 2, 147, 148, 7, 11, 2, 2, 148,
8, 3, 2, 2, 2, 149, 150, 7, 12, 2, 2, 150, 10, 3, 2, 2, 2, 151, 152, 7,
13, 2, 2, 152, 12, 3, 2, 2, 2, 153, 154, 7, 14, 2, 2, 154, 14, 3, 2, 2,
2, 155, 156, 7, 15, 2, 2, 156, 16, 3, 2, 2, 2, 157, 158, 4, 16, 33, 2,
158, 18, 3, 2, 2, 2, 159, 160, 7, 34, 2, 2, 160, 20, 3, 2, 2, 2, 161, 162,
7, 35, 2, 2, 162, 22, 3, 2, 2, 2, 163, 164, 7, 36, 2, 2, 164, 24, 3, 2,
2, 2, 165, 166, 7, 37, 2, 2, 166, 26, 3, 2, 2, 2, 167, 168, 7, 38, 2, 2,
168, 28, 3, 2, 2, 2, 169, 170, 7, 39, 2, 2, 170, 30, 3, 2, 2, 2, 171, 172,
7, 40, 2, 2, 172, 32, 3, 2, 2, 2, 173, 174, 7, 41, 2, 2, 174, 34, 3, 2,
2, 2, 175, 176, 7, 42, 2, 2, 176, 36, 3, 2, 2, 2, 177, 178, 7, 43, 2, 2,
178, 38, 3, 2, 2, 2, 179, 180, 7, 44, 2, 2, 180, 40, 3, 2, 2, 2, 181, 182,
7, 45, 2, 2, 182, 42, 3, 2, 2, 2, 183, 184, 7, 46, 2, 2, 184, 44, 3, 2,
2, 2, 185, 186, 7, 47, 2, 2, 186, 46, 3, 2, 2, 2, 187, 188, 7, 48, 2, 2,
188, 48, 3, 2, 2, 2, 189, 190, 7, 49, 2, 2, 190, 50, 3, 2, 2, 2, 191, 192,
9, 2, 2, 2, 192, 52, 3, 2, 2, 2, 193, 194, 7, 60, 2, 2, 194, 54, 3, 2,
2, 2, 195, 196, 7, 61, 2, 2, 196, 56, 3, 2, 2, 2, 197, 198, 7, 62, 2, 2,
198, 58, 3, 2, 2, 2, 199, 200, 7, 63, 2, 2, 200, 60, 3, 2, 2, 2, 201, 202,
7, 64, 2, 2, 202, 62, 3, 2, 2, 2, 203, 204, 7, 65, 2, 2, 204, 64, 3, 2,
2, 2, 205, 206, 7, 66, 2, 2, 206, 66, 3, 2, 2, 2, 207, 208, 7, 93, 2, 2,
208, 68, 3, 2, 2, 2, 209, 210, 7, 94, 2, 2, 210, 70, 3, 2, 2, 2, 211, 212,
7, 95, 2, 2, 212, 72, 3, 2, 2, 2, 213, 214, 7, 96, 2, 2, 214, 74, 3, 2,
2, 2, 215, 216, 7, 97, 2, 2, 216, 76, 3, 2, 2, 2, 217, 218, 7, 98, 2, 2,
218, 78, 3, 2, 2, 2, 219, 220, 7, 125, 2, 2, 220, 80, 3, 2, 2, 2, 221,
222, 7, 126, 2, 2, 222, 82, 3, 2, 2, 2, 223, 224, 7, 127, 2, 2, 224, 84,
3, 2, 2, 2, 225, 226, 7, 128, 2, 2, 226, 86, 3, 2, 2, 2, 227, 228, 7, 129,
2, 2, 228, 88, 3, 2, 2, 2, 229, 230, 4, 130, 1, 2, 230, 90, 3, 2, 2, 2,
231, 232, 9, 3, 2, 2, 232, 92, 3, 2, 2, 2, 233, 234, 9, 4, 2, 2, 234, 94,
3, 2, 2, 2, 235, 236, 9, 5, 2, 2, 236, 96, 3, 2, 2, 2, 237, 238, 9, 6,
2, 2, 238, 98, 3, 2, 2, 2, 239, 240, 9, 7, 2, 2, 240, 100, 3, 2, 2, 2,
241, 242, 9, 8, 2, 2, 242, 102, 3, 2, 2, 2, 243, 244, 9, 9, 2, 2, 244,
104, 3, 2, 2, 2, 245, 246, 9, 10, 2, 2, 246, 106, 3, 2, 2, 2, 247, 248,
9, 11, 2, 2, 248, 108, 3, 2, 2, 2, 249, 250, 9, 12, 2, 2, 250, 110, 3,
2, 2, 2, 251, 252, 9, 13, 2, 2, 252, 112, 3, 2, 2, 2, 253, 254, 9, 14,
2, 2, 254, 114, 3, 2, 2, 2, 255, 256, 9, 15, 2, 2, 256, 116, 3, 2, 2, 2,
257, 258, 9, 16, 2, 2, 258, 118, 3, 2, 2, 2, 259, 260, 9, 17, 2, 2, 260,
120, 3, 2, 2, 2, 261, 262, 9, 18, 2, 2, 262, 122, 3, 2, 2, 2, 263, 264,
9, 19, 2, 2, 264, 124, 3, 2, 2, 2, 265, 266, 9, 20, 2, 2, 266, 126, 3,
2, 2, 2, 267, 268, 9, 21, 2, 2, 268, 128, 3, 2, 2, 2, 269, 270, 9, 22,
2, 2, 270, 130, 3, 2, 2, 2, 271, 272, 9, 23, 2, 2, 272, 132, 3, 2, 2, 2,
273, 274, 9, 24, 2, 2, 274, 134, 3, 2, 2, 2, 275, 276, 9, 25, 2, 2, 276,
136, 3, 2, 2, 2, 277, 278, 9, 26, 2, 2, 278, 138, 3, 2, 2, 2, 279, 280,
9, 27, 2, 2, 280, 140, 3, 2, 2, 2, 281, 282, 9, 28, 2, 2, 282, 142, 3,
2, 2, 2, 3, 2, 2,
}
var lexerDeserializer = antlr.NewATNDeserializer(nil)
var lexerAtn = lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
var lexerChannelNames = []string{
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
}
var lexerModeNames = []string{
"DEFAULT_MODE",
}
var lexerLiteralNames = []string{
"", "'\u0000'", "", "'\t'", "'\n'", "'\u000B'", "'\u000C'", "'\r'", "",
"' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'''", "'('", "')'",
"'*'", "'+'", "','", "'-'", "'.'", "'/'", "", "':'", "';'", "'<'", "'='",
"'>'", "'?'", "'@'", "'['", "'\\'", "']'", "'^'", "'_'", "'`'", "'{'",
"'|'", "'}'", "'~'", "'\u007F'",
}
var lexerSymbolicNames = []string{
"", "U_00", "U_01_08", "TAB", "LF", "U_0B", "U_0C", "CR", "U_0E_1F", "SP",
"Exclamation", "DQuote", "Hash", "Dollar", "Percent", "Ampersand", "SQuote",
"LParens", "RParens", "Asterisk", "Plus", "Comma", "Minus", "Period", "Slash",
"Digit", "Colon", "Semicolon", "Less", "Equal", "Greater", "Question",
"At", "LBracket", "Backslash", "RBracket", "Caret", "Underscore", "Backtick",
"LCurly", "Pipe", "RCurly", "Tilde", "Delete", "UTF8NonAscii", "A", "B",
"C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
"R", "S", "T", "U", "V", "W", "X", "Y", "Z",
}
var lexerRuleNames = []string{
"U_00", "U_01_08", "TAB", "LF", "U_0B", "U_0C", "CR", "U_0E_1F", "SP",
"Exclamation", "DQuote", "Hash", "Dollar", "Percent", "Ampersand", "SQuote",
"LParens", "RParens", "Asterisk", "Plus", "Comma", "Minus", "Period", "Slash",
"Digit", "Colon", "Semicolon", "Less", "Equal", "Greater", "Question",
"At", "LBracket", "Backslash", "RBracket", "Caret", "Underscore", "Backtick",
"LCurly", "Pipe", "RCurly", "Tilde", "Delete", "UTF8NonAscii", "A", "B",
"C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
"R", "S", "T", "U", "V", "W", "X", "Y", "Z",
}
type RFC5322Lexer struct {
*antlr.BaseLexer
channelNames []string
modeNames []string
// TODO: EOF string
}
var lexerDecisionToDFA = make([]*antlr.DFA, len(lexerAtn.DecisionToState))
func init() {
for index, ds := range lexerAtn.DecisionToState {
lexerDecisionToDFA[index] = antlr.NewDFA(ds, index)
}
}
func NewRFC5322Lexer(input antlr.CharStream) *RFC5322Lexer {
l := new(RFC5322Lexer)
l.BaseLexer = antlr.NewBaseLexer(input)
l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache())
l.channelNames = lexerChannelNames
l.modeNames = lexerModeNames
l.RuleNames = lexerRuleNames
l.LiteralNames = lexerLiteralNames
l.SymbolicNames = lexerSymbolicNames
l.GrammarFileName = "RFC5322Lexer.g4"
// TODO: l.EOF = antlr.TokenEOF
return l
}
// RFC5322Lexer tokens.
const (
RFC5322LexerU_00 = 1
RFC5322LexerU_01_08 = 2
RFC5322LexerTAB = 3
RFC5322LexerLF = 4
RFC5322LexerU_0B = 5
RFC5322LexerU_0C = 6
RFC5322LexerCR = 7
RFC5322LexerU_0E_1F = 8
RFC5322LexerSP = 9
RFC5322LexerExclamation = 10
RFC5322LexerDQuote = 11
RFC5322LexerHash = 12
RFC5322LexerDollar = 13
RFC5322LexerPercent = 14
RFC5322LexerAmpersand = 15
RFC5322LexerSQuote = 16
RFC5322LexerLParens = 17
RFC5322LexerRParens = 18
RFC5322LexerAsterisk = 19
RFC5322LexerPlus = 20
RFC5322LexerComma = 21
RFC5322LexerMinus = 22
RFC5322LexerPeriod = 23
RFC5322LexerSlash = 24
RFC5322LexerDigit = 25
RFC5322LexerColon = 26
RFC5322LexerSemicolon = 27
RFC5322LexerLess = 28
RFC5322LexerEqual = 29
RFC5322LexerGreater = 30
RFC5322LexerQuestion = 31
RFC5322LexerAt = 32
RFC5322LexerLBracket = 33
RFC5322LexerBackslash = 34
RFC5322LexerRBracket = 35
RFC5322LexerCaret = 36
RFC5322LexerUnderscore = 37
RFC5322LexerBacktick = 38
RFC5322LexerLCurly = 39
RFC5322LexerPipe = 40
RFC5322LexerRCurly = 41
RFC5322LexerTilde = 42
RFC5322LexerDelete = 43
RFC5322LexerUTF8NonAscii = 44
RFC5322LexerA = 45
RFC5322LexerB = 46
RFC5322LexerC = 47
RFC5322LexerD = 48
RFC5322LexerE = 49
RFC5322LexerF = 50
RFC5322LexerG = 51
RFC5322LexerH = 52
RFC5322LexerI = 53
RFC5322LexerJ = 54
RFC5322LexerK = 55
RFC5322LexerL = 56
RFC5322LexerM = 57
RFC5322LexerN = 58
RFC5322LexerO = 59
RFC5322LexerP = 60
RFC5322LexerQ = 61
RFC5322LexerR = 62
RFC5322LexerS = 63
RFC5322LexerT = 64
RFC5322LexerU = 65
RFC5322LexerV = 66
RFC5322LexerW = 67
RFC5322LexerX = 68
RFC5322LexerY = 69
RFC5322LexerZ = 70
)

File diff suppressed because it is too large Load Diff

View File

@ -1,435 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated from RFC5322Parser.g4 by ANTLR 4.8. DO NOT EDIT.
package parser // RFC5322Parser
import "github.com/antlr/antlr4/runtime/Go/antlr"
// BaseRFC5322ParserListener is a complete listener for a parse tree produced by RFC5322Parser.
type BaseRFC5322ParserListener struct{}
var _ RFC5322ParserListener = &BaseRFC5322ParserListener{}
// VisitTerminal is called when a terminal node is visited.
func (s *BaseRFC5322ParserListener) VisitTerminal(node antlr.TerminalNode) {}
// VisitErrorNode is called when an error node is visited.
func (s *BaseRFC5322ParserListener) VisitErrorNode(node antlr.ErrorNode) {}
// EnterEveryRule is called when any rule is entered.
func (s *BaseRFC5322ParserListener) EnterEveryRule(ctx antlr.ParserRuleContext) {}
// ExitEveryRule is called when any rule is exited.
func (s *BaseRFC5322ParserListener) ExitEveryRule(ctx antlr.ParserRuleContext) {}
// EnterQuotedChar is called when production quotedChar is entered.
func (s *BaseRFC5322ParserListener) EnterQuotedChar(ctx *QuotedCharContext) {}
// ExitQuotedChar is called when production quotedChar is exited.
func (s *BaseRFC5322ParserListener) ExitQuotedChar(ctx *QuotedCharContext) {}
// EnterQuotedPair is called when production quotedPair is entered.
func (s *BaseRFC5322ParserListener) EnterQuotedPair(ctx *QuotedPairContext) {}
// ExitQuotedPair is called when production quotedPair is exited.
func (s *BaseRFC5322ParserListener) ExitQuotedPair(ctx *QuotedPairContext) {}
// EnterFws is called when production fws is entered.
func (s *BaseRFC5322ParserListener) EnterFws(ctx *FwsContext) {}
// ExitFws is called when production fws is exited.
func (s *BaseRFC5322ParserListener) ExitFws(ctx *FwsContext) {}
// EnterCtext is called when production ctext is entered.
func (s *BaseRFC5322ParserListener) EnterCtext(ctx *CtextContext) {}
// ExitCtext is called when production ctext is exited.
func (s *BaseRFC5322ParserListener) ExitCtext(ctx *CtextContext) {}
// EnterCcontent is called when production ccontent is entered.
func (s *BaseRFC5322ParserListener) EnterCcontent(ctx *CcontentContext) {}
// ExitCcontent is called when production ccontent is exited.
func (s *BaseRFC5322ParserListener) ExitCcontent(ctx *CcontentContext) {}
// EnterComment is called when production comment is entered.
func (s *BaseRFC5322ParserListener) EnterComment(ctx *CommentContext) {}
// ExitComment is called when production comment is exited.
func (s *BaseRFC5322ParserListener) ExitComment(ctx *CommentContext) {}
// EnterCfws is called when production cfws is entered.
func (s *BaseRFC5322ParserListener) EnterCfws(ctx *CfwsContext) {}
// ExitCfws is called when production cfws is exited.
func (s *BaseRFC5322ParserListener) ExitCfws(ctx *CfwsContext) {}
// EnterAtext is called when production atext is entered.
func (s *BaseRFC5322ParserListener) EnterAtext(ctx *AtextContext) {}
// ExitAtext is called when production atext is exited.
func (s *BaseRFC5322ParserListener) ExitAtext(ctx *AtextContext) {}
// EnterAtom is called when production atom is entered.
func (s *BaseRFC5322ParserListener) EnterAtom(ctx *AtomContext) {}
// ExitAtom is called when production atom is exited.
func (s *BaseRFC5322ParserListener) ExitAtom(ctx *AtomContext) {}
// EnterDotAtom is called when production dotAtom is entered.
func (s *BaseRFC5322ParserListener) EnterDotAtom(ctx *DotAtomContext) {}
// ExitDotAtom is called when production dotAtom is exited.
func (s *BaseRFC5322ParserListener) ExitDotAtom(ctx *DotAtomContext) {}
// EnterQtext is called when production qtext is entered.
func (s *BaseRFC5322ParserListener) EnterQtext(ctx *QtextContext) {}
// ExitQtext is called when production qtext is exited.
func (s *BaseRFC5322ParserListener) ExitQtext(ctx *QtextContext) {}
// EnterQuotedContent is called when production quotedContent is entered.
func (s *BaseRFC5322ParserListener) EnterQuotedContent(ctx *QuotedContentContext) {}
// ExitQuotedContent is called when production quotedContent is exited.
func (s *BaseRFC5322ParserListener) ExitQuotedContent(ctx *QuotedContentContext) {}
// EnterQuotedValue is called when production quotedValue is entered.
func (s *BaseRFC5322ParserListener) EnterQuotedValue(ctx *QuotedValueContext) {}
// ExitQuotedValue is called when production quotedValue is exited.
func (s *BaseRFC5322ParserListener) ExitQuotedValue(ctx *QuotedValueContext) {}
// EnterQuotedString is called when production quotedString is entered.
func (s *BaseRFC5322ParserListener) EnterQuotedString(ctx *QuotedStringContext) {}
// ExitQuotedString is called when production quotedString is exited.
func (s *BaseRFC5322ParserListener) ExitQuotedString(ctx *QuotedStringContext) {}
// EnterWord is called when production word is entered.
func (s *BaseRFC5322ParserListener) EnterWord(ctx *WordContext) {}
// ExitWord is called when production word is exited.
func (s *BaseRFC5322ParserListener) ExitWord(ctx *WordContext) {}
// EnterDateTime is called when production dateTime is entered.
func (s *BaseRFC5322ParserListener) EnterDateTime(ctx *DateTimeContext) {}
// ExitDateTime is called when production dateTime is exited.
func (s *BaseRFC5322ParserListener) ExitDateTime(ctx *DateTimeContext) {}
// EnterDayOfweek is called when production dayOfweek is entered.
func (s *BaseRFC5322ParserListener) EnterDayOfweek(ctx *DayOfweekContext) {}
// ExitDayOfweek is called when production dayOfweek is exited.
func (s *BaseRFC5322ParserListener) ExitDayOfweek(ctx *DayOfweekContext) {}
// EnterDayName is called when production dayName is entered.
func (s *BaseRFC5322ParserListener) EnterDayName(ctx *DayNameContext) {}
// ExitDayName is called when production dayName is exited.
func (s *BaseRFC5322ParserListener) ExitDayName(ctx *DayNameContext) {}
// EnterDay is called when production day is entered.
func (s *BaseRFC5322ParserListener) EnterDay(ctx *DayContext) {}
// ExitDay is called when production day is exited.
func (s *BaseRFC5322ParserListener) ExitDay(ctx *DayContext) {}
// EnterMonth is called when production month is entered.
func (s *BaseRFC5322ParserListener) EnterMonth(ctx *MonthContext) {}
// ExitMonth is called when production month is exited.
func (s *BaseRFC5322ParserListener) ExitMonth(ctx *MonthContext) {}
// EnterYear is called when production year is entered.
func (s *BaseRFC5322ParserListener) EnterYear(ctx *YearContext) {}
// ExitYear is called when production year is exited.
func (s *BaseRFC5322ParserListener) ExitYear(ctx *YearContext) {}
// EnterHour is called when production hour is entered.
func (s *BaseRFC5322ParserListener) EnterHour(ctx *HourContext) {}
// ExitHour is called when production hour is exited.
func (s *BaseRFC5322ParserListener) ExitHour(ctx *HourContext) {}
// EnterMinute is called when production minute is entered.
func (s *BaseRFC5322ParserListener) EnterMinute(ctx *MinuteContext) {}
// ExitMinute is called when production minute is exited.
func (s *BaseRFC5322ParserListener) ExitMinute(ctx *MinuteContext) {}
// EnterSecond is called when production second is entered.
func (s *BaseRFC5322ParserListener) EnterSecond(ctx *SecondContext) {}
// ExitSecond is called when production second is exited.
func (s *BaseRFC5322ParserListener) ExitSecond(ctx *SecondContext) {}
// EnterOffset is called when production offset is entered.
func (s *BaseRFC5322ParserListener) EnterOffset(ctx *OffsetContext) {}
// ExitOffset is called when production offset is exited.
func (s *BaseRFC5322ParserListener) ExitOffset(ctx *OffsetContext) {}
// EnterZone is called when production zone is entered.
func (s *BaseRFC5322ParserListener) EnterZone(ctx *ZoneContext) {}
// ExitZone is called when production zone is exited.
func (s *BaseRFC5322ParserListener) ExitZone(ctx *ZoneContext) {}
// EnterAddress is called when production address is entered.
func (s *BaseRFC5322ParserListener) EnterAddress(ctx *AddressContext) {}
// ExitAddress is called when production address is exited.
func (s *BaseRFC5322ParserListener) ExitAddress(ctx *AddressContext) {}
// EnterMailbox is called when production mailbox is entered.
func (s *BaseRFC5322ParserListener) EnterMailbox(ctx *MailboxContext) {}
// ExitMailbox is called when production mailbox is exited.
func (s *BaseRFC5322ParserListener) ExitMailbox(ctx *MailboxContext) {}
// EnterNameAddr is called when production nameAddr is entered.
func (s *BaseRFC5322ParserListener) EnterNameAddr(ctx *NameAddrContext) {}
// ExitNameAddr is called when production nameAddr is exited.
func (s *BaseRFC5322ParserListener) ExitNameAddr(ctx *NameAddrContext) {}
// EnterAngleAddr is called when production angleAddr is entered.
func (s *BaseRFC5322ParserListener) EnterAngleAddr(ctx *AngleAddrContext) {}
// ExitAngleAddr is called when production angleAddr is exited.
func (s *BaseRFC5322ParserListener) ExitAngleAddr(ctx *AngleAddrContext) {}
// EnterGroup is called when production group is entered.
func (s *BaseRFC5322ParserListener) EnterGroup(ctx *GroupContext) {}
// ExitGroup is called when production group is exited.
func (s *BaseRFC5322ParserListener) ExitGroup(ctx *GroupContext) {}
// EnterDisplayName is called when production displayName is entered.
func (s *BaseRFC5322ParserListener) EnterDisplayName(ctx *DisplayNameContext) {}
// ExitDisplayName is called when production displayName is exited.
func (s *BaseRFC5322ParserListener) ExitDisplayName(ctx *DisplayNameContext) {}
// EnterMailboxList is called when production mailboxList is entered.
func (s *BaseRFC5322ParserListener) EnterMailboxList(ctx *MailboxListContext) {}
// ExitMailboxList is called when production mailboxList is exited.
func (s *BaseRFC5322ParserListener) ExitMailboxList(ctx *MailboxListContext) {}
// EnterAddressList is called when production addressList is entered.
func (s *BaseRFC5322ParserListener) EnterAddressList(ctx *AddressListContext) {}
// ExitAddressList is called when production addressList is exited.
func (s *BaseRFC5322ParserListener) ExitAddressList(ctx *AddressListContext) {}
// EnterGroupList is called when production groupList is entered.
func (s *BaseRFC5322ParserListener) EnterGroupList(ctx *GroupListContext) {}
// ExitGroupList is called when production groupList is exited.
func (s *BaseRFC5322ParserListener) ExitGroupList(ctx *GroupListContext) {}
// EnterAddrSpec is called when production addrSpec is entered.
func (s *BaseRFC5322ParserListener) EnterAddrSpec(ctx *AddrSpecContext) {}
// ExitAddrSpec is called when production addrSpec is exited.
func (s *BaseRFC5322ParserListener) ExitAddrSpec(ctx *AddrSpecContext) {}
// EnterLocalPart is called when production localPart is entered.
func (s *BaseRFC5322ParserListener) EnterLocalPart(ctx *LocalPartContext) {}
// ExitLocalPart is called when production localPart is exited.
func (s *BaseRFC5322ParserListener) ExitLocalPart(ctx *LocalPartContext) {}
// EnterPort is called when production port is entered.
func (s *BaseRFC5322ParserListener) EnterPort(ctx *PortContext) {}
// ExitPort is called when production port is exited.
func (s *BaseRFC5322ParserListener) ExitPort(ctx *PortContext) {}
// EnterDomain is called when production domain is entered.
func (s *BaseRFC5322ParserListener) EnterDomain(ctx *DomainContext) {}
// ExitDomain is called when production domain is exited.
func (s *BaseRFC5322ParserListener) ExitDomain(ctx *DomainContext) {}
// EnterDomainLiteral is called when production domainLiteral is entered.
func (s *BaseRFC5322ParserListener) EnterDomainLiteral(ctx *DomainLiteralContext) {}
// ExitDomainLiteral is called when production domainLiteral is exited.
func (s *BaseRFC5322ParserListener) ExitDomainLiteral(ctx *DomainLiteralContext) {}
// EnterDtext is called when production dtext is entered.
func (s *BaseRFC5322ParserListener) EnterDtext(ctx *DtextContext) {}
// ExitDtext is called when production dtext is exited.
func (s *BaseRFC5322ParserListener) ExitDtext(ctx *DtextContext) {}
// EnterObsNoWSCTL is called when production obsNoWSCTL is entered.
func (s *BaseRFC5322ParserListener) EnterObsNoWSCTL(ctx *ObsNoWSCTLContext) {}
// ExitObsNoWSCTL is called when production obsNoWSCTL is exited.
func (s *BaseRFC5322ParserListener) ExitObsNoWSCTL(ctx *ObsNoWSCTLContext) {}
// EnterObsCtext is called when production obsCtext is entered.
func (s *BaseRFC5322ParserListener) EnterObsCtext(ctx *ObsCtextContext) {}
// ExitObsCtext is called when production obsCtext is exited.
func (s *BaseRFC5322ParserListener) ExitObsCtext(ctx *ObsCtextContext) {}
// EnterObsQtext is called when production obsQtext is entered.
func (s *BaseRFC5322ParserListener) EnterObsQtext(ctx *ObsQtextContext) {}
// ExitObsQtext is called when production obsQtext is exited.
func (s *BaseRFC5322ParserListener) ExitObsQtext(ctx *ObsQtextContext) {}
// EnterObsQP is called when production obsQP is entered.
func (s *BaseRFC5322ParserListener) EnterObsQP(ctx *ObsQPContext) {}
// ExitObsQP is called when production obsQP is exited.
func (s *BaseRFC5322ParserListener) ExitObsQP(ctx *ObsQPContext) {}
// EnterObsFWS is called when production obsFWS is entered.
func (s *BaseRFC5322ParserListener) EnterObsFWS(ctx *ObsFWSContext) {}
// ExitObsFWS is called when production obsFWS is exited.
func (s *BaseRFC5322ParserListener) ExitObsFWS(ctx *ObsFWSContext) {}
// EnterObsZone is called when production obsZone is entered.
func (s *BaseRFC5322ParserListener) EnterObsZone(ctx *ObsZoneContext) {}
// ExitObsZone is called when production obsZone is exited.
func (s *BaseRFC5322ParserListener) ExitObsZone(ctx *ObsZoneContext) {}
// EnterObsAngleAddr is called when production obsAngleAddr is entered.
func (s *BaseRFC5322ParserListener) EnterObsAngleAddr(ctx *ObsAngleAddrContext) {}
// ExitObsAngleAddr is called when production obsAngleAddr is exited.
func (s *BaseRFC5322ParserListener) ExitObsAngleAddr(ctx *ObsAngleAddrContext) {}
// EnterObsRoute is called when production obsRoute is entered.
func (s *BaseRFC5322ParserListener) EnterObsRoute(ctx *ObsRouteContext) {}
// ExitObsRoute is called when production obsRoute is exited.
func (s *BaseRFC5322ParserListener) ExitObsRoute(ctx *ObsRouteContext) {}
// EnterObsDomainList is called when production obsDomainList is entered.
func (s *BaseRFC5322ParserListener) EnterObsDomainList(ctx *ObsDomainListContext) {}
// ExitObsDomainList is called when production obsDomainList is exited.
func (s *BaseRFC5322ParserListener) ExitObsDomainList(ctx *ObsDomainListContext) {}
// EnterObsMboxList is called when production obsMboxList is entered.
func (s *BaseRFC5322ParserListener) EnterObsMboxList(ctx *ObsMboxListContext) {}
// ExitObsMboxList is called when production obsMboxList is exited.
func (s *BaseRFC5322ParserListener) ExitObsMboxList(ctx *ObsMboxListContext) {}
// EnterObsAddrList is called when production obsAddrList is entered.
func (s *BaseRFC5322ParserListener) EnterObsAddrList(ctx *ObsAddrListContext) {}
// ExitObsAddrList is called when production obsAddrList is exited.
func (s *BaseRFC5322ParserListener) ExitObsAddrList(ctx *ObsAddrListContext) {}
// EnterObsGroupList is called when production obsGroupList is entered.
func (s *BaseRFC5322ParserListener) EnterObsGroupList(ctx *ObsGroupListContext) {}
// ExitObsGroupList is called when production obsGroupList is exited.
func (s *BaseRFC5322ParserListener) ExitObsGroupList(ctx *ObsGroupListContext) {}
// EnterObsLocalPart is called when production obsLocalPart is entered.
func (s *BaseRFC5322ParserListener) EnterObsLocalPart(ctx *ObsLocalPartContext) {}
// ExitObsLocalPart is called when production obsLocalPart is exited.
func (s *BaseRFC5322ParserListener) ExitObsLocalPart(ctx *ObsLocalPartContext) {}
// EnterObsDomain is called when production obsDomain is entered.
func (s *BaseRFC5322ParserListener) EnterObsDomain(ctx *ObsDomainContext) {}
// ExitObsDomain is called when production obsDomain is exited.
func (s *BaseRFC5322ParserListener) ExitObsDomain(ctx *ObsDomainContext) {}
// EnterEncodedWord is called when production encodedWord is entered.
func (s *BaseRFC5322ParserListener) EnterEncodedWord(ctx *EncodedWordContext) {}
// ExitEncodedWord is called when production encodedWord is exited.
func (s *BaseRFC5322ParserListener) ExitEncodedWord(ctx *EncodedWordContext) {}
// EnterCharset is called when production charset is entered.
func (s *BaseRFC5322ParserListener) EnterCharset(ctx *CharsetContext) {}
// ExitCharset is called when production charset is exited.
func (s *BaseRFC5322ParserListener) ExitCharset(ctx *CharsetContext) {}
// EnterEncoding is called when production encoding is entered.
func (s *BaseRFC5322ParserListener) EnterEncoding(ctx *EncodingContext) {}
// ExitEncoding is called when production encoding is exited.
func (s *BaseRFC5322ParserListener) ExitEncoding(ctx *EncodingContext) {}
// EnterToken is called when production token is entered.
func (s *BaseRFC5322ParserListener) EnterToken(ctx *TokenContext) {}
// ExitToken is called when production token is exited.
func (s *BaseRFC5322ParserListener) ExitToken(ctx *TokenContext) {}
// EnterTokenChar is called when production tokenChar is entered.
func (s *BaseRFC5322ParserListener) EnterTokenChar(ctx *TokenCharContext) {}
// ExitTokenChar is called when production tokenChar is exited.
func (s *BaseRFC5322ParserListener) ExitTokenChar(ctx *TokenCharContext) {}
// EnterEncodedText is called when production encodedText is entered.
func (s *BaseRFC5322ParserListener) EnterEncodedText(ctx *EncodedTextContext) {}
// ExitEncodedText is called when production encodedText is exited.
func (s *BaseRFC5322ParserListener) ExitEncodedText(ctx *EncodedTextContext) {}
// EnterEncodedChar is called when production encodedChar is entered.
func (s *BaseRFC5322ParserListener) EnterEncodedChar(ctx *EncodedCharContext) {}
// ExitEncodedChar is called when production encodedChar is exited.
func (s *BaseRFC5322ParserListener) ExitEncodedChar(ctx *EncodedCharContext) {}
// EnterCrlf is called when production crlf is entered.
func (s *BaseRFC5322ParserListener) EnterCrlf(ctx *CrlfContext) {}
// ExitCrlf is called when production crlf is exited.
func (s *BaseRFC5322ParserListener) ExitCrlf(ctx *CrlfContext) {}
// EnterWsp is called when production wsp is entered.
func (s *BaseRFC5322ParserListener) EnterWsp(ctx *WspContext) {}
// ExitWsp is called when production wsp is exited.
func (s *BaseRFC5322ParserListener) ExitWsp(ctx *WspContext) {}
// EnterVchar is called when production vchar is entered.
func (s *BaseRFC5322ParserListener) EnterVchar(ctx *VcharContext) {}
// ExitVchar is called when production vchar is exited.
func (s *BaseRFC5322ParserListener) ExitVchar(ctx *VcharContext) {}
// EnterAlpha is called when production alpha is entered.
func (s *BaseRFC5322ParserListener) EnterAlpha(ctx *AlphaContext) {}
// ExitAlpha is called when production alpha is exited.
func (s *BaseRFC5322ParserListener) ExitAlpha(ctx *AlphaContext) {}

View File

@ -1,423 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated from RFC5322Parser.g4 by ANTLR 4.8. DO NOT EDIT.
package parser // RFC5322Parser
import "github.com/antlr/antlr4/runtime/Go/antlr"
// RFC5322ParserListener is a complete listener for a parse tree produced by RFC5322Parser.
type RFC5322ParserListener interface {
antlr.ParseTreeListener
// EnterQuotedChar is called when entering the quotedChar production.
EnterQuotedChar(c *QuotedCharContext)
// EnterQuotedPair is called when entering the quotedPair production.
EnterQuotedPair(c *QuotedPairContext)
// EnterFws is called when entering the fws production.
EnterFws(c *FwsContext)
// EnterCtext is called when entering the ctext production.
EnterCtext(c *CtextContext)
// EnterCcontent is called when entering the ccontent production.
EnterCcontent(c *CcontentContext)
// EnterComment is called when entering the comment production.
EnterComment(c *CommentContext)
// EnterCfws is called when entering the cfws production.
EnterCfws(c *CfwsContext)
// EnterAtext is called when entering the atext production.
EnterAtext(c *AtextContext)
// EnterAtom is called when entering the atom production.
EnterAtom(c *AtomContext)
// EnterDotAtom is called when entering the dotAtom production.
EnterDotAtom(c *DotAtomContext)
// EnterQtext is called when entering the qtext production.
EnterQtext(c *QtextContext)
// EnterQuotedContent is called when entering the quotedContent production.
EnterQuotedContent(c *QuotedContentContext)
// EnterQuotedValue is called when entering the quotedValue production.
EnterQuotedValue(c *QuotedValueContext)
// EnterQuotedString is called when entering the quotedString production.
EnterQuotedString(c *QuotedStringContext)
// EnterWord is called when entering the word production.
EnterWord(c *WordContext)
// EnterDateTime is called when entering the dateTime production.
EnterDateTime(c *DateTimeContext)
// EnterDayOfweek is called when entering the dayOfweek production.
EnterDayOfweek(c *DayOfweekContext)
// EnterDayName is called when entering the dayName production.
EnterDayName(c *DayNameContext)
// EnterDay is called when entering the day production.
EnterDay(c *DayContext)
// EnterMonth is called when entering the month production.
EnterMonth(c *MonthContext)
// EnterYear is called when entering the year production.
EnterYear(c *YearContext)
// EnterHour is called when entering the hour production.
EnterHour(c *HourContext)
// EnterMinute is called when entering the minute production.
EnterMinute(c *MinuteContext)
// EnterSecond is called when entering the second production.
EnterSecond(c *SecondContext)
// EnterOffset is called when entering the offset production.
EnterOffset(c *OffsetContext)
// EnterZone is called when entering the zone production.
EnterZone(c *ZoneContext)
// EnterAddress is called when entering the address production.
EnterAddress(c *AddressContext)
// EnterMailbox is called when entering the mailbox production.
EnterMailbox(c *MailboxContext)
// EnterNameAddr is called when entering the nameAddr production.
EnterNameAddr(c *NameAddrContext)
// EnterAngleAddr is called when entering the angleAddr production.
EnterAngleAddr(c *AngleAddrContext)
// EnterGroup is called when entering the group production.
EnterGroup(c *GroupContext)
// EnterDisplayName is called when entering the displayName production.
EnterDisplayName(c *DisplayNameContext)
// EnterMailboxList is called when entering the mailboxList production.
EnterMailboxList(c *MailboxListContext)
// EnterAddressList is called when entering the addressList production.
EnterAddressList(c *AddressListContext)
// EnterGroupList is called when entering the groupList production.
EnterGroupList(c *GroupListContext)
// EnterAddrSpec is called when entering the addrSpec production.
EnterAddrSpec(c *AddrSpecContext)
// EnterLocalPart is called when entering the localPart production.
EnterLocalPart(c *LocalPartContext)
// EnterPort is called when entering the port production.
EnterPort(c *PortContext)
// EnterDomain is called when entering the domain production.
EnterDomain(c *DomainContext)
// EnterDomainLiteral is called when entering the domainLiteral production.
EnterDomainLiteral(c *DomainLiteralContext)
// EnterDtext is called when entering the dtext production.
EnterDtext(c *DtextContext)
// EnterObsNoWSCTL is called when entering the obsNoWSCTL production.
EnterObsNoWSCTL(c *ObsNoWSCTLContext)
// EnterObsCtext is called when entering the obsCtext production.
EnterObsCtext(c *ObsCtextContext)
// EnterObsQtext is called when entering the obsQtext production.
EnterObsQtext(c *ObsQtextContext)
// EnterObsQP is called when entering the obsQP production.
EnterObsQP(c *ObsQPContext)
// EnterObsFWS is called when entering the obsFWS production.
EnterObsFWS(c *ObsFWSContext)
// EnterObsZone is called when entering the obsZone production.
EnterObsZone(c *ObsZoneContext)
// EnterObsAngleAddr is called when entering the obsAngleAddr production.
EnterObsAngleAddr(c *ObsAngleAddrContext)
// EnterObsRoute is called when entering the obsRoute production.
EnterObsRoute(c *ObsRouteContext)
// EnterObsDomainList is called when entering the obsDomainList production.
EnterObsDomainList(c *ObsDomainListContext)
// EnterObsMboxList is called when entering the obsMboxList production.
EnterObsMboxList(c *ObsMboxListContext)
// EnterObsAddrList is called when entering the obsAddrList production.
EnterObsAddrList(c *ObsAddrListContext)
// EnterObsGroupList is called when entering the obsGroupList production.
EnterObsGroupList(c *ObsGroupListContext)
// EnterObsLocalPart is called when entering the obsLocalPart production.
EnterObsLocalPart(c *ObsLocalPartContext)
// EnterObsDomain is called when entering the obsDomain production.
EnterObsDomain(c *ObsDomainContext)
// EnterEncodedWord is called when entering the encodedWord production.
EnterEncodedWord(c *EncodedWordContext)
// EnterCharset is called when entering the charset production.
EnterCharset(c *CharsetContext)
// EnterEncoding is called when entering the encoding production.
EnterEncoding(c *EncodingContext)
// EnterToken is called when entering the token production.
EnterToken(c *TokenContext)
// EnterTokenChar is called when entering the tokenChar production.
EnterTokenChar(c *TokenCharContext)
// EnterEncodedText is called when entering the encodedText production.
EnterEncodedText(c *EncodedTextContext)
// EnterEncodedChar is called when entering the encodedChar production.
EnterEncodedChar(c *EncodedCharContext)
// EnterCrlf is called when entering the crlf production.
EnterCrlf(c *CrlfContext)
// EnterWsp is called when entering the wsp production.
EnterWsp(c *WspContext)
// EnterVchar is called when entering the vchar production.
EnterVchar(c *VcharContext)
// EnterAlpha is called when entering the alpha production.
EnterAlpha(c *AlphaContext)
// ExitQuotedChar is called when exiting the quotedChar production.
ExitQuotedChar(c *QuotedCharContext)
// ExitQuotedPair is called when exiting the quotedPair production.
ExitQuotedPair(c *QuotedPairContext)
// ExitFws is called when exiting the fws production.
ExitFws(c *FwsContext)
// ExitCtext is called when exiting the ctext production.
ExitCtext(c *CtextContext)
// ExitCcontent is called when exiting the ccontent production.
ExitCcontent(c *CcontentContext)
// ExitComment is called when exiting the comment production.
ExitComment(c *CommentContext)
// ExitCfws is called when exiting the cfws production.
ExitCfws(c *CfwsContext)
// ExitAtext is called when exiting the atext production.
ExitAtext(c *AtextContext)
// ExitAtom is called when exiting the atom production.
ExitAtom(c *AtomContext)
// ExitDotAtom is called when exiting the dotAtom production.
ExitDotAtom(c *DotAtomContext)
// ExitQtext is called when exiting the qtext production.
ExitQtext(c *QtextContext)
// ExitQuotedContent is called when exiting the quotedContent production.
ExitQuotedContent(c *QuotedContentContext)
// ExitQuotedValue is called when exiting the quotedValue production.
ExitQuotedValue(c *QuotedValueContext)
// ExitQuotedString is called when exiting the quotedString production.
ExitQuotedString(c *QuotedStringContext)
// ExitWord is called when exiting the word production.
ExitWord(c *WordContext)
// ExitDateTime is called when exiting the dateTime production.
ExitDateTime(c *DateTimeContext)
// ExitDayOfweek is called when exiting the dayOfweek production.
ExitDayOfweek(c *DayOfweekContext)
// ExitDayName is called when exiting the dayName production.
ExitDayName(c *DayNameContext)
// ExitDay is called when exiting the day production.
ExitDay(c *DayContext)
// ExitMonth is called when exiting the month production.
ExitMonth(c *MonthContext)
// ExitYear is called when exiting the year production.
ExitYear(c *YearContext)
// ExitHour is called when exiting the hour production.
ExitHour(c *HourContext)
// ExitMinute is called when exiting the minute production.
ExitMinute(c *MinuteContext)
// ExitSecond is called when exiting the second production.
ExitSecond(c *SecondContext)
// ExitOffset is called when exiting the offset production.
ExitOffset(c *OffsetContext)
// ExitZone is called when exiting the zone production.
ExitZone(c *ZoneContext)
// ExitAddress is called when exiting the address production.
ExitAddress(c *AddressContext)
// ExitMailbox is called when exiting the mailbox production.
ExitMailbox(c *MailboxContext)
// ExitNameAddr is called when exiting the nameAddr production.
ExitNameAddr(c *NameAddrContext)
// ExitAngleAddr is called when exiting the angleAddr production.
ExitAngleAddr(c *AngleAddrContext)
// ExitGroup is called when exiting the group production.
ExitGroup(c *GroupContext)
// ExitDisplayName is called when exiting the displayName production.
ExitDisplayName(c *DisplayNameContext)
// ExitMailboxList is called when exiting the mailboxList production.
ExitMailboxList(c *MailboxListContext)
// ExitAddressList is called when exiting the addressList production.
ExitAddressList(c *AddressListContext)
// ExitGroupList is called when exiting the groupList production.
ExitGroupList(c *GroupListContext)
// ExitAddrSpec is called when exiting the addrSpec production.
ExitAddrSpec(c *AddrSpecContext)
// ExitLocalPart is called when exiting the localPart production.
ExitLocalPart(c *LocalPartContext)
// ExitPort is called when exiting the port production.
ExitPort(c *PortContext)
// ExitDomain is called when exiting the domain production.
ExitDomain(c *DomainContext)
// ExitDomainLiteral is called when exiting the domainLiteral production.
ExitDomainLiteral(c *DomainLiteralContext)
// ExitDtext is called when exiting the dtext production.
ExitDtext(c *DtextContext)
// ExitObsNoWSCTL is called when exiting the obsNoWSCTL production.
ExitObsNoWSCTL(c *ObsNoWSCTLContext)
// ExitObsCtext is called when exiting the obsCtext production.
ExitObsCtext(c *ObsCtextContext)
// ExitObsQtext is called when exiting the obsQtext production.
ExitObsQtext(c *ObsQtextContext)
// ExitObsQP is called when exiting the obsQP production.
ExitObsQP(c *ObsQPContext)
// ExitObsFWS is called when exiting the obsFWS production.
ExitObsFWS(c *ObsFWSContext)
// ExitObsZone is called when exiting the obsZone production.
ExitObsZone(c *ObsZoneContext)
// ExitObsAngleAddr is called when exiting the obsAngleAddr production.
ExitObsAngleAddr(c *ObsAngleAddrContext)
// ExitObsRoute is called when exiting the obsRoute production.
ExitObsRoute(c *ObsRouteContext)
// ExitObsDomainList is called when exiting the obsDomainList production.
ExitObsDomainList(c *ObsDomainListContext)
// ExitObsMboxList is called when exiting the obsMboxList production.
ExitObsMboxList(c *ObsMboxListContext)
// ExitObsAddrList is called when exiting the obsAddrList production.
ExitObsAddrList(c *ObsAddrListContext)
// ExitObsGroupList is called when exiting the obsGroupList production.
ExitObsGroupList(c *ObsGroupListContext)
// ExitObsLocalPart is called when exiting the obsLocalPart production.
ExitObsLocalPart(c *ObsLocalPartContext)
// ExitObsDomain is called when exiting the obsDomain production.
ExitObsDomain(c *ObsDomainContext)
// ExitEncodedWord is called when exiting the encodedWord production.
ExitEncodedWord(c *EncodedWordContext)
// ExitCharset is called when exiting the charset production.
ExitCharset(c *CharsetContext)
// ExitEncoding is called when exiting the encoding production.
ExitEncoding(c *EncodingContext)
// ExitToken is called when exiting the token production.
ExitToken(c *TokenContext)
// ExitTokenChar is called when exiting the tokenChar production.
ExitTokenChar(c *TokenCharContext)
// ExitEncodedText is called when exiting the encodedText production.
ExitEncodedText(c *EncodedTextContext)
// ExitEncodedChar is called when exiting the encodedChar production.
ExitEncodedChar(c *EncodedCharContext)
// ExitCrlf is called when exiting the crlf production.
ExitCrlf(c *CrlfContext)
// ExitWsp is called when exiting the wsp production.
ExitWsp(c *WspContext)
// ExitVchar is called when exiting the vchar production.
ExitVchar(c *VcharContext)
// ExitAlpha is called when exiting the alpha production.
ExitAlpha(c *AlphaContext)
}

View File

@ -1,49 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type qtext struct {
value string
}
func (w *walker) EnterQtext(ctx *parser.QtextContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering qtext")
w.enter(&qtext{
value: ctx.GetText(),
})
}
func (w *walker) ExitQtext(ctx *parser.QtextContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting qtext")
type withQtext interface {
withQtext(*qtext)
}
res := w.exit().(*qtext)
if parent, ok := w.parent().(withQtext); ok {
parent.withQtext(res)
}
}

View File

@ -1,49 +0,0 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package rfc5322
import (
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type quotedChar struct {
value string
}
func (w *walker) EnterQuotedChar(ctx *parser.QuotedCharContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering quotedChar")
w.enter(&quotedChar{
value: ctx.GetText(),
})
}
func (w *walker) ExitQuotedChar(ctx *parser.QuotedCharContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting quotedChar")
type withQuotedChar interface {
withQuotedChar(*quotedChar)
}
res := w.exit().(*quotedChar)
if parent, ok := w.parent().(withQuotedChar); ok {
parent.withQuotedChar(res)
}
}

Some files were not shown because too many files have changed in this diff Show More