mail 2.7.1 → 2.8.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -28
  3. data/lib/mail/attachments_list.rb +2 -5
  4. data/lib/mail/body.rb +24 -47
  5. data/lib/mail/constants.rb +27 -5
  6. data/lib/mail/elements/address.rb +27 -27
  7. data/lib/mail/elements/address_list.rb +1 -1
  8. data/lib/mail/elements/content_disposition_element.rb +1 -1
  9. data/lib/mail/elements/content_location_element.rb +1 -1
  10. data/lib/mail/elements/content_transfer_encoding_element.rb +1 -1
  11. data/lib/mail/elements/content_type_element.rb +8 -4
  12. data/lib/mail/elements/date_time_element.rb +1 -1
  13. data/lib/mail/elements/envelope_from_element.rb +13 -7
  14. data/lib/mail/elements/message_ids_element.rb +14 -5
  15. data/lib/mail/elements/mime_version_element.rb +1 -1
  16. data/lib/mail/elements/phrase_list.rb +7 -2
  17. data/lib/mail/elements/received_element.rb +20 -6
  18. data/lib/mail/encodings/7bit.rb +5 -0
  19. data/lib/mail/encodings/base64.rb +2 -2
  20. data/lib/mail/encodings/quoted_printable.rb +2 -2
  21. data/lib/mail/encodings.rb +30 -59
  22. data/lib/mail/envelope.rb +11 -14
  23. data/lib/mail/field.rb +37 -53
  24. data/lib/mail/field_list.rb +60 -7
  25. data/lib/mail/fields/bcc_field.rb +34 -52
  26. data/lib/mail/fields/cc_field.rb +28 -49
  27. data/lib/mail/fields/comments_field.rb +27 -37
  28. data/lib/mail/fields/common_address_field.rb +170 -0
  29. data/lib/mail/fields/common_date_field.rb +58 -0
  30. data/lib/mail/fields/common_field.rb +77 -0
  31. data/lib/mail/fields/common_message_id_field.rb +42 -0
  32. data/lib/mail/fields/content_description_field.rb +7 -14
  33. data/lib/mail/fields/content_disposition_field.rb +13 -38
  34. data/lib/mail/fields/content_id_field.rb +24 -51
  35. data/lib/mail/fields/content_location_field.rb +11 -25
  36. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  37. data/lib/mail/fields/content_type_field.rb +46 -71
  38. data/lib/mail/fields/date_field.rb +23 -51
  39. data/lib/mail/fields/from_field.rb +28 -49
  40. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  41. data/lib/mail/fields/keywords_field.rb +18 -31
  42. data/lib/mail/fields/message_id_field.rb +25 -71
  43. data/lib/mail/fields/mime_version_field.rb +19 -30
  44. data/lib/mail/fields/named_structured_field.rb +11 -0
  45. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  46. data/lib/mail/fields/optional_field.rb +5 -6
  47. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
  48. data/lib/mail/fields/received_field.rb +43 -57
  49. data/lib/mail/fields/references_field.rb +35 -49
  50. data/lib/mail/fields/reply_to_field.rb +28 -49
  51. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  52. data/lib/mail/fields/resent_cc_field.rb +28 -49
  53. data/lib/mail/fields/resent_date_field.rb +5 -29
  54. data/lib/mail/fields/resent_from_field.rb +28 -49
  55. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  56. data/lib/mail/fields/resent_sender_field.rb +27 -56
  57. data/lib/mail/fields/resent_to_field.rb +28 -49
  58. data/lib/mail/fields/return_path_field.rb +50 -54
  59. data/lib/mail/fields/sender_field.rb +34 -55
  60. data/lib/mail/fields/structured_field.rb +3 -30
  61. data/lib/mail/fields/subject_field.rb +9 -11
  62. data/lib/mail/fields/to_field.rb +28 -49
  63. data/lib/mail/fields/unstructured_field.rb +16 -48
  64. data/lib/mail/header.rb +69 -110
  65. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  66. data/lib/mail/message.rb +46 -64
  67. data/lib/mail/multibyte/chars.rb +8 -166
  68. data/lib/mail/multibyte/utils.rb +26 -43
  69. data/lib/mail/multibyte.rb +1 -11
  70. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  71. data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
  72. data/lib/mail/network/delivery_methods/logger_delivery.rb +2 -5
  73. data/lib/mail/network/delivery_methods/sendmail.rb +27 -35
  74. data/lib/mail/network/delivery_methods/smtp.rb +3 -3
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -12
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
  77. data/lib/mail/network/retriever_methods/base.rb +8 -8
  78. data/lib/mail/network/retriever_methods/imap.rb +2 -2
  79. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  80. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  81. data/lib/mail/parsers/address_lists_parser.rb +33070 -33064
  82. data/lib/mail/parsers/address_lists_parser.rl +7 -0
  83. data/lib/mail/parsers/content_disposition_parser.rb +833 -827
  84. data/lib/mail/parsers/content_disposition_parser.rl +7 -0
  85. data/lib/mail/parsers/content_location_parser.rb +770 -764
  86. data/lib/mail/parsers/content_location_parser.rl +7 -0
  87. data/lib/mail/parsers/content_transfer_encoding_parser.rb +474 -468
  88. data/lib/mail/parsers/content_transfer_encoding_parser.rl +7 -0
  89. data/lib/mail/parsers/content_type_parser.rb +971 -965
  90. data/lib/mail/parsers/content_type_parser.rl +7 -0
  91. data/lib/mail/parsers/date_time_parser.rb +838 -832
  92. data/lib/mail/parsers/date_time_parser.rl +7 -0
  93. data/lib/mail/parsers/envelope_from_parser.rb +3623 -3529
  94. data/lib/mail/parsers/envelope_from_parser.rl +7 -0
  95. data/lib/mail/parsers/message_ids_parser.rb +5107 -2800
  96. data/lib/mail/parsers/message_ids_parser.rl +12 -1
  97. data/lib/mail/parsers/mime_version_parser.rb +463 -457
  98. data/lib/mail/parsers/mime_version_parser.rl +7 -0
  99. data/lib/mail/parsers/phrase_lists_parser.rb +836 -830
  100. data/lib/mail/parsers/phrase_lists_parser.rl +8 -1
  101. data/lib/mail/parsers/received_parser.rb +8688 -8682
  102. data/lib/mail/parsers/received_parser.rl +7 -0
  103. data/lib/mail/parsers/rfc5322.rl +28 -13
  104. data/lib/mail/parsers.rb +11 -17
  105. data/lib/mail/part.rb +5 -9
  106. data/lib/mail/parts_list.rb +57 -0
  107. data/lib/mail/smtp_envelope.rb +57 -0
  108. data/lib/mail/utilities.rb +307 -69
  109. data/lib/mail/version.rb +3 -3
  110. data/lib/mail/yaml.rb +30 -0
  111. data/lib/mail.rb +0 -20
  112. metadata +74 -21
  113. data/lib/mail/check_delivery_params.rb +0 -60
  114. data/lib/mail/core_extensions/smtp.rb +0 -28
  115. data/lib/mail/core_extensions/string.rb +0 -17
  116. data/lib/mail/fields/common/address_container.rb +0 -17
  117. data/lib/mail/fields/common/common_address.rb +0 -161
  118. data/lib/mail/fields/common/common_date.rb +0 -36
  119. data/lib/mail/fields/common/common_field.rb +0 -52
  120. data/lib/mail/fields/common/common_message_id.rb +0 -49
  121. data/lib/mail/version_specific/ruby_1_8.rb +0 -163
  122. data/lib/mail/version_specific/ruby_1_9.rb +0 -278
