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
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 = subject =~ /^Re:/i ? subject : "Re: #{subject}"
305
298
  end
306
299
  if reply_to || from
307
300
  reply.to = self[reply_to ? :reply_to : :from].to_s
@@ -406,9 +399,9 @@ module Mail
406
399
  end
407
400
 
408
401
  # Sets the envelope from for the email
409
- def set_envelope( val )
402
+ def set_envelope(val)
410
403
  @raw_envelope = val
411
- @envelope = Mail::Envelope.new( val )
404
+ @envelope = Mail::Envelope.parse(val) rescue nil
412
405
  end
413
406
 
414
407
  # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
@@ -1339,7 +1332,7 @@ module Mail
1339
1332
  # mail['foo'] = '1234'
1340
1333
  # mail['foo'].to_s #=> '1234'
1341
1334
  def [](name)
1342
- header[underscoreize(name)]
1335
+ header[Utilities.underscoreize(name)]
1343
1336
  end
1344
1337
 
1345
1338
  # Method Missing in this implementation allows you to set any of the
@@ -1385,7 +1378,7 @@ module Mail
1385
1378
  #:nodoc:
1386
1379
  # Only take the structured fields, as we could take _anything_ really
1387
1380
  # as it could become an optional field... "but therin lies the dark side"
1388
- field_name = underscoreize(name).chomp("=")
1381
+ field_name = Utilities.underscoreize(name).chomp("=")
1389
1382
  if Mail::Field::KNOWN_FIELDS.include?(field_name)
1390
1383
  if args.empty?
1391
1384
  header[field_name]
@@ -1436,11 +1429,6 @@ module Mail
1436
1429
  header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1437
1430
  end
1438
1431
 
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
1432
  # Creates a new empty Message-ID field and inserts it in the correct order
1445
1433
  # into the Header. The MessageIdField object will automatically generate
1446
1434
  # a unique message ID if you try and encode it or output it to_s without
@@ -1489,7 +1477,9 @@ module Mail
1489
1477
  warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1490
1478
  warn(warning)
1491
1479
  end
1492
- header[:content_type].parameters['charset'] = @charset
1480
+ if @charset
1481
+ header[:content_type].parameters['charset'] = @charset
1482
+ end
1493
1483
  end
1494
1484
  end
1495
1485
 
@@ -1498,26 +1488,11 @@ module Mail
1498
1488
  header[:content_transfer_encoding] ||= body.default_encoding
1499
1489
  end
1500
1490
 
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
1491
  # Returns the MIME media type of part we are on, this is taken from the content-type header
1512
1492
  def mime_type
1513
1493
  has_content_type? ? header[:content_type].string : nil rescue nil
1514
1494
  end
1515
1495
 
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
1496
  # Returns the character set defined in the content type field
1522
1497
  def charset
1523
1498
  if @header
@@ -1544,12 +1519,6 @@ module Mail
1544
1519
  has_content_type? ? header[:content_type].sub_type : nil rescue nil
1545
1520
  end
1546
1521
 
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
1522
  # Returns the content type parameters
1554
1523
  def content_type_parameters
1555
1524
  has_content_type? ? header[:content_type].parameters : nil rescue nil
@@ -1798,12 +1767,22 @@ module Mail
1798
1767
  self.attachments[basename] = filedata
1799
1768
  end
1800
1769
 
1770
+ MULTIPART_CONVERSION_CONTENT_FIELDS = [ :content_description, :content_disposition, :content_transfer_encoding, :content_type ]
1771
+ private_constant :MULTIPART_CONVERSION_CONTENT_FIELDS if respond_to?(:private_constant)
1772
+
1801
1773
  def convert_to_multipart
1802
- text = body.decoded
1803
- self.body = ''
1804
- text_part = Mail::Part.new({:content_type => 'text/plain;',
1805
- :body => text})
1774
+ text_part = Mail::Part.new(:body => body.decoded)
1775
+
1776
+ MULTIPART_CONVERSION_CONTENT_FIELDS.each do |field_name|
1777
+ if value = send(field_name)
1778
+ writer = :"#{field_name}="
1779
+ text_part.send writer, value
1780
+ send writer, nil
1781
+ end
1782
+ end
1806
1783
  text_part.charset = charset unless @defaulted_charset
1784
+
1785
+ self.body = ''
1807
1786
  self.body << text_part
1808
1787
  end
1809
1788
 
@@ -1818,11 +1797,6 @@ module Mail
1818
1797
  add_required_fields
1819
1798
  end
1820
1799
 
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
1800
  # Outputs an encoded string representation of the mail message including
1827
1801
  # all headers, attachments, etc. This is an encoded email in US-ASCII,
1828
1802
  # so it is able to be directly sent to an email server.
@@ -1836,7 +1810,7 @@ module Mail
1836
1810
 
1837
1811
  def without_attachments!
1838
1812
  if has_attachments?
1839
- parts.delete_if { |p| p.attachment? }
1813
+ parts.delete_attachments
1840
1814
 
