mail 2.5.3 → 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 (151) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +70 -0
  3. data/CONTRIBUTING.md +17 -4
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +50 -29
  8. data/Rakefile +11 -20
  9. data/VERSION +4 -0
  10. data/lib/mail/attachments_list.rb +2 -2
  11. data/lib/mail/body.rb +5 -5
  12. data/lib/mail/check_delivery_params.rb +12 -22
  13. data/lib/mail/core_extensions/object.rb +8 -8
  14. data/lib/mail/core_extensions/smtp.rb +12 -13
  15. data/lib/mail/core_extensions/string.rb +16 -6
  16. data/lib/mail/elements/address.rb +43 -79
  17. data/lib/mail/elements/address_list.rb +19 -42
  18. data/lib/mail/elements/content_disposition_element.rb +3 -7
  19. data/lib/mail/elements/content_location_element.rb +2 -6
  20. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  21. data/lib/mail/elements/content_type_element.rb +4 -8
  22. data/lib/mail/elements/date_time_element.rb +3 -7
  23. data/lib/mail/elements/envelope_from_element.rb +18 -13
  24. data/lib/mail/elements/message_ids_element.rb +1 -6
  25. data/lib/mail/elements/mime_version_element.rb +3 -7
  26. data/lib/mail/elements/phrase_list.rb +2 -7
  27. data/lib/mail/elements/received_element.rb +3 -7
  28. data/lib/mail/encodings/quoted_printable.rb +4 -3
  29. data/lib/mail/encodings.rb +60 -28
  30. data/lib/mail/envelope.rb +0 -5
  31. data/lib/mail/field.rb +63 -28
  32. data/lib/mail/field_list.rb +18 -18
  33. data/lib/mail/fields/bcc_field.rb +2 -2
  34. data/lib/mail/fields/cc_field.rb +2 -2
  35. data/lib/mail/fields/comments_field.rb +1 -1
  36. data/lib/mail/fields/common/common_address.rb +26 -21
  37. data/lib/mail/fields/common/common_date.rb +0 -7
  38. data/lib/mail/fields/common/common_field.rb +5 -5
  39. data/lib/mail/fields/content_id_field.rb +1 -2
  40. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  41. data/lib/mail/fields/content_type_field.rb +1 -1
  42. data/lib/mail/fields/date_field.rb +1 -1
  43. data/lib/mail/fields/from_field.rb +2 -2
  44. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  45. data/lib/mail/fields/message_id_field.rb +2 -3
  46. data/lib/mail/fields/references_field.rb +2 -1
  47. data/lib/mail/fields/reply_to_field.rb +2 -2
  48. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  49. data/lib/mail/fields/resent_cc_field.rb +2 -2
  50. data/lib/mail/fields/resent_from_field.rb +2 -2
  51. data/lib/mail/fields/resent_sender_field.rb +3 -3
  52. data/lib/mail/fields/resent_to_field.rb +2 -2
  53. data/lib/mail/fields/sender_field.rb +8 -8
  54. data/lib/mail/fields/to_field.rb +2 -2
  55. data/lib/mail/fields/unstructured_field.rb +8 -2
  56. data/lib/mail/header.rb +13 -23
  57. data/lib/mail/mail.rb +12 -0
  58. data/lib/mail/matchers/has_sent_mail.rb +34 -1
  59. data/lib/mail/message.rb +151 -48
  60. data/lib/mail/multibyte/chars.rb +2 -2
  61. data/lib/mail/multibyte/unicode.rb +6 -4
  62. data/lib/mail/network/delivery_methods/exim.rb +1 -6
  63. data/lib/mail/network/delivery_methods/file_delivery.rb +1 -1
  64. data/lib/mail/network/delivery_methods/sendmail.rb +32 -10
  65. data/lib/mail/network/delivery_methods/smtp.rb +31 -34
  66. data/lib/mail/network/delivery_methods/smtp_connection.rb +6 -6
  67. data/lib/mail/network/delivery_methods/test_mailer.rb +2 -2
  68. data/lib/mail/network/retriever_methods/imap.rb +18 -13
  69. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  70. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  71. data/lib/mail/parsers/content_location_parser.rb +35 -0
  72. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  73. data/lib/mail/parsers/content_type_parser.rb +64 -0
  74. data/lib/mail/parsers/date_time_parser.rb +36 -0
  75. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  76. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  77. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  78. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  79. data/lib/mail/parsers/ragel/common.rl +184 -0
  80. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  81. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  82. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  83. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  84. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  85. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  86. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  87. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  88. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  89. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  90. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  91. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  92. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  93. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  105. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  106. data/lib/mail/parsers/ragel/ruby.rb +29 -0
  107. data/lib/mail/parsers/ragel.rb +17 -0
  108. data/lib/mail/parsers/received_parser.rb +47 -0
  109. data/lib/mail/parsers.rb +26 -0
  110. data/lib/mail/part.rb +6 -2
  111. data/lib/mail/parts_list.rb +5 -3
  112. data/lib/mail/patterns.rb +4 -1
  113. data/lib/mail/utilities.rb +28 -18
  114. data/lib/mail/version.rb +1 -1
  115. data/lib/mail/version_specific/ruby_1_8.rb +6 -2
  116. data/lib/mail/version_specific/ruby_1_9.rb +61 -21
  117. data/lib/mail.rb +1 -2
  118. metadata +112 -73
  119. data/lib/VERSION +0 -4
  120. data/lib/load_parsers.rb +0 -35
  121. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  122. data/lib/mail/parsers/address_lists.rb +0 -64
  123. data/lib/mail/parsers/address_lists.treetop +0 -19
  124. data/lib/mail/parsers/content_disposition.rb +0 -535
  125. data/lib/mail/parsers/content_disposition.treetop +0 -46
  126. data/lib/mail/parsers/content_location.rb +0 -139
  127. data/lib/mail/parsers/content_location.treetop +0 -20
  128. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  129. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  130. data/lib/mail/parsers/content_type.rb +0 -967
  131. data/lib/mail/parsers/content_type.treetop +0 -68
  132. data/lib/mail/parsers/date_time.rb +0 -114
  133. data/lib/mail/parsers/date_time.treetop +0 -11
  134. data/lib/mail/parsers/envelope_from.rb +0 -194
  135. data/lib/mail/parsers/envelope_from.treetop +0 -32
  136. data/lib/mail/parsers/message_ids.rb +0 -45
  137. data/lib/mail/parsers/message_ids.treetop +0 -15
  138. data/lib/mail/parsers/mime_version.rb +0 -144
  139. data/lib/mail/parsers/mime_version.treetop +0 -19
  140. data/lib/mail/parsers/phrase_lists.rb +0 -45
  141. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  142. data/lib/mail/parsers/received.rb +0 -71
  143. data/lib/mail/parsers/received.treetop +0 -11
  144. data/lib/mail/parsers/rfc2045.rb +0 -464
  145. data/lib/mail/parsers/rfc2045.treetop +0 -36
  146. data/lib/mail/parsers/rfc2822.rb +0 -5397
  147. data/lib/mail/parsers/rfc2822.treetop +0 -410
  148. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  149. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  150. data/lib/tasks/corpus.rake +0 -125
  151. data/lib/tasks/treetop.rake +0 -10
