mail 2.6.3 → 2.6.4.rc1

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +22 -0
  3. data/Gemfile +1 -4
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +38 -2
  6. data/Rakefile +2 -2
  7. data/lib/mail.rb +8 -2
  8. data/lib/mail/body.rb +4 -4
  9. data/lib/mail/check_delivery_params.rb +3 -3
  10. data/lib/mail/constants.rb +2 -1
  11. data/lib/mail/core_extensions/nil.rb +0 -6
  12. data/lib/mail/core_extensions/string.rb +0 -6
  13. data/lib/mail/elements/address.rb +7 -7
  14. data/lib/mail/encodings.rb +25 -28
  15. data/lib/mail/encodings/8bit.rb +5 -0
  16. data/lib/mail/encodings/base64.rb +5 -0
  17. data/lib/mail/encodings/quoted_printable.rb +6 -1
  18. data/lib/mail/encodings/transfer_encoding.rb +26 -17
  19. data/lib/mail/field.rb +18 -4
  20. data/lib/mail/fields/bcc_field.rb +14 -3
  21. data/lib/mail/fields/cc_field.rb +0 -1
  22. data/lib/mail/fields/common/common_address.rb +7 -7
  23. data/lib/mail/fields/common/common_date.rb +1 -1
  24. data/lib/mail/fields/common/common_field.rb +1 -1
  25. data/lib/mail/fields/common/common_message_id.rb +2 -2
  26. data/lib/mail/fields/content_disposition_field.rb +11 -11
  27. data/lib/mail/fields/content_id_field.rb +2 -2
  28. data/lib/mail/fields/content_location_field.rb +1 -1
  29. data/lib/mail/fields/content_transfer_encoding_field.rb +1 -1
  30. data/lib/mail/fields/content_type_field.rb +1 -1
  31. data/lib/mail/fields/date_field.rb +1 -1
  32. data/lib/mail/fields/from_field.rb +0 -1
  33. data/lib/mail/fields/keywords_field.rb +1 -2
  34. data/lib/mail/fields/message_id_field.rb +1 -1
  35. data/lib/mail/fields/mime_version_field.rb +2 -2
  36. data/lib/mail/fields/received_field.rb +3 -3
  37. data/lib/mail/fields/reply_to_field.rb +0 -1
  38. data/lib/mail/fields/resent_bcc_field.rb +0 -1
  39. data/lib/mail/fields/resent_cc_field.rb +0 -1
  40. data/lib/mail/fields/resent_date_field.rb +1 -1
  41. data/lib/mail/fields/resent_from_field.rb +0 -1
  42. data/lib/mail/fields/resent_sender_field.rb +0 -1
  43. data/lib/mail/fields/resent_to_field.rb +0 -1
  44. data/lib/mail/fields/return_path_field.rb +0 -1
  45. data/lib/mail/fields/sender_field.rb +0 -1
  46. data/lib/mail/fields/to_field.rb +0 -1
  47. data/lib/mail/fields/unstructured_field.rb +1 -1
  48. data/lib/mail/header.rb +3 -3
  49. data/lib/mail/matchers/attachment_matchers.rb +28 -0
  50. data/lib/mail/matchers/has_sent_mail.rb +30 -7
  51. data/lib/mail/message.rb +15 -21
  52. data/lib/mail/multibyte/unicode.rb +20 -16
  53. data/lib/mail/parsers/address_lists_parser.rb +1 -1
  54. data/lib/mail/parsers/content_disposition_parser.rb +1 -1
  55. data/lib/mail/parsers/content_location_parser.rb +1 -1
  56. data/lib/mail/parsers/content_transfer_encoding_parser.rb +1 -1
  57. data/lib/mail/parsers/envelope_from_parser.rb +1 -1
  58. data/lib/mail/parsers/message_ids_parser.rb +1 -1
  59. data/lib/mail/parsers/mime_version_parser.rb +1 -1
  60. data/lib/mail/part.rb +4 -2
  61. data/lib/mail/parts_list.rb +25 -8
  62. data/lib/mail/utilities.rb +15 -0
  63. data/lib/mail/values/unicode_tables.dat +0 -0
  64. data/lib/mail/version.rb +2 -2
  65. data/lib/mail/version_specific/ruby_1_8.rb +10 -4
  66. data/lib/mail/version_specific/ruby_1_9.rb +39 -10
  67. metadata +8 -8
  68. data/lib/mail/core_extensions/object.rb +0 -13