1841
1815
  reencoded = parts.empty? ? '' : body.encoded(content_transfer_encoding)
1842
1816
  @body = nil # So the new parts won't be added to the existing body
@@ -1867,7 +1841,7 @@ module Mail
1867
1841
  end
1868
1842
 
1869
1843
  def self.from_yaml(str)
1870
- hash = YAML.load(str)
1844
+ hash = Mail::YAML.load(str)
1871
1845
  m = self.new(:headers => hash['headers'])
1872
1846
  hash.delete('headers')
1873
1847
  hash.each do |k,v|
@@ -1900,6 +1874,15 @@ module Mail
1900
1874
  "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1901
1875
  end
1902
1876
 
1877
+ def inspect_structure
1878
+ inspect +
1879
+ if self.multipart?
1880
+ "\n" + parts.inspect_structure
1881
+ else
1882
+ ''
1883
+ end
1884
+ end
1885
+
1903
1886
  def decoded
1904
1887
  case
1905
1888
  when self.text?
@@ -1984,7 +1967,7 @@ module Mail
1984
1967
 
1985
1968
  private
1986
1969
 
1987
- HEADER_SEPARATOR = /#{Constants::CRLF}#{Constants::CRLF}/
1970
+ HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
1988
1971
 
1989
1972
  # 2.1. General Description
1990
1973
  # A message consists of header fields (collectively called "the header
@@ -2029,7 +2012,7 @@ module Mail
2029
2012
 
2030
2013
  def set_envelope_header
2031
2014
  raw_string = raw_source.to_s