@@ -1,18 +1,15 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
3
  require 'mail/constants'
4
+ require 'socket'
4
5
 
5
6
  module Mail
6
7
  module Utilities
7
-
8
- LF = "\n"
9
- CRLF = "\r\n"
10
-
11
- include Constants
8
+ extend self
12
9
 
13
10
  # Returns true if the string supplied is free from characters not allowed as an ATOM
14
11
  def atom_safe?( str )
15
- not ATOM_UNSAFE === str
12
+ not Constants::ATOM_UNSAFE === str
16
13
  end
17
14
 
18
15
  # If the string supplied has ATOM unsafe characters in it, will return the string quoted
@@ -27,19 +24,19 @@ module Mail
27
24
  if str.respond_to?(:force_encoding)
28
25
  original_encoding = str.encoding
29
26
  ascii_str = str.to_s.dup.force_encoding('ASCII-8BIT')
30
- if (PHRASE_UNSAFE === ascii_str)
27
+ if Constants::PHRASE_UNSAFE === ascii_str
31
28
  dquote(ascii_str).force_encoding(original_encoding)
32
29
  else
33
30
  str
34
31
  end
35
32
  else
36
- (PHRASE_UNSAFE === str) ? dquote(str) : str
33
+ Constants::PHRASE_UNSAFE === str ? dquote(str) : str
37
34
  end