@@ -39,7 +39,6 @@ module Mail
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
41
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
42
  self
44
43
  end
45
44
 
@@ -13,7 +13,7 @@ module Mail
13
13
 
14
14
  def initialize(value = nil, charset = 'utf-8')
15
15
  self.charset = charset
16
- if value.blank?
16
+ if Utilities.blank?(value)
17
17
  value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
18
18
  else
19
19
  value = strip_field(FIELD_NAME, value)
@@ -39,7 +39,6 @@ module Mail
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
41
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
42
  self
44
43
  end
45
44
 
@@ -38,7 +38,6 @@ module Mail
38
38
  def initialize(value = nil, charset = 'utf-8')
39
39
  self.charset = charset
40
40
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
41
- self.parse
42
41
  self
43
42
  end
44
43
 
@@ -39,7 +39,6 @@ module Mail
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
41
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
42
  self
44
43
  end
45
44
 
@@ -41,7 +41,6 @@ module Mail
41
41
  value = nil if value == '<>'
42
42
  self.charset = charset
43
43
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
44
- self.parse
45
44
  self
46
45
  end
47
46
 
@@ -39,7 +39,6 @@ module Mail
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
41
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
42
  self
44
43
  end
45
44
 
@@ -39,7 +39,6 @@ module Mail
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
41
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
42
  self
44
43
  end
45
44
 
@@ -70,7 +70,7 @@ module Mail
70
70
  end
71
71
 
72
72
  def do_decode
73
- value.blank? ? nil : Encodings.decode_encode(value, :decode)
73
+ Utilities.blank?(value) ? nil : Encodings.decode_encode(value, :decode)
74
74
  end
75
75
 
76
76
  # 2.2.3. Long Header Fields
@@ -136,7 +136,7 @@ module Mail
136
136
  case
137
137
  when selected.length > 1
138
138
  selected.map { |f| f }
139
- when !selected.blank?
139
+ when !Utilities.blank?(selected)
140
140
  selected.first
141
141
  else
142
142
  nil
@@ -166,11 +166,11 @@ module Mail
166
166
 
167
167
  case
168
168
  # User wants to delete the field
169
- when !selected.blank? && value == nil
169
+ when !Utilities.blank?(selected) && value == nil
170
170
  fields.delete_if { |f| selected.include?(f) }
171
171
 
172
172
  # User wants to change the field
173
- when !selected.blank? && limited_field?(fn)
173
+ when !Utilities.blank?(selected) && limited_field?(fn)
174
174
  selected.first.update(fn, value)
175
175
 
176
176
  # User wants to create the field
@@ -0,0 +1,28 @@
1
+ module Mail
2
+ module Matchers
3
+ def any_attachment
4
+ AnyAttachmentMatcher.new
5
+ end
6
+
7
+ def an_attachment_with_filename(filename)
8
+ AttachmentFilenameMatcher.new(filename)
9
+ end
10
+
11
+ class AnyAttachmentMatcher
12
+ def ===(other)
13
+ other.attachment?
14
+ end
15
+ end
16
+
17
+ class AttachmentFilenameMatcher
18
+ attr_reader :filename
19
+ def initialize(filename)
20
+ @filename = filename
21
+ end
22
+
23
+ def ===(other)
24
+ other.attachment? && other.filename == filename
25
+ end
26
+ end
27
+ end
28
+ end
@@ -42,15 +42,25 @@ module Mail
42
42
 
