feat: custom address/date parser based on rfc5322 abnf

This commit is contained in:
James Houlahan
2020-10-21 13:37:41 +02:00
parent 9e0635a6a4
commit 3496599723
69 changed files with 20607 additions and 185 deletions

View File

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

View File

@ -57,7 +57,6 @@ ifeq "${TARGET_CMD}" "Import-Export"
TGZ_TARGET:=ie_${TARGET_OS}_${REVISION}.tgz
endif
build: ${TGZ_TARGET}
build-ie:
TARGET_CMD=Import-Export $(MAKE) build
@ -265,7 +264,6 @@ run-ie-qt:
run-ie-nogui:
TARGET_CMD=Import-Export $(MAKE) run-nogui
clean-frontend-qt:
$(MAKE) -C internal/frontend/qt -f Makefile.local clean
clean-frontend-qt-ie:
@ -282,3 +280,8 @@ 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

1
go.mod
View File

@ -24,6 +24,7 @@ require (
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

2
go.sum
View File

@ -37,6 +37,8 @@ github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc h1:m
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/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=

View File

@ -28,6 +28,7 @@ import (
"sort"
"strings"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322"
"github.com/pkg/errors"
)
@ -136,14 +137,21 @@ func getFilePathsWithSuffixInner(prefix, root, suffix string, includeDir bool) (
// getMessageTime returns time of the message specified in the message header.
func getMessageTime(body []byte) (int64, error) {
mailHeader, err := getMessageHeader(body)
hdr, err := getMessageHeader(body)
if err != nil {
return 0, err
}
if t, err := mailHeader.Date(); err == nil && !t.IsZero() {
return t.Unix(), nil
t, err := rfc5322.ParseDateTime(hdr.Get("Date"))
if err != nil {
return 0, err
}
return 0, nil
if t.IsZero() {
return 0, nil
}
return t.Unix(), nil
}
// getMessageHeader returns headers of the message body.

View File

@ -19,9 +19,7 @@ package message
import (
"mime"
"net/mail"
"net/textproto"
"regexp"
"strings"
"time"
@ -141,46 +139,3 @@ func GetAttachmentHeader(att *pmapi.Attachment) textproto.MIMEHeader {
return h
}
var reEmailComment = regexp.MustCompile("[(][^)]*[)]") // nolint[gochecknoglobals]
// parseAddressComment removes the comments completely even though they should be allowed
// http://tools.wordtothewise.com/rfc/822
// NOTE: This should be supported in go>1.10 but it seems it's not ¯\_(ツ)_/¯
func parseAddressComment(raw string) string {
return reEmailComment.ReplaceAllString(raw, "")
}
func parseAddressList(val string) (addrs []*mail.Address, err error) {
if val == "" || val == "<>" {
return
}
addrs, err = mail.ParseAddressList(parseAddressComment(val))
if err == nil {
if addrs == nil {
addrs = []*mail.Address{}
}
return
}
// Probably missing encoding error -- try to at least parse addresses in brackets.
first := strings.Index(val, "<")
last := strings.LastIndex(val, ">")
if first < 0 || last < 0 || first >= last {
return
}
var addrList []string
open := first
for open < last && 0 <= open {
val = val[open:]
close := strings.Index(val, ">")
addrList = append(addrList, val[:close+1])
val = val[close:]
open = strings.Index(val, "<")
last = strings.LastIndex(val, ">")
}
val = strings.Join(addrList, ", ")
return mail.ParseAddressList(val)
}

View File

@ -27,6 +27,7 @@ import (
"strings"
"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"
@ -365,7 +366,6 @@ func attachPublicKey(p *parser.Part, key, keyName string) {
})
}
// NOTE: We should use our own ParseAddressList here.
func parseMessageHeader(m *pmapi.Message, h message.Header) error { // nolint[funlen]
mimeHeader, err := toMailHeader(h)
if err != nil {
@ -373,59 +373,64 @@ func parseMessageHeader(m *pmapi.Message, h message.Header) error { // nolint[fu
}
m.Header = mimeHeader
if err := forEachDecodedHeaderField(h, func(key, val string) error {
switch strings.ToLower(key) {
fields := h.Fields()
for fields.Next() {
switch strings.ToLower(fields.Key()) {
case "subject":
m.Subject = val
s, err := fields.Text()
if err != nil {
if s, err = pmmime.DecodeHeader(fields.Value()); err != nil {
return errors.Wrap(err, "failed to parse subject")
}
}
m.Subject = s
case "from":
sender, err := parseAddressList(val)
sender, err := rfc5322.ParseAddressList(fields.Value())
if err != nil {
return err
return errors.Wrap(err, "failed to parse from")
}
if len(sender) > 0 {
m.Sender = sender[0]
}
case "to":
toList, err := parseAddressList(val)
toList, err := rfc5322.ParseAddressList(fields.Value())
if err != nil {
return err
return errors.Wrap(err, "failed to parse to")
}
m.ToList = toList
case "reply-to":
replyTos, err := parseAddressList(val)
replyTos, err := rfc5322.ParseAddressList(fields.Value())
if err != nil {
return err
return errors.Wrap(err, "failed to parse reply-to")
}
m.ReplyTos = replyTos
case "cc":
ccList, err := parseAddressList(val)
ccList, err := rfc5322.ParseAddressList(fields.Value())
if err != nil {
return err
return errors.Wrap(err, "failed to parse cc")
}
m.CCList = ccList
case "bcc":
bccList, err := parseAddressList(val)
bccList, err := rfc5322.ParseAddressList(fields.Value())
if err != nil {
return err
return errors.Wrap(err, "failed to parse bcc")
}
m.BCCList = bccList
case "date":
date, err := mail.ParseDate(val)
date, err := rfc5322.ParseDateTime(fields.Value())
if err != nil {
return err
return errors.Wrap(err, "failed to parse date")
}
m.Time = date.Unix()
}
return nil
}); err != nil {
return err
}
return nil
@ -469,29 +474,6 @@ func parseAttachment(h message.Header) (*pmapi.Attachment, error) {
return att, nil
}
func forEachDecodedHeaderField(h message.Header, fn func(string, string) error) error {
fields := h.Fields()
for fields.Next() {
text, err := fields.Text()
if err != nil {
if !message.IsUnknownCharset(err) {
return err
}
if text, err = pmmime.DecodeHeader(fields.Value()); err != nil {
return err
}
}
if err := fn(fields.Key(), text); err != nil {
return err
}
}
return nil
}
func toMailHeader(h message.Header) (mail.Header, error) {
mimeHeader := make(mail.Header)
@ -517,3 +499,26 @@ func toMIMEHeader(h message.Header) (textproto.MIMEHeader, error) {
return mimeHeader, nil
}
func forEachDecodedHeaderField(h message.Header, fn func(string, string) error) error {
fields := h.Fields()
for fields.Next() {
text, err := fields.Text()
if err != nil {
if !message.IsUnknownCharset(err) {
return err
}
if text, err = pmmime.DecodeHeader(fields.Value()); err != nil {
return err
}
}
if err := fn(fields.Key(), text); err != nil {
return err
}
}
return nil
}

View File

@ -498,80 +498,3 @@ func readerToString(r io.Reader) string {
return string(b)
}
func TestRFC822AddressFormat(t *testing.T) { //nolint[funlen]
tests := []struct {
address string
expected []string
}{
{
" normal name <username@server.com>",
[]string{
"\"normal name\" <username@server.com>",
},
},
{
" \"comma, name\" <username@server.com>",
[]string{
"\"comma, name\" <username@server.com>",
},
},
{
" name <username@server.com> (ignore comment)",
[]string{
"\"name\" <username@server.com>",
},
},
{
" name (ignore comment) <username@server.com>, (Comment as name) username2@server.com",
[]string{
"\"name\" <username@server.com>",
"<username2@server.com>",
},
},
{
" normal name <username@server.com>, (comment)All.(around)address@(the)server.com",
[]string{
"\"normal name\" <username@server.com>",
"<All.address@server.com>",
},
},
{
" normal name <username@server.com>, All.(\"comma, in comment\")address@(the)server.com",
[]string{
"\"normal name\" <username@server.com>",
"<All.address@server.com>",
},
},
{
" \"normal name\" <username@server.com>, \"comma, name\" <address@server.com>",
[]string{
"\"normal name\" <username@server.com>",
"\"comma, name\" <address@server.com>",
},
},
{
" \"comma, one\" <username@server.com>, \"comma, two\" <address@server.com>",
[]string{
"\"comma, one\" <username@server.com>",
"\"comma, two\" <address@server.com>",
},
},
{
" \"comma, name\" <username@server.com>, another, name <address@server.com>",
[]string{
"\"comma, name\" <username@server.com>",
"\"another, name\" <address@server.com>",
},
},
}
for _, data := range tests {
result, err := parseAddressList(data.address)
assert.NoError(t, err)
assert.Len(t, result, len(data.expected))
for i, result := range result {
assert.Equal(t, data.expected[i], result.String())
}
}
}

View File

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

View File

@ -0,0 +1,160 @@
# 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

@ -0,0 +1,98 @@
// 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

@ -0,0 +1,530 @@
// 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

@ -0,0 +1,58 @@
// 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

@ -0,0 +1,59 @@
// 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

@ -0,0 +1,43 @@
// 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

@ -0,0 +1,56 @@
// 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

@ -0,0 +1,49 @@
// 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 atom struct {
value string
}
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)
}
}

View File

@ -0,0 +1,79 @@
// 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

@ -0,0 +1,62 @@
// 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

@ -0,0 +1,50 @@
// 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

@ -0,0 +1,60 @@
// 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

@ -0,0 +1,49 @@
// 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

@ -0,0 +1,49 @@
// 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

@ -0,0 +1,55 @@
// 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

@ -0,0 +1,49 @@
// 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 fws struct {
value string
}
func (w *walker) EnterFws(ctx *parser.FwsContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering fws")
w.enter(&fws{
value: ctx.GetText(),
})
}
func (w *walker) ExitFws(ctx *parser.FwsContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting fws")
type withFws interface {
withFws(*fws)
}
res := w.exit().(*fws)
if parent, ok := w.parent().(withFws); ok {
parent.withFws(res)
}
}

View File

@ -0,0 +1,20 @@
// 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
//go:generate make antlr

View File

@ -0,0 +1,52 @@
// 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

@ -0,0 +1,52 @@
// 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

@ -0,0 +1,62 @@
// 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

@ -0,0 +1,60 @@
// 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

@ -0,0 +1,57 @@
// 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

@ -0,0 +1,59 @@
// 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

@ -0,0 +1,62 @@
// 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

@ -0,0 +1,84 @@
// 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

@ -0,0 +1,56 @@
// 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

@ -0,0 +1,54 @@
// 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

@ -0,0 +1,50 @@
// 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

@ -0,0 +1,50 @@
// 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

@ -0,0 +1,55 @@
// 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

@ -0,0 +1,82 @@
// 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

@ -0,0 +1,73 @@
// 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

@ -0,0 +1,701 @@
// 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

@ -0,0 +1,248 @@
// 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

@ -0,0 +1,83 @@
// 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

@ -0,0 +1,110 @@
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

@ -0,0 +1,110 @@
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

@ -0,0 +1,309 @@
// 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

@ -0,0 +1,435 @@
// 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

@ -0,0 +1,423 @@
// 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

@ -0,0 +1,49 @@
// 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 port struct {
value string
}
func (w *walker) EnterPort(ctx *parser.PortContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering port")
w.enter(&port{
value: ctx.GetText(),
})
}
func (w *walker) ExitPort(ctx *parser.PortContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting port")
type withPort interface {
withPort(*port)
}
res := w.exit().(*port)
if parent, ok := w.parent().(withPort); ok {
parent.withPort(res)
}
}

View File

@ -0,0 +1,49 @@
// 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

@ -0,0 +1,49 @@
// 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)
}
}

