mail 2.4.4 → 2.6.3

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 (163) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +156 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +52 -32
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +2 -2
  10. data/lib/mail/body.rb +33 -9
  11. data/lib/mail/check_delivery_params.rb +20 -0
  12. data/lib/mail/configuration.rb +1 -1
  13. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  14. data/lib/mail/core_extensions/nil.rb +4 -2
  15. data/lib/mail/core_extensions/object.rb +8 -8
  16. data/lib/mail/core_extensions/smtp.rb +12 -13
  17. data/lib/mail/core_extensions/string.rb +19 -6
  18. data/lib/mail/elements/address.rb +82 -141
  19. data/lib/mail/elements/address_list.rb +9 -42
  20. data/lib/mail/elements/content_disposition_element.rb +3 -7
  21. data/lib/mail/elements/content_location_element.rb +2 -6
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  23. data/lib/mail/elements/content_type_element.rb +4 -8
  24. data/lib/mail/elements/date_time_element.rb +3 -7
  25. data/lib/mail/elements/envelope_from_element.rb +18 -13
  26. data/lib/mail/elements/message_ids_element.rb +1 -6
  27. data/lib/mail/elements/mime_version_element.rb +3 -7
  28. data/lib/mail/elements/phrase_list.rb +2 -7
  29. data/lib/mail/elements/received_element.rb +3 -7
  30. data/lib/mail/elements.rb +12 -12
  31. data/lib/mail/encodings/quoted_printable.rb +4 -3
  32. data/lib/mail/encodings/unix_to_unix.rb +17 -0
  33. data/lib/mail/encodings.rb +53 -38
  34. data/lib/mail/envelope.rb +0 -5
  35. data/lib/mail/field.rb +131 -118
  36. data/lib/mail/field_list.rb +18 -18
  37. data/lib/mail/fields/bcc_field.rb +2 -2
  38. data/lib/mail/fields/cc_field.rb +2 -2
  39. data/lib/mail/fields/comments_field.rb +1 -1
  40. data/lib/mail/fields/common/common_address.rb +32 -22
  41. data/lib/mail/fields/common/common_date.rb +0 -7
  42. data/lib/mail/fields/common/common_field.rb +10 -3
  43. data/lib/mail/fields/common/common_message_id.rb +9 -5
  44. data/lib/mail/fields/common/parameter_hash.rb +3 -3
  45. data/lib/mail/fields/content_disposition_field.rb +1 -0
  46. data/lib/mail/fields/content_id_field.rb +1 -2
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  48. data/lib/mail/fields/content_type_field.rb +5 -2
  49. data/lib/mail/fields/date_field.rb +14 -14
  50. data/lib/mail/fields/from_field.rb +2 -2
  51. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  52. data/lib/mail/fields/keywords_field.rb +1 -1
  53. data/lib/mail/fields/message_id_field.rb +2 -3
  54. data/lib/mail/fields/references_field.rb +2 -1
  55. data/lib/mail/fields/reply_to_field.rb +2 -2
  56. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  57. data/lib/mail/fields/resent_cc_field.rb +2 -2
  58. data/lib/mail/fields/resent_from_field.rb +2 -2
  59. data/lib/mail/fields/resent_sender_field.rb +3 -3
  60. data/lib/mail/fields/resent_to_field.rb +2 -2
  61. data/lib/mail/fields/sender_field.rb +8 -8
  62. data/lib/mail/fields/to_field.rb +2 -2
  63. data/lib/mail/fields/unstructured_field.rb +44 -31
  64. data/lib/mail/fields.rb +32 -32
  65. data/lib/mail/header.rb +47 -37
  66. data/lib/mail/mail.rb +15 -1
  67. data/lib/mail/matchers/has_sent_mail.rb +36 -3
  68. data/lib/mail/message.rb +164 -57
  69. data/lib/mail/multibyte/chars.rb +4 -4
  70. data/lib/mail/multibyte/unicode.rb +9 -1
  71. data/lib/mail/network/delivery_methods/exim.rb +2 -3
  72. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  73. data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
  74. data/lib/mail/network/delivery_methods/smtp.rb +34 -45
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
  77. data/lib/mail/network/retriever_methods/imap.rb +32 -19
  78. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  79. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  80. data/lib/mail/network.rb +9 -9
  81. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  82. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  83. data/lib/mail/parsers/content_location_parser.rb +35 -0
  84. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  85. data/lib/mail/parsers/content_type_parser.rb +66 -0
  86. data/lib/mail/parsers/date_time_parser.rb +36 -0
  87. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  88. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  89. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  90. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  91. data/lib/mail/parsers/ragel/common.rl +185 -0
  92. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  93. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  105. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  106. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  107. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  108. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  109. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  110. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  111. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  112. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  113. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  114. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  115. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  117. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  118. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  119. data/lib/mail/parsers/ragel.rb +17 -0
  120. data/lib/mail/parsers/received_parser.rb +47 -0
  121. data/lib/mail/parsers.rb +26 -0
  122. data/lib/mail/part.rb +6 -2
  123. data/lib/mail/parts_list.rb +5 -3
  124. data/lib/mail/utilities.rb +70 -60
  125. data/lib/mail/values/unicode_tables.dat +0 -0
  126. data/lib/mail/version.rb +7 -15
  127. data/lib/mail/version_specific/ruby_1_8.rb +25 -4
  128. data/lib/mail/version_specific/ruby_1_9.rb +110 -30
  129. data/lib/mail.rb +20 -19
  130. metadata +129 -73
  131. data/Gemfile.lock +0 -36
  132. data/lib/VERSION +0 -4
  133. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  134. data/lib/mail/parsers/address_lists.rb +0 -64
  135. data/lib/mail/parsers/address_lists.treetop +0 -19
  136. data/lib/mail/parsers/content_disposition.rb +0 -535
  137. data/lib/mail/parsers/content_disposition.treetop +0 -46
  138. data/lib/mail/parsers/content_location.rb +0 -139
  139. data/lib/mail/parsers/content_location.treetop +0 -20
  140. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  141. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  142. data/lib/mail/parsers/content_type.rb +0 -967
  143. data/lib/mail/parsers/content_type.treetop +0 -68
  144. data/lib/mail/parsers/date_time.rb +0 -114
  145. data/lib/mail/parsers/date_time.treetop +0 -11
  146. data/lib/mail/parsers/envelope_from.rb +0 -194
  147. data/lib/mail/parsers/envelope_from.treetop +0 -32
  148. data/lib/mail/parsers/message_ids.rb +0 -45
  149. data/lib/mail/parsers/message_ids.treetop +0 -15
  150. data/lib/mail/parsers/mime_version.rb +0 -144
  151. data/lib/mail/parsers/mime_version.treetop +0 -19
  152. data/lib/mail/parsers/phrase_lists.rb +0 -45
  153. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  154. data/lib/mail/parsers/received.rb +0 -71
  155. data/lib/mail/parsers/received.treetop +0 -11
  156. data/lib/mail/parsers/rfc2045.rb +0 -464
  157. data/lib/mail/parsers/rfc2045.treetop +0 -36
  158. data/lib/mail/parsers/rfc2822.rb +0 -5341
  159. data/lib/mail/parsers/rfc2822.treetop +0 -410
  160. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  161. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  162. data/lib/tasks/corpus.rake +0 -125
  163. data/lib/tasks/treetop.rake +0 -10
