mail 2.6.6 → 2.8.1

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.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +134 -119
  4. data/lib/mail/attachments_list.rb +10 -9
  5. data/lib/mail/body.rb +73 -84
  6. data/lib/mail/check_delivery_params.rb +28 -21
  7. data/lib/mail/configuration.rb +2 -0
  8. data/lib/mail/constants.rb +27 -5
  9. data/lib/mail/elements/address.rb +53 -47
  10. data/lib/mail/elements/address_list.rb +11 -19
  11. data/lib/mail/elements/content_disposition_element.rb +9 -16
  12. data/lib/mail/elements/content_location_element.rb +6 -11
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  14. data/lib/mail/elements/content_type_element.rb +16 -23
  15. data/lib/mail/elements/date_time_element.rb +7 -15
  16. data/lib/mail/elements/envelope_from_element.rb +22 -23
  17. data/lib/mail/elements/message_ids_element.rb +18 -13
  18. data/lib/mail/elements/mime_version_element.rb +7 -15
  19. data/lib/mail/elements/phrase_list.rb +12 -10
  20. data/lib/mail/elements/received_element.rb +27 -19
  21. data/lib/mail/encodings/7bit.rb +9 -14
  22. data/lib/mail/encodings/8bit.rb +2 -21
  23. data/lib/mail/encodings/base64.rb +11 -12
  24. data/lib/mail/encodings/binary.rb +3 -22
  25. data/lib/mail/encodings/identity.rb +24 -0
  26. data/lib/mail/encodings/quoted_printable.rb +6 -6
  27. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  28. data/lib/mail/encodings/unix_to_unix.rb +3 -1
  29. data/lib/mail/encodings.rb +81 -54
  30. data/lib/mail/envelope.rb +11 -14
  31. data/lib/mail/field.rb +119 -98
  32. data/lib/mail/field_list.rb +60 -7
  33. data/lib/mail/fields/bcc_field.rb +34 -52
  34. data/lib/mail/fields/cc_field.rb +28 -49
  35. data/lib/mail/fields/comments_field.rb +27 -37
  36. data/lib/mail/fields/common_address_field.rb +170 -0
  37. data/lib/mail/fields/common_date_field.rb +58 -0
  38. data/lib/mail/fields/common_field.rb +77 -0
  39. data/lib/mail/fields/common_message_id_field.rb +42 -0
  40. data/lib/mail/fields/content_description_field.rb +7 -14
  41. data/lib/mail/fields/content_disposition_field.rb +13 -38
  42. data/lib/mail/fields/content_id_field.rb +24 -51
  43. data/lib/mail/fields/content_location_field.rb +11 -25
  44. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  45. data/lib/mail/fields/content_type_field.rb +50 -80
  46. data/lib/mail/fields/date_field.rb +23 -52
  47. data/lib/mail/fields/from_field.rb +28 -49
  48. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  49. data/lib/mail/fields/keywords_field.rb +18 -31
  50. data/lib/mail/fields/message_id_field.rb +25 -71
  51. data/lib/mail/fields/mime_version_field.rb +19 -30
  52. data/lib/mail/fields/named_structured_field.rb +11 -0
  53. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  54. data/lib/mail/fields/optional_field.rb +9 -7
  55. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  56. data/lib/mail/fields/received_field.rb +43 -57
  57. data/lib/mail/fields/references_field.rb +35 -49
  58. data/lib/mail/fields/reply_to_field.rb +28 -49
  59. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  60. data/lib/mail/fields/resent_cc_field.rb +28 -49
  61. data/lib/mail/fields/resent_date_field.rb +5 -30
  62. data/lib/mail/fields/resent_from_field.rb +28 -49
  63. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  64. data/lib/mail/fields/resent_sender_field.rb +27 -56
  65. data/lib/mail/fields/resent_to_field.rb +28 -49
  66. data/lib/mail/fields/return_path_field.rb +50 -54
  67. data/lib/mail/fields/sender_field.rb +34 -55
  68. data/lib/mail/fields/structured_field.rb +3 -30
  69. data/lib/mail/fields/subject_field.rb +9 -11
  70. data/lib/mail/fields/to_field.rb +28 -49
  71. data/lib/mail/fields/unstructured_field.rb +32 -47
  72. data/lib/mail/header.rb +71 -110
  73. data/lib/mail/mail.rb +2 -10
  74. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  75. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  76. data/lib/mail/message.rb +113 -117
  77. data/lib/mail/multibyte/chars.rb +21 -178
  78. data/lib/mail/multibyte/unicode.rb +10 -10
  79. data/lib/mail/multibyte/utils.rb +26 -43
  80. data/lib/mail/multibyte.rb +55 -16
  81. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  82. data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
  83. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  84. data/lib/mail/network/delivery_methods/sendmail.rb +62 -21
  85. data/lib/mail/network/delivery_methods/smtp.rb +75 -50
  86. data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -4
  87. data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
  88. data/lib/mail/network/retriever_methods/base.rb +8 -8
  89. data/lib/mail/network/retriever_methods/imap.rb +20 -7
  90. data/lib/mail/network/retriever_methods/pop3.rb +5 -3
  91. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  92. data/lib/mail/network.rb +1 -0
  93. data/lib/mail/parser_tools.rb +15 -0
  94. data/lib/mail/parsers/address_lists_parser.rb +33225 -116
  95. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  96. data/lib/mail/parsers/content_disposition_parser.rb +882 -49
  97. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  98. data/lib/mail/parsers/content_location_parser.rb +809 -23
  99. data/lib/mail/parsers/content_location_parser.rl +78 -0
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
  101. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  102. data/lib/mail/parsers/content_type_parser.rb +1037 -56
  103. data/lib/mail/parsers/content_type_parser.rl +90 -0
  104. data/lib/mail/parsers/date_time_parser.rb +877 -25
  105. data/lib/mail/parsers/date_time_parser.rl +69 -0
  106. data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
  107. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  108. data/lib/mail/parsers/message_ids_parser.rb +5146 -25
  109. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  110. data/lib/mail/parsers/mime_version_parser.rb +497 -26
  111. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  112. data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
  113. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  114. data/lib/mail/parsers/received_parser.rb +8776 -43
  115. data/lib/mail/parsers/received_parser.rl +91 -0
  116. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  117. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  118. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  119. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  120. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  121. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  122. data/lib/mail/parsers/rfc5322.rl +74 -0
  123. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  124. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  125. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  126. data/lib/mail/parsers.rb +11 -25
  127. data/lib/mail/part.rb +6 -10
  128. data/lib/mail/parts_list.rb +62 -6
  129. data/lib/mail/smtp_envelope.rb +57 -0
  130. data/lib/mail/utilities.rb +343 -74
  131. data/lib/mail/version.rb +2 -2
  132. data/lib/mail/yaml.rb +30 -0
  133. data/lib/mail.rb +5 -35
  134. metadata +111 -66
  135. data/CHANGELOG.rdoc +0 -803
  136. data/CONTRIBUTING.md +0 -60
  137. data/Dependencies.txt +0 -2
  138. data/Gemfile +0 -14
  139. data/Rakefile +0 -29
  140. data/TODO.rdoc +0 -9
  141. data/lib/mail/core_extensions/smtp.rb +0 -25
  142. data/lib/mail/core_extensions/string/access.rb +0 -146
  143. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  144. data/lib/mail/core_extensions/string.rb +0 -21
  145. data/lib/mail/fields/common/address_container.rb +0 -17
  146. data/lib/mail/fields/common/common_address.rb +0 -136
  147. data/lib/mail/fields/common/common_date.rb +0 -36
  148. data/lib/mail/fields/common/common_field.rb +0 -61
  149. data/lib/mail/fields/common/common_message_id.rb +0 -49
  150. data/lib/mail/multibyte/exceptions.rb +0 -9
  151. data/lib/mail/parsers/ragel/common.rl +0 -185
  152. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  173. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  177. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  178. data/lib/mail/parsers/ragel.rb +0 -18
  179. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  180. data/lib/mail/version_specific/ruby_1_9.rb +0 -226