@@ -1,5 +1,3 @@
1
- require 'mail/check_delivery_params'
2
-
3
1
  module Mail
4
2
 
5
3
  # A delivery method implementation which sends via exim.
@@ -38,16 +36,13 @@ module Mail
38
36
  #
39
37
  # mail.deliver!
40
38
  class Exim < Sendmail
41
- include Mail::CheckDeliveryParams
42
-
43
39
  def initialize(values)
44
40
  self.settings = { :location => '/usr/sbin/exim',
45
41
  :arguments => '-i -t' }.merge(values)
46
42
  end
47
43
 
48
44
  def self.call(path, arguments, destinations, mail)
49
- check_params(mail)
50
- IO.popen("#{path} #{arguments}", "w+") do |io|
45
+ popen "#{path} #{arguments}" do |io|
51
46
  io.puts mail.encoded.to_lf
52
47
  io.flush
53
48
  end
@@ -28,7 +28,7 @@ module Mail
28
28
  attr_accessor :settings
29
29
 
30
30
  def deliver!(mail)
31
- check_params(mail)
31
+ check_delivery_params(mail)
32
32
 
33
33
  if ::File.respond_to?(:makedirs)
34
34
  ::File.makedirs settings[:location]
@@ -41,27 +41,49 @@ module Mail
41
41
 