38
35
  end
39
36
 
40
37
  # Returns true if the string supplied is free from characters not allowed as a TOKEN
41
38
  def token_safe?( str )
42
- not TOKEN_UNSAFE === str
39
+ not Constants::TOKEN_UNSAFE === str
43
40
  end
44
41
 
45
42
  # If the string supplied has TOKEN unsafe characters in it, will return the string quoted
@@ -89,7 +86,6 @@ module Mail
89
86
  str
90
87
  end
91
88
  end
92
- module_function :unquote
93
89
 
94
90
  # Removes any \-escaping.
95
91
  #
@@ -103,7 +99,6 @@ module Mail
103
99
  def unescape( str )
104
100
  str.gsub(/\\(.)/, '\1')
105
101
  end
106
- module_function :unescape
107
102
 
108
103
  # Wraps a string in parenthesis and escapes any that are in the string itself.
109
104
  #
@@ -111,7 +106,7 @@ module Mail
111
106
  #
112
107
  # paren( 'This is a string' ) #=> '(This is a string)'
113
108
  def paren( str )
114
- RubyVer.paren( str )
109
+ Utilities.paren( str )
115
110
  end
116
111
 
117
112
  # Unwraps a string from being wrapped in parenthesis
@@ -121,8 +116,11 @@ module Mail
121
116
  # str = '(This is a string)'
122
117
  # unparen( str ) #=> 'This is a string'
123
118
  def unparen( str )
124
- match = str.match(/^\((.*?)\)$/)
125
- match ? match[1] : str
119
+ if str.start_with?('(') && str.end_with?(')')
120
+ str.slice(1..-2)
121
+ else
122
+ str
123
+ end
126
124
  end
127
125
 
128
126
  # Wraps a string in angle brackets and escapes any that are in the string itself
@@ -131,7 +129,7 @@ module Mail
131
129
  #
132
130
  # bracket( 'This is a string' ) #=> '<This is a string>'
133
131
  def bracket( str )
134
- RubyVer.bracket( str )
132
+ Utilities.bracket( str )
135
133
  end
136
134
 
137
135
  # Unwraps a string from being wrapped in parenthesis
@@ -141,8 +139,11 @@ module Mail
141
139
  # str = '<This is a string>'
142
140
  # unbracket( str ) #=> 'This is a string'
143
141
  def unbracket( str )
144
- match = str.match(/^\<(.*?)\>$/)
145
- match ? match[1] : str
142
+ if str.start_with?('<') && str.end_with?('>')
143
+ str.slice(1..-2)
144
+ else
145
+ str
146
+ end
146
147
  end
147
148
 
148
149
  # Escape parenthesies in a string
@@ -152,7 +153,7 @@ module Mail
152
153
  # str = 'This is (a) string'
153
154
  # escape_paren( str ) #=> 'This is \(a\) string'
154
155
  def escape_paren( str )
155
- RubyVer.escape_paren( str )
156
+ Utilities.escape_paren( str )
156
157
  end
157
158
 
158
159
  def uri_escape( str )
@@ -164,7 +165,7 @@ module Mail
164
165
  end
165
166
 
166
167
  def uri_parser
167
- @uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
168
+ @uri_parser ||= URI.const_defined?(:DEFAULT_PARSER) ? URI::DEFAULT_PARSER : URI
168
169
  end
169
170
 
170
171
  # Matches two objects with their to_s values case insensitively
@@ -207,7 +208,7 @@ module Mail
207
208
  # string = :resent_from_field
208
209
  # dasherize( string ) #=> 'resent-from-field'
