mail 2.5.5 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }%%