43
43
  def bcc(recipient_or_list)
44
44
  @blind_copy_recipients ||= []
45
+ @blind_copy_recipients.concat(Array(recipient_or_list))
46
+ self
47
+ end
45
48
 
46
- if recipient_or_list.kind_of?(Array)
47
- @blind_copy_recipients += recipient_or_list
48
- else
49
- @blind_copy_recipients << recipient_or_list
50
- end
49
+ def with_attachments(attachments)
50
+ @attachments ||= []
51
+ @attachments.concat(Array(attachments))
52
+ self
53
+ end
54
+
55
+ def with_no_attachments
56
+ @having_attachments = false
51
57
  self
52
58
  end
53
59
 
60
+ def with_any_attachments
61
+ @having_attachments = true
62
+ self
63
+ end
54
64
 
55
65
  def with_subject(subject)
56
66
  @subject = subject
@@ -95,8 +105,10 @@ module Mail
95
105
 
96
106
  def filter_matched_deliveries(deliveries)
97
107
  candidate_deliveries = deliveries
98
-
99
- %w(sender recipients copy_recipients blind_copy_recipients subject subject_matcher body body_matcher).each do |modifier_name|
108
+ modifiers =
109
+ %w(sender recipients copy_recipients blind_copy_recipients subject
110
+ subject_matcher body body_matcher having_attachments attachments)
111
+ modifiers.each do |modifier_name|
100
112
  next unless instance_variable_defined?("@#{modifier_name}")
101
113
  candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
102
114
  end
@@ -128,6 +140,17 @@ module Mail
128
140
  @subject_matcher.match delivery.subject
129
141
  end
130
142
 
143
+ def matches_on_having_attachments?(delivery)
144
+ @having_attachments && delivery.attachments.any? ||
145
+ (!@having_attachments && delivery.attachments.none?)
146
+ end
147
+
148
+ def matches_on_attachments?(delivery)
149
+ @attachments.each_with_index.inject( true ) do |sent_attachments, (attachment, index)|
150
+ sent_attachments &&= (attachment === delivery.attachments[index])
151
+ end
152
+ end
153
+
131
154
  def matches_on_body?(delivery)
132
155
  delivery.body == @body
133
156
  end
@@ -1415,7 +1415,7 @@ module Mail
1415
1415
  end
1416
1416
 
1417
1417
  def has_content_transfer_encoding?
1418
- header[:content_transfer_encoding] && header[:content_transfer_encoding].errors.blank?
1418
+ header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1419
1419
  end
1420
1420
 
1421
1421
  def has_transfer_encoding? # :nodoc:
@@ -1669,6 +1669,8 @@ module Mail
1669
1669
  def html_part=(msg)
1670
1670
  # Assign the html part and set multipart/alternative if there's a text part.
1671
1671
  if msg
1672
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1673
+
1672
1674
  @html_part = msg
1673
1675
  @html_part.content_type = 'text/html' unless @html_part.has_content_type?
1674
1676
  add_multipart_alternate_header if text_part
@@ -1691,6 +1693,8 @@ module Mail
1691
1693
  def text_part=(msg)
1692
1694
  # Assign the text part and set multipart/alternative if there's an html part.
1693
1695
  if msg
1696
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1697
+
1694
1698
  @text_part = msg
1695
1699
  @text_part.content_type = 'text/plain' unless @text_part.has_content_type?
1696
1700
  add_multipart_alternate_header if html_part
@@ -1709,7 +1713,7 @@ module Mail
1709
1713
 
1710
1714
  # Adds a part to the parts list or creates the part list
1711
1715
  def add_part(part)
1712
- if !body.multipart? && !self.body.decoded.blank?
1716
+ if !body.multipart? && !Utilities.blank?(self.body.decoded)
1713
1717
  @text_part = Mail::Part.new('Content-Type: text/plain;')
1714
1718
  @text_part.body = body.decoded
1715
1719
  self.body << @text_part