209
210
  def dasherize( str )
210
- str.to_s.tr(UNDERSCORE, HYPHEN)
211
+ str.to_s.tr(Constants::UNDERSCORE, Constants::HYPHEN)
211
212
  end
212
213
 
213
214
  # Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
@@ -218,68 +219,36 @@ module Mail
218
219
  # string = :resent_from_field
219
220
  # underscoreize ( string ) #=> 'resent_from_field'
220
221
  def underscoreize( str )
221
- str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
222
+ str.to_s.downcase.tr(Constants::HYPHEN, Constants::UNDERSCORE)
222
223
  end
223
224
 
224
- if RUBY_VERSION <= '1.8.6'
225
-
226
- def map_lines( str, &block )
227
- results = []
228
- str.each_line do |line|
229
- results << yield(line)
230
- end
231
- results
232
- end
233
-
234
- def map_with_index( enum, &block )
235
- results = []
236
- enum.each_with_index do |token, i|
237
- results[i] = yield(token, i)
238
- end
239
- results
240
- end
241
-
242
- else
243
-
244
- def map_lines( str, &block )
245
- str.each_line.map(&block)
246
- end
247
-
248
- def map_with_index( enum, &block )
249
- enum.each_with_index.map(&block)
250
- end
225
+ def map_lines( str, &block )
226
+ str.each_line.map(&block)
227
+ end
251
228
 
229
+ def map_with_index( enum, &block )
230
+ enum.each_with_index.map(&block)
252
231
  end
253
232
 
254
233
  def self.binary_unsafe_to_lf(string) #:nodoc:
255
- string.gsub(/\r\n|\r/, LF)
234
+ string.gsub(/\r\n|\r/, Constants::LF)
256
235
  end
257
236
 
258
237
  TO_CRLF_REGEX =
259
- if RUBY_VERSION >= '1.9'
260
- # This 1.9 only regex can save a reasonable amount of time (~20%)
261
- # by not matching "\r\n" so the string is returned unchanged in
262
- # the common case.
263
- Regexp.new("(?<!\r)\n|\r(?!\n)")
264
- else
265
- /\n|\r\n|\r/
266
- end
238
+ # This 1.9 only regex can save a reasonable amount of time (~20%)
239
+ # by not matching "\r\n" so the string is returned unchanged in
240
+ # the common case.
241
+ Regexp.new("(?<!\r)\n|\r(?!\n)")
267
242
 
268
243
  def self.binary_unsafe_to_crlf(string) #:nodoc:
269
- string.gsub(TO_CRLF_REGEX, CRLF)
244
+ string.gsub(TO_CRLF_REGEX, Constants::CRLF)
270
245
  end
271
246
 
272
- if RUBY_VERSION < '1.9'
273
- def self.safe_for_line_ending_conversion?(string) #:nodoc:
247
+ def self.safe_for_line_ending_conversion?(string) #:nodoc:
248
+ if string.encoding == Encoding::BINARY
274
249
  string.ascii_only?
275
- end
276
- else
277
- def self.safe_for_line_ending_conversion?(string) #:nodoc:
278
- if string.encoding == Encoding::BINARY
279
- string.ascii_only?
280
- else
281
- string.valid_encoding?
282
- end
250
+ else
251
+ string.valid_encoding?
283
252
  end
284
253
  end
285
254
 
@@ -311,7 +280,7 @@ module Mail
311
280
  # and arrays and hashes that have nothing in them.
312
281
  #
313
282
  # This logic is mostly shared with ActiveSupport's blank?
314
- def self.blank?(value)
283
+ def blank?(value)
315
284
  if value.kind_of?(NilClass)
316
285
  true
317
286
  elsif value.kind_of?(String)
@@ -320,5 +289,274 @@ module Mail
320
289
  value.respond_to?(:empty?) ? value.empty? : !value
321
290
  end
322
291
  end