2032
- if match_data = raw_string.match(/\AFrom\s(#{TEXT}+)#{Constants::CRLF}/m)
2015
+ if match_data = raw_string.match(/\AFrom\s+([^:\s]#{Constants::TEXT}*)#{Constants::LAX_CRLF}/m)
2033
2016
  set_envelope(match_data[1])
2034
2017
  self.raw_source = raw_string.sub(match_data[0], "")
2035
2018
  end
@@ -2053,10 +2036,12 @@ module Mail
2053
2036
  end
2054
2037
 
2055
2038
  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
2039
+ if body
2040
+ if body.multipart?
2041
+ self.content_transfer_encoding = @transport_encoding
2042
+ else
2043
+ self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
2044
+ end
2060
2045
  end
2061
2046
  end
2062
2047
 
@@ -2076,7 +2061,6 @@ module Mail
2076
2061
 
2077
2062
  def add_multipart_alternate_header
2078
2063
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2079
- header['content_type'].parameters[:charset] = @charset
2080
2064
  body.boundary = boundary
2081
2065
  end
2082
2066
 
@@ -2084,7 +2068,6 @@ module Mail
2084
2068
  unless body.boundary && boundary
2085
2069
  header['content-type'] = 'multipart/mixed' unless header['content-type']
2086
2070
  header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2087
- header['content_type'].parameters[:charset] = @charset
2088
2071
  body.boundary = boundary
2089
2072
  end
2090
2073
  end
@@ -2092,7 +2075,6 @@ module Mail
2092
2075
  def add_multipart_mixed_header
2093
2076
  unless header['content-type']
2094
2077
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2095
- header['content_type'].parameters[:charset] = @charset
2096
2078
  body.boundary = boundary
2097
2079
  end
2098
2080
  end
@@ -2109,7 +2091,7 @@ module Mail
2109
2091
  body_content = nil
2110
2092
 
2111
2093
  passed_in_options.each_pair do |k,v|
2112
- k = underscoreize(k).to_sym if k.class == String
2094
+ k = Utilities.underscoreize(k).to_sym if k.class == String
2113
2095
  if k == :headers
2114
2096
  self.headers(v)
2115
2097
  elsif k == :body
@@ -38,16 +38,10 @@ 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.
@@ -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
@@ -3,59 +3,42 @@
3
3
 
4
4
  module Mail #:nodoc:
5
5
  module Multibyte #:nodoc:
6
- if RUBY_VERSION >= "1.9"
7
- # Returns a regular expression that matches valid characters in the current encoding
8
- def self.valid_character
9
- VALID_CHARACTER[Encoding.default_external.to_s]
10
- end
11
- else
12
- def self.valid_character
13
- case $KCODE
14
- when 'UTF8'
15
- VALID_CHARACTER['UTF-8']
16
- when 'SJIS'
17
- VALID_CHARACTER['Shift_JIS']
18
- end
19
- end
6
+ # Returns a regular expression that matches valid characters in the current encoding
7
+ def self.valid_character
8
+ VALID_CHARACTER[Encoding.default_external.to_s]
20
9
  end
21
10
 
22
- if 'string'.respond_to?(:valid_encoding?)
23
- # Verifies the encoding of a string
24
- def self.verify(string)
25
- string.valid_encoding?
26
- end
27
- else
28
- def self.verify(string)
29
- if expression = valid_character
30
- # Splits the string on character boundaries, which are determined based on $KCODE.
31
- string.split(//).all? { |c| expression =~ c }
32
- else
33
- true
34
- end
11
+ # Returns true if string has valid utf-8 encoding
12
+ def self.is_utf8?(string)
13
+ case string.encoding
14
+ when Encoding::UTF_8
15
+ verify(string)
16
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
17
+ verify(to_utf8(string))
18
+ else
19
+ false
35
20
  end
36
21
  end
37
22
 
23
+ # Verifies the encoding of a string
24
+ def self.verify(string)
25
+ string.valid_encoding?
26
+ end
27
+
38
28
  # Verifies the encoding of the string and raises an exception when it's not valid
39
29
  def self.verify!(string)
40
30
  raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
41
31
  end
42
32
 
43
- if 'string'.respond_to?(:force_encoding)
44
- # Removes all invalid characters from the string.
45
- #
46
- # Note: this method is a no-op in Ruby 1.9
47
- def self.clean(string)
48
- string
49
- end
50
- else
51
- def self.clean(string)
52
- if expression = valid_character
53
- # Splits the string on character boundaries, which are determined based on $KCODE.
54
- string.split(//).grep(expression).join
55
- else
56
- string
57
- end
58
- end
33
+ # Removes all invalid characters from the string.
34
+ #
35
+ # Note: this method is a no-op in Ruby 1.9
36
+ def self.clean(string)
37
+ string
38
+ end
39
+
40
+ def self.to_utf8(string)
41
+ string.dup.force_encoding(Encoding::UTF_8)
59
42
  end
60
43
  end
61
44
  end
@@ -19,7 +19,6 @@ module Mail #:nodoc:
19
19
 
20
20
  self.proxy_class = Mail::Multibyte::Chars
21
21
 
22
- if RUBY_VERSION >= "1.9"
23
22
  # == Multibyte proxy
24
23
  #
25
24
  # +mb_chars+ is a multibyte safe proxy for string methods.
@@ -54,21 +53,12 @@ module Mail #:nodoc:
54
53
  # For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
55
54
  # information about how to change the default Multibyte behaviour see Mail::Multibyte.
56
55
  def self.mb_chars(str)
57
- if proxy_class.consumes?(str)
56
+ if is_utf8?(str)
58
57
  proxy_class.new(str)
59
58
  else
60
59
  str
61
60
  end
62
61
  end
63
- else
64
- def self.mb_chars(str)
65
- if proxy_class.wants?(str)
66
- proxy_class.new(str)
67
- else
68
- str
69
- end
70
- end
71
- end
72
62
 
73
63
  # Regular expressions that describe valid byte sequences for a character
74
64
  VALID_CHARACTER = {
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- module Mail
3
2
 
3
+ module Mail
4
4
  # A delivery method implementation which sends via exim.
5
5
  #
6
6
  # To use this, first find out where the exim binary is on your computer,
@@ -39,11 +39,12 @@ module Mail
39
39
  class Exim < Sendmail
40
40
  DEFAULTS = {
41
41
  :location => '/usr/sbin/exim',
42
- :arguments => '-i -t'
42
+ :arguments => %w[ -i -t ]
43
43
  }
44
44
 
45
- def self.call(path, arguments, destinations, encoded_message)
46
- super path, arguments, nil, encoded_message
45
+ # Uses -t option to extract recipients from the message.
46
+ def destinations_for(envelope)
47
+ nil
47
48
  end
48
49
  end
49
50
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'mail/check_delivery_params'
2
+ require 'mail/smtp_envelope'
3
3
 
4
4
  module Mail
5
5
  # FileDelivery class delivers emails into multiple files based on the destination
@@ -13,20 +13,16 @@ module Mail
13
13
  # Make sure the path you specify with :location is writable by the Ruby process
14
14
  # running Mail.
15
15
  class FileDelivery
16
- if RUBY_VERSION >= '1.9.1'
17
- require 'fileutils'
18
- else
19
- require 'ftools'
20
- end
16
+ require 'fileutils'
21
17
 
22
18
  attr_accessor :settings
23
19
 
24
20
  def initialize(values)
25
- self.settings = { :location => './mails' }.merge!(values)
21
+ self.settings = { :location => './mails', :extension => '' }.merge!(values)
26
22
  end
27
23
 
28
24
  def deliver!(mail)
29
- Mail::CheckDeliveryParams.check(mail)
25
+ envelope = Mail::SmtpEnvelope.new(mail)
30
26
 
31
27
  if ::File.respond_to?(:makedirs)
32
28
  ::File.makedirs settings[:location]
@@ -34,8 +30,13 @@ module Mail
34
30
  ::FileUtils.mkdir_p settings[:location]
35
31
  end
36
32
 
37
- mail.destinations.uniq.each do |to|
38
- ::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
33
+ envelope.to.uniq.each do |to|
34
+ path = ::File.join(settings[:location], File.basename(to.to_s+settings[:extension]))
35
+
36
+ ::File.open(path, 'a') do |f|
37
+ f.write envelope.message
38
+ f.write "\r\n\r\n"
39
+ end
39
40
  end
40
41
  end
41
42
  end