42
42
  def initialize(values)
43
43
  self.settings = { :location => '/usr/sbin/sendmail',
44
- :arguments => '-i -t' }.merge(values)
44
+ :arguments => '-i' }.merge(values)
45
45
  end
46
46
 
47
47
  attr_accessor :settings
48
48
 
49
49
  def deliver!(mail)
50
- check_params(mail)
50
+ smtp_from, smtp_to, message = check_delivery_params(mail)
51
51
 
52
- envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
53
- return_path = "-f " + '"' + envelope_from.escape_for_shell + '"' if envelope_from
52
+ from = "-f #{self.class.shellquote(smtp_from)}"
53
+ to = smtp_to.map { |_to| self.class.shellquote(_to) }.join(' ')
54
54
 
55
- arguments = [settings[:arguments], return_path].compact.join(" ")
56
-
57
- self.class.call(settings[:location], arguments, mail.destinations.collect(&:escape_for_shell).join(" "), mail)
55
+ arguments = "#{settings[:arguments]} #{from} --"
56
+ self.class.call(settings[:location], arguments, to, message)
58
57
  end
59
58
 
60
- def self.call(path, arguments, destinations, mail)
61
- IO.popen("#{path} #{arguments} #{destinations}", "w+") do |io|
62
- io.puts mail.encoded.to_lf
59
+ def self.call(path, arguments, destinations, encoded_message)
60
+ popen "#{path} #{arguments} #{destinations}" do |io|
61
+ io.puts encoded_message.to_lf
63
62
  io.flush
64
63
  end
65
64
  end
65
+
66
+ if RUBY_VERSION < '1.9.0'
67
+ def self.popen(command, &block)
68
+ IO.popen "#{command} 2>&1", 'w+', &block
69
+ end
70
+ else
71
+ def self.popen(command, &block)
72
+ IO.popen command, 'w+', :err => :out, &block
73
+ end
74
+ end
75
+
76
+ # The following is an adaptation of ruby 1.9.2's shellwords.rb file,
77
+ # it is modified to include '+' in the allowed list to allow for
78
+ # sendmail to accept email addresses as the sender with a + in them.
79
+ def self.shellquote(address)
80
+ # Process as a single byte sequence because not all shell
81
+ # implementations are multibyte aware.
82
+ #
83
+ # A LF cannot be escaped with a backslash because a backslash + LF
84
+ # combo is regarded as line continuation and simply ignored. Strip it.
85
+ escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@])/n, "\\\\\\1").gsub("\n", '')
86
+ %("#{escaped}")
87
+ end
66
88
  end
67
89
  end
@@ -89,57 +89,54 @@ module Mail
89
89
  :tls => nil
90
90
  }.merge!(values)
91
91
  end
92
-
92
+
93
93
  attr_accessor :settings
94
-
94
+
95
95
  # Send the message via SMTP.
96
96
  # The from and to attributes are optional. If not set, they are retrieve from the Message.
97
97
  def deliver!(mail)
98
- envelope_from, destinations, message = check_params(mail)
98
+ smtp_from, smtp_to, message = check_delivery_params(mail)
99
99
 
100
100
  smtp = Net::SMTP.new(settings[:address], settings[:port])
101
101
  if settings[:tls] || settings[:ssl]
102
102
  if smtp.respond_to?(:enable_tls)
103
- unless settings[:openssl_verify_mode]
104
- smtp.enable_tls
105
- else
106
- openssl_verify_mode = settings[:openssl_verify_mode]
107
- if openssl_verify_mode.kind_of?(String)
108
- openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
109
- end
110
- context = Net::SMTP.default_ssl_context
111
- context.verify_mode = openssl_verify_mode
112
- smtp.enable_tls(context)
113
- end
103
+ smtp.enable_tls(ssl_context)
114
104
  end
115
105
  elsif settings[:enable_starttls_auto]
