mail 2.5.5 → 2.6.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.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +8 -9
  3. data/CONTRIBUTING.md +13 -0
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +7 -24
  6. data/README.md +36 -15
  7. data/Rakefile +10 -2
  8. data/VERSION +4 -0
  9. data/lib/mail.rb +1 -1
  10. data/lib/mail/body.rb +2 -2
  11. data/lib/mail/check_delivery_params.rb +10 -47
  12. data/lib/mail/core_extensions/string.rb +12 -2
  13. data/lib/mail/elements/address.rb +38 -82
  14. data/lib/mail/elements/address_list.rb +19 -42
  15. data/lib/mail/elements/content_disposition_element.rb +3 -7
  16. data/lib/mail/elements/content_location_element.rb +2 -6
  17. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  18. data/lib/mail/elements/content_type_element.rb +4 -8
  19. data/lib/mail/elements/date_time_element.rb +3 -7
  20. data/lib/mail/elements/envelope_from_element.rb +3 -11
  21. data/lib/mail/elements/message_ids_element.rb +1 -6
  22. data/lib/mail/elements/mime_version_element.rb +3 -7
  23. data/lib/mail/elements/phrase_list.rb +2 -7
  24. data/lib/mail/elements/received_element.rb +3 -7
  25. data/lib/mail/encodings.rb +0 -1
  26. data/lib/mail/envelope.rb +0 -5
  27. data/lib/mail/field.rb +53 -17
  28. data/lib/mail/field_list.rb +18 -18
  29. data/lib/mail/fields/common/common_address.rb +15 -20
  30. data/lib/mail/fields/common/common_date.rb +0 -7
  31. data/lib/mail/fields/common/common_field.rb +1 -1
  32. data/lib/mail/fields/content_transfer_encoding_field.rb +0 -6
  33. data/lib/mail/fields/resent_sender_field.rb +1 -1
  34. data/lib/mail/fields/sender_field.rb +1 -1
  35. data/lib/mail/fields/unstructured_field.rb +7 -1
  36. data/lib/mail/header.rb +8 -22
  37. data/lib/mail/mail.rb +12 -0
  38. data/lib/mail/matchers/has_sent_mail.rb +34 -1
  39. data/lib/mail/message.rb +18 -11
  40. data/lib/mail/multibyte/unicode.rb +1 -1
  41. data/lib/mail/network/delivery_methods/exim.rb +10 -6
  42. data/lib/mail/network/delivery_methods/file_delivery.rb +8 -4
  43. data/lib/mail/network/delivery_methods/sendmail.rb +7 -9
  44. data/lib/mail/network/delivery_methods/smtp.rb +5 -2
  45. data/lib/mail/network/delivery_methods/smtp_connection.rb +6 -2
  46. data/lib/mail/network/delivery_methods/test_mailer.rb +8 -5
  47. data/lib/mail/network/retriever_methods/imap.rb +18 -13
  48. data/lib/mail/parsers.rb +26 -0
  49. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  50. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  51. data/lib/mail/parsers/content_location_parser.rb +35 -0
  52. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  53. data/lib/mail/parsers/content_type_parser.rb +64 -0
  54. data/lib/mail/parsers/date_time_parser.rb +36 -0
  55. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  56. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  57. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  58. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  59. data/lib/mail/parsers/ragel.rb +17 -0
  60. data/lib/mail/parsers/ragel/common.rl +184 -0
  61. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  62. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  63. data/lib/mail/parsers/ragel/ruby.rb +29 -0
  64. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  65. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  66. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  67. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  68. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  69. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  70. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  71. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  72. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  73. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  74. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  75. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  76. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  77. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  78. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  79. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  80. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  81. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  82. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  83. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  84. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  85. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  86. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  87. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  88. data/lib/mail/parsers/received_parser.rb +47 -0
  89. data/lib/mail/parts_list.rb +4 -2
  90. data/lib/mail/patterns.rb +3 -1
  91. data/lib/mail/utilities.rb +3 -1
  92. data/lib/mail/version.rb +1 -1
  93. data/lib/mail/version_specific/ruby_1_8.rb +1 -1
  94. data/lib/mail/version_specific/ruby_1_9.rb +13 -1
  95. metadata +55 -51
  96. data/lib/VERSION +0 -4
  97. data/lib/load_parsers.rb +0 -35
  98. data/lib/mail/parsers/address_lists.rb +0 -64
  99. data/lib/mail/parsers/address_lists.treetop +0 -19
  100. data/lib/mail/parsers/content_disposition.rb +0 -535
  101. data/lib/mail/parsers/content_disposition.treetop +0 -46
  102. data/lib/mail/parsers/content_location.rb +0 -139
  103. data/lib/mail/parsers/content_location.treetop +0 -20
  104. data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
  105. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
  106. data/lib/mail/parsers/content_type.rb +0 -971
  107. data/lib/mail/parsers/content_type.treetop +0 -68
  108. data/lib/mail/parsers/date_time.rb +0 -114
  109. data/lib/mail/parsers/date_time.treetop +0 -11
  110. data/lib/mail/parsers/envelope_from.rb +0 -194
  111. data/lib/mail/parsers/envelope_from.treetop +0 -32
  112. data/lib/mail/parsers/message_ids.rb +0 -45
  113. data/lib/mail/parsers/message_ids.treetop +0 -15
  114. data/lib/mail/parsers/mime_version.rb +0 -144
  115. data/lib/mail/parsers/mime_version.treetop +0 -19
  116. data/lib/mail/parsers/phrase_lists.rb +0 -45
  117. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  118. data/lib/mail/parsers/received.rb +0 -71
  119. data/lib/mail/parsers/received.treetop +0 -11
  120. data/lib/mail/parsers/rfc2045.rb +0 -421
  121. data/lib/mail/parsers/rfc2045.treetop +0 -35
  122. data/lib/mail/parsers/rfc2822.rb +0 -5397
  123. data/lib/mail/parsers/rfc2822.treetop +0 -408
  124. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  125. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  126. data/lib/tasks/corpus.rake +0 -125
  127. data/lib/tasks/treetop.rake +0 -10