View File

@ -0,0 +1,54 @@
// 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 quotedContent struct {
value string
}
func (c *quotedContent) withQtext(qtext *qtext) {
c.value = qtext.value
}
func (c *quotedContent) withQuotedPair(quotedPair *quotedPair) {
c.value = quotedPair.value
}
func (w *walker) EnterQuotedContent(ctx *parser.QuotedContentContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering quotedContent")
w.enter(&quotedContent{})
}
func (w *walker) ExitQuotedContent(ctx *parser.QuotedContentContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting quotedContent")
type withQuotedContent interface {
withQuotedContent(*quotedContent)
}
res := w.exit().(*quotedContent)
if parent, ok := w.parent().(withQuotedContent); ok {
parent.withQuotedContent(res)
}
}

View File

@ -0,0 +1,50 @@
// 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 quotedPair struct {
value string
}
func (p *quotedPair) withQuotedChar(quotedChar *quotedChar) {
p.value = quotedChar.value
}
func (w *walker) EnterQuotedPair(ctx *parser.QuotedPairContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering quotedPair")
w.enter(&quotedPair{})
}
func (w *walker) ExitQuotedPair(ctx *parser.QuotedPairContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting quotedPair")
type withQuotedPair interface {
withQuotedPair(*quotedPair)
}
res := w.exit().(*quotedPair)
if parent, ok := w.parent().(withQuotedPair); ok {
parent.withQuotedPair(res)
}
}