116
- if smtp.respond_to?(:enable_starttls_auto)
117
- unless settings[:openssl_verify_mode]
118
- smtp.enable_starttls_auto
119
- else
120
- openssl_verify_mode = settings[:openssl_verify_mode]
121
- if openssl_verify_mode.kind_of?(String)
122
- openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
123
- end
124
- if RUBY_VERSION >= '1.9.0'
125
- context = Net::SMTP.default_ssl_context
126
- context.verify_mode = openssl_verify_mode
127
- smtp.enable_tls(context)
128
- else
129
- smtp.enable_tls(openssl_verify_mode)
130
- end
131
- end
106
+ if smtp.respond_to?(:enable_starttls_auto)
107
+ smtp.enable_starttls_auto(ssl_context)
132
108
  end
133
109
  end
134
-
110
+
135
111
  response = nil
136
112
  smtp.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do |smtp_obj|
137
- response = smtp_obj.sendmail(message, envelope_from, destinations)
113
+ response = smtp_obj.sendmail(message, smtp_from, smtp_to)
138
114
  end
139
115
 
140
- return settings[:return_response] ? response : self
116
+ if settings[:return_response]
117
+ response
118
+ else
119
+ self
120
+ end
141
121
  end
142
122
 
143
-
123
+
124
+ private
125
+
126
+ # Allow SSL context to be configured via settings, for Ruby >= 1.9
127
+ # Just returns openssl verify mode for Ruby 1.8.x
128
+ def ssl_context
129
+ openssl_verify_mode = settings[:openssl_verify_mode]
130
+
131
+ if openssl_verify_mode.kind_of?(String)
132
+ openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
133
+ end
134
+
135
+ context = Net::SMTP.default_ssl_context
136
+ context.verify_mode = openssl_verify_mode
137
+ context.ca_path = settings[:ca_path] if settings[:ca_path]
138
+ context.ca_file = settings[:ca_file] if settings[:ca_file]
139
+ context
140
+ end
144
141
  end
145
142
  end
@@ -44,18 +44,18 @@ module Mail
44
44
  self.smtp = values[:connection]
45
45
  self.settings = values
46
46
  end
47
-
47
+
48
48
  attr_accessor :smtp
49
49
  attr_accessor :settings
50
-
50
+
51
51
  # Send the message via SMTP.
52
52
  # The from and to attributes are optional. If not set, they are retrieve from the Message.
53
53
  def deliver!(mail)
54
- envelope_from, destinations, message = check_params(mail)
55
- response = smtp.sendmail(message, envelope_from, destinations)
54
+ smtp_from, smtp_to, message = check_delivery_params(mail)
55
+ response = smtp.sendmail(message, smtp_from, smtp_to)
56
56
 
57
- settings[:return_response] ? response : self
57
+ settings[:return_response] ? response : self
58
58
  end
59
-
59
+
60
60
  end
61
61
  end
@@ -30,13 +30,13 @@ module Mail
30
30
  end
31
31
 
32
32
  def initialize(values)
33
- @settings = {}
33
+ @settings = values.dup
34
34
  end
35
35
 
36
36
  attr_accessor :settings
37
37
 
38
38
  def deliver!(mail)
39
- check_params(mail)
39
+ check_delivery_params(mail)
40
40
  Mail::TestMailer.deliveries << mail
41
41
  end
42
42
 
@@ -73,31 +73,35 @@ module Mail
73
73
  start do |imap|
74
74
  options[:read_only] ? imap.examine(options[:mailbox]) : imap.select(options[:mailbox])
75
75
 
76
- message_ids = imap.uid_search(options[:keys])
77
- message_ids.reverse! if options[:what].to_sym == :last
78
- message_ids = message_ids.first(options[:count]) if options[:count].is_a?(Integer)
79
- message_ids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
76
+ uids = imap.uid_search(options[:keys])
77
+ uids.reverse! if options[:what].to_sym == :last
78
+ uids = uids.first(options[:count]) if options[:count].is_a?(Integer)
79
+ uids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
80
80
  (options[:what].to_sym != :last && options[:order].to_sym == :desc)
81
81
 
82
82
  if block_given?
83
- message_ids.each do |message_id|
84
- fetchdata = imap.uid_fetch(message_id, ['RFC822'])[0]
83
+ uids.each do |uid|
84
+ uid = options[:uid].to_i unless options[:uid].nil?
85
+ fetchdata = imap.uid_fetch(uid, ['RFC822'])[0]
85
86
  new_message = Mail.new(fetchdata.attr['RFC822'])
86
87
  new_message.mark_for_delete = true if options[:delete_after_find]
