mail 2.6.4 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +208 -156
- data/lib/mail/attachments_list.rb +13 -10
- data/lib/mail/body.rb +96 -107
- data/lib/mail/configuration.rb +2 -0
- data/lib/mail/constants.rb +27 -5
- data/lib/mail/elements/address.rb +61 -50
- data/lib/mail/elements/address_list.rb +11 -19
- data/lib/mail/elements/content_disposition_element.rb +9 -16
- data/lib/mail/elements/content_location_element.rb +6 -11
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
- data/lib/mail/elements/content_type_element.rb +16 -23
- data/lib/mail/elements/date_time_element.rb +7 -15
- data/lib/mail/elements/envelope_from_element.rb +22 -23
- data/lib/mail/elements/message_ids_element.rb +18 -13
- data/lib/mail/elements/mime_version_element.rb +7 -15
- data/lib/mail/elements/phrase_list.rb +12 -10
- data/lib/mail/elements/received_element.rb +27 -19
- data/lib/mail/encodings/7bit.rb +9 -14
- data/lib/mail/encodings/8bit.rb +2 -21
- data/lib/mail/encodings/base64.rb +11 -12
- data/lib/mail/encodings/binary.rb +3 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +6 -6
- data/lib/mail/encodings/transfer_encoding.rb +38 -29
- data/lib/mail/encodings/unix_to_unix.rb +4 -2
- data/lib/mail/encodings.rb +83 -56
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +181 -130
- data/lib/mail/field_list.rb +61 -8
- data/lib/mail/fields/bcc_field.rb +33 -52
- data/lib/mail/fields/cc_field.rb +27 -49
- data/lib/mail/fields/comments_field.rb +26 -37
- data/lib/mail/fields/common_address_field.rb +162 -0
- data/lib/mail/fields/common_date_field.rb +56 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +41 -0
- data/lib/mail/fields/content_description_field.rb +6 -14
- data/lib/mail/fields/content_disposition_field.rb +11 -38
- data/lib/mail/fields/content_id_field.rb +23 -51
- data/lib/mail/fields/content_location_field.rb +10 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
- data/lib/mail/fields/content_type_field.rb +53 -84
- data/lib/mail/fields/date_field.rb +22 -52
- data/lib/mail/fields/from_field.rb +27 -49
- data/lib/mail/fields/in_reply_to_field.rb +37 -49
- data/lib/mail/fields/keywords_field.rb +17 -31
- data/lib/mail/fields/message_id_field.rb +24 -71
- data/lib/mail/fields/mime_version_field.rb +18 -30
- data/lib/mail/fields/named_structured_field.rb +10 -0
- data/lib/mail/fields/named_unstructured_field.rb +10 -0
- data/lib/mail/fields/optional_field.rb +9 -8
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
- data/lib/mail/fields/received_field.rb +42 -57
- data/lib/mail/fields/references_field.rb +34 -49
- data/lib/mail/fields/reply_to_field.rb +27 -49
- data/lib/mail/fields/resent_bcc_field.rb +27 -49
- data/lib/mail/fields/resent_cc_field.rb +27 -49
- data/lib/mail/fields/resent_date_field.rb +4 -30
- data/lib/mail/fields/resent_from_field.rb +27 -49
- data/lib/mail/fields/resent_message_id_field.rb +4 -29
- data/lib/mail/fields/resent_sender_field.rb +26 -56
- data/lib/mail/fields/resent_to_field.rb +27 -49
- data/lib/mail/fields/return_path_field.rb +49 -54
- data/lib/mail/fields/sender_field.rb +33 -55
- data/lib/mail/fields/structured_field.rb +2 -30
- data/lib/mail/fields/subject_field.rb +8 -11
- data/lib/mail/fields/to_field.rb +27 -49
- data/lib/mail/fields/unstructured_field.rb +31 -47
- data/lib/mail/fields.rb +9 -0
- data/lib/mail/header.rb +71 -110
- data/lib/mail/mail.rb +34 -37
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/matchers/has_sent_mail.rb +21 -1
- data/lib/mail/message.rb +126 -127
- data/lib/mail/multibyte/chars.rb +24 -181
- data/lib/mail/multibyte/unicode.rb +11 -11
- data/lib/mail/multibyte/utils.rb +26 -43
- data/lib/mail/multibyte.rb +55 -16
- data/lib/mail/network/delivery_methods/exim.rb +8 -11
- data/lib/mail/network/delivery_methods/file_delivery.rb +15 -18
- data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +32 -35
- data/lib/mail/network/delivery_methods/smtp.rb +125 -68
- data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -16
- data/lib/mail/network/delivery_methods/test_mailer.rb +12 -13
- data/lib/mail/network/retriever_methods/base.rb +13 -13
- data/lib/mail/network/retriever_methods/imap.rb +25 -9
- data/lib/mail/network/retriever_methods/pop3.rb +25 -23
- data/lib/mail/network/retriever_methods/test_retriever.rb +3 -2
- data/lib/mail/network.rb +1 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33228 -116
- data/lib/mail/parsers/address_lists_parser.rl +183 -0
- data/lib/mail/parsers/content_disposition_parser.rb +885 -49
- data/lib/mail/parsers/content_disposition_parser.rl +93 -0
- data/lib/mail/parsers/content_location_parser.rb +812 -23
- data/lib/mail/parsers/content_location_parser.rl +82 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +512 -21
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
- data/lib/mail/parsers/content_type_parser.rb +1039 -55
- data/lib/mail/parsers/content_type_parser.rl +94 -0
- data/lib/mail/parsers/date_time_parser.rb +880 -25
- data/lib/mail/parsers/date_time_parser.rl +73 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3672 -40
- data/lib/mail/parsers/envelope_from_parser.rl +93 -0
- data/lib/mail/parsers/message_ids_parser.rb +5149 -25
- data/lib/mail/parsers/message_ids_parser.rl +97 -0
- data/lib/mail/parsers/mime_version_parser.rb +500 -26
- data/lib/mail/parsers/mime_version_parser.rl +72 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +873 -22
- data/lib/mail/parsers/phrase_lists_parser.rl +94 -0
- data/lib/mail/parsers/received_parser.rb +8779 -43
- data/lib/mail/parsers/received_parser.rl +95 -0
- data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/lib/mail/parsers/rfc5322.rl +74 -0
- data/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
- data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/lib/mail/parsers.rb +11 -25
- data/lib/mail/part.rb +25 -29
- data/lib/mail/parts_list.rb +62 -6
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +361 -74
- data/lib/mail/version.rb +2 -2
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +4 -37
- metadata +125 -67
- data/CHANGELOG.rdoc +0 -787
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -11
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/lib/mail/check_delivery_params.rb +0 -21
- data/lib/mail/core_extensions/smtp.rb +0 -25
- data/lib/mail/core_extensions/string/access.rb +0 -146
- data/lib/mail/core_extensions/string/multibyte.rb +0 -79
- data/lib/mail/core_extensions/string.rb +0 -21
- data/lib/mail/fields/common/address_container.rb +0 -17
- data/lib/mail/fields/common/common_address.rb +0 -136
- data/lib/mail/fields/common/common_date.rb +0 -36
- data/lib/mail/fields/common/common_field.rb +0 -61
- data/lib/mail/fields/common/common_message_id.rb +0 -49
- data/lib/mail/multibyte/exceptions.rb +0 -9
- data/lib/mail/parsers/ragel/common.rl +0 -185
- data/lib/mail/parsers/ragel/parser_info.rb +0 -61
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
- data/lib/mail/parsers/ragel/ruby.rb +0 -40
- data/lib/mail/parsers/ragel.rb +0 -18
- data/lib/mail/version_specific/ruby_1_8.rb +0 -126
- data/lib/mail/version_specific/ruby_1_9.rb +0 -223
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'mail/utilities'
|
|
3
|
+
require 'mail/parser_tools'
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
original_verbose, $VERBOSE = $VERBOSE, nil
|
|
7
|
+
|
|
8
|
+
%%{
|
|
9
|
+
machine date_time;
|
|
10
|
+
alphtype int;
|
|
11
|
+
|
|
12
|
+
# Received Tokens
|
|
13
|
+
action received_tokens_s { received_tokens_s = p }
|
|
14
|
+
action received_tokens_e { received.info = chars(data, received_tokens_s, p-1) }
|
|
15
|
+
|
|
16
|
+
# Date
|
|
17
|
+
action date_s { date_s = p }
|
|
18
|
+
action date_e { received.date = chars(data, date_s, p-1).strip }
|
|
19
|
+
|
|
20
|
+
# Time
|
|
21
|
+
action time_s { time_s = p }
|
|
22
|
+
action time_e { received.time = chars(data, time_s, p-1) }
|
|
23
|
+
|
|
24
|
+
# No-op actions
|
|
25
|
+
action address_s {}
|
|
26
|
+
action address_e {}
|
|
27
|
+
action angle_addr_s {}
|
|
28
|
+
action ctime_date_s {}
|
|
29
|
+
action ctime_date_e {}
|
|
30
|
+
action comment_e {}
|
|
31
|
+
action comment_s {}
|
|
32
|
+
action phrase_s {}
|
|
33
|
+
action phrase_e {}
|
|
34
|
+
action domain_e {}
|
|
35
|
+
action domain_s {}
|
|
36
|
+
action local_dot_atom_e {}
|
|
37
|
+
action local_dot_atom_pre_comment_e {}
|
|
38
|
+
action local_dot_atom_pre_comment_s {}
|
|
39
|
+
action local_dot_atom_s {}
|
|
40
|
+
action qstr_e {}
|
|
41
|
+
action qstr_s {}
|
|
42
|
+
action local_quoted_string_s {}
|
|
43
|
+
action local_quoted_string_e {}
|
|
44
|
+
action obs_domain_list_s {}
|
|
45
|
+
action obs_domain_list_e {}
|
|
46
|
+
action group_name_s {}
|
|
47
|
+
action group_name_e {}
|
|
48
|
+
action msg_id_s {}
|
|
49
|
+
action msg_id_e {}
|
|
50
|
+
|
|
51
|
+
include rfc5322 "rfc5322.rl";
|
|
52
|
+
main := received;
|
|
53
|
+
}%%
|
|
54
|
+
|
|
55
|
+
module Mail::Parsers
|
|
56
|
+
module ReceivedParser
|
|
57
|
+
extend Mail::ParserTools
|
|
58
|
+
|
|
59
|
+
ReceivedStruct = Struct.new(:date, :time, :info, :error)
|
|
60
|
+
|
|
61
|
+
%%write data noprefix;
|
|
62
|
+
|
|
63
|
+
def self.parse(data)
|
|
64
|
+
data = data.dup.force_encoding(Encoding::ASCII_8BIT) if data.respond_to?(:force_encoding)
|
|
65
|
+
|
|
66
|
+
raise Mail::Field::NilParseError.new(Mail::ReceivedElement) if data.nil?
|
|
67
|
+
|
|
68
|
+
# Parser state
|
|
69
|
+
received = ReceivedStruct.new
|
|
70
|
+
received_tokens_s = date_s = time_s = nil
|
|
71
|
+
|
|
72
|
+
# 5.1 Variables Used by Ragel
|
|
73
|
+
p = 0
|
|
74
|
+
eof = pe = data.length
|
|
75
|
+
stack = []
|
|
76
|
+
|
|
77
|
+
%%write init;
|
|
78
|
+
%%write exec;
|
|
79
|
+
|
|
80
|
+
if false
|
|
81
|
+
testEof
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if p != eof || cs < %%{ write first_final; }%%
|
|
85
|
+
raise Mail::Field::IncompleteParseError.new(Mail::ReceivedElement, data, p)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
received
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
ensure
|
|
94
|
+
$VERBOSE = original_verbose
|
|
95
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 2045 Section 6.1: Content-Transfer-Encoding Header Field
|
|
3
|
+
# https://tools.ietf.org/html/rfc2045#section-6.1
|
|
4
|
+
machine rfc2045_content_transfer_encoding;
|
|
5
|
+
alphtype int;
|
|
6
|
+
|
|
7
|
+
include rfc2045_content_type "rfc2045_content_type.rl";
|
|
8
|
+
|
|
9
|
+
encoding = ('7bits' | '8bits' | '7bit' | '8bit' | 'binary' |
|
|
10
|
+
'quoted-printable' | 'base64' | ietf_token |
|
|
11
|
+
custom_x_token) >encoding_s %encoding_e;
|
|
12
|
+
content_transfer_encoding = CFWS? encoding CFWS? ";"? CFWS?;
|
|
13
|
+
}%%
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 2045 Section 5.1: Content-Type Header Field
|
|
3
|
+
# https://tools.ietf.org/html/rfc2045#section-5.1
|
|
4
|
+
# Previously: https://tools.ietf.org/html/rfc1049#section-3
|
|
5
|
+
machine rfc2045_content_type;
|
|
6
|
+
alphtype int;
|
|
7
|
+
|
|
8
|
+
include rfc5322_lexical_tokens "rfc5322_lexical_tokens.rl";
|
|
9
|
+
|
|
10
|
+
token = 0x21..0x27 | 0x2a..0x2b | 0x2c..0x2e | 0x30..0x39 | 0x41..0x5a | 0x5e..0x7e;
|
|
11
|
+
value = (quoted_string | (token -- '"' | 0x3d)+) >param_val_s %param_val_e;
|
|
12
|
+
attribute = (token+) >param_attr_s %param_attr_e;
|
|
13
|
+
parameter = CFWS? attribute "=" value CFWS?;
|
|
14
|
+
|
|
15
|
+
ietf_token = token+;
|
|
16
|
+
custom_x_token = 'x'i "-" token+;
|
|
17
|
+
extension_token = ietf_token | custom_x_token;
|
|
18
|
+
discrete_type = 'text'i | 'image'i | 'audio'i | 'video'i |
|
|
19
|
+
'application'i | extension_token;
|
|
20
|
+
composite_type = 'message'i | 'multipart'i | extension_token;
|
|
21
|
+
iana_token = token+;
|
|
22
|
+
main_type = (discrete_type | composite_type) >main_type_s %main_type_e;
|
|
23
|
+
sub_type = (extension_token | iana_token) >sub_type_s %sub_type_e;
|
|
24
|
+
content_type = main_type "/" sub_type (((CFWS? ";"+) | CFWS) parameter CFWS?)*;
|
|
25
|
+
}%%
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 2045 MIME
|
|
3
|
+
# https://tools.ietf.org/html/rfc2045
|
|
4
|
+
machine rfc2045_mime;
|
|
5
|
+
alphtype int;
|
|
6
|
+
|
|
7
|
+
include rfc5322_lexical_tokens "rfc5322_lexical_tokens.rl";
|
|
8
|
+
|
|
9
|
+
# 4. MIME-Version Header Field
|
|
10
|
+
# https://tools.ietf.org/html/rfc2045#section-4
|
|
11
|
+
mime_version = CFWS?
|
|
12
|
+
(DIGIT+ >major_digits_s %major_digits_e)
|
|
13
|
+
comment? "." comment?
|
|
14
|
+
(DIGIT+ >minor_digits_s %minor_digits_e)
|
|
15
|
+
CFWS?;
|
|
16
|
+
}%%
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 2183 The Content-Disposition Header Field
|
|
3
|
+
# https://tools.ietf.org/html/rfc2183#section-2
|
|
4
|
+
#
|
|
5
|
+
# TODO: recognize filename, size, creation date, etc.
|
|
6
|
+
machine rfc2183_content_disposition;
|
|
7
|
+
alphtype int;
|
|
8
|
+
|
|
9
|
+
include rfc2045_content_type "rfc2045_content_type.rl";
|
|
10
|
+
|
|
11
|
+
disposition_type = 'inline'i | 'attachment'i | extension_token;
|
|
12
|
+
disposition_parm = parameter;
|
|
13
|
+
disposition = (disposition_type >disp_type_s %disp_type_e)
|
|
14
|
+
(";" disposition_parm)*;
|
|
15
|
+
}%%
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 3629 4. Syntax of UTF-8 Byte Sequences
|
|
3
|
+
# https://tools.ietf.org/html/rfc3629#section-4
|
|
4
|
+
machine rfc3629_utf8;
|
|
5
|
+
alphtype int;
|
|
6
|
+
|
|
7
|
+
utf8_tail = 0x80..0xBF;
|
|
8
|
+
|
|
9
|
+
utf8_2byte = 0xC2..0xDF utf8_tail;
|
|
10
|
+
utf8_3byte = 0xE0 0xA0..0xBF utf8_tail |
|
|
11
|
+
0xE1..0xEC utf8_tail utf8_tail |
|
|
12
|
+
0xED 0x80..0x9F utf8_tail |
|
|
13
|
+
0xEE..0xEF utf8_tail utf8_tail;
|
|
14
|
+
utf8_4byte = 0xF0 0x90..0xBF utf8_tail utf8_tail |
|
|
15
|
+
0xF1..0xF3 utf8_tail utf8_tail utf8_tail |
|
|
16
|
+
0xF4 0x80..0x8F utf8_tail utf8_tail;
|
|
17
|
+
|
|
18
|
+
utf8_non_ascii = utf8_2byte | utf8_3byte | utf8_4byte;
|
|
19
|
+
}%%
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 5234 B.1. Core Rules
|
|
3
|
+
# https://tools.ietf.org/html/rfc5234#appendix-B.1
|
|
4
|
+
machine rfc5234_abnf_core_rules;
|
|
5
|
+
alphtype int;
|
|
6
|
+
|
|
7
|
+
include rfc3629_utf8 "rfc3629_utf8.rl";
|
|
8
|
+
|
|
9
|
+
LF = "\n";
|
|
10
|
+
CR = "\r";
|
|
11
|
+
CRLF = "\r\n";
|
|
12
|
+
SP = " ";
|
|
13
|
+
HTAB = "\t";
|
|
14
|
+
WSP = SP | HTAB;
|
|
15
|
+
DQUOTE = '"';
|
|
16
|
+
DIGIT = [0-9];
|
|
17
|
+
ALPHA = [a-zA-Z];
|
|
18
|
+
|
|
19
|
+
# RFC6532 extension for UTF-8 content
|
|
20
|
+
rfc5234_VCHAR = 0x21..0x7e;
|
|
21
|
+
VCHAR = rfc5234_VCHAR | utf8_non_ascii;
|
|
22
|
+
}%%
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 5322 Internet Message Format
|
|
3
|
+
# https://tools.ietf.org/html/rfc5322
|
|
4
|
+
#
|
|
5
|
+
# RFC 6854 Update to Internet Message Format to Allow Group Syntax in the "From:" and "Sender:" Header Fields
|
|
6
|
+
# https://tools.ietf.org/html/rfc6854
|
|
7
|
+
machine rfc5322;
|
|
8
|
+
alphtype int;
|
|
9
|
+
|
|
10
|
+
include rfc5234_abnf_core_rules "rfc5234_abnf_core_rules.rl";
|
|
11
|
+
|
|
12
|
+
# 3.2. Lexical Tokens
|
|
13
|
+
include rfc5322_lexical_tokens "rfc5322_lexical_tokens.rl";
|
|
14
|
+
|
|
15
|
+
# 3.3. Date and Time Specification
|
|
16
|
+
include rfc5322_date_time "rfc5322_date_time.rl";
|
|
17
|
+
|
|
18
|
+
# 3.4. Address Specification
|
|
19
|
+
include rfc5322_address "rfc5322_address.rl";
|
|
20
|
+
|
|
21
|
+
# 3.5. Overall Message Syntax
|
|
22
|
+
#rfc5322_text = 0x01..0x09 | "\v" | "\f" | 0x0e..0x1f;
|
|
23
|
+
#text = rfc5322_text | utf8_non_ascii; # RFC6532 for UTF-8
|
|
24
|
+
#obs_body = ((LF* CR* ((0x00 | text) LF* CR*)*) | CRLF)*
|
|
25
|
+
#body = ((text{,998} CRLF)* text{,998}) | obs_body;
|
|
26
|
+
#message = (fields | obs_fields) (CRLF body)?;
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# 3.6. Field Definitions
|
|
30
|
+
|
|
31
|
+
# 3.6.4. Identification Fields
|
|
32
|
+
obs_id_left = local_part;
|
|
33
|
+
obs_id_right = domain;
|
|
34
|
+
no_fold_literal = "[" (dtext)* "]";
|
|
35
|
+
|
|
36
|
+
msg_id_atext = rfc5322_atext | ":" | "," | "." | " ";
|
|
37
|
+
|
|
38
|
+
id_left = msg_id_atext+ | obs_id_left;
|
|
39
|
+
id_left_ns = ( msg_id_atext - ( " " | "," ) )+;
|
|
40
|
+
|
|
41
|
+
# id_right modifications to support multiple '@' in msg_id.
|
|
42
|
+
id_right = ( msg_id_atext | "@" )+ | no_fold_literal | obs_id_right;
|
|
43
|
+
id_right_ns = ( msg_id_atext - ( " " | "," ) | "@" )+ | no_fold_literal;
|
|
44
|
+
|
|
45
|
+
# Handle various message-id formats:
|
|
46
|
+
# <id_left@id_right>
|
|
47
|
+
# <id_left@id_right...
|
|
48
|
+
# <id_left@>
|
|
49
|
+
# <id_left>
|
|
50
|
+
# <id_left...
|
|
51
|
+
# id_left@id_right
|
|
52
|
+
# id_left
|
|
53
|
+
# Handle comma-separated message_ids.
|
|
54
|
+
msg_id = (CFWS)? (
|
|
55
|
+
(("<" id_left "@" id_right? ">") >msg_id_s %msg_id_e) |
|
|
56
|
+
(("<" id_left "@" id_right? :>> "...") >msg_id_s %msg_id_e) |
|
|
57
|
+
(("<" id_left ">") >msg_id_s %msg_id_e) |
|
|
58
|
+
(("<" id_left :>> "..." ) >msg_id_s %msg_id_e) |
|
|
59
|
+
((id_left_ns ("@" id_right_ns)? ) >msg_id_s %msg_id_e)
|
|
60
|
+
) (CFWS)? <: ","?;
|
|
61
|
+
message_ids = msg_id**;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# 3.6.7 Trace Fields
|
|
65
|
+
# Added CFWS? to increase robustness (qmail likes to include a comment)
|
|
66
|
+
received_token = word | angle_addr | addr_spec_no_angle_brackets | domain;
|
|
67
|
+
received = ((CFWS? received_token*) >received_tokens_s %received_tokens_e)
|
|
68
|
+
";" date_time;
|
|
69
|
+
|
|
70
|
+
# Envelope From
|
|
71
|
+
ctime_date = day_name " "+ month " "+ day " " time_of_day " " year;
|
|
72
|
+
null_sender = ('<>' ' '{0,1});
|
|
73
|
+
envelope_from = (addr_spec_no_angle_brackets | null_sender) >address_s %address_e (" " (ctime_date >ctime_date_s %ctime_date_e))?;
|
|
74
|
+
}%%
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 5322 Internet Message Format
|
|
3
|
+
# Section 3.4. Address Specification
|
|
4
|
+
# https://tools.ietf.org/html/rfc5322#section-3.4
|
|
5
|
+
machine rfc5322_address;
|
|
6
|
+
alphtype int;
|
|
7
|
+
|
|
8
|
+
include rfc5234_abnf_core_rules "rfc5234_abnf_core_rules.rl";
|
|
9
|
+
include rfc5322_lexical_tokens "rfc5322_lexical_tokens.rl";
|
|
10
|
+
|
|
11
|
+
# local_part:
|
|
12
|
+
domain_text = (DQUOTE (FWS? qcontent)+ FWS? DQUOTE) | atext+;
|
|
13
|
+
local_dot_atom_text = ("."* domain_text "."*)+;
|
|
14
|
+
local_dot_atom = CFWS?
|
|
15
|
+
(local_dot_atom_text >local_dot_atom_s %local_dot_atom_pre_comment_e)
|
|
16
|
+
CFWS?;
|
|
17
|
+
obs_local_part = word ("." word)*;
|
|
18
|
+
local_part = (local_dot_atom >local_dot_atom_s %local_dot_atom_e |
|
|
19
|
+
(quoted_string %local_quoted_string_e) |
|
|
20
|
+
obs_local_part);
|
|
21
|
+
|
|
22
|
+
# Treetop parser behavior was to ignore addresses missing '@' inside of angle
|
|
23
|
+
# brackets. This construction preserves that behavior.
|
|
24
|
+
local_part_no_capture = (local_dot_atom | quoted_string | obs_local_part);
|
|
25
|
+
|
|
26
|
+
# domain:
|
|
27
|
+
domain_dot_atom_text = "."* domain_text ("."* domain_text)*;
|
|
28
|
+
obs_dtext = obs_NO_WS_CTL | quoted_pair;
|
|
29
|
+
rfc5322_dtext = 0x21..0x5a | 0x5e..0x7e | obs_dtext;
|
|
30
|
+
dtext = rfc5322_dtext | utf8_non_ascii; # RFC6532 for UTF-8
|
|
31
|
+
domain_dot_atom = CFWS? domain_dot_atom_text (CFWS? >(comment_after_address,1));
|
|
32
|
+
domain_literal = CFWS? "[" (FWS? dtext)* FWS? "]" CFWS?;
|
|
33
|
+
obs_domain = atom ("." atom)*;
|
|
34
|
+
domain = (domain_dot_atom | domain_literal | obs_domain) >domain_s %domain_e;
|
|
35
|
+
|
|
36
|
+
# 3.4.1. Addr-Spec Specification
|
|
37
|
+
|
|
38
|
+
# The %(end_addr,N) priority resolves uncertainty when whitespace
|
|
39
|
+
# after an addr_spec could cause it to be interpreted as a
|
|
40
|
+
# display name: "bar@example.com ,..."
|
|
41
|
+
|
|
42
|
+
addr_spec_in_angle_brackets =
|
|
43
|
+
(local_part "@" domain) %(end_addr,1) |
|
|
44
|
+
local_part_no_capture %(end_addr,0);
|
|
45
|
+
|
|
46
|
+
addr_spec_no_angle_brackets =
|
|
47
|
+
(local_part "@" domain) %(end_addr,1) |
|
|
48
|
+
local_part %(end_addr,0);
|
|
49
|
+
|
|
50
|
+
# angle_addr:
|
|
51
|
+
obs_domain_list = (CFWS | ",")* "@" domain ("," CFWS? ("@" domain)?)*;
|
|
52
|
+
obs_route = (obs_domain_list ":") >obs_domain_list_s %obs_domain_list_e;
|
|
53
|
+
obs_angle_addr = CFWS? "<" obs_route? addr_spec_in_angle_brackets ">" CFWS?;
|
|
54
|
+
|
|
55
|
+
angle_addr = CFWS? ("<" >angle_addr_s) addr_spec_in_angle_brackets ">" CFWS? |
|
|
56
|
+
obs_angle_addr;
|
|
57
|
+
|
|
58
|
+
# 3.4. Address Specification
|
|
59
|
+
display_name = phrase;
|
|
60
|
+
name_addr = display_name? %(end_addr,2) angle_addr;
|
|
61
|
+
mailbox = (name_addr | addr_spec_no_angle_brackets) >address_s %address_e;
|
|
62
|
+
obs_mbox_list = (CFWS? ",")* mailbox ("," (mailbox | CFWS)?)*;
|
|
63
|
+
mailbox_list = (mailbox (("," | ";") mailbox)*) | obs_mbox_list;
|
|
64
|
+
obs_group_list = (CFWS? ",")+ CFWS?;
|
|
65
|
+
group_list = mailbox_list | CFWS | obs_group_list;
|
|
66
|
+
group = (display_name >group_name_s %group_name_e) ":"
|
|
67
|
+
(group_list?) ";" CFWS?;
|
|
68
|
+
address = group | mailbox;
|
|
69
|
+
#obs_addr_list = (CFWS? ",")* address ("," (address | CFWS)?)*;
|
|
70
|
+
address_lists = address? %(comment_after_address,0)
|
|
71
|
+
(FWS* ("," | ";") FWS* address?)*;
|
|
72
|
+
}%%
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
%%{
|
|
2
|
+
# RFC 5322 Internet Message Format
|
|
3
|
+
# Section 3.3. Date and Time Specification
|
|
4
|
+
# https://tools.ietf.org/html/rfc5322#section-3.3
|
|
5
|
+
machine rfc5322_date_time;
|
|
6
|
+
alphtype int;
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
include rfc5322_lexical_tokens "rfc5322_lexical_tokens.rl";
|
|
4
9
|
|
|
5
10
|
# day_of_week
|
|
6
11
|
day_name = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun";
|
|
@@ -27,4 +32,6 @@
|
|
|
27
32
|
zone = FWS ((("+" | "-") DIGIT DIGIT DIGIT DIGIT) | obs_zone);
|
|
28
33
|
time = time_of_day zone;
|
|
29
34
|
|
|
35
|
+
date_time = (day_of_week ",")?
|
|
36
|
+
(date >date_s %date_e) <: (time >time_s %time_e) CFWS?;
|
|
30
37
|
}%%
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
# RFC 5322 Internet Message Format
|
|
3
|
+
# Section 3.2. Lexical Tokens
|
|
4
|
+
# https://tools.ietf.org/html/rfc5322#section-3.2
|
|
5
|
+
machine rfc5322_lexical_tokens;
|
|
6
|
+
alphtype int;
|
|
7
|
+
|
|
8
|
+
include rfc5234_abnf_core_rules "rfc5234_abnf_core_rules.rl";
|
|
9
|
+
|
|
10
|
+
# 3.2.1. Quoted characters
|
|
11
|
+
obs_NO_WS_CTL = 0x01..0x08 | "\v" | "\f" | 0x0e..0x1f | 0x7f;
|
|
12
|
+
obs_qp = "\\" (0x00 | obs_NO_WS_CTL | LF | CR);
|
|
13
|
+
quoted_pair = ("\\" (VCHAR | WSP)) | obs_qp;
|
|
14
|
+
|
|
15
|
+
# 3.2.2. Folding White Space and Comments
|
|
16
|
+
obs_FWS = (CRLF? WSP)+;
|
|
17
|
+
FWS = (WSP* CRLF WSP+) | (CRLF WSP+) | obs_FWS;
|
|
18
|
+
|
|
19
|
+
obs_ctext = obs_NO_WS_CTL;
|
|
20
|
+
rfc5322_ctext = 0x21..0x27 | 0x2a..0x5b | 0x5d..0x7e | obs_ctext;
|
|
21
|
+
ctext = rfc5322_ctext | utf8_non_ascii; # RFC6532 for UTF-8
|
|
22
|
+
|
|
23
|
+
# Recursive comments
|
|
24
|
+
action comment_begin { fcall comment_tail; }
|
|
25
|
+
action comment_exit { fret; }
|
|
26
|
+
ccontent = ctext | quoted_pair | "(" @comment_begin;
|
|
27
|
+
comment_tail := ((FWS? ccontent)* >comment_s) FWS? ")" @comment_exit;
|
|
28
|
+
comment = "(" @comment_begin %comment_e;
|
|
29
|
+
CFWS = ((FWS? comment)+ FWS?) | FWS;
|
|
30
|
+
|
|
31
|
+
# 3.2.3. Atom
|
|
32
|
+
rfc5322_atext = ALPHA | DIGIT | "!" | "#" | "$" | "%" | "&" |
|
|
33
|
+
"'" | "*" | "+" | "-" | "/" | "=" | "?" | "^" |
|
|
34
|
+
"_" | "`" | "{" | "|" | "}" | "~";
|
|
35
|
+
atext = rfc5322_atext | utf8_non_ascii; # RFC6532 for UTF-8
|
|
36
|
+
atom = CFWS? atext+ CFWS?;
|
|
37
|
+
dot_atom_text = atext ("." atext)*;
|
|
38
|
+
dot_atom = CFWS? dot_atom_text CFWS?;
|
|
39
|
+
|
|
40
|
+
# 3.2.4. Quoted Strings
|
|
41
|
+
obs_qtext = obs_NO_WS_CTL;
|
|
42
|
+
rfc5322_qtext = 0x21 | 0x23..0x5b | 0x5d..0x7e | obs_qtext;
|
|
43
|
+
qtext = rfc5322_qtext | utf8_non_ascii; # RFC6532 for UTF-8
|
|
44
|
+
|
|
45
|
+
qcontent = qtext | quoted_pair;
|
|
46
|
+
quoted_string = CFWS?
|
|
47
|
+
(DQUOTE
|
|
48
|
+
(((FWS? qcontent)* FWS?) >qstr_s %qstr_e)
|
|
49
|
+
DQUOTE)
|
|
50
|
+
CFWS?;
|
|
51
|
+
|
|
52
|
+
# 3.2.5. Miscellaneous Tokens
|
|
53
|
+
word = atom | quoted_string;
|
|
54
|
+
|
|
55
|
+
obs_phrase = (word | "." | "@")+;
|
|
56
|
+
phrase = (obs_phrase | word+) >phrase_s %phrase_e;
|
|
57
|
+
|
|
58
|
+
# Not part of RFC, used for keywords per 3.6.5 Information Fields
|
|
59
|
+
phrase_lists = phrase ("," FWS* phrase)*;
|
|
60
|
+
}%%
|
data/lib/mail/parsers.rb
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
module Mail
|
|
3
|
-
module Parsers
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
EnvelopeFromStruct = Struct.new(:address, :ctime_date, :error)
|
|
17
|
-
MessageIdsStruct = Struct.new(:message_ids, :error)
|
|
18
|
-
MimeVersionStruct = Struct.new(:major, :minor, :error)
|
|
19
|
-
PhraseListsStruct = Struct.new(:phrases, :error)
|
|
20
|
-
ReceivedStruct = Struct.new(:date, :time, :info, :error)
|
|
21
|
-
|
|
22
|
-
require 'mail/parsers/ragel/parser_info'
|
|
23
|
-
Ragel::FIELD_PARSERS.each do |field_parser|
|
|
24
|
-
require "mail/parsers/#{field_parser}_parser"
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
3
|
+
require 'mail/parsers/address_lists_parser'
|
|
4
|
+
require 'mail/parsers/content_disposition_parser'
|
|
5
|
+
require 'mail/parsers/content_location_parser'
|
|
6
|
+
require 'mail/parsers/content_transfer_encoding_parser'
|
|
7
|
+
require 'mail/parsers/content_type_parser'
|
|
8
|
+
require 'mail/parsers/date_time_parser'
|
|
9
|
+
require 'mail/parsers/envelope_from_parser'
|
|
10
|
+
require 'mail/parsers/message_ids_parser'
|
|
11
|
+
require 'mail/parsers/mime_version_parser'
|
|
12
|
+
require 'mail/parsers/phrase_lists_parser'
|
|
13
|
+
require 'mail/parsers/received_parser'
|
data/lib/mail/part.rb
CHANGED
|
@@ -1,43 +1,39 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# frozen_string_literal: true
|
|
3
|
+
require 'mail/constants'
|
|
4
|
+
require 'mail/utilities'
|
|
5
|
+
|
|
3
6
|
module Mail
|
|
4
7
|
class Part < Message
|
|
5
|
-
|
|
6
8
|
# Creates a new empty Content-ID field and inserts it in the correct order
|
|
7
9
|
# into the Header. The ContentIdField object will automatically generate
|
|
8
10
|
# a unique content ID if you try and encode it or output it to_s without
|
|
9
11
|
# specifying a content id.
|
|
10
|
-
#
|
|
12
|
+
#
|
|
11
13
|
# It will preserve the content ID you specify if you do.
|
|
12
14
|
def add_content_id(content_id_val = '')
|
|
13
15
|
header['content-id'] = content_id_val
|
|
14
16
|
end
|
|
15
|
-
|
|
17
|
+
|
|
16
18
|
# Returns true if the part has a content ID field, the field may or may
|
|
17
19
|
# not have a value, but the field exists or not.
|
|
18
20
|
def has_content_id?
|
|
19
21
|
header.has_content_id?
|
|
20
22
|
end
|
|
21
|
-
|
|
22
|
-
def inline_content_id
|
|
23
|
-
# TODO: Deprecated in 2.2.2 - Remove in 2.3
|
|
24
|
-
STDERR.puts("Part#inline_content_id is deprecated, please call Part#cid instead")
|
|
25
|
-
cid
|
|
26
|
-
end
|
|
27
|
-
|
|
23
|
+
|
|
28
24
|
def cid
|
|
29
25
|
add_content_id unless has_content_id?
|
|
30
|
-
uri_escape(unbracket(content_id))
|
|
26
|
+
Utilities.uri_escape(Utilities.unbracket(content_id))
|
|
31
27
|
end
|
|
32
|
-
|
|
28
|
+
|
|
33
29
|
def url
|
|
34
30
|
"cid:#{cid}"
|
|
35
31
|
end
|
|
36
|
-
|
|
32
|
+
|
|
37
33
|
def inline?
|
|
38
34
|
header[:content_disposition].disposition_type == 'inline' if header[:content_disposition].respond_to?(:disposition_type)
|
|
39
35
|
end
|
|
40
|
-
|
|
36
|
+
|
|
41
37
|
def add_required_fields
|
|
42
38
|
super
|
|
43
39
|
add_content_id if !has_content_id? && inline?
|
|
@@ -46,15 +42,15 @@ module Mail
|
|
|
46
42
|
def add_required_message_fields
|
|
47
43
|
# Override so we don't add Date, MIME-Version, or Message-ID.
|
|
48
44
|
end
|
|
49
|
-
|
|
45
|
+
|
|
50
46
|
def delivery_status_report_part?
|
|
51
47
|
(main_type =~ /message/i && sub_type =~ /delivery-status/i) && body =~ /Status:/
|
|
52
48
|
end
|
|
53
|
-
|
|
49
|
+
|
|
54
50
|
def delivery_status_data
|
|
55
51
|
delivery_status_report_part? ? parse_delivery_status_report : {}
|
|
56
52
|
end
|
|
57
|
-
|
|
53
|
+
|
|
58
54
|
def bounced?
|
|
59
55
|
if action.is_a?(Array)
|
|
60
56
|
!!(action.first =~ /failed/i)
|
|
@@ -62,18 +58,18 @@ module Mail
|
|
|
62
58
|
!!(action =~ /failed/i)
|
|
63
59
|
end
|
|
64
60
|
end
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
|
|
62
|
+
|
|
67
63
|
# Either returns the action if the message has just a single report, or an
|
|
68
64
|
# array of all the actions, one for each report
|
|
69
65
|
def action
|
|
70
66
|
get_return_values('action')
|
|
71
67
|
end
|
|
72
|
-
|
|
68
|
+
|
|
73
69
|
def final_recipient
|
|
74
70
|
get_return_values('final-recipient')
|
|
75
71
|
end
|
|
76
|
-
|
|
72
|
+
|
|
77
73
|
def error_status
|
|
78
74
|
get_return_values('status')
|
|
79
75
|
end
|
|
@@ -81,17 +77,17 @@ module Mail
|
|
|
81
77
|
def diagnostic_code
|
|
82
78
|
get_return_values('diagnostic-code')
|
|
83
79
|
end
|
|
84
|
-
|
|
80
|
+
|
|
85
81
|
def remote_mta
|
|
86
82
|
get_return_values('remote-mta')
|
|
87
83
|
end
|
|
88
|
-
|
|
84
|
+
|
|
89
85
|
def retryable?
|
|
90
86
|
!(error_status =~ /^5/)
|
|
91
87
|
end
|
|
92
88
|
|
|
93
89
|
private
|
|
94
|
-
|
|
90
|
+
|
|
95
91
|
def get_return_values(key)
|
|
96
92
|
if delivery_status_data[key].is_a?(Array)
|
|
97
93
|
delivery_status_data[key].map { |a| a.value }
|
|
@@ -101,11 +97,11 @@ module Mail
|
|
|
101
97
|
nil
|
|
102
98
|
end
|
|
103
99
|
end
|
|
104
|
-
|
|
100
|
+
|
|
105
101
|
# A part may not have a header.... so, just init a body if no header
|
|
106
102
|
def parse_message
|
|
107
|
-
header_part, body_part = raw_source.split(/#{
|
|
108
|
-
if header_part =~ HEADER_LINE
|
|
103
|
+
header_part, body_part = raw_source.split(/#{Constants::LAX_CRLF}#{Constants::WSP}*#{Constants::LAX_CRLF}/m, 2)
|
|
104
|
+
if header_part =~ Constants::HEADER_LINE
|
|
109
105
|
self.header = header_part
|
|
110
106
|
self.body = body_part
|
|
111
107
|
else
|
|
@@ -113,11 +109,11 @@ module Mail
|
|
|
113
109
|
self.body = raw_source
|
|
114
110
|
end
|
|
115
111
|
end
|
|
116
|
-
|
|
112
|
+
|
|
117
113
|
def parse_delivery_status_report
|
|
118
114
|
@delivery_status_data ||= Header.new(body.to_s.gsub("\r\n\r\n", "\r\n"))
|
|
119
115
|
end
|
|
120
116
|
|
|
121
117
|
end
|
|
122
|
-
|
|
118
|
+
|
|
123
119
|
end
|