mail 2.6.3 → 2.6.4.rc1

Sign up to get free protection for your applications and to get access to all the features.
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