View File

@ -0,0 +1,50 @@
// 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 quotedString struct {
value string
}
func (s *quotedString) withQuotedValue(quotedValue *quotedValue) {
s.value = quotedValue.value
}
func (w *walker) EnterQuotedString(ctx *parser.QuotedStringContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering quotedString")
w.enter(&quotedString{})
}
func (w *walker) ExitQuotedString(ctx *parser.QuotedStringContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting quotedString")
type withQuotedString interface {
withQuotedString(*quotedString)
}
res := w.exit().(*quotedString)
if parent, ok := w.parent().(withQuotedString); ok {
parent.withQuotedString(res)
}
}

View File

@ -0,0 +1,54 @@
// 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 quotedValue struct {
value string
}
func (v *quotedValue) withFws(fws *fws) {
v.value += fws.value
}
func (v *quotedValue) withQuotedContent(quotedContent *quotedContent) {
v.value += quotedContent.value
}
func (w *walker) EnterQuotedValue(ctx *parser.QuotedValueContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering quotedValue")
w.enter(&quotedValue{})
}
func (w *walker) ExitQuotedValue(ctx *parser.QuotedValueContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting quotedValue")
type withQuotedValue interface {
withQuotedValue(*quotedValue)
}
res := w.exit().(*quotedValue)
if parent, ok := w.parent().(withQuotedValue); ok {
parent.withQuotedValue(res)
}
}