@@ -0,0 +1,91 @@
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 p != eof || cs < %%{ write first_final; }%%
81
+ raise Mail::Field::IncompleteParseError.new(Mail::ReceivedElement, data, p)
82
+ end
83
+
84
+ received
85
+ end
86
+ end
87
+ end
88
+
89
+ ensure
90
+ $VERBOSE = original_verbose
91
+ 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
- machine date_time;
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
- # Low-level ragel based parsers
6
- require 'mail/parsers/ragel'
7
-
8
- AddressListStruct = Struct.new(:addresses, :group_names, :error)
9
- AddressStruct = Struct.new(:raw, :domain, :comments, :local,
10
- :obs_domain_list, :display_name, :group, :error)
11
- ContentDispositionStruct = Struct.new(:disposition_type, :parameters, :error)
12
- ContentLocationStruct = Struct.new(:location, :error)
13
- ContentTransferEncodingStruct = Struct.new(:encoding, :error)
14
- ContentTypeStruct = Struct.new(:main_type, :sub_type, :parameters, :error)
15
- DateTimeStruct = Struct.new(:date_string, :time_string, :error)
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,8 +1,10 @@
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
@@ -19,15 +21,9 @@ module Mail
19
21
  header.has_content_id?
20
22
  end
21
23
 
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
-
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
@@ -104,8 +100,8 @@ module Mail
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(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
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
@@ -44,6 +44,63 @@ module Mail
44
44
  raise NoMethodError, "#collect! is not defined, please call #collect and create a new PartsList"
45
45
  end
46
46
 
47
+ def inspect_structure(parent_id = '')
48
+ enum_for(:map).with_index { |part, i|
49
+ i = i + 1 # Use 1-based indexes since this is for humans to read
50
+ id = parent_id.empty? ? "#{i}" : "#{parent_id}.#{i}"
51
+ if part.content_type == "message/rfc822"
52
+ sub_list = Mail.new(part.body).parts
53
+ else
54
+ sub_list = part.parts
55
+ end
56
+ id + '. ' + part.inspect +
57
+ if sub_list.any?
58
+ "\n" + sub_list.inspect_structure(id)
59
+ end.to_s
60
+ }.join("\n")
61
+ end
62
+
63
+ def recursive_each(&block)
64
+ each do |part|
65
+ if part.content_type == "message/rfc822"
66
+ sub_list = Mail.new(part.body).parts
67
+ else
68
+ sub_list = part.parts
69
+ end
70
+
71
+ yield part
72
+
73
+ sub_list.recursive_each(&block)
74
+ end
75
+ end
76
+
77
+ def recursive_size
78
+ i = 0
79
+ recursive_each {|p| i += 1 }
80
+ i
81
+ end
82
+
83
+ def recursive_delete_if
84
+ delete_if { |part|
85
+ if part.content_type == "message/rfc822"
86
+ sub_list = Mail.new(part.body).parts
87
+ else
88
+ sub_list = part.parts
89
+ end
90
+ (yield part).tap {
91
+ if sub_list.any?
92
+ sub_list.recursive_delete_if {|part| yield part }
93
+ end
94
+ }
95
+ }
96
+ end
97
+
98
+ def delete_attachments
99
+ recursive_delete_if { |part|
100
+ part.attachment?
101
+ }
102
+ end
103
+
47
104
  def sort
48
105
  self.class.new(@parts.sort)
49
106
  end
@@ -55,7 +112,7 @@ module Mail
55
112
  # OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
56
113
  # single email message... please show me a use case and I'll put more work into this method,
57
114
  # in the meantime, it works :)
58
- [get_order_value(a, order), i += 1]
115
+ get_order_value(a, order) << (i += 1)
59
116
  end
60
117
  @parts.clear
61
118
  sorted.each { |p| @parts << p }
@@ -64,11 +121,10 @@ module Mail
64
121
  private
65
122
 
66
123
  def get_order_value(part, order)
67
- if part.respond_to?(:content_type) && !part[:content_type].nil?
68
- order.index(part[:content_type].string.downcase) || 10000
69
- else
70
- 10000
71
- end
124
+ is_attachment = part.respond_to?(:attachment?) && part.attachment?
125
+ has_content_type = part.respond_to?(:content_type) && !part[:content_type].nil?
126
+
127
+ [is_attachment ? 1 : 0, (has_content_type ? order.index(part[:content_type].string.downcase) : nil) || 10000]
72
128
  end
73
129
 
74
130
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class SmtpEnvelope #:nodoc:
5
+ # Reasonable cap on address length to avoid SMTP line length
6
+ # overflow on old SMTP servers.
7
+ MAX_ADDRESS_BYTESIZE = 2000
8
+
9
+ attr_reader :from, :to, :message
10
+
11
+ def initialize(mail)
12
+ self.from = mail.smtp_envelope_from
13
+ self.to = mail.smtp_envelope_to
14
+ self.message = mail.encoded
15
+ end
16
+
17
+ def from=(addr)
18
+ if Utilities.blank? addr
19
+ raise ArgumentError, "SMTP From address may not be blank: #{addr.inspect}"
20
+ end
21
+
22
+ @from = validate_addr 'From', addr
23
+ end
24
+
25
+ def to=(addr)
26
+ if Utilities.blank?(addr)
27
+ raise ArgumentError, "SMTP To address may not be blank: #{addr.inspect}"
28
+ end
29
+
30
+ @to = Array(addr).map do |addr|
31
+ validate_addr 'To', addr
32
+ end
33
+ end
34
+
35
+ def message=(message)
36
+ if Utilities.blank?(message)
37
+ raise ArgumentError, 'SMTP message may not be blank'
38
+ end
39
+
40
+ @message = message
41
+ end
42
+
43
+
44
+ private
45
+ def validate_addr(addr_name, addr)
46
+ if addr.bytesize > MAX_ADDRESS_BYTESIZE
47
+ raise ArgumentError, "SMTP #{addr_name} address may not exceed #{MAX_ADDRESS_BYTESIZE} bytes: #{addr.inspect}"
48
+ end
49
+
50
+ if /[\r\n]/ =~ addr
51
+ raise ArgumentError, "SMTP #{addr_name} address may not contain CR or LF line breaks: #{addr.inspect}"
52
+ end
53
+
54
+ addr
55
+ end
56
+ end
57
+ end