mail 2.5.2 → 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.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +78 -0
- data/CONTRIBUTING.md +17 -4
- data/Dependencies.txt +0 -1
- data/Gemfile +10 -21
- data/MIT-LICENSE +20 -0
- data/README.md +50 -29
- data/Rakefile +11 -22
- data/VERSION +4 -0
- data/lib/mail/attachments_list.rb +2 -2
- data/lib/mail/body.rb +5 -5
- data/lib/mail/check_delivery_params.rb +12 -22
- data/lib/mail/core_extensions/object.rb +8 -8
- data/lib/mail/core_extensions/smtp.rb +12 -13
- data/lib/mail/core_extensions/string.rb +16 -6
- data/lib/mail/elements/address.rb +43 -79
- data/lib/mail/elements/address_list.rb +19 -42
- data/lib/mail/elements/content_disposition_element.rb +3 -7
- data/lib/mail/elements/content_location_element.rb +2 -6
- data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
- data/lib/mail/elements/content_type_element.rb +4 -8
- data/lib/mail/elements/date_time_element.rb +3 -7
- data/lib/mail/elements/envelope_from_element.rb +18 -13
- data/lib/mail/elements/message_ids_element.rb +1 -6
- data/lib/mail/elements/mime_version_element.rb +3 -7
- data/lib/mail/elements/phrase_list.rb +2 -7
- data/lib/mail/elements/received_element.rb +3 -7
- data/lib/mail/encodings/quoted_printable.rb +4 -3
- data/lib/mail/encodings.rb +60 -28
- data/lib/mail/envelope.rb +0 -5
- data/lib/mail/field.rb +63 -28
- data/lib/mail/field_list.rb +18 -18
- data/lib/mail/fields/bcc_field.rb +2 -2
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +1 -1
- data/lib/mail/fields/common/common_address.rb +26 -21
- data/lib/mail/fields/common/common_date.rb +0 -7
- data/lib/mail/fields/common/common_field.rb +5 -5
- data/lib/mail/fields/content_id_field.rb +1 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
- data/lib/mail/fields/content_type_field.rb +1 -1
- data/lib/mail/fields/date_field.rb +14 -14
- data/lib/mail/fields/from_field.rb +2 -2
- data/lib/mail/fields/in_reply_to_field.rb +2 -1
- data/lib/mail/fields/message_id_field.rb +2 -3
- data/lib/mail/fields/references_field.rb +2 -1
- data/lib/mail/fields/reply_to_field.rb +2 -2
- data/lib/mail/fields/resent_bcc_field.rb +2 -2
- data/lib/mail/fields/resent_cc_field.rb +2 -2
- data/lib/mail/fields/resent_from_field.rb +2 -2
- data/lib/mail/fields/resent_sender_field.rb +3 -3
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +8 -8
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +8 -2
- data/lib/mail/header.rb +13 -23
- data/lib/mail/mail.rb +12 -0
- data/lib/mail/matchers/has_sent_mail.rb +34 -1
- data/lib/mail/message.rb +151 -48
- data/lib/mail/multibyte/chars.rb +2 -2
- data/lib/mail/multibyte/unicode.rb +6 -4
- data/lib/mail/network/delivery_methods/exim.rb +1 -6
- data/lib/mail/network/delivery_methods/file_delivery.rb +1 -1
- data/lib/mail/network/delivery_methods/sendmail.rb +32 -10
- data/lib/mail/network/delivery_methods/smtp.rb +31 -34
- data/lib/mail/network/delivery_methods/smtp_connection.rb +6 -6
- data/lib/mail/network/delivery_methods/test_mailer.rb +2 -2
- data/lib/mail/network/retriever_methods/imap.rb +18 -13
- data/lib/mail/parsers/address_lists_parser.rb +132 -0
- data/lib/mail/parsers/content_disposition_parser.rb +67 -0
- data/lib/mail/parsers/content_location_parser.rb +35 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
- data/lib/mail/parsers/content_type_parser.rb +64 -0
- data/lib/mail/parsers/date_time_parser.rb +36 -0
- data/lib/mail/parsers/envelope_from_parser.rb +45 -0
- data/lib/mail/parsers/message_ids_parser.rb +39 -0
- data/lib/mail/parsers/mime_version_parser.rb +41 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
- data/lib/mail/parsers/ragel/common.rl +184 -0
- data/lib/mail/parsers/ragel/date_time.rl +30 -0
- data/lib/mail/parsers/ragel/parser_info.rb +61 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
- data/lib/mail/parsers/ragel/ruby.rb +29 -0
- data/lib/mail/parsers/ragel.rb +17 -0
- data/lib/mail/parsers/received_parser.rb +47 -0
- data/lib/mail/parsers.rb +26 -0
- data/lib/mail/part.rb +6 -2
- data/lib/mail/parts_list.rb +5 -3
- data/lib/mail/patterns.rb +4 -1
- data/lib/mail/utilities.rb +28 -18
- data/lib/mail/version.rb +1 -1
- data/lib/mail/version_specific/ruby_1_8.rb +6 -2
- data/lib/mail/version_specific/ruby_1_9.rb +61 -21
- data/lib/mail.rb +1 -2
- metadata +112 -73
- data/lib/VERSION +0 -4
- data/lib/load_parsers.rb +0 -41
- data/lib/mail/core_extensions/shell_escape.rb +0 -56
- data/lib/mail/parsers/address_lists.rb +0 -64
- data/lib/mail/parsers/address_lists.treetop +0 -19
- data/lib/mail/parsers/content_disposition.rb +0 -535
- data/lib/mail/parsers/content_disposition.treetop +0 -46
- data/lib/mail/parsers/content_location.rb +0 -139
- data/lib/mail/parsers/content_location.treetop +0 -20
- data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
- data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
- data/lib/mail/parsers/content_type.rb +0 -967
- data/lib/mail/parsers/content_type.treetop +0 -68
- data/lib/mail/parsers/date_time.rb +0 -114
- data/lib/mail/parsers/date_time.treetop +0 -11
- data/lib/mail/parsers/envelope_from.rb +0 -194
- data/lib/mail/parsers/envelope_from.treetop +0 -32
- data/lib/mail/parsers/message_ids.rb +0 -45
- data/lib/mail/parsers/message_ids.treetop +0 -15
- data/lib/mail/parsers/mime_version.rb +0 -144
- data/lib/mail/parsers/mime_version.treetop +0 -19
- data/lib/mail/parsers/phrase_lists.rb +0 -45
- data/lib/mail/parsers/phrase_lists.treetop +0 -15
- data/lib/mail/parsers/received.rb +0 -71
- data/lib/mail/parsers/received.treetop +0 -11
- data/lib/mail/parsers/rfc2045.rb +0 -464
- data/lib/mail/parsers/rfc2045.treetop +0 -36
- data/lib/mail/parsers/rfc2822.rb +0 -5397
- data/lib/mail/parsers/rfc2822.treetop +0 -410
- data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
- data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
- data/lib/tasks/corpus.rake +0 -125
- data/lib/tasks/treetop.rake +0 -10
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
module Mail
|
|
3
3
|
class Address
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
# Mail::Address handles all email addresses in Mail. It takes an email address string
|
|
8
|
-
# and parses it, breaking it down into
|
|
8
|
+
# and parses it, breaking it down into its component parts and allowing you to get the
|
|
9
9
|
# address, comments, display name, name, local part, domain part and fully formatted
|
|
10
10
|
# address.
|
|
11
11
|
#
|
|
@@ -21,26 +21,24 @@ module Mail
|
|
|
21
21
|
# a.comments #=> ['My email address']
|
|
22
22
|
# a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
23
23
|
def initialize(value = nil)
|
|
24
|
-
@output_type =
|
|
25
|
-
|
|
26
|
-
@raw_text = value
|
|
27
|
-
case
|
|
28
|
-
when value.nil?
|
|
24
|
+
@output_type = :decode
|
|
25
|
+
if value.nil?
|
|
29
26
|
@parsed = false
|
|
27
|
+
@data = nil
|
|
30
28
|
return
|
|
31
29
|
else
|
|
32
30
|
parse(value)
|
|
33
31
|
end
|
|
34
32
|
end
|
|
35
33
|
|
|
36
|
-
# Returns the raw
|
|
34
|
+
# Returns the raw input of the passed in string, this is before it is passed
|
|
37
35
|
# by the parser.
|
|
38
36
|
def raw
|
|
39
|
-
@
|
|
37
|
+
@data.raw
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
# Returns a correctly formatted address for the email going out. If given
|
|
43
|
-
# an incorrectly formatted address as input, Mail::Address will do
|
|
41
|
+
# an incorrectly formatted address as input, Mail::Address will do its best
|
|
44
42
|
# to format it correctly. This includes quoting display names as needed and
|
|
45
43
|
# putting the address in angle brackets etc.
|
|
46
44
|
#
|
|
@@ -48,13 +46,14 @@ module Mail
|
|
|
48
46
|
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
49
47
|
def format
|
|
50
48
|
parse unless @parsed
|
|
51
|
-
|
|
52
|
-
when tree.nil?
|
|
49
|
+
if @data.nil?
|
|
53
50
|
''
|
|
54
|
-
|
|
51
|
+
elsif display_name
|
|
55
52
|
[quote_phrase(display_name), "<#{address}>", format_comments].compact.join(" ")
|
|
56
|
-
|
|
53
|
+
elsif address
|
|
57
54
|
[address, format_comments].compact.join(" ")
|
|
55
|
+
else
|
|
56
|
+
raw
|
|
58
57
|
end
|
|
59
58
|
end
|
|
60
59
|
|
|
@@ -104,7 +103,7 @@ module Mail
|
|
|
104
103
|
# a.local #=> 'mikel'
|
|
105
104
|
def local
|
|
106
105
|
parse unless @parsed
|
|
107
|
-
"#{obs_domain_list}#{get_local.strip}" if get_local
|
|
106
|
+
"#{@data.obs_domain_list}#{get_local.strip}" if get_local
|
|
108
107
|
end
|
|
109
108
|
|
|
110
109
|
# Returns the domain part (the right hand side of the @ sign in the email address) of
|
|
@@ -172,29 +171,24 @@ module Mail
|
|
|
172
171
|
|
|
173
172
|
def parse(value = nil)
|
|
174
173
|
@parsed = true
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
|
|
175
|
+
case value
|
|
176
|
+
when NilClass
|
|
177
|
+
@data = nil
|
|
177
178
|
nil
|
|
178
|
-
when
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
when Mail::Parsers::AddressStruct
|
|
180
|
+
@data = value
|
|
181
|
+
when String
|
|
182
|
+
@raw_text = value
|
|
183
|
+
if value.blank?
|
|
184
|
+
@data = nil
|
|
185
|
+
else
|
|
186
|
+
address_list = Mail::Parsers::AddressListsParser.new.parse(value)
|
|
187
|
+
@data = address_list.addresses.first
|
|
188
|
+
end
|
|
182
189
|
end
|
|
183
190
|
end
|
|
184
191
|
|
|
185
|
-
|
|
186
|
-
def get_domain
|
|
187
|
-
if tree.respond_to?(:angle_addr) && tree.angle_addr.respond_to?(:addr_spec) && tree.angle_addr.addr_spec.respond_to?(:domain)
|
|
188
|
-
@domain_text ||= tree.angle_addr.addr_spec.domain.text_value.strip
|
|
189
|
-
elsif tree.respond_to?(:domain)
|
|
190
|
-
@domain_text ||= tree.domain.text_value.strip
|
|
191
|
-
elsif tree.respond_to?(:addr_spec) && tree.addr_spec.respond_to?(:domain)
|
|
192
|
-
tree.addr_spec.domain.text_value.strip
|
|
193
|
-
else
|
|
194
|
-
nil
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
192
|
def strip_all_comments(string)
|
|
199
193
|
unless comments.blank?
|
|
200
194
|
comments.each do |comment|
|
|
@@ -207,7 +201,7 @@ module Mail
|
|
|
207
201
|
def strip_domain_comments(value)
|
|
208
202
|
unless comments.blank?
|
|
209
203
|
comments.each do |comment|
|
|
210
|
-
if
|
|
204
|
+
if @data.domain && @data.domain.include?("(#{comment})")
|
|
211
205
|
value = value.gsub("(#{comment})", '')
|
|
212
206
|
end
|
|
213
207
|
end
|
|
@@ -215,20 +209,11 @@ module Mail
|
|
|
215
209
|
value.to_s.strip
|
|
216
210
|
end
|
|
217
211
|
|
|
218
|
-
def get_comments
|
|
219
|
-
if tree.respond_to?(:comments)
|
|
220
|
-
@comments = tree.comments.map { |c| unparen(c.text_value.to_str) }
|
|
221
|
-
else
|
|
222
|
-
@comments = []
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
212
|
def get_display_name
|
|
227
|
-
if
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if domain
|
|
213
|
+
if @data.display_name
|
|
214
|
+
str = strip_all_comments(@data.display_name.to_s)
|
|
215
|
+
elsif @data.comments
|
|
216
|
+
if @data.domain
|
|
232
217
|
str = strip_domain_comments(format_comments)
|
|
233
218
|
else
|
|
234
219
|
str = nil
|
|
@@ -261,15 +246,6 @@ module Mail
|
|
|
261
246
|
end
|
|
262
247
|
end
|
|
263
248
|
|
|
264
|
-
# Provides access to the Treetop parse tree for this address
|
|
265
|
-
def tree
|
|
266
|
-
@tree
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
def tree=(value)
|
|
270
|
-
@tree = value
|
|
271
|
-
end
|
|
272
|
-
|
|
273
249
|
def format_comments
|
|
274
250
|
if comments
|
|
275
251
|
comment_text = comments.map {|c| escape_paren(c) }.join(' ').squeeze(" ")
|
|
@@ -278,29 +254,17 @@ module Mail
|
|
|
278
254
|
nil
|
|
279
255
|
end
|
|
280
256
|
end
|
|
281
|
-
|
|
282
|
-
def obs_domain_list
|
|
283
|
-
if tree.respond_to?(:angle_addr)
|
|
284
|
-
obs = tree.angle_addr.elements.select { |e| e.respond_to?(:obs_domain_list) }
|
|
285
|
-
!obs.empty? ? obs.first.text_value : nil
|
|
286
|
-
else
|
|
287
|
-
nil
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
257
|
+
|
|
291
258
|
def get_local
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
when tree.respond_to?(:addr_spec) && tree.addr_spec.respond_to?(:local_part)
|
|
298
|
-
tree.addr_spec.local_part.text_value
|
|
299
|
-
else
|
|
300
|
-
tree && tree.respond_to?(:local_part) ? tree.local_part.text_value : nil
|
|
301
|
-
end
|
|
259
|
+
@data && @data.local
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def get_domain
|
|
263
|
+
@data && @data.domain
|
|
302
264
|
end
|
|
303
265
|
|
|
304
|
-
|
|
266
|
+
def get_comments
|
|
267
|
+
@data && @data.comments
|
|
268
|
+
end
|
|
305
269
|
end
|
|
306
|
-
end
|
|
270
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
module Mail
|
|
3
3
|
class AddressList # :nodoc:
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
# Mail::AddressList is the class that parses To, From and other address fields from
|
|
6
6
|
# emails passed into Mail.
|
|
7
7
|
#
|
|
@@ -18,57 +18,34 @@ module Mail
|
|
|
18
18
|
# a.addresses #=> [#<Mail::Address:14943130 Address: |ada@test.lindsaar.net...
|
|
19
19
|
# a.group_names #=> ["My Group"]
|
|
20
20
|
def initialize(string)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return self
|
|
24
|
-
end
|
|
25
|
-
parser = Mail::AddressListsParser.new
|
|
26
|
-
if tree = parser.parse(string)
|
|
27
|
-
@address_nodes = tree.addresses
|
|
28
|
-
else
|
|
29
|
-
raise Mail::Field::ParseError.new(AddressListsParser, string, parser.failure_reason)
|
|
30
|
-
end
|
|
21
|
+
@addresses_grouped_by_group = nil
|
|
22
|
+
@address_list = Parsers::AddressListsParser.new.parse(string)
|
|
31
23
|
end
|
|
32
24
|
|
|
33
25
|
# Returns a list of address objects from the parsed line
|
|
34
26
|
def addresses
|
|
35
|
-
@addresses ||=
|
|
36
|
-
Mail::Address.new(
|
|
27
|
+
@addresses ||= @address_list.addresses.map do |address_data|
|
|
28
|
+
Mail::Address.new(address_data)
|
|
37
29
|
end
|
|
38
30
|
end
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
31
|
+
|
|
32
|
+
def addresses_grouped_by_group
|
|
33
|
+
return @addresses_grouped_by_group if @addresses_grouped_by_group
|
|
34
|
+
|
|
35
|
+
@addresses_grouped_by_group = {}
|
|
36
|
+
|
|
37
|
+
@address_list.addresses.each do |address_data|
|
|
38
|
+
if group = address_data.group
|
|
39
|
+
@addresses_grouped_by_group[group] ||= []
|
|
40
|
+
@addresses_grouped_by_group[group] << Mail::Address.new(address_data)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
@addresses_grouped_by_group
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
# Returns the names as an array of strings of all groups
|
|
51
47
|
def group_names # :nodoc:
|
|
52
|
-
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Returns a list of address syntax trees
|
|
56
|
-
def address_nodes # :nodoc:
|
|
57
|
-
@address_nodes
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
def get_addresses
|
|
63
|
-
(individual_recipients + group_recipients.map { |g| get_group_addresses(g) }).flatten
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def get_group_addresses(g)
|
|
67
|
-
if g.group_list.respond_to?(:addresses)
|
|
68
|
-
g.group_list.addresses
|
|
69
|
-
else
|
|
70
|
-
[]
|
|
71
|
-
end
|
|
48
|
+
@address_list.group_names
|
|
72
49
|
end
|
|
73
50
|
end
|
|
74
51
|
end
|
|
@@ -5,13 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@parameters = tree.parameters
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(ContentDispositionElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
8
|
+
content_disposition = Mail::Parsers::ContentDispositionParser.new.parse(cleaned(string))
|
|
9
|
+
@disposition_type = content_disposition.disposition_type
|
|
10
|
+
@parameters = content_disposition.parameters
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def disposition_type
|
|
@@ -5,12 +5,8 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@location = tree.location.text_value
|
|
11
|
-
else
|
|
12
|
-
raise Mail::Field::ParseError.new(ContentLocationElement, string, parser.failure_reason)
|
|
13
|
-
end
|
|
8
|
+
content_location = Mail::Parsers::ContentLocationParser.new.parse(string)
|
|
9
|
+
@location = content_location.location
|
|
14
10
|
end
|
|
15
11
|
|
|
16
12
|
def location
|
|
@@ -4,16 +4,9 @@ module Mail
|
|
|
4
4
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
|
-
def initialize(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
when string.blank?
|
|
11
|
-
@encoding = ''
|
|
12
|
-
when tree = parser.parse(string.to_s.downcase)
|
|
13
|
-
@encoding = tree.encoding.text_value
|
|
14
|
-
else
|
|
15
|
-
raise Mail::Field::ParseError.new(ContentTransferEncodingElement, string, parser.failure_reason)
|
|
16
|
-
end
|
|
7
|
+
def initialize(string)
|
|
8
|
+
content_transfer_encoding = Mail::Parsers::ContentTransferEncodingParser.new.parse(string)
|
|
9
|
+
@encoding = content_transfer_encoding.encoding
|
|
17
10
|
end
|
|
18
11
|
|
|
19
12
|
def encoding
|
|
@@ -5,14 +5,10 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@parameters = tree.parameters
|
|
13
|
-
else
|
|
14
|
-
raise Mail::Field::ParseError.new(ContentTypeElement, string, parser.failure_reason)
|
|
15
|
-
end
|
|
8
|
+
content_type = Mail::Parsers::ContentTypeParser.new.parse(cleaned(string))
|
|
9
|
+
@main_type = content_type.main_type
|
|
10
|
+
@sub_type = content_type.sub_type
|
|
11
|
+
@parameters = content_type.parameters
|
|
16
12
|
end
|
|
17
13
|
|
|
18
14
|
def main_type
|
|
@@ -5,13 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@time_string = tree.time.text_value
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(DateTimeElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
8
|
+
date_time = Mail::Parsers::DateTimeParser.new.parse(string)
|
|
9
|
+
@date_string = date_time.date_string
|
|
10
|
+
@time_string = date_time.time_string
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def date_string
|
|
@@ -5,17 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@date_time = ::DateTime.parse("#{tree.ctime_date.text_value}")
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(EnvelopeFromElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def tree
|
|
18
|
-
@tree
|
|
8
|
+
@envelope_from = Mail::Parsers::EnvelopeFromParser.new.parse(string)
|
|
9
|
+
@address = @envelope_from.address
|
|
10
|
+
@date_time = ::DateTime.parse(@envelope_from.ctime_date)
|
|
19
11
|
end
|
|
20
12
|
|
|
21
13
|
def date_time
|
|
@@ -26,8 +18,21 @@ module Mail
|
|
|
26
18
|
@address
|
|
27
19
|
end
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
# RFC 4155:
|
|
22
|
+
# a timestamp indicating the UTC date and time when the message
|
|
23
|
+
# was originally received, conformant with the syntax of the
|
|
24
|
+
# traditional UNIX 'ctime' output sans timezone (note that the
|
|
25
|
+
# use of UTC precludes the need for a timezone indicator);
|
|
26
|
+
def formatted_date_time
|
|
27
|
+
if @date_time.respond_to?(:ctime)
|
|
28
|
+
@date_time.ctime
|
|
29
|
+
else
|
|
30
|
+
@date_time.strftime '%a %b %e %T %Y'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
35
|
+
"#{@address} #{formatted_date_time}"
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
end
|
|
@@ -5,12 +5,7 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize(string)
|
|
8
|
-
|
|
9
|
-
if tree = parser.parse(string)
|
|
10
|
-
@message_ids = tree.message_ids.map { |msg_id| clean_msg_id(msg_id.text_value) }
|
|
11
|
-
else
|
|
12
|
-
raise Mail::Field::ParseError.new(MessageIdsElement, string, parser.failure_reason)
|
|
13
|
-
end
|
|
8
|
+
@message_ids = Mail::Parsers::MessageIdsParser.new.parse(string).message_ids.map { |msg_id| clean_msg_id(msg_id) }
|
|
14
9
|
end
|
|
15
10
|
|
|
16
11
|
def message_ids
|
|
@@ -5,13 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@minor = tree.minor.text_value
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(MimeVersionElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
8
|
+
mime_version = Mail::Parsers::MimeVersionParser.new.parse(string)
|
|
9
|
+
@major = mime_version.major
|
|
10
|
+
@minor = mime_version.minor
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def major
|
|
@@ -5,16 +5,11 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize(string)
|
|
8
|
-
|
|
9
|
-
if tree = parser.parse(string)
|
|
10
|
-
@phrases = tree.phrases
|
|
11
|
-
else
|
|
12
|
-
raise Mail::Field::ParseError.new(PhraseList, string, parser.failure_reason)
|
|
13
|
-
end
|
|
8
|
+
@phrase_lists = Mail::Parsers::PhraseListsParser.new.parse(string)
|
|
14
9
|
end
|
|
15
10
|
|
|
16
11
|
def phrases
|
|
17
|
-
@phrases.map { |p| unquote(p
|
|
12
|
+
@phrase_lists.phrases.map { |p| unquote(p) }
|
|
18
13
|
end
|
|
19
14
|
|
|
20
15
|
end
|
|
@@ -5,13 +5,9 @@ module Mail
|
|
|
5
5
|
include Mail::Utilities
|
|
6
6
|
|
|
7
7
|
def initialize( string )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@info = tree.name_val_list.text_value
|
|
12
|
-
else
|
|
13
|
-
raise Mail::Field::ParseError.new(ReceivedElement, string, parser.failure_reason)
|
|
14
|
-
end
|
|
8
|
+
received = Mail::Parsers::ReceivedParser.new.parse(string)
|
|
9
|
+
@date_time = ::DateTime.parse("#{received.date} #{received.time}")
|
|
10
|
+
@info = received.info
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def date_time
|
|
@@ -12,13 +12,14 @@ module Mail
|
|
|
12
12
|
EightBit.can_encode? str
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# Decode the string from Quoted-Printable
|
|
15
|
+
# Decode the string from Quoted-Printable. Cope with hard line breaks
|
|
16
|
+
# that were incorrectly encoded as hex instead of literal CRLF.
|
|
16
17
|
def self.decode(str)
|
|
17
|
-
str.unpack("M*").first
|
|
18
|
+
str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first.to_lf
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def self.encode(str)
|
|
21
|
-
[str].pack("M").to_crlf
|
|
22
|
+
[str.to_lf].pack("M").to_crlf
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def self.cost(str)
|
data/lib/mail/encodings.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Mail
|
|
|
53
53
|
# Encodes a parameter value using URI Escaping, note the language field 'en' can
|
|
54
54
|
# be set using Mail::Configuration, like so:
|
|
55
55
|
#
|
|
56
|
-
# Mail.defaults
|
|
56
|
+
# Mail.defaults do
|
|
57
57
|
# param_encode_language 'jp'
|
|
58
58
|
# end
|
|
59
59
|
#
|
|
@@ -114,41 +114,40 @@ module Mail
|
|
|
114
114
|
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
|
115
115
|
def Encodings.value_decode(str)
|
|
116
116
|
# Optimization: If there's no encoded-words in the string, just return it
|
|
117
|
-
return str unless str
|
|
117
|
+
return str unless str =~ /\=\?[^?]+\?[QB]\?[^?]+?\?\=/xmi
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
lines = collapse_adjacent_encodings(str)
|
|
120
120
|
|
|
121
121
|
# Split on white-space boundaries with capture, so we capture the white-space as well
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
text
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
string
|
|
122
|
+
lines.map do |line|
|
|
123
|
+
line.split(/([ \t])/).map do |text|
|
|
124
|
+
if text.index('=?').nil?
|
|
125
|
+
text
|
|
126
|
+
else
|
|
127
|
+
# Search for occurences of quoted strings or plain strings
|
|
128
|
+
text.scan(/( # Group around entire regex to include it in matches
|
|
129
|
+
\=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
|
|
130
|
+
| # or
|
|
131
|
+
.+?(?=\=\?|$) # Plain String
|
|
132
|
+
)/xmi).map do |matches|
|
|
133
|
+
string, method = *matches
|
|
134
|
+
if method == 'b' || method == 'B'
|
|
135
|
+
b_value_decode(string)
|
|
136
|
+
elsif method == 'q' || method == 'Q'
|
|
137
|
+
q_value_decode(string)
|
|
138
|
+
else
|
|
139
|
+
string
|
|
140
|
+
end
|
|
142
141
|
end
|
|
143
142
|
end
|
|
144
143
|
end
|
|
145
|
-
end.join("")
|
|
144
|
+
end.flatten.join("")
|
|
146
145
|
end
|
|
147
146
|
|
|
148
147
|
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
|
|
149
148
|
def Encodings.unquote_and_convert_to(str, to_encoding)
|
|
150
149
|
output = value_decode( str ).to_s # output is already converted to UTF-8
|
|
151
|
-
|
|
150
|
+
|
|
152
151
|
if 'utf8' == to_encoding.to_s.downcase.gsub("-", "")
|
|
153
152
|
output
|
|
154
153
|
elsif to_encoding
|
|
@@ -157,7 +156,7 @@ module Mail
|
|
|
157
156
|
output.encode(to_encoding)
|
|
158
157
|
else
|
|
159
158
|
require 'iconv'
|
|
160
|
-
Iconv.iconv(to_encoding, 'UTF-8', output).first
|
|
159
|
+
Iconv.iconv(to_encoding, 'UTF-8', output).first
|
|
161
160
|
end
|
|
162
161
|
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
|
|
163
162
|
# the 'from' parameter specifies a charset other than what the text
|
|
@@ -194,8 +193,8 @@ module Mail
|
|
|
194
193
|
if word.ascii_only?
|
|
195
194
|
word
|
|
196
195
|
else
|
|
197
|
-
previous_non_ascii = tokens[i-1] && !tokens[i-1].ascii_only?
|
|
198
|
-
if previous_non_ascii
|
|
196
|
+
previous_non_ascii = i>0 && tokens[i-1] && !tokens[i-1].ascii_only?
|
|
197
|
+
if previous_non_ascii #why are we adding an extra space here?
|
|
199
198
|
word = " #{word}"
|
|
200
199
|
end
|
|
201
200
|
Encodings.b_value_encode(word, charset)
|
|
@@ -268,5 +267,38 @@ module Mail
|
|
|
268
267
|
def Encodings.find_encoding(str)
|
|
269
268
|
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
|
|
270
269
|
end
|
|
270
|
+
|
|
271
|
+
# Gets the encoding type (Q or B) from the string.
|
|
272
|
+
def Encodings.split_value_encoding_from_string(str)
|
|
273
|
+
match = str.match(/\=\?[^?]+?\?([QB])\?(.+)?\?\=/mi)
|
|
274
|
+
if match
|
|
275
|
+
match[1]
|
|
276
|
+
else
|
|
277
|
+
nil
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# When the encoded string consists of multiple lines, lines with the same
|
|
282
|
+
# encoding (Q or B) can be joined together.
|
|
283
|
+
#
|
|
284
|
+
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
|
285
|
+
def Encodings.collapse_adjacent_encodings(str)
|
|
286
|
+
lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
|
|
287
|
+
results = []
|
|
288
|
+
previous_encoding = nil
|
|
289
|
+
|
|
290
|
+
lines.each do |line|
|
|
291
|
+
encoding = split_value_encoding_from_string(line)
|
|
292
|
+
|
|
293
|
+
if encoding == previous_encoding
|
|
294
|
+
line = results.pop + line
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
previous_encoding = encoding
|
|
298
|
+
results << line
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
results
|
|
302
|
+
end
|
|
271
303
|
end
|
|
272
304
|
end
|
data/lib/mail/envelope.rb
CHANGED
|
@@ -14,11 +14,6 @@ module Mail
|
|
|
14
14
|
super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def tree
|
|
18
|
-
@element ||= Mail::EnvelopeFromElement.new(value)
|
|
19
|
-
@tree ||= @element.tree
|
|
20
|
-
end
|
|
21
|
-
|
|
22
17
|
def element
|
|
23
18
|
@element ||= Mail::EnvelopeFromElement.new(value)
|
|
24
19
|
end
|