@@ -1765,14 +1769,17 @@ module Mail
1765
1769
  #
1766
1770
  # See also #attachments
1767
1771
  def add_file(values)
1768
- convert_to_multipart unless self.multipart? || self.body.decoded.blank?
1772
+ convert_to_multipart unless self.multipart? || Utilities.blank?(self.body.decoded)
1769
1773
  add_multipart_mixed_header
1770
1774
  if values.is_a?(String)
1771
1775
  basename = File.basename(values)
1772
1776
  filedata = File.open(values, 'rb') { |f| f.read }
1773
1777
  else
1774
1778
  basename = values[:filename]
1775
- filedata = values[:content] || File.open(values[:filename], 'rb') { |f| f.read }
1779
+ filedata = values
1780
+ unless filedata[:content]
1781
+ filedata = values.merge(:content=>File.open(values[:filename], 'rb') { |f| f.read })
1782
+ end
1776
1783
  end
1777
1784
  self.attachments[basename] = filedata
1778
1785
  end
@@ -1857,7 +1864,7 @@ module Mail
1857
1864
  case
1858
1865
  when k == 'delivery_handler'
1859
1866
  begin
1860
- m.delivery_handler = Object.const_get(v) unless v.blank?
1867
+ m.delivery_handler = Object.const_get(v) unless Utilities.blank?(v)
1861
1868
  rescue NameError
1862
1869
  end
1863
1870
  when k == 'transport_encoding'
@@ -2020,7 +2027,7 @@ module Mail
2020
2027
  raw_string = raw_source.to_s
2021
2028
  if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
2022
2029
  set_envelope(match_data[1])
2023
- self.raw_source = raw_string.sub(match_data[0], "")
2030
+ self.raw_source = raw_string.sub(match_data[0], "")
2024
2031
  end
2025
2032
  end
2026
2033
 
@@ -2046,7 +2053,7 @@ module Mail
2046
2053
  add_required_message_fields
2047
2054
  add_multipart_mixed_header if body.multipart?
2048
2055
  add_content_type unless has_content_type?
2049
- add_charset unless has_charset?
2056
+ add_charset if text? && !has_charset?
2050
2057
  add_content_transfer_encoding unless has_content_transfer_encoding?
2051
2058
  end
2052
2059
 
@@ -2146,20 +2153,7 @@ module Mail
2146
2153
  end
2147
2154
 
2148
2155
  def decode_body_as_text
2149
- body_text = decode_body
2150
- if charset
2151
- if RUBY_VERSION < '1.9'
2152
- require 'iconv'
2153
- return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
2154
- else
2155
- if encoding = Encoding.find(charset) rescue nil
2156
- body_text.force_encoding(encoding)
2157
- return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
2158
- end
2159
- end
2160
- end
2161
- body_text
2156
+ Encodings.transcode_charset decode_body, charset, 'UTF-8'
2162
2157
  end
2163
-
2164
2158
  end
2165
2159
  end
@@ -1,6 +1,26 @@
1
1
  module Mail
2
2
  module Multibyte
3
3
  module Unicode
4
+ # Adapted from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/multibyte/unicode.rb
5
+ # under the MIT license
6
+ # The Unicode version that is supported by the implementation
7
+ UNICODE_VERSION = '7.0.0'
8
+
9
+ # Holds data about a codepoint in the Unicode database.
10
+ class Codepoint
11
+ attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
12
+
13
+ # Initializing Codepoint object with default values
14
+ def initialize
15
+ @combining_class = 0
16
+ @uppercase_mapping = 0
17
+ @lowercase_mapping = 0
18
+ end
19
+
20
+ def swapcase_mapping
21
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
22
+ end
23
+ end
4
24
 
5
25
  extend self
6
26
 
@@ -8,9 +28,6 @@ module Mail
8
28
  # information about normalization.
9
29
  NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
10
30
 