View File

@ -0,0 +1,62 @@
// 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 second struct {
value int
}
func (w *walker) EnterSecond(ctx *parser.SecondContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering second")
var text string
for _, digit := range ctx.AllDigit() {
text += digit.GetText()
}
val, err := strconv.Atoi(text)
if err != nil {
w.err = err
}
w.enter(&second{
value: val,
})
}
func (w *walker) ExitSecond(ctx *parser.SecondContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting second")
type withSecond interface {
withSecond(*second)
}
res := w.exit().(*second)
if parent, ok := w.parent().(withSecond); ok {
parent.withSecond(res)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
// 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/antlr/antlr4/runtime/Go/antlr"
)
// walker implements parser.BaseRFC5322ParserListener, defining what to do at
// each node while traversing the syntax tree.
// It also implements antlr.DefaultErrorListener, allowing us to react to
// errors encountered while trying to determine the syntax tree of the input.
type walker struct {
parser.BaseRFC5322ParserListener
antlr.DefaultErrorListener
// nodes acts as a stack; when entering a node, it is pushed here, and when
// exiting a node, it is popped from here.
nodes []interface{}
// res holds the result of walking the parse tree.
res interface{}
// err holds the error encountered during parsing, if any.
err error
}
func (w *walker) enter(b interface{}) {
w.nodes = append(w.nodes, b)
}
func (w *walker) exit() interface{} {
b := w.nodes[len(w.nodes)-1]
w.nodes = w.nodes[:len(w.nodes)-1]
return b
}
func (w *walker) parent() (b interface{}) {
return w.nodes[len(w.nodes)-1]
}
func (w *walker) SyntaxError(_ antlr.Recognizer, _ interface{}, _, _ int, msg string, _ antlr.RecognitionException) {
w.err = fmt.Errorf("error parsing rfc5322 input: %v", msg)
}

View File

@ -0,0 +1,61 @@
// 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 word struct {
value string
}
func (w *word) withAtom(atom *atom) {
w.value = atom.value
}
func (w *word) withQuotedString(quotedString *quotedString) {
w.value = quotedString.value
}
func (w *word) withEncodedWord(encodedWord *encodedWord) {
w.value = encodedWord.value
}
func (w *walker) EnterWord(ctx *parser.WordContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering word")
w.enter(&word{
value: ctx.GetText(),
})
}
func (w *walker) ExitWord(ctx *parser.WordContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting word")
type withWord interface {
withWord(*word)
}
res := w.exit().(*word)
if parent, ok := w.parent().(withWord); ok {
parent.withWord(res)
}
}

View File

@ -0,0 +1,72 @@
// 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"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322/parser"
"github.com/sirupsen/logrus"
)
type year struct {
value int
}
func (w *walker) EnterYear(ctx *parser.YearContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering year")
var text string
for _, digit := range ctx.AllDigit() {
text += digit.GetText()
}
val, err := strconv.Atoi(text)
if err != nil {
w.err = err
}
// NOTE: 2-digit years are obsolete but let's just have some simple handling anyway.
if len(text) == 2 {
if val > time.Now().Year()%100 {
val += 1900
} else {
val += 2000
}
}
w.enter(&year{
value: val,
})
}
func (w *walker) ExitYear(ctx *parser.YearContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting year")
type withYear interface {
withYear(*year)
}
res := w.exit().(*year)
if parent, ok := w.parent().(withYear); ok {
parent.withYear(res)
}
}