@@ -0,0 +1,35 @@
1
+ module Mail::Parsers
2
+ class ContentLocationParser
3
+ def parse(s)
4
+ content_location = ContentLocationStruct.new(nil)
5
+ if s.blank?
6
+ return content_location
7
+ end
8
+
9
+ actions, error = Ragel.parse(:content_location, s)
10
+ if error
11
+ raise Mail::Field::ParseError.new(Mail::ContentLocationElement, s, error)
12
+ end
13
+
14
+ qstr_s = token_string_s = nil
15
+ actions.each_slice(2) do |action_id, p|
16
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
17
+ case action
18
+
19
+ # Quoted String.
20
+ when :qstr_s then qstr_s = p
21
+ when :qstr_e then content_location.location = s[qstr_s..(p-1)]
22
+
23
+ # Token String
24
+ when :token_string_s then token_string_s = p
25
+ when :token_string_e
26
+ content_location.location = s[token_string_s..(p-1)]
27
+
28
+ else
29
+ raise Mail::Field::ParseError.new(Mail::ContentLocationElement, s, "Failed to process unknown action: #{action}")
30
+ end
31
+ end
32
+ content_location
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ module Mail::Parsers
2
+ class ContentTransferEncodingParser
3
+
4
+ def parse(s)
5
+ content_transfer_encoding = ContentTransferEncodingStruct.new("")
6
+ if s.blank?
7
+ return content_transfer_encoding
8
+ end
9
+
10
+ actions, error = Ragel.parse(:content_transfer_encoding, s)
11
+ if error
12
+ raise Mail::Field::ParseError.new(Mail::ContentTransferEncodingElement, s, error)
13
+ end
14
+
15
+ encoding_s = nil
16
+ actions.each_slice(2) do |action_id, p|
17
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
18
+ case action
19
+
20
+ # Encoding
21
+ when :encoding_s then encoding_s = p
22
+ when :encoding_e
23
+ content_transfer_encoding.encoding = s[encoding_s..(p-1)].downcase
24
+
25
+ else
26
+ raise Mail::Field::ParseError.new(Mail::ContentTransferEncodingElement, s, "Failed to process unknown action: #{action}")
27
+ end
28
+ end
29
+
30
+ content_transfer_encoding
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,64 @@
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)].downcase
25
+
26
+ # Sub Type
27
+ when :sub_type_s then sub_type_s = p
28
+ when :sub_type_e
29
+ content_type.sub_type = s[sub_type_s..(p-1)].downcase
30
+
31
+ # Parameter Attribute
32
+ when :param_attr_s then param_attr_s = p
33
+ when :param_attr_e then param_attr = s[param_attr_s..(p-1)]
34
+
35
+ # Quoted String.
36
+ when :qstr_s then qstr_s = p
37
+ when :qstr_e then qstr = s[qstr_s..(p-1)]
38
+
39
+ # Parameter Value
40
+ when :param_val_s then param_val_s = p
41
+ when :param_val_e
42
+ if param_attr.nil?
43
+ raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, "no attribute for value")
44
+ end
45
+
46
+ # Use quoted s value if one exists, otherwise use parameter value
47
+ if qstr
48
+ value = qstr
49
+ else
50
+ value = s[param_val_s..(p-1)]
51
+ end
52
+
53
+ content_type.parameters << { param_attr => value }
54
+ param_attr = nil
55
+ qstr = nil
56
+
57
+ else
58
+ raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, "Failed to process unknown action: #{action}")
59
+ end
60
+ end
61
+ content_type
62
+ end
63
+ end
64
+ 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,17 @@
1
+ module Mail
2
+ module Parsers
3
+ module Ragel
4
+ require 'mail/parsers/ragel/parser_info'
5
+ require "mail/parsers/ragel/ruby"
6
+
7
+ def self.parse(machine, string)
8
+ @machine_module ||= Ruby
9
+ @machine_module.parse(machine, string)
10
+ end
11
+
12
+ def self.machine_module=(m)
13
+ @machine_module = m
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,184 @@
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
+ envelope_from = (addr_spec_no_angle_brackets) >address_s %address_e " "
179
+ (ctime_date >ctime_date_s %ctime_date_e);
180
+
181
+ # content_location
182
+ location = quoted_string | ((token | 0x3d)+ >token_string_s %token_string_e);
183
+ content_location = CFWS? location CFWS?;
184
+ }%%