11
- # The Unicode version that is supported by the implementation
12
- UNICODE_VERSION = '5.2.0'
13
-
14
31
  # The default normalization used for operations that require normalization. It can be set to any of the
15
32
  # normalizations in NORMALIZATION_FORMS.
16
33
  #
@@ -308,11 +325,6 @@ module Mail
308
325
  end.pack('U*')
309
326
  end
310
327
 
311
- # Holds data about a codepoint in the Unicode database
312
- class Codepoint
313
- attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
314
- end
315
-
316
328
  # Holds static data from the Unicode database
317
329
  class UnicodeDatabase
318
330
  ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
@@ -390,11 +402,3 @@ module Mail
390
402
  end
391
403
  end
392
404
  end
393
-
394
- unless defined?(ActiveSupport)
395
- module ActiveSupport
396
- unless const_defined?(:Multibyte)
397
- Multibyte = Mail::Multibyte
398
- end
399
- end
400
- end
@@ -5,7 +5,7 @@ module Mail::Parsers
5
5
  def parse(s)
6
6
  address_list = AddressListStruct.new([],[])
7
7
 
8
- if s.blank?
8
+ if Mail::Utilities.blank?(s)
9
9
  return address_list
10
10
  end
11
11
 
@@ -4,7 +4,7 @@ module Mail::Parsers
4
4
 
5
5
  def parse(s)
6
6
  content_disposition = ContentDispositionStruct.new("", nil)
7
- if s.blank?
7
+ if Mail::Utilities.blank?(s)
8
8
  return content_disposition
9
9
  end
10
10
 
@@ -2,7 +2,7 @@ module Mail::Parsers
2
2
  class ContentLocationParser
3
3
  def parse(s)
4
4
  content_location = ContentLocationStruct.new(nil)
5
- if s.blank?
5
+ if Mail::Utilities.blank?(s)
6
6
  return content_location
7
7
  end
8
8
 
@@ -3,7 +3,7 @@ module Mail::Parsers
3
3
 
4
4
  def parse(s)
5
5
  content_transfer_encoding = ContentTransferEncodingStruct.new("")
6
- if s.blank?
6
+ if Mail::Utilities.blank?(s)
7
7
  return content_transfer_encoding
8
8
  end
9
9
 
@@ -2,7 +2,7 @@ module Mail::Parsers
2
2
  class EnvelopeFromParser
3
3
  def parse(s)
4
4
  envelope_from = EnvelopeFromStruct.new
5
- if s.blank?
5
+ if Mail::Utilities.blank?(s)
6
6
  return envelope_from
7
7
  end
8
8
 
@@ -1,7 +1,7 @@
1
1
  module Mail::Parsers
2
2
  class MessageIdsParser
3
3
  def parse(s)
4
- if s.blank?
4
+ if Mail::Utilities.blank?(s)
5
5
  return MessageIdsStruct.new
6
6
  end
7
7
 
@@ -3,7 +3,7 @@ module Mail::Parsers
3
3
  include Mail::Utilities
4
4
 
5
5
  def parse(s)
6
- if s.blank?
6
+ if Mail::Utilities.blank?(s)
7
7
  return MimeVersionStruct.new("", nil)
8
8
  end
9
9
 
@@ -34,7 +34,7 @@ module Mail
34
34
  end
35
35
 
36
36
  def inline?
37
- header[:content_disposition].disposition_type == 'inline' if header[:content_disposition]
37
+ header[:content_disposition].disposition_type == 'inline' if header[:content_disposition].respond_to?(:disposition_type)
38
38
  end
39
39
 
40
40
  def add_required_fields
@@ -94,8 +94,10 @@ module Mail
94
94
  def get_return_values(key)
95
95
  if delivery_status_data[key].is_a?(Array)
96
96
  delivery_status_data[key].map { |a| a.value }
97
- else
97
+ elsif !delivery_status_data[key].nil?
98
98
  delivery_status_data[key].value
99
+ else
100
+ nil
99
101
  end
100
102
  end
101
103