87
88
  if block.arity == 3
88
- yield new_message, imap, message_id
89
+ yield new_message, imap, uid
89
90
  else
90
91
  yield new_message
91
92
  end
92
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
93
+ imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
94
+ break unless options[:uid].nil?
93
95
  end
94
96
  imap.expunge if options[:delete_after_find]
95
97
  else
96
98
  emails = []
97
- message_ids.each do |message_id|
98
- fetchdata = imap.uid_fetch(message_id, ['RFC822'])[0]
99
+ uids.each do |uid|
100
+ uid = options[:uid].to_i unless options[:uid].nil?
101
+ fetchdata = imap.uid_fetch(uid, ['RFC822'])[0]
99
102
  emails << Mail.new(fetchdata.attr['RFC822'])
100
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find]
103
+ imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find]
104
+ break unless options[:uid].nil?
101
105
  end
102
106
  imap.expunge if options[:delete_after_find]
103
107
  emails.size == 1 && options[:count] == 1 ? emails.first : emails
@@ -111,8 +115,8 @@ module Mail
111
115
  mailbox = Net::IMAP.encode_utf7(mailbox)
112
116
 
113
117
  start do |imap|
114
- imap.uid_search(['ALL']).each do |message_id|
115
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED])
118
+ imap.uid_search(['ALL']).each do |uid|
119
+ imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED])
116
120
  end
117
121
  imap.expunge
118
122
  end
@@ -137,6 +141,7 @@ module Mail
137
141
  options[:order] ||= :asc
138
142
  options[:what] ||= :first
139
143
  options[:keys] ||= 'ALL'
144
+ options[:uid] ||= nil
140
145
  options[:delete_after_find] ||= false
141
146
  options[:mailbox] = Net::IMAP.encode_utf7(options[:mailbox])
142
147
  options[:read_only] ||= false