@@ -0,0 +1,66 @@
1
+ module Mail::Parsers
2
+ class ContentTypeParser
3
+ include Mail::Utilities
4
+
5
+ def parse(s)
6
+ actions, error = Ragel.parse(:content_type, s)
7
+ if error
8
+ raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, error)
9
+ end
10
+
11
+ content_type = ContentTypeStruct.new(nil,nil,[])
12
+
13
+ content_type.parameters = []
14
+
15
+ main_type_s = sub_type_s = param_attr_s = param_attr = nil
16
+ qstr_s = qstr = param_val_s = nil
17
+ actions.each_slice(2) do |action_id, p|
18
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
19
+ case action
20
+
21
+ # Main Type
22
+ when :main_type_s then main_type_s = p
23
+ when :main_type_e then
24
+ content_type.main_type = s[main_type_s..(p-1)]
25
+ content_type.main_type.downcase!
26
+
27
+ # Sub Type
28
+ when :sub_type_s then sub_type_s = p
29
+ when :sub_type_e
30
+ content_type.sub_type = s[sub_type_s..(p-1)]
31
+ content_type.sub_type.downcase!
32
+
33
+ # Parameter Attribute
34
+ when :param_attr_s then param_attr_s = p
35
+ when :param_attr_e then param_attr = s[param_attr_s..(p-1)]
36
+
37
+ # Quoted String.
38
+ when :qstr_s then qstr_s = p
39
+ when :qstr_e then qstr = s[qstr_s..(p-1)]
40
+
41
+ # Parameter Value
42
+ when :param_val_s then param_val_s = p
43
+ when :param_val_e
44
+ if param_attr.nil?
45
+ raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, "no attribute for value")
46
+ end
47
+
48
+ # Use quoted s value if one exists, otherwise use parameter value
49
+ if qstr
50
+ value = qstr
51
+ else
52
+ value = s[param_val_s..(p-1)]
53
+ end
54
+
55
+ content_type.parameters << { param_attr => value }
56
+ param_attr = nil
57
+ qstr = nil
58
+
59
+ else
60
+ raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, "Failed to process unknown action: #{action}")
61
+ end
62
+ end
63
+ content_type
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,36 @@
1
+ module Mail::Parsers
2
+ class DateTimeParser
3
+ include Mail::Utilities
4
+
5
+ def parse(s)
6
+ date_time = DateTimeStruct.new([])
7
+
8
+ actions, error = Ragel.parse(:date_time, s)
9
+ if error
10
+ raise Mail::Field::ParseError.new(Mail::DateTimeElement, s, error)
11
+ end
12
+
13
+ date_s = time_s = nil
14
+ actions.each_slice(2) do |action_id, p|
15
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
16
+ case action
17
+
18
+ # Date
19
+ when :date_s then date_s = p
20
+ when :date_e
21
+ date_time.date_string = s[date_s..(p-1)]
22
+
23
+ # Time
24
+ when :time_s then time_s = p
25
+ when :time_e
26
+ date_time.time_string = s[time_s..(p-1)]
27
+
28
+ else
29
+ raise Mail::Field::ParseError.new(Mail::DateTimeElement, s, "Failed to process unknown action: #{action}")
30
+ end
31
+ end
32
+
33
+ date_time
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,45 @@
1
+ module Mail::Parsers
2
+ class EnvelopeFromParser
3
+ def parse(s)
4
+ envelope_from = EnvelopeFromStruct.new
5
+ if s.blank?
6
+ return envelope_from
7
+ end
8
+
9
+ actions, error = Ragel.parse(:envelope_from, s)
10
+ if error
11
+ raise Mail::Field::ParseError.new(Mail::EnvelopeFromElement, s, error)
12
+ end
13
+
14
+ address_s = ctime_date_s = nil
15
+ envelope_from = EnvelopeFromStruct.new
16
+ actions.each_slice(2) do |action_id, p|
17
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
18
+ case action
19
+
20
+ # Address
21
+ when :address_s then address_s = p
22
+ when :address_e
23
+ envelope_from.address = s[address_s..(p-1)].rstrip
24
+
25
+ # ctime_date
26
+ when :ctime_date_s then ctime_date_s = p
27
+ when :ctime_date_e
28
+ envelope_from.ctime_date = s[ctime_date_s..(p-1)]
29
+
30
+ # ignored actions
31
+ when :angle_addr_s, :comment_e, :comment_s,
32
+ :domain_e, :domain_s, :local_dot_atom_e,
33
+ :local_dot_atom_pre_comment_e,
34
+ :local_dot_atom_pre_comment_s,
35
+ :local_dot_atom_s, :qstr_e, :qstr_s
36
+ nil
37
+
38
+ else
39
+ raise Mail::Field::ParseError.new(Mail::EnvelopeFromElement, s, "Failed to process unknown action: #{action}")
40
+ end
41
+ end
42
+ envelope_from
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ module Mail::Parsers
2
+ class MessageIdsParser
3
+ def parse(s)
4
+ if s.blank?
5
+ return MessageIdsStruct.new
6
+ end
7
+
8
+ message_ids = MessageIdsStruct.new([])
9
+
10
+ actions, error = Ragel.parse(:message_ids, s)
11
+ if error
12
+ raise Mail::Field::ParseError.new(Mail::MessageIdsElement, s, error)
13
+ end
14
+
15
+ msg_id_s = nil
16
+ actions.each_slice(2) do |action_id, p|
17
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
18
+ case action
19
+
20
+ # Message Ids
21
+ when :msg_id_s then msg_id_s = p
22
+ when :msg_id_e
23
+ message_ids.message_ids << s[msg_id_s..(p-1)].rstrip
24
+
25
+ when :domain_e, :domain_s, :local_dot_atom_e,
26
+ :local_dot_atom_pre_comment_e,
27
+ :local_dot_atom_pre_comment_s,
28
+ :local_dot_atom_s
29
+
30
+ # ignored actions
31
+
32
+ else
33
+ raise Mail::Field::ParseError.new(Mail::MessageIdsElement, s, "Failed to process unknown action: #{action}")
34
+ end
35
+ end
36
+ message_ids
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ module Mail::Parsers
2
+ class MimeVersionParser
3
+ include Mail::Utilities
4
+
5
+ def parse(s)
6
+ if s.blank?
7
+ return MimeVersionStruct.new("", nil)
8
+ end
9
+
10
+ mime_version = MimeVersionStruct.new
11
+
12
+ actions, error = Ragel.parse(:mime_version, s)
13
+ if error
14
+ raise Mail::Field::ParseError.new(Mail::MimeVersionElement, s, error)
15
+ end
16
+
17
+ major_digits_s = minor_digits_s = nil
18
+ actions.each_slice(2) do |action_id, p|
19
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
20
+ case action
21
+
22
+ # Major Digits
23
+ when :major_digits_s then major_digits_s = p
24
+ when :major_digits_e
25
+ mime_version.major = s[major_digits_s..(p-1)]
26
+
27
+ # Minor Digits
28
+ when :minor_digits_s then minor_digits_s = p
29
+ when :minor_digits_e
30
+ mime_version.minor = s[minor_digits_s..(p-1)]
31
+
32
+ when :comment_e, :comment_s then nil
33
+
34
+ else
35
+ raise Mail::Field::ParseError.new(Mail::MimeVersionElement, s, "Failed to process unknown action: #{action}")
36
+ end
37
+ end
38
+ mime_version
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ module Mail::Parsers
2
+ class PhraseListsParser
3
+
4
+ def parse(s)
5
+ actions, error = Ragel.parse(:phrase_lists, s)
6
+ if error
7
+ raise Mail::Field::ParseError.new(Mail::PhraseList, s, error)
8
+ end
9
+
10
+ phrase_lists = PhraseListsStruct.new([])
11
+
12
+ phrase_s = nil
13
+ actions.each_slice(2) do |action_id, p|
14
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
15
+ case action
16
+
17
+ # Phrase
18
+ when :phrase_s then phrase_s = p
19
+ when :phrase_e
20
+ phrase_lists.phrases << s[phrase_s..(p-1)] if phrase_s
21
+ phrase_s = nil
22
+
23
+ when :comment_e, :comment_s, :qstr_s, :qstr_e then nil
24
+
25
+ else
26
+ raise Mail::Field::ParseError.new(Mail::PhraseList, s, "Failed to process unknown action: #{action}")
27
+ end
28
+ end
29
+
30
+ phrase_lists
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,185 @@
1
+ %%{
2
+
3
+ machine common;
4
+
5
+ action comment_begin { fcall comment_tail; }
6
+ action comment_exit { fret; }
7
+
8
+ # RFC5322: address_lists, date_time, message_ids, phrase_lists, received
9
+
10
+ obs_NO_WS_CTL = 0x01..0x08 | 0x0b | 0x0c | 0x0e..0x1f | 0x7f;
11
+ LF = "\n";
12
+ CR = "\r";
13
+ CRLF = "\r\n";
14
+ WSP = 0x09 | 0x20;
15
+ obs_ctext = obs_NO_WS_CTL;
16
+ VCHAR = 0x21..0x7e;
17
+ obs_qp = "\\" (0x00 | obs_NO_WS_CTL | LF | CR);
18
+ obs_FWS = (CRLF? WSP)+;
19
+ ctext = 0x21..0x27 | 0x2a..0x5b | 0x5d..0x7e | obs_ctext;
20
+ quoted_pair = ("\\" (VCHAR | WSP)) | obs_qp;
21
+ FWS = (WSP* CRLF WSP+) | (CRLF WSP+) | obs_FWS;
22
+ ALPHA = [a-zA-Z];
23
+ DIGIT = [0-9];
24
+ DQUOTE = '"';
25
+ obs_qtext = obs_NO_WS_CTL;
26
+ atext = ALPHA | DIGIT | "!" | "#" | "$" | "%" | "&" |
27
+ "'" | "*" | "+" | "-" | "/" | "=" | "?" | "^" |
28
+ "_" | "`" | "{" | "|" | "}" | "~";
29
+ qtext = 0x21 | 0x23..0x5b | 0x5d..0x7e | obs_qtext;
30
+ obs_dtext = obs_NO_WS_CTL | quoted_pair;
31
+ qcontent = qtext | quoted_pair;
32
+
33
+ # Handle recursive comments
34
+ ccontent = ctext | quoted_pair | "(" @comment_begin;
35
+ comment_tail := ((FWS? ccontent)* >comment_s) FWS? ")" @comment_exit;
36
+ comment = "(" @comment_begin %comment_e;
37
+ CFWS = ((FWS? comment)+ FWS?) | FWS;
38
+
39
+ quoted_string = CFWS?
40
+ (DQUOTE
41
+ (((FWS? qcontent)* FWS?) >qstr_s %qstr_e)
42
+ DQUOTE)
43
+ CFWS?;
44
+
45
+ atom = CFWS? atext+ CFWS?;
46
+ word = atom | quoted_string;
47
+
48
+ # phrase_lists
49
+ obs_phrase = (word | "." | "@")+;
50
+ phrase = (obs_phrase | word+) >phrase_s %phrase_e;
51
+ phrase_lists = phrase ("," FWS* phrase)*;
52
+
53
+ # address_lists
54
+
55
+ # local_part:
56
+ domain_text = (DQUOTE (FWS? qcontent)+ FWS? DQUOTE) | atext+;
57
+ local_dot_atom_text = ("."* domain_text "."*)+;
58
+ local_dot_atom = CFWS?
59
+ (local_dot_atom_text >local_dot_atom_s %local_dot_atom_pre_comment_e)
60
+ CFWS?;
61
+ obs_local_part = word ("." word)*;
62
+ local_part = (local_dot_atom >local_dot_atom_s %local_dot_atom_e |
63
+ (quoted_string %local_quoted_string_e) |
64
+ obs_local_part);
65
+
66
+ # Treetop parser behavior was to ignore addresses missing '@' inside of angle
67
+ # brackets. This construction preserves that behavior.
68
+ local_part_no_capture = (local_dot_atom | quoted_string | obs_local_part);
69
+
70
+ # domain:
71
+ dot_atom_text = "."* domain_text ("."* domain_text)*;
72
+ dtext = 0x21..0x5a | 0x5e..0x7e | obs_dtext;
73
+ dot_atom = CFWS? dot_atom_text (CFWS? >(comment_after_address,1));
74
+ domain_literal = CFWS? "[" (FWS? dtext)* FWS? "]" CFWS?;
75
+ obs_domain = atom ("." atom)*;
76
+ domain = (dot_atom | domain_literal | obs_domain) >domain_s %domain_e;
77
+
78
+ # addr_spec:
79
+
80
+ # The %(end_addr,N) priority resolves uncertainty when whitespace
81
+ # after an addr_spec could cause it to be interpreted as a
82
+ # display name: "bar@example.com ,..."
83
+
84
+ addr_spec_in_angle_brackets =
85
+ (local_part "@" domain) %(end_addr,1) |
86
+ local_part_no_capture %(end_addr,0);
87
+
88
+ addr_spec_no_angle_brackets =
89
+ (local_part "@" domain) %(end_addr,1) |
90
+ local_part %(end_addr,0);
91
+
92
+ # angle_addr:
93
+ obs_domain_list = (CFWS | ",")* "@" domain ("," CFWS? ("@" domain)?)*;
94
+ obs_route = (obs_domain_list ":") >obs_domain_list_s %obs_domain_list_e;
95
+ obs_angle_addr = CFWS? "<" obs_route? addr_spec_in_angle_brackets ">" CFWS?;
96
+
97
+ angle_addr = CFWS? ("<" >angle_addr_s) addr_spec_in_angle_brackets ">" CFWS? |
98
+ obs_angle_addr;
99
+
100
+ # Address
101
+ display_name = phrase;
102
+ name_addr = display_name? %(end_addr,2) angle_addr;
103
+ mailbox = (name_addr | addr_spec_no_angle_brackets) >address_s %address_e;
104
+ obs_mbox_list = (CFWS? ",")* mailbox ("," (mailbox | CFWS)?)*;
105
+ mailbox_list = (mailbox (("," | ";") mailbox)*) | obs_mbox_list;
106
+ obs_group_list = (CFWS? ",")+ CFWS?;
107
+ group_list = mailbox_list | CFWS | obs_group_list;
108
+ group = (display_name >group_name_s %group_name_e) ":"
109
+ (group_list?) ";" CFWS?;
110
+ address = group | mailbox;
111
+ #obs_addr_list = (CFWS? ",")* address ("," (address | CFWS)?)*;
112
+ address_lists = address? %(comment_after_address,0)
113
+ (FWS* ("," | ";") FWS* address?)*;
114
+
115
+ # message_ids
116
+ obs_id_left = local_part;
117
+ id_left = dot_atom_text | obs_id_left;
118
+ # id_right modifications to support multiple '@' in msg_id.
119
+ msg_id_atext = ALPHA | DIGIT | "!" | "#" | "$" | "%" | "&" | "'" | "*" |
120
+ "+" | "-" | "/" | "=" | "?" | "^" | "_" | "`" | "{" | "|" |
121
+ "}" | "~" | "@";
122
+ msg_id_dot_atom_text = (msg_id_atext+ "."?)+;
123
+ obs_id_right = domain;
124
+ no_fold_literal = "[" (dtext)* "]";
125
+ id_right = msg_id_dot_atom_text | no_fold_literal | obs_id_right;
126
+ msg_id = (CFWS)?
127
+ (("<" id_left "@" id_right ">") >msg_id_s %msg_id_e)
128
+ (CFWS)?;
129
+ message_ids = msg_id (CFWS? msg_id)*;
130
+
131
+ include date_time "date_time.rl";
132
+ date_time = (day_of_week ",")?
133
+ (date >date_s %date_e) <: (time >time_s %time_e) CFWS?;
134
+
135
+ # Added CFWS? to increase robustness
136
+ # (qmail likes to include a comment style string...?)
137
+ received_token = word | angle_addr | addr_spec_no_angle_brackets | domain;
138
+ received = ((CFWS? received_token*) >received_tokens_s %received_tokens_e)
139
+ ";" date_time;
140
+
141
+ # RFC2045: mime_version, content_type, content_transfer_encoding
142
+ mime_version = CFWS?
143
+ (DIGIT+ >major_digits_s %major_digits_e)
144
+ comment? "." comment?
145
+ (DIGIT+ >minor_digits_s %minor_digits_e)
146
+ CFWS?;
147
+
148
+ token = 0x21..0x27 | 0x2a..0x2b | 0x2c..0x2e |
149
+ 0x30..0x39 | 0x41..0x5a | 0x5e..0x7e;
150
+ value = (quoted_string | (token -- '"' | 0x3d)+) >param_val_s %param_val_e;
151
+ attribute = (token+) >param_attr_s %param_attr_e;
152
+ parameter = CFWS? attribute "=" value CFWS?;
153
+
154
+ ietf_token = token+;
155
+ custom_x_token = 'x'i "-" token+;
156
+ extension_token = ietf_token | custom_x_token;
157
+ discrete_type = 'text'i | 'image'i | 'audio'i | 'video'i |
158
+ 'application'i | extension_token;
159
+ composite_type = 'message'i | 'multipart'i | extension_token;
160
+ iana_token = token+;
161
+ main_type = (discrete_type | composite_type) >main_type_s %main_type_e;
162
+ sub_type = (extension_token | iana_token) >sub_type_s %sub_type_e;
163
+ content_type = main_type "/" sub_type (((CFWS? ";"+) | CFWS) parameter CFWS?)*;
164
+
165
+ encoding = ('7bits' | '8bits' | '7bit' | '8bit' | 'binary' |
166
+ 'quoted-printable' | 'base64' | ietf_token |
167
+ custom_x_token) >encoding_s %encoding_e;
168
+ content_transfer_encoding = CFWS? encoding CFWS? ";"? CFWS?;
169
+
170
+ # RFC2183: content_disposition
171
+ # TODO: recognize filename, size, creation date, etc.
172
+ disposition_type = 'inline'i | 'attachment'i | extension_token | '';
173
+ content_disposition = (disposition_type >disp_type_s %disp_type_e)
174
+ (CFWS? ";" parameter CFWS?)*;
175
+
176
+ # Envelope From
177
+ ctime_date = day_name " "+ month " "+ day " " time_of_day " " year;
178
+ null_sender = ('<>' ' '{0,1});
179
+ envelope_from = (addr_spec_no_angle_brackets | null_sender) >address_s %address_e " "
180
+ (ctime_date >ctime_date_s %ctime_date_e);
181
+
182
+ # content_location
183
+ location = quoted_string | ((token | 0x3d)+ >token_string_s %token_string_e);
184
+ content_location = CFWS? location CFWS?;
185
+ }%%
@@ -0,0 +1,30 @@
1
+ %%{
2
+
3
+ machine date_time;
4
+
5
+ # day_of_week
6
+ day_name = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun";
7
+ obs_day_of_week = CFWS? day_name CFWS?;
8
+ day_of_week = (FWS? day_name) | obs_day_of_week;
9
+
10
+ # date
11
+ obs_day = CFWS? (DIGIT | (DIGIT DIGIT)) CFWS?;
12
+ day = (FWS? DIGIT DIGIT? FWS) | obs_day;
13
+ month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec";
14
+ obs_year = CFWS? (DIGIT DIGIT DIGIT*) CFWS?;
15
+ year = FWS DIGIT DIGIT DIGIT DIGIT FWS | obs_year;
16
+ date = day month year;
17
+
18
+ # time
19
+ obs_hour = CFWS? (DIGIT DIGIT) CFWS?;
20
+ hour = DIGIT DIGIT | obs_hour;
21
+ obs_minute = CFWS? (DIGIT DIGIT) CFWS?;
22
+ minute = DIGIT DIGIT | obs_minute;
23
+ obs_second = CFWS? (DIGIT DIGIT) CFWS?;
24
+ second = DIGIT DIGIT | obs_second;
25
+ obs_zone = "UT" | "GMT" | "EST" | "EDT" | "CST" | "CDT" | "MST" | "MDT" | "PST" | "PDT" | 0x41..0x49 | 0x4B..0x5A | 0x61..0x69 | 0x6B..0x7A;
26
+ time_of_day = hour ":" minute (":" second)?;
27
+ zone = FWS ((("+" | "-") DIGIT DIGIT DIGIT DIGIT) | obs_zone);
28
+ time = time_of_day zone;
29
+
30
+ }%%
@@ -0,0 +1,61 @@
1
+ module Mail
2
+ module Parsers
3
+ module Ragel
4
+ ACTIONS = [
5
+ :addr_spec,
6
+ :address_e,
7
+ :address_s,
8
+ :angle_addr_s,
9
+ :comment_e,
10
+ :comment_s,
11
+ :ctime_date_e,
12
+ :ctime_date_s,
13
+ :date_e,
14
+ :date_s,
15
+ :disp_type_e,
16
+ :disp_type_s,
17
+ :domain_e,
18
+ :domain_s,
19
+ :encoding_e,
20
+ :encoding_s,
21
+ :group_name_e,
22
+ :group_name_s,
23
+ :local_dot_atom_e,
24
+ :local_dot_atom_pre_comment_e,
25
+ :local_dot_atom_s,
26
+ :local_quoted_string_e,
27
+ :main_type_e,
28
+ :main_type_s,
29
+ :major_digits_e,
30
+ :major_digits_s,
31
+ :minor_digits_e,
32
+ :minor_digits_s,
33
+ :msg_id_e,
34
+ :msg_id_s,
35
+ :obs_domain_list_e,
36
+ :obs_domain_list_s,
37
+ :param_attr_e,
38
+ :param_attr_s,
39
+ :param_val_e,
40
+ :param_val_s,
41
+ :phrase_e,
42
+ :phrase_s,
43
+ :qstr_e,
44
+ :qstr_s,
45
+ :received_tokens_e,
46
+ :received_tokens_s,
47
+ :sub_type_e,
48
+ :sub_type_s,
49
+ :time_e,
50
+ :time_s,
51
+ :token_string_e,
52
+ :token_string_s
53
+ ]
54
+
55
+ FIELD_PARSERS = %w[ address_lists phrase_lists
56
+ date_time received message_ids envelope_from
57
+ mime_version content_type content_disposition
58
+ content_transfer_encoding content_location ]
59
+ end
60
+ end
61
+ end