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
data/lib/mail/message.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- require "yaml"
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+ require 'mail/yaml'
4
6
 
5
7
  module Mail
6
8
  # The Message class provides a single point of access to all things to do with an
@@ -46,10 +48,6 @@ module Mail
46
48
  # follows the header and is separated from the header by an empty line
47
49
  # (i.e., a line with nothing preceding the CRLF).
48
50
  class Message
49
-
50
- include Constants
51
- include Utilities
52
-
53
51
  # ==Making an email
54
52
  #
55
53
  # You can make an new mail object via a block, passing a string, file or direct assignment.
@@ -149,7 +147,7 @@ module Mail
149
147
  # m.to 'recipient@example.com'
150
148
  # end
151
149
  if block_given?
152
- if block.arity.zero? || (RUBY_VERSION < '1.9' && block.arity < 1)
150
+ if block.arity.zero?
153
151
  instance_eval(&block)
154
152
  else
155
153
  yield self
@@ -235,11 +233,6 @@ module Mail
235
233
  def self.default_charset=(charset); @@default_charset = charset; end
236
234
  self.default_charset = 'UTF-8'
237
235
 
238
- def register_for_delivery_notification(observer)
239
- warn("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
240
- Mail.register_observer(observer)
241
- end
242
-
243
236
  def inform_observers
244
237
  Mail.inform_observers(self)
245
238
  end
@@ -301,7 +294,7 @@ module Mail
301
294
  reply.references ||= bracketed_message_id
302
295
  end
303
296
  if subject
304
- reply.subject = subject =~ /^Re:/i ? subject : "RE: #{subject}"
297
+ reply.subject = /^Re:/i.match?(subject) ? subject : "Re: #{subject}"
305
298
  end
306
299
  if reply_to || from
307
300
  reply.to = self[reply_to ? :reply_to : :from].to_s
@@ -356,6 +349,10 @@ module Mail
356
349
  # the same content, ignoring the Message-ID field, unless BOTH emails have a defined and
357
350
  # different Message-ID value, then they are false.
358
351
  #
352
+ # Note that Mail creates Date and Mime-Type fields if they don't exist.
353
+ # The Date field is derived from the current time, so this needs to be allowed for in comparisons.
354
+ # (Mime-type does not depend on dynamic data, so cannot affect equality)
355
+ #
359
356
  # So, in practice the == operator works like this:
360
357
  #
361
358
  # m1 = Mail.new("Subject: Hello\r\n\r\nHello")
@@ -377,14 +374,17 @@ module Mail
377
374
  # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
378
375
  # m2 = Mail.new("Message-ID: <DIFFERENT@test>\r\nSubject: Hello\r\n\r\nHello")
379
376
  # m1 == m2 #=> false
380
- def ==(other)
377
+ def ==(other) # TODO could be more efficient
381
378
  return false unless other.respond_to?(:encoded)
382
379
 
380
+ stamp = Mail::CommonDateField.normalize_datetime('')
381
+ # Note: must always dup the inputs so they are not altered by encoded
383
382
  if self.message_id && other.message_id
384
- self.encoded == other.encoded
383
+ dup.tap { |m| m.date ||= stamp }.encoded ==
384
+ other.dup.tap { |m| m.date ||= stamp }.encoded
385
385
  else
386
- dup.tap { |m| m.message_id = '<temp@test>' }.encoded ==
387
- other.dup.tap { |m| m.message_id = '<temp@test>' }.encoded
386
+ dup.tap { |m| m.message_id = '<temp@test>'; m.date ||= stamp }.encoded ==
387
+ other.dup.tap { |m| m.message_id = '<temp@test>'; m.date ||= stamp }.encoded
388
388
  end
389
389
  end
390
390
 
@@ -406,9 +406,9 @@ module Mail
406
406
  end
407
407
 
408
408
  # Sets the envelope from for the email
409
- def set_envelope( val )
409
+ def set_envelope(val)
410
410
  @raw_envelope = val
411
- @envelope = Mail::Envelope.new( val )
411
+ @envelope = Mail::Envelope.parse(val) rescue nil
412
412
  end
413
413
 
414
414
  # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
@@ -471,7 +471,7 @@ module Mail
471
471
  # message.errors.size #=> 1
472
472
  # message.errors.first[0] #=> "Content-Transfer-Encoding"
473
473
  # message.errors.first[1] #=> "weirdo"
474
- # message.errors.first[3] #=> <The original error message exception>
474
+ # message.errors.first[2] #=> <The original error message exception>
475
475
  #
476
476
  # This is a good first defence on detecting spam by the way. Some spammers send
477
477
  # invalid emails to try and get email parsers to give up parsing them.
@@ -1323,7 +1323,7 @@ module Mail
1323
1323
  def []=(name, value)
1324
1324
  if name.to_s == 'body'
1325
1325
  self.body = value
1326
- elsif name.to_s =~ /content[-_]type/i
1326
+ elsif /content[-_]type/i.match?(name.to_s)
1327
1327
  header[name] = value
1328
1328
  elsif name.to_s == 'charset'
1329
1329
  self.charset = value
@@ -1339,7 +1339,7 @@ module Mail
1339
1339
  # mail['foo'] = '1234'
1340
1340
  # mail['foo'].to_s #=> '1234'
1341
1341
  def [](name)
1342
- header[underscoreize(name)]
1342
+ header[Utilities.underscoreize(name)]
1343
1343
  end
1344
1344
 
1345
1345
  # Method Missing in this implementation allows you to set any of the
@@ -1385,7 +1385,7 @@ module Mail
1385
1385
  #:nodoc:
1386
1386
  # Only take the structured fields, as we could take _anything_ really
1387
1387
  # as it could become an optional field... "but therin lies the dark side"
1388
- field_name = underscoreize(name).chomp("=")
1388
+ field_name = Utilities.underscoreize(name).chomp("=")
1389
1389
  if Mail::Field::KNOWN_FIELDS.include?(field_name)
1390
1390
  if args.empty?
1391
1391
  header[field_name]
@@ -1416,7 +1416,7 @@ module Mail
1416
1416
  header.has_date?
1417
1417
  end
1418
1418
 
1419
- # Returns true if the message has a Mime-Version field, the field may or may
1419
+ # Returns true if the message has a MIME-Version field, the field may or may
1420
1420
  # not have a value, but the field exists or not.
1421
1421
  def has_mime_version?
1422
1422
  header.has_mime_version?
@@ -1436,11 +1436,6 @@ module Mail
1436
1436
  header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1437
1437
  end
1438
1438
 
1439
- def has_transfer_encoding? # :nodoc:
1440
- warn(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
1441
- has_content_transfer_encoding?
1442
- end
1443
-
1444
1439
  # Creates a new empty Message-ID field and inserts it in the correct order
1445
1440
  # into the Header. The MessageIdField object will automatically generate
1446
1441
  # a unique message ID if you try and encode it or output it to_s without
@@ -1489,7 +1484,9 @@ module Mail
1489
1484
  warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1490
1485
  warn(warning)
1491
1486
  end
1492
- header[:content_type].parameters['charset'] = @charset
1487
+ if @charset
1488
+ header[:content_type].parameters['charset'] = @charset
1489
+ end
1493
1490
  end
1494
1491
  end
1495
1492
 
@@ -1498,30 +1495,15 @@ module Mail
1498
1495
  header[:content_transfer_encoding] ||= body.default_encoding
1499
1496
  end
1500
1497
 
1501
- def add_transfer_encoding # :nodoc:
1502
- warn(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
1503
- add_content_transfer_encoding
1504
- end
1505
-
1506
- def transfer_encoding # :nodoc:
1507
- warn(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
1508
- content_transfer_encoding
1509
- end
1510
-
1511
1498
  # Returns the MIME media type of part we are on, this is taken from the content-type header
1512
1499
  def mime_type
1513
1500
  has_content_type? ? header[:content_type].string : nil rescue nil
1514
1501
  end
1515
1502
 
1516
- def message_content_type
1517
- warn(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
1518
- mime_type
1519
- end
1520
-
1521
1503
  # Returns the character set defined in the content type field
1522
1504
  def charset
1523
1505
  if @header
1524
- has_content_type? ? content_type_parameters['charset'] : @charset
1506
+ has_content_type? && !multipart? ? content_type_parameters['charset'] : @charset
1525
1507
  else
1526
1508
  @charset
1527
1509
  end
@@ -1544,12 +1526,6 @@ module Mail
1544
1526
  has_content_type? ? header[:content_type].sub_type : nil rescue nil
1545
1527
  end
1546
1528
 
1547
- # Returns the content type parameters
1548
- def mime_parameters
1549
- warn(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
1550
- content_type_parameters
1551
- end
1552
-
1553
1529
  # Returns the content type parameters
1554
1530
  def content_type_parameters
1555
1531
  has_content_type? ? header[:content_type].parameters : nil rescue nil
@@ -1557,17 +1533,17 @@ module Mail
1557
1533
 
1558
1534
  # Returns true if the message is multipart
1559
1535
  def multipart?
1560
- has_content_type? ? !!(main_type =~ /^multipart$/i) : false
1536
+ has_content_type? ? /^multipart$/i.match?(main_type) : false
1561
1537
  end
1562
1538
 
1563
1539
  # Returns true if the message is a multipart/report
1564
1540
  def multipart_report?
1565
- multipart? && sub_type =~ /^report$/i
1541
+ multipart? && /^report$/i.match?(sub_type)
1566
1542
  end
1567
1543
 
1568
1544
  # Returns true if the message is a multipart/report; report-type=delivery-status;
1569
1545
  def delivery_status_report?
1570
- multipart_report? && content_type_parameters['report-type'] =~ /^delivery-status$/i
1546
+ multipart_report? && /^delivery-status$/i.match?(content_type_parameters['report-type'])
1571
1547
  end
1572
1548
 
1573
1549
  # returns the part in a multipart/report email that has the content-type delivery-status
@@ -1798,12 +1774,22 @@ module Mail
1798
1774
  self.attachments[basename] = filedata
1799
1775
  end
1800
1776
 
1777
+ MULTIPART_CONVERSION_CONTENT_FIELDS = [ :content_description, :content_disposition, :content_transfer_encoding, :content_type ]
1778
+ private_constant :MULTIPART_CONVERSION_CONTENT_FIELDS if respond_to?(:private_constant)
1779
+
1801
1780
  def convert_to_multipart
1802
- text = body.decoded
1803
- self.body = ''
1804
- text_part = Mail::Part.new({:content_type => 'text/plain;',
1805
- :body => text})
1781
+ text_part = Mail::Part.new(:content_type => 'text/plain;', :body => body.decoded)
1782
+
1783
+ MULTIPART_CONVERSION_CONTENT_FIELDS.each do |field_name|
1784
+ if value = send(field_name)
1785
+ writer = :"#{field_name}="
1786
+ text_part.send writer, value
1787
+ send writer, nil
1788
+ end
1789
+ end
1806
1790
  text_part.charset = charset unless @defaulted_charset
1791
+
1792
+ self.body = ''
1807
1793
  self.body << text_part
1808
1794
  end
1809
1795
 
@@ -1818,11 +1804,6 @@ module Mail
1818
1804
  add_required_fields
1819
1805
  end
1820
1806
 
1821
- def encode!
1822
- warn("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
1823
- ready_to_send!
1824
- end
1825
-
1826
1807
  # Outputs an encoded string representation of the mail message including
1827
1808
  # all headers, attachments, etc. This is an encoded email in US-ASCII,
1828
1809
  # so it is able to be directly sent to an email server.
@@ -1836,7 +1817,7 @@ module Mail
1836
1817
 
1837
1818
  def without_attachments!
1838
1819
  if has_attachments?
1839
- parts.delete_if { |p| p.attachment? }
1820
+ parts.delete_attachments
1840
1821
 
1841
1822
  reencoded = parts.empty? ? '' : body.encoded(content_transfer_encoding)
1842
1823
  @body = nil # So the new parts won't be added to the existing body
@@ -1867,7 +1848,7 @@ module Mail
1867
1848
  end
1868
1849
 
1869
1850
  def self.from_yaml(str)
1870
- hash = YAML.load(str)
1851
+ hash = Mail::YAML.load(str)
1871
1852
  m = self.new(:headers => hash['headers'])
1872
1853
  hash.delete('headers')
1873
1854
  hash.each do |k,v|
@@ -1881,7 +1862,7 @@ module Mail
1881
1862
  m.transport_encoding(v)
1882
1863
  when k == 'multipart_body'
1883
1864
  v.map {|part| m.add_part Mail::Part.from_yaml(part) }
1884
- when k =~ /^@/
1865
+ when k.start_with?('@')
1885
1866
  m.instance_variable_set(k.to_sym, v)
1886
1867
  end
1887
1868
  end
@@ -1900,6 +1881,15 @@ module Mail
1900
1881
  "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1901
1882
  end
1902
1883
 
1884
+ def inspect_structure
1885
+ inspect +
1886
+ if self.multipart?
1887
+ "\n" + parts.inspect_structure
1888
+ else
1889
+ ''
1890
+ end
1891
+ end
1892
+
1903
1893
  def decoded
1904
1894
  case
1905
1895
  when self.text?
@@ -1909,7 +1899,7 @@ module Mail
1909
1899
  when !self.multipart?
1910
1900
  body.decoded
1911
1901
  else
1912
- raise NoMethodError, 'Can not decode an entire message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
1902
+ raise NoMethodError, 'This message cannot be decoded as _entire_ message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
1913
1903
  end
1914
1904
  end
1915
1905
 
@@ -1979,12 +1969,12 @@ module Mail
1979
1969
  end
1980
1970
 
1981
1971
  def text?
1982
- has_content_type? ? !!(main_type =~ /^text$/i) : false
1972
+ has_content_type? ? /^text$/i.match?(main_type) : false
1983
1973
  end
1984
1974
 
1985
1975
  private
1986
1976
 
1987
- HEADER_SEPARATOR = /#{Constants::CRLF}#{Constants::CRLF}/
1977
+ HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
1988
1978
 
1989
1979
  # 2.1. General Description
1990
1980
  # A message consists of header fields (collectively called "the header
@@ -2029,7 +2019,7 @@ module Mail
2029
2019
 
2030
2020
  def set_envelope_header
2031
2021
  raw_string = raw_source.to_s
2032
- if match_data = raw_string.match(/\AFrom\s(#{TEXT}+)#{Constants::CRLF}/m)
2022
+ if match_data = raw_string.match(/\AFrom\s+([^:\s]#{Constants::TEXT}*)#{Constants::LAX_CRLF}/m)
2033
2023
  set_envelope(match_data[1])
2034
2024
  self.raw_source = raw_string.sub(match_data[0], "")
2035
2025
  end
@@ -2053,10 +2043,12 @@ module Mail
2053
2043
  end
2054
2044
 
2055
2045
  def identify_and_set_transfer_encoding
2056
- if body && body.multipart?
2057
- self.content_transfer_encoding = @transport_encoding
2058
- else
2059
- self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
2046
+ if body
2047
+ if body.multipart?
2048
+ self.content_transfer_encoding = @transport_encoding
2049
+ else
2050
+ self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
2051
+ end
2060
2052
  end
2061
2053
  end
2062
2054
 
@@ -2076,7 +2068,6 @@ module Mail
2076
2068
 
2077
2069
  def add_multipart_alternate_header
2078
2070
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2079
- header['content_type'].parameters[:charset] = @charset
2080
2071
  body.boundary = boundary
2081
2072
  end
2082
2073
 
@@ -2084,7 +2075,6 @@ module Mail
2084
2075
  unless body.boundary && boundary
2085
2076
  header['content-type'] = 'multipart/mixed' unless header['content-type']
2086
2077
  header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2087
- header['content_type'].parameters[:charset] = @charset
2088
2078
  body.boundary = boundary
2089
2079
  end
2090
2080
  end
@@ -2092,7 +2082,6 @@ module Mail
2092
2082
  def add_multipart_mixed_header
2093
2083
  unless header['content-type']
2094
2084
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2095
- header['content_type'].parameters[:charset] = @charset
2096
2085
  body.boundary = boundary
2097
2086
  end
2098
2087
  end
@@ -2109,7 +2098,7 @@ module Mail
2109
2098
  body_content = nil
2110
2099
 
2111
2100
  passed_in_options.each_pair do |k,v|
2112
- k = underscoreize(k).to_sym if k.class == String
2101
+ k = Utilities.underscoreize(k).to_sym if k.class == String
2113
2102
  if k == :headers
2114
2103
  self.headers(v)
2115
2104
  elsif k == :body
@@ -38,21 +38,15 @@ module Mail #:nodoc:
38
38
  alias to_s wrapped_string
39
39
  alias to_str wrapped_string
40
40
 
41
- if RUBY_VERSION >= "1.9"
42
- # Creates a new Chars instance by wrapping _string_.
43
- def initialize(string)
44
- @wrapped_string = string.dup
45
- @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
46
- end
47
- else
48
- def initialize(string) #:nodoc:
49
- @wrapped_string = string
50
- end
41
+ # Creates a new Chars instance by wrapping _string_.
42
+ def initialize(string)
43
+ @wrapped_string = string.dup
44
+ @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
51
45
  end
52
46
 
53
47
  # Forward all undefined methods to the wrapped string.
54
48
  def method_missing(method, *args, &block)
55
- if method.to_s =~ /!$/
49
+ if method.to_s.end_with?('!')
56
50
  @wrapped_string.__send__(method, *args, &block)
57
51
  self
58
52
  else
@@ -72,15 +66,6 @@ module Mail #:nodoc:
72
66
  true
73
67
  end
74
68
 
75
- # Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
76
- def self.consumes?(string)
77
- # Unpack is a little bit faster than regular expressions.
78
- string.unpack('U*')
79
- true
80
- rescue ArgumentError
81
- false
82
- end
83
-
84
69
  include Comparable
85
70
 
86
71
  # Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
@@ -94,151 +79,8 @@ module Mail #:nodoc:
94
79
  @wrapped_string <=> other.to_s
95
80
  end
96
81
 
97
- if RUBY_VERSION < "1.9"
98
- # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
99
- # +false+ otherwise.
100
- def self.wants?(string)
101
- $KCODE == 'UTF8' && consumes?(string)
102
- end
103
-
104
- # Returns a new Chars object containing the _other_ object concatenated to the string.
105
- #
106
- # Example:
107
- # (Mail::Multibyte.mb_chars('Café') + ' périferôl').to_s # => "Café périferôl"
108
- def +(other)
109
- chars(@wrapped_string + other)
110
- end
111
-
112
- # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
113
- #
114
- # Example:
115
- # Mail::Multibyte.mb_chars('Café périferôl') =~ /ô/ # => 12
116
- def =~(other)
117
- translate_offset(@wrapped_string =~ other)
118
- end
119
-
120
- # Inserts the passed string at specified codepoint offsets.
121
- #
122
- # Example:
123
- # Mail::Multibyte.mb_chars('Café').insert(4, ' périferôl').to_s # => "Café périferôl"
124
- def insert(offset, fragment)
125
- unpacked = Unicode.u_unpack(@wrapped_string)
126
- unless offset > unpacked.length
127
- @wrapped_string.replace(
128
- Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
129
- )
130
- else
131
- raise IndexError, "index #{offset} out of string"
132
- end
133
- self
134
- end
135
-
136
- # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
137
- #
138
- # Example:
139
- # Mail::Multibyte.mb_chars('Café').include?('é') # => true
140
- def include?(other)
141
- # We have to redefine this method because Enumerable defines it.
142
- @wrapped_string.include?(other)
143
- end
144
-
145
- # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
146
- #
147
- # Example:
148
- # Mail::Multibyte.mb_chars('Café périferôl').index('ô') # => 12
149
- # Mail::Multibyte.mb_chars('Café périferôl').index(/\w/u) # => 0
150
- def index(needle, offset=0)
151
- wrapped_offset = first(offset).wrapped_string.length
152
- index = @wrapped_string.index(needle, wrapped_offset)
153
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
154
- end
155
-
156
- # Returns the position _needle_ in the string, counting in
157
- # codepoints, searching backward from _offset_ or the end of the
158
- # string. Returns +nil+ if _needle_ isn't found.
159
- #
160
- # Example:
161
- # Mail::Multibyte.mb_chars('Café périferôl').rindex('é') # => 6
162
- # Mail::Multibyte.mb_chars('Café périferôl').rindex(/\w/u) # => 13
163
- def rindex(needle, offset=nil)
164
- offset ||= length
165
- wrapped_offset = first(offset).wrapped_string.length
166
- index = @wrapped_string.rindex(needle, wrapped_offset)
167
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
168
- end
169
-
170
- # Returns the number of codepoints in the string
171
- def size
172
- Unicode.u_unpack(@wrapped_string).size
173
- end
174
- alias_method :length, :size
175
-
176
- # Strips entire range of Unicode whitespace from the right of the string.
177
- def rstrip
178
- chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
179
- end
180
-
181
- # Strips entire range of Unicode whitespace from the left of the string.
182
- def lstrip
183
- chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
184
- end
185
-
186
- # Strips entire range of Unicode whitespace from the right and left of the string.
187
- def strip
188
- rstrip.lstrip
189
- end
190
-
191
- # Returns the codepoint of the first character in the string.
192
- #
193
- # Example:
194
- # Mail::Multibyte.mb_chars('こんにちは').ord # => 12371
195
- def ord
196
- Unicode.u_unpack(@wrapped_string)[0]
197
- end
198
-
199
- # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
200
- #
201
- # Example:
202
- #
203
- # Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
204
- # # => " ¾ cup"
205
- #
206
- # Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
207
- # # => "   ¾ cup"
208
- def rjust(integer, padstr=' ')
209
- justify(integer, :right, padstr)
210
- end
211
-
212
- # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
213
- #
214
- # Example:
215
- #
216
- # Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
217
- # # => "¾ cup "
218
- #
219
- # Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
220
- # # => "¾ cup   "
221
- def ljust(integer, padstr=' ')
222
- justify(integer, :left, padstr)
223
- end
224
-
225
- # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
226
- #
227
- # Example:
228
- #
229
- # Mail::Multibyte.mb_chars("¾ cup").center(8).to_s
230
- # # => " ¾ cup "
231
- #
232
- # Mail::Multibyte.mb_chars("¾ cup").center(8, " ").to_s # Use non-breaking whitespace
233
- # # => " ¾ cup  "
234
- def center(integer, padstr=' ')
235
- justify(integer, :center, padstr)
236
- end
237
-
238
- else
239
- def =~(other)
240
- @wrapped_string =~ other
241
- end
82
+ def =~(other)
83
+ @wrapped_string =~ other
242
84
  end
243
85
 
244
86
  # Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
@@ -331,7 +173,7 @@ module Mail #:nodoc:
331
173
  #
332
174
  # Example:
333
175
  # s = 'こんにちは'
334
- # s.mb_chars.limit(7) # => "こに"
176
+ # s.mb_chars.limit(7) # => "こん"
335
177
  def limit(limit)
336
178
  slice(0...translate_offset(limit))
337
179
  end
@@ -419,7 +261,7 @@ module Mail #:nodoc:
419
261
  # exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
420
262
  if public_method_defined?(method)
421
263
  define_method("#{method}!") do |*args|
422
- @wrapped_string = send(args.nil? ? method : method, *args).to_s
264
+ @wrapped_string = send(method, *args).to_s
423
265
  self
424
266
  end
425
267
  end
@@ -375,7 +375,7 @@ module Mail
375
375
 
376
376
  # Returns the directory in which the data files are stored
377
377
  def self.dirname
378
- File.dirname(__FILE__) + '/../values/'
378
+ __dir__ + '/../values/'
379
379
  end
380
380
 
381
381
  # Returns the filename for the data file for this version