@@ -0,0 +1,132 @@
1
+ module Mail::Parsers
2
+ class AddressListsParser
3
+ include Mail::Utilities
4
+
5
+ def parse(s)
6
+ address_list = AddressListStruct.new([],[])
7
+
8
+ if s.blank?
9
+ return address_list
10
+ end
11
+
12
+ actions, error = Ragel.parse(:address_lists, s)
13
+ if error
14
+ raise Mail::Field::ParseError.new(Mail::AddressList, s, error)
15
+ end
16
+
17
+
18
+ phrase_s = phrase_e = qstr_s = qstr = comment_s = nil
19
+ group_name_s = domain_s = group_name = nil
20
+ local_dot_atom_s = local_dot_atom_e = nil
21
+ local_dot_atom_pre_comment_e = nil
22
+ obs_domain_list_s = nil
23
+
24
+ address_s = 0
25
+ address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
26
+ actions.each_slice(2) do |action_id, p|
27
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
28
+ case action
29
+
30
+ # Phrase
31
+ when :phrase_s then phrase_s = p
32
+ when :phrase_e then phrase_e = p-1
33
+
34
+ # Quoted String.
35
+ when :qstr_s then qstr_s = p
36
+ when :qstr_e then qstr = s[qstr_s..(p-1)]
37
+
38
+ # Comment
39
+ when :comment_s
40
+ comment_s = p unless comment_s
41
+ when :comment_e
42
+ if address
43
+ address.comments << s[comment_s..(p-2)]
44
+ end
45
+ comment_s = nil
46
+
47
+ # Group Name
48
+ when :group_name_s then group_name_s = p
49
+ when :group_name_e
50
+ if qstr
51
+ group = qstr
52
+ qstr = nil
53
+ else
54
+ group = s[group_name_s..(p-1)]
55
+ group_name_s = nil
56
+ end
57
+ address_list.group_names << group
58
+ group_name = group
59
+
60
+ # Start next address
61
+ address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
62
+ address_s = p
63
+ address.group = group_name
64
+
65
+ # Address
66
+ when :address_s
67
+ address_s = p
68
+ when :address_e
69
+ # Ignore address end events if they don't have
70
+ # a matching address start event.
71
+ next if address_s.nil?
72
+ if address.local.nil? && local_dot_atom_pre_comment_e && local_dot_atom_s && local_dot_atom_e
73
+ if address.domain
74
+ address.local = s[local_dot_atom_s..local_dot_atom_e] if address
75
+ else
76
+ address.local = s[local_dot_atom_s..local_dot_atom_pre_comment_e] if address
77
+ end
78
+ end
79
+ address.raw = s[address_s..(p-1)]
80
+ address_list.addresses << address if address
81
+
82
+ # Start next address
83
+ address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
84
+ address.group = group_name
85
+ address_s = nil
86
+
87
+ # Don't set the display name until the
88
+ # address has actually started. This allows
89
+ # us to choose quoted_s version if it
90
+ # exists and always use the 'full' phrase
91
+ # version.
92
+ when :angle_addr_s
93
+ if qstr
94
+ address.display_name = qstr
95
+ qstr = nil
96
+ elsif phrase_e
97
+ address.display_name = s[phrase_s..phrase_e].strip
98
+ phrase_e = phrase_s = nil
99
+ end
100
+
101
+ # Domain
102
+ when :domain_s
103
+ domain_s = p
104
+ when :domain_e
105
+ address.domain = s[domain_s..(p-1)].rstrip if address
106
+
107
+ # Local
108
+ when :local_dot_atom_s then local_dot_atom_s = p
109
+ when :local_dot_atom_e then local_dot_atom_e = p-1
110
+ when :local_dot_atom_pre_comment_e
111
+ local_dot_atom_pre_comment_e = p-1
112
+ when :local_quoted_string_e
113
+ address.local = '"' + qstr + '"' if address
114
+
115
+ # obs_domain_list
116
+ when :obs_domain_list_s then obs_domain_list_s = p
117
+ when :obs_domain_list_e
118
+ address.obs_domain_list = s[obs_domain_list_s..(p-1)]
119
+
120
+ else
121
+ raise Mail::Field::ParseError.new(Mail::AddressList, s, "Failed to process unknown action: #{action}")
122
+ end
123
+ end
124
+
125
+ if address_list.addresses.empty? && address_list.group_names.empty?
126
+ raise Mail::Field::ParseError.new(Mail::AddressList, s, "Didn't find any addresses or groups")
127
+ end
128
+
129
+ address_list
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,67 @@
1
+ module Mail::Parsers
2
+ class ContentDispositionParser
3
+ include Mail::Utilities
4
+
5
+ def parse(s)
6
+ content_disposition = ContentDispositionStruct.new("", nil)
7
+ if s.blank?
8
+ return content_disposition
9
+ end
10
+
11
+ actions, error = Ragel.parse(:content_disposition, s)
12
+ if error
13
+ raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, error)
14
+ end
15
+
16
+ content_disposition.parameters = []
17
+
18
+ disp_type_s = param_attr_s = param_attr = qstr_s = qstr = param_val_s = nil
19
+ actions.each_slice(2) do |action_id, p|
20
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
21
+ case action
22
+
23
+ # Disposition Type
24
+ when :disp_type_s then disp_type_s = p
25
+ when :disp_type_e
26
+ content_disposition.disposition_type = s[disp_type_s..(p-1)].downcase
27
+
28
+ # Parameter Attribute
29
+ when :param_attr_s then param_attr_s = p
30
+ when :param_attr_e then param_attr = s[param_attr_s..(p-1)]
31
+
32
+ # Quoted String.
33
+ when :qstr_s then qstr_s = p
34
+ when :qstr_e then qstr = s[qstr_s..(p-1)]
35
+
36
+ # Parameter Value
37
+ when :param_val_s then param_val_s = p
38
+ when :param_val_e
39
+ if param_attr.nil?
40
+ raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, "no attribute for value")
41
+ end
42
+
43
+ # Use quoted string value if one exists, otherwise use parameter value
44
+ if qstr
45
+ value = qstr
46
+ else
47
+ value = s[param_val_s..(p-1)]
48
+ end
49
+
50
+ content_disposition.parameters << { param_attr => value }
51
+ param_attr = nil
52
+ qstr = nil
53
+
54
+ else
55
+ raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, "Failed to process unknown action: #{action}")
56
+ end
57
+ end
58
+
59
+ content_disposition
60
+ end
61
+
62
+ private
63
+ def cleaned(string)
64
+ string =~ /(.+);\s*$/ ? $1 : string
65
+ end
66
+ end
67
+ end
@@ -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