mail 2.7.1 → 2.9.1

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