292
+
293
+ def generate_message_id
294
+ "<#{Mail.random_tag}@#{::Socket.gethostname}.mail>"
295
+ end
296
+
297
+ class StrictCharsetEncoder
298
+ def encode(string, charset)
299
+ case charset
300
+ when /utf-?7/i
301
+ Mail::Utilities.decode_utf7(string)
302
+ else
303
+ string.force_encoding(Mail::Utilities.pick_encoding(charset))
304
+ end
305
+ end
306
+ end
307
+
308
+ class BestEffortCharsetEncoder
309
+ def encode(string, charset)
310
+ case charset
311
+ when /utf-?7/i
312
+ Mail::Utilities.decode_utf7(string)
313
+ else
314
+ string.force_encoding(pick_encoding(charset))
315
+ end
316
+ end
317
+
318
+ private
319
+
320
+ def pick_encoding(charset)
321
+ charset = case charset
322
+ when /ansi_x3.110-1983/
323
+ 'ISO-8859-1'
324
+ when /Windows-?1258/i # Windows-1258 is similar to 1252
325
+ "Windows-1252"
326
+ else
327
+ charset
328
+ end
329
+ Mail::Utilities.pick_encoding(charset)
330
+ end
331
+ end
332
+
333
+ class << self
334
+ attr_accessor :charset_encoder
335
+ end
336
+ self.charset_encoder = BestEffortCharsetEncoder.new
337
+
338
+ # Escapes any parenthesis in a string that are unescaped this uses
339
+ # a Ruby 1.9.1 regexp feature of negative look behind
340
+ def Utilities.escape_paren( str )
341
+ re = /(?<!\\)([\(\)])/ # Only match unescaped parens
342
+ str.gsub(re) { |s| '\\' + s }
343
+ end
344
+
345
+ def Utilities.paren( str )
346
+ str = ::Mail::Utilities.unparen( str )
347
+ str = escape_paren( str )
348
+ '(' + str + ')'
349
+ end
350
+
351
+ def Utilities.escape_bracket( str )
352
+ re = /(?<!\\)([\<\>])/ # Only match unescaped brackets
353
+ str.gsub(re) { |s| '\\' + s }
354
+ end
355
+
356
+ def Utilities.bracket( str )
357
+ str = ::Mail::Utilities.unbracket( str )
358
+ str = escape_bracket( str )
359
+ '<' + str + '>'
360
+ end
361
+
362
+ def Utilities.decode_base64(str)
363
+ if !str.end_with?("=") && str.length % 4 != 0
364
+ str = str.ljust((str.length + 3) & ~3, "=")
365
+ end
366
+ str.unpack( 'm' ).first
367
+ end
368
+
369
+ def Utilities.encode_base64(str)
370
+ [str].pack( 'm' )
371
+ end
372
+
373
+ def Utilities.has_constant?(klass, string)
374
+ klass.const_defined?( string, false )
375
+ end
376
+
377
+ def Utilities.get_constant(klass, string)
378
+ klass.const_get( string )
379
+ end
380
+
381
+ def Utilities.transcode_charset(str, from_encoding, to_encoding = Encoding::UTF_8)
382
+ to_encoding = Encoding.find(to_encoding)
383
+ replacement_char = to_encoding == Encoding::UTF_8 ? '�' : '?'
384
+ charset_encoder.encode(str.dup, from_encoding).encode(to_encoding, :undef => :replace, :invalid => :replace, :replace => replacement_char)
385
+ end
386
+
387
+ # From Ruby stdlib Net::IMAP
388
+ def Utilities.encode_utf7(string)
389
+ string.gsub(/(&)|[^\x20-\x7e]+/) do
390
+ if $1
391
+ "&-"
392
+ else
393
+ base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
394
+ "&" + base64.delete("=").tr("/", ",") + "-"
395
+ end
396
+ end.force_encoding(Encoding::ASCII_8BIT)
397
+ end
398
+
399
+ def Utilities.decode_utf7(utf7)
400
+ utf7.gsub(/&([^-]+)?-/n) do
401
+ if $1
402
+ ($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE)
403
+ else
404
+ "&"
405
+ end
406
+ end
407
+ end
408
+
409
+ def Utilities.b_value_encode(str, encoding = nil)
410
+ encoding = str.encoding.to_s
411
+ [Utilities.encode_base64(str), encoding]
412
+ end
413
+
414
+ def Utilities.b_value_decode(str)
415
+ match = str.match(/\=\?(.+)?\?[Bb]\?(.*)\?\=/m)
416
+ if match
417
+ charset = match[1]
418
+ str = Utilities.decode_base64(match[2])
419
+ str = charset_encoder.encode(str, charset)
420
+ end
421
+ transcode_to_scrubbed_utf8(str)
422
+ rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError, Encoding::InvalidByteSequenceError
423
+ warn "Encoding conversion failed #{$!}"
424
+ str.dup.force_encoding(Encoding::UTF_8)
425
+ end
426
+
427
+ def Utilities.q_value_encode(str, encoding = nil)
428
+ encoding = str.encoding.to_s
429
+ [Encodings::QuotedPrintable.encode(str), encoding]
430
+ end
431
+
432
+ def Utilities.q_value_decode(str)
433
+ match = str.match(/\=\?(.+)?\?[Qq]\?(.*)\?\=/m)
434
+ if match
435
+ charset = match[1]
436
+ string = match[2].gsub(/_/, '=20')
437
+ # Remove trailing = if it exists in a Q encoding
438
+ string = string.sub(/\=$/, '')
439
+ str = Encodings::QuotedPrintable.decode(string)
440
+ str = charset_encoder.encode(str, charset)
441
+ # We assume that binary strings hold utf-8 directly to work around
442
+ # jruby/jruby#829 which subtly changes String#encode semantics.
443
+ str.force_encoding(Encoding::UTF_8) if str.encoding == Encoding::ASCII_8BIT
444
+ end
445
+ transcode_to_scrubbed_utf8(str)
446
+ rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
447
+ warn "Encoding conversion failed #{$!}"
448
+ str.dup.force_encoding(Encoding::UTF_8)
449
+ end
450
+
451
+ def Utilities.param_decode(str, encoding)
452
+ str = uri_parser.unescape(str)
453
+ str = charset_encoder.encode(str, encoding) if encoding
454
+ transcode_to_scrubbed_utf8(str)
455
+ rescue Encoding::UndefinedConversionError, ArgumentError, Encoding::ConverterNotFoundError
456
+ warn "Encoding conversion failed #{$!}"
457
+ str.dup.force_encoding(Encoding::UTF_8)
458
+ end
459
+
460
+ def Utilities.param_encode(str)
461
+ encoding = str.encoding.to_s.downcase
462
+ language = Configuration.instance.param_encode_language
463
+ "#{encoding}'#{language}'#{uri_parser.escape(str)}"
464
+ end
465
+
466
+ def Utilities.uri_parser
467
+ URI::DEFAULT_PARSER
468
+ end
469
+
470
+ # Pick a Ruby encoding corresponding to the message charset. Most
471
+ # charsets have a Ruby encoding, but some need manual aliasing here.
472
+ #
473
+ # TODO: add this as a test somewhere:
474
+ # Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
475
+ # Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
476
+ def Utilities.pick_encoding(charset)
477
+ charset = charset.to_s
478
+ encoding = case charset.downcase
479
+
480
+ # ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
481
+ when /^iso[-_]?8859-(\d+)(-i)?$/
482
+ "ISO-8859-#{$1}"
483
+
484
+ # ISO-8859-15, ISO-2022-JP and alike
485
+ when /^iso[-_]?(\d{4})-?(\w{1,2})$/
486
+ "ISO-#{$1}-#{$2}"
487
+
488
+ # "ISO-2022-JP-KDDI" and alike
489
+ when /^iso[-_]?(\d{4})-?(\w{1,2})-?(\w*)$/
490
+ "ISO-#{$1}-#{$2}-#{$3}"
491
+
492
+ # UTF-8, UTF-32BE and alike
493
+ when /^utf[\-_]?(\d{1,2})?(\w{1,2})$/
494
+ "UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
495
+
496
+ # Windows-1252 and alike
497
+ when /^windows-?(.*)$/
498
+ "Windows-#{$1}"
499
+
500
+ when '8bit'
501
+ Encoding::ASCII_8BIT
502
+
503
+ # alternatives/misspellings of us-ascii seen in the wild
504
+ when /^iso[-_]?646(-us)?$/, 'us=ascii'
505
+ Encoding::ASCII
506
+
507
+ # Microsoft-specific alias for MACROMAN
508
+ when 'macintosh'
509
+ Encoding::MACROMAN
510
+
511
+ # Microsoft-specific alias for CP949 (Korean)
512
+ when 'ks_c_5601-1987'
513
+ Encoding::CP949
514
+
515
+ # Wrongly written Shift_JIS (Japanese)
516
+ when 'shift-jis'
517
+ Encoding::Shift_JIS
518
+
519
+ # GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
520
+ when 'gb2312'
521
+ Encoding::GB18030
522
+
523
+ when 'cp-850'
524
+ Encoding::CP850
525
+
526
+ when 'latin2'
527
+ Encoding::ISO_8859_2
528
+
529
+ else
530
+ charset
531
+ end
532
+
533
+ convert_to_encoding(encoding)
534
+ end
535
+
536
+ def Utilities.string_byteslice(str, *args)
537
+ str.byteslice(*args)
538
+ end
539
+
540
+ class << self
541
+ private
542
+
543
+ def convert_to_encoding(encoding)
544
+ if encoding.is_a?(Encoding)
545
+ encoding
546
+ else
547
+ # Fall back to ASCII for charsets that Ruby doesn't recognize
548
+ begin
549
+ Encoding.find(encoding)
550
+ rescue ArgumentError
551
+ Encoding::BINARY
552
+ end
553
+ end
554
+ end
555
+
556
+ def transcode_to_scrubbed_utf8(str)
557
+ decoded = str.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => "�")
558
+ decoded.valid_encoding? ? decoded : decoded.encode(Encoding::UTF_16LE, :invalid => :replace, :replace => "�").encode(Encoding::UTF_8)
559
+ end
560
+ end
323
561
  end