View File

@ -0,0 +1,59 @@
// 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 zone struct {
location *time.Location
}
func (z *zone) withOffset(offset *offset) {
z.location = time.FixedZone(offset.rep, offset.value)
}
func (z *zone) withObsZone(obsZone *obsZone) {
z.location = obsZone.location
}
func (w *walker) EnterZone(ctx *parser.ZoneContext) {
logrus.WithField("text", ctx.GetText()).Trace("Entering zone")
w.enter(&zone{
location: time.UTC,
})
}
func (w *walker) ExitZone(ctx *parser.ZoneContext) {
logrus.WithField("text", ctx.GetText()).Trace("Exiting zone")
type withZone interface {
withZone(*zone)
}
res := w.exit().(*zone)
if parent, ok := w.parent().(withZone); ok {
parent.withZone(res)
}
}

View File

@ -32,7 +32,7 @@ import (
"golang.org/x/text/encoding/htmlindex"
)
var wordDec = &mime.WordDecoder{
var WordDec = &mime.WordDecoder{
CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
dec, err := SelectDecoder(charset)
if err != nil {
@ -180,7 +180,7 @@ func SelectDecoder(charset string) (decoder *encoding.Decoder, err error) {
// DecodeHeader if needed. Returns error if raw contains non-utf8 characters.
func DecodeHeader(raw string) (decoded string, err error) {
if decoded, err = wordDec.DecodeHeader(raw); err != nil {
if decoded, err = WordDec.DecodeHeader(raw); err != nil {
decoded = raw
}
if !utf8.ValidString(decoded) {

View File

@ -23,12 +23,12 @@ import (
"fmt"
"io"
"net"
"net/mail"
"os"
"strings"
"sync"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -148,12 +148,12 @@ func (c *SMTPClient) SendMail(r io.Reader, bcc string) *SMTPResponse {
from = c.address
if from == "" && strings.HasPrefix(line, "From: ") {
if addr, err := mail.ParseAddress(line[6:]); err == nil {
from = addr.Address
if addrs, err := rfc5322.ParseAddressList(line[6:]); err == nil {
from = addrs[0].Address
}
}
if strings.HasPrefix(line, "To: ") || strings.HasPrefix(line, "CC: ") {
if addrs, err := mail.ParseAddressList(line[4:]); err == nil {
if addrs, err := rfc5322.ParseAddressList(line[4:]); err == nil {
for _, addr := range addrs {
tos = append(tos, addr.Address)
}

View File

@ -19,11 +19,11 @@ package tests
import (
"fmt"
"net/mail"
"strings"
"time"
"github.com/ProtonMail/proton-bridge/internal/store"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/test/accounts"
"github.com/cucumber/godog"
@ -276,15 +276,15 @@ func messagesContainsMessageRow(account *accounts.TestAccount, allMessages []int
}
func areAddressesSame(first, second string) bool {
firstAddress, err := mail.ParseAddress(first)
firstAddress, err := rfc5322.ParseAddressList(first)
if err != nil {
return false
}
secondAddress, err := mail.ParseAddress(second)
secondAddress, err := rfc5322.ParseAddressList(second)
if err != nil {
return false
}
return firstAddress.Address == secondAddress.Address
return firstAddress[0].Address == secondAddress[0].Address
}
func messagesInMailboxForUserIsMarkedAsRead(bddMessageIDs, mailboxName, bddUserID string) error {

View File

@ -21,13 +21,13 @@ import (
"fmt"
"io"
"io/ioutil"
"net/mail"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/ProtonMail/proton-bridge/pkg/message/rfc5322"
"github.com/cucumber/godog"
"github.com/cucumber/godog/gherkin"
"github.com/emersion/go-mbox"
@ -243,7 +243,7 @@ func parseTime(input string) (time.Time, error) {
}
func parseAddresses(input string) ([]string, error) {
addresses, err := mail.ParseAddressList(input)
addresses, err := rfc5322.ParseAddressList(input)
if err != nil {
return nil, err
}
@ -255,11 +255,11 @@ func parseAddresses(input string) ([]string, error) {
}
func parseAddress(input string) (string, error) {
address, err := mail.ParseAddress(input)
address, err := rfc5322.ParseAddressList(input)
if err != nil {
return "", err
}
return address.Address, nil
return address[0].Address, nil
}
// BySubject implements sort.Interface based on the subject field.