324
562
  end
data/lib/mail/version.rb CHANGED
@@ -3,9 +3,9 @@ module Mail
3
3
  module VERSION
4
4
 
5
5
  MAJOR = 2
6
- MINOR = 7
7
- PATCH = 1
8
- BUILD = nil
6
+ MINOR = 8
7
+ PATCH = 0
8
+ BUILD = 'rc1'
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
11
11
 
data/lib/mail/yaml.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'yaml'
2
+
3
+ module Mail
4
+ module YAML
5
+ def self.load(yaml)
6
+ permitted_classes = [
7
+ Symbol,
8
+
9
+ Mail::Body,
10
+
11
+ # Delivery methods as listed in mail/configuration.rb
12
+ Mail::SMTP,
13
+ Mail::Sendmail,
14
+ Mail::Exim,
15
+ Mail::FileDelivery,
16
+ Mail::SMTPConnection,
17
+ Mail::TestMailer,
18
+ Mail::LoggerDelivery,
19
+
20
+ Mail.delivery_method.class,
21
+ ]
22
+
23
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
24
+ ::YAML.safe_load(yaml, :permitted_classes => permitted_classes)
25
+ else
26
+ ::YAML.safe_load(yaml, permitted_classes)
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/mail.rb CHANGED
@@ -9,26 +9,8 @@ module Mail # :doc:
9
9
  require 'net/smtp'
10
10
  require 'mini_mime'
11
11
 
12
- if RUBY_VERSION <= '1.8.6'
13
- begin
14
- require 'tlsmail'
15
- rescue LoadError
16
- raise "You need to install tlsmail if you are using ruby <= 1.8.6"
17
- end
18
- end
19
-
20
- if RUBY_VERSION >= "1.9.0"
21
- require 'mail/version_specific/ruby_1_9'
22
- RubyVer = Ruby19
23
- else
24
- require 'mail/version_specific/ruby_1_8'
25
- RubyVer = Ruby18
26
- end
27
-
28
12
  require 'mail/version'
29
13
 
30
- require 'mail/core_extensions/string'
31
- require 'mail/core_extensions/smtp'
32
14
  require 'mail/indifferent_hash'
33
15
 
34
16
  require 'mail/multibyte'
@@ -68,8 +50,6 @@ module Mail # :doc:
68
50
 
69
51
  require 'mail/envelope'
70
52
 
71
- register_autoload :Parsers, "mail/parsers"
72
-
73
53
  # Autoload header field elements and transfer encodings.
74
54
  require 'mail/elements'
75
55
  require 'mail/encodings'