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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3af499471587d4cb917ab992eb4bb45f19b9579e
4
- data.tar.gz: f05e6aaf887b9a79b9252e7edc2dd74b81f7cb6c
3
+ metadata.gz: 9294e13f17b710d1817da0765d4f366904f79b0a
4
+ data.tar.gz: 7c7e6bb27d50e7d0a77aeff4b0024a575950fa6e
5
5
  SHA512:
6
- metadata.gz: 75bb13148f71ffbbef303ca23f01b0c6c8a14aeea255bd528ba3ac0545c056b75ff3a420dc0578c7f15efd894a3367072496121c8479aa8ddc78e376c115168f
7
- data.tar.gz: 0f26c569a7c210d3ee57d95cf5011f8ff4941f5c1cd054ef804f018aa829bec44db3fdde7ab2fe1a89056cf81a5e6d618735caaaf9ccf4e8810e4295256f012e
6
+ metadata.gz: 21c9ad8d24e777f6eb598f91f26502d9a22e8ee7477d222b58a5d08d33cb395e0fbe7423ef4d55b28b41a0829a081f2f489e9b5ab60b3a5aa6f5a3dcb6d91460
7
+ data.tar.gz: ee66d0c7ad442f39ecc5a383098d156309faf9ee231197e9a7cac7899f7b286d234d2de83a170c4069699650c75d21f74c0eff4cd369473d2b0dba5ceb426c38
@@ -1,5 +1,27 @@
1
1
  == HEAD
2
2
 
3
+ Features:
4
+ * #772 - Normalize encoding matchers (grosser)
5
+ * #775 - Avoid failed encodings / stop bad charsets early (grosser)
6
+ * #782 – Make the gem compatible with Rubinius (robin850)
7
+ * #865 - Allow a body with an invalid encoding to be round tripped (kjg)
8
+ * #866 - Support decoding message bodies with non-Ruby-standard charsets (jeremy)
9
+ * #868 - Use the Ruby19.charset_encoder when decoding message bodies (kjg)
10
+ * #872 - Low-level option to include BCC field in the encoded message (grossadamm)
11
+ * #901 - Allow mail.text_part = '…' and mail.html_part = '<p>…</p>' (taavo)
12
+ * #924 - Matcher for having attachments (schepedw)
13
+
14
+ Bugs:
15
+ * #719 - Fix to not extract header content that looks like its field name (kjg)
16
+ * #789 - Fix encoding collapsing not dealing with multiple encodings in 1 line (grosser)
17
+ * #808 - Mail::Field correctly responds_to? the methods of its instantiated field (thegcat)
18
+ * #849 - Handle calling Part#inline? when the Content-Disposition field couldn't be parsed (kjg)
19
+ * #874 – Stay under 1000-char SMTP line length limits (pushrax)
20
+ * #877 - Make Mail::Field == other take the field value into account (kjg)
21
+ * #907 - Mail::ContentDispositionField should work with nil value (kjg)
22
+ * #910 - Mail::Address should handle b_value_encoded local and domain parts (kjg)
23
+ * #918 - Account for possibility of absent delivery-status headers (kjg)
24
+
3
25
  == Version 2.6.3 - Mon Nov 3 23:53 +1100 2014 Mikel Lindsaar <mikel@reinteractive.net>
4
26
 
5
27
  * #796 support uu encoding (grosser)
data/Gemfile CHANGED
@@ -5,11 +5,8 @@ gemspec
5
5
  gem "tlsmail", "~> 0.0.1" if RUBY_VERSION <= "1.8.6"
6
6
  gem "jruby-openssl", :platforms => :jruby
7
7
 
8
- group :development, :test do
9
- gem "appraisal", "~> 1.0"
10
- end
11
-
12
8
  # For gems not required to run tests
13
9
  group :local_development, :test do
10
+ gem "appraisal", "~> 1.0" unless RUBY_VERSION <= "1.8.7"
14
11
  gem "ruby-debug", :platforms => :mri_18
15
12
  end
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2013 Mikel Lindsaar
1
+ Copyright (c) 2009-2015 Mikel Lindsaar
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -110,6 +110,8 @@ the gem gets released.
110
110
 
111
111
  It also means you can be sure Mail will behave correctly.
112
112
 
113
+ Note: If you care about core extensions (aka "monkey-patching"), please read the Core Extensions section near the end of this README.
114
+
113
115
  API Policy
114
116
  ----------
115
117
 
@@ -588,7 +590,7 @@ Mail::TestMailer.deliveries.clear
588
590
  => []
589
591
  ```
590
592
 
591
- There is also a set of RSpec matchers stolen fr^H^H^H^H^H^H^H^H inspired by Shoulda's ActionMailer matchers (you'll want to set <code>delivery_method</code> as above too):
593
+ There is also a set of RSpec matchers stolen/inspired by Shoulda's ActionMailer matchers (you'll want to set <code>delivery_method</code> as above too):
592
594
 
593
595
  ```ruby
594
596
  Mail.defaults do
@@ -634,9 +636,43 @@ describe "sending an email" do
634
636
  # Note that apart from recipients, repeating a modifier overwrites old value.
635
637
 
636
638
  it { should have_sent_email.from('you@you.com').to('mike1@me.com').matching_body(/hell/)
639
+
640
+ # test for attachments
641
+
642
+ # ... by specific attachment
643
+ it { should_have_sent_email.with_attachments(my_attachment) }
644
+
645
+ # ... or any attachment
646
+ it { should_have_sent_email.with_attachments(any_attachment) }
647
+
648
+ # ... by array of attachments
649
+ it { should_have_sent_email.with_attachments([my_attachment1, my_attachment2]) } #note that order is important
650
+
651
+ #... by presence
652
+ it { should_have_sent_email.with_any_attachments }
653
+
654
+ #... or by absence
655
+ it { should_have_sent_email.with_no_attachments }
656
+
637
657
  end
638
658
  ```
639
659
 
660
+ Core Extensions
661
+ ---------------
662
+
663
+ The mail gem adds several constants and methods to Ruby's core objects (similar to the activesupport gem from the Rails project). For example:
664
+
665
+ NilClass::blank?
666
+ NilClass::to_crlf
667
+ NilClass::to_lf
668
+ Object::blank?
669
+ String::to_crlf
670
+ String::to_lf
671
+ String::blank?
672
+ ...etc...
673
+
674
+ For all the details, check out lib/mail/core_extensions/.
675
+
640
676
  Excerpts from TREC Spam Corpus 2005
641
677
  -----------------------------------
642
678
 
@@ -661,7 +697,7 @@ License
661
697
 
662
698
  (The MIT License)
663
699
 
664
- Copyright (c) 2009-2013 Mikel Lindsaar
700
+ Copyright (c) 2009-2015 Mikel Lindsaar
665
701
 
666
702
  Permission is hereby granted, free of charge, to any person obtaining
667
703
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -21,8 +21,8 @@ end
21
21
 
22
22
  begin
23
23
  require "appraisal"
24
- rescue LoadError
25
- warn "Appraisal is only available in test/development"
24
+ rescue LoadError, SyntaxError
25
+ warn "Appraisal is only available in test/development on Ruby 1.9+"
26
26
  end
27
27
 
28
28
  # load custom rake tasks
@@ -6,7 +6,13 @@ module Mail # :doc:
6
6
 
7
7
  require 'uri'
8
8
  require 'net/smtp'
9
- require 'mime/types'
9
+
10
+ begin
11
+ # Use mime/types/columnar if available, for reduced memory usage
12
+ require 'mime/types/columnar'
13
+ rescue LoadError
14
+ require 'mime/types'
15
+ end
10
16
 
11
17
  if RUBY_VERSION <= '1.8.6'
12
18
  begin
@@ -27,7 +33,6 @@ module Mail # :doc:
27
33
  require 'mail/version'
28
34
 
29
35
  require 'mail/core_extensions/nil'
30
- require 'mail/core_extensions/object'
31
36
  require 'mail/core_extensions/string'
32
37
  require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
33
38
  require 'mail/indifferent_hash'
@@ -86,6 +91,7 @@ module Mail # :doc:
86
91
  require 'mail/encodings/unix_to_unix'
87
92
 
88
93
  require 'mail/matchers/has_sent_mail'
94
+ require 'mail/matchers/attachment_matchers.rb'
89
95
 
90
96
  # Finally... require all the Mail.methods
91
97
  require 'mail/mail'
@@ -33,7 +33,7 @@ module Mail
33
33
  @charset = nil
34
34
  @part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
35
35
  @parts = Mail::PartsList.new
36
- if string.blank?
36
+ if Utilities.blank?(string)
37
37
  @raw_source = ''
38
38
  else
39
39
  # Do join first incase we have been given an Array in Ruby 1.9
@@ -156,7 +156,7 @@ module Mail
156
156
  be = get_best_encoding(transfer_encoding)
157
157
  dec = Mail::Encodings::get_encoding(encoding)
158
158
  enc = Mail::Encodings::get_encoding(be)
159
- if transfer_encoding == encoding and dec.nil?
159
+ if dec.nil?
160
160
  # Cannot decode, so skip normalization
161
161
  raw_source
162
162
  else
@@ -201,7 +201,7 @@ module Mail
201
201
  end
202
202
 
203
203
  def encoding=( val )
204
- @encoding = if val == "text" || val.blank?
204
+ @encoding = if val == "text" || Utilities.blank?(val)
205
205
  (only_us_ascii? ? '7bit' : '8bit')
206
206
  else
207
207
  val
@@ -291,7 +291,7 @@ module Mail
291
291
  (?=\s*$) # lookahead matching zero or more spaces followed by line-ending
292
292
  /x
293
293
  parts = raw_source.split(parts_regex).each_slice(2).to_a
294
- parts.each_with_index { |(part, _), index| parts.delete_at(index) if index > 0 && part.blank? }
294
+ parts.each_with_index { |(part, _), index| parts.delete_at(index) if index > 0 && Utilities.blank?(part) }
295
295
 
296
296
  if parts.size > 1
297
297
  final_separator = parts[-2][1]
@@ -1,16 +1,16 @@
1
1
  module Mail
2
2
  module CheckDeliveryParams
3
3
  def check_delivery_params(mail)
4
- if mail.smtp_envelope_from.blank?
4
+ if Utilities.blank?(mail.smtp_envelope_from)
5
5
  raise ArgumentError.new('An SMTP From address is required to send a message. Set the message smtp_envelope_from, return_path, sender, or from address.')
6
6
  end
7
7
 
8
- if mail.smtp_envelope_to.blank?
8
+ if Utilities.blank?(mail.smtp_envelope_to)
9
9
  raise ArgumentError.new('An SMTP To address is required to send a message. Set the message smtp_envelope_to, to, cc, or bcc address.')
10
10
  end
11
11
 
12
12
  message = mail.encoded if mail.respond_to?(:encoded)
13
- if message.blank?
13
+ if Utilities.blank?(message)
14
14
  raise ArgumentError.new('An encoded message is required to send an email')
15
15
  end
16
16
 
@@ -33,7 +33,8 @@ module Mail
33
33
  ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{sp}]/n
34
34
  PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
35
35
  TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{sp}]/n
36
- ENCODED_VALUE = /\=\?[^?]+\?([QB])\?[^?]*?\?\=/mi
36
+ ENCODED_VALUE = /\=\?([^?]+)\?([QB])\?[^?]*?\?\=/mi
37
+ FULL_ENCODED_VALUE = /(\=\?[^?]+\?[QB]\?[^?]*?\?\=)/mi
37
38
 
38
39
  EMPTY = ''
39
40
  SPACE = ' '
@@ -3,12 +3,6 @@
3
3
  # This is not loaded if ActiveSupport is already loaded
4
4
 
5
5
  class NilClass #:nodoc:
6
- unless nil.respond_to? :blank?
7
- def blank?
8
- true
9
- end
10
- end
11
-
12
6
  def to_crlf
13
7
  ''
14
8
  end
@@ -21,12 +21,6 @@ class String #:nodoc:
21
21
  to_str.gsub(/\r\n|\r/, LF)
22
22
  end
23
23
 
24
- unless String.instance_methods(false).map {|m| m.to_sym}.include?(:blank?)
25
- def blank?
26
- self !~ /\S/
27
- end
28
- end
29
-
30
24
  unless method_defined?(:ascii_only?)
31
25
  # Backport from Ruby 1.9 checks for non-us-ascii characters.
32
26
  def ascii_only?
@@ -102,7 +102,7 @@ module Mail
102
102
  # a.local #=> 'mikel'
103
103
  def local
104
104
  parse unless @parsed
105
- "#{@data.obs_domain_list}#{get_local.strip}" if get_local
105
+ Encodings.decode_encode("#{@data.obs_domain_list}#{get_local.strip}", @output_type) if get_local
106
106
  end
107
107
 
108
108
  # Returns the domain part (the right hand side of the @ sign in the email address) of
@@ -112,7 +112,7 @@ module Mail
112
112
  # a.domain #=> 'test.lindsaar.net'
113
113
  def domain
114
114
  parse unless @parsed
115
- strip_all_comments(get_domain) if get_domain
115
+ Encodings.decode_encode(strip_all_comments(get_domain), @output_type) if get_domain
116
116
  end
117
117
 
118
118
  # Returns an array of comments that are in the email, or an empty array if there
@@ -176,7 +176,7 @@ module Mail
176
176
  when Mail::Parsers::AddressStruct
177
177
  @data = value
178
178
  when String
179
- unless value.blank?
179
+ unless Utilities.blank?(value)
180
180
  address_list = Mail::Parsers::AddressListsParser.new.parse(value)
181
181
  @data = address_list.addresses.first
182
182
  end
@@ -184,7 +184,7 @@ module Mail
184
184
  end
185
185
 
186
186
  def strip_all_comments(string)
187
- unless comments.blank?
187
+ unless Utilities.blank?(comments)
188
188
  comments.each do |comment|
189
189
  string = string.gsub("(#{comment})", EMPTY)
190
190
  end
@@ -193,7 +193,7 @@ module Mail
193
193
  end
194
194
 
195
195
  def strip_domain_comments(value)
196
- unless comments.blank?
196
+ unless Utilities.blank?(comments)
197
197
  comments.each do |comment|
198
198
  if @data.domain && @data.domain.include?("(#{comment})")
199
199
  value = value.gsub("(#{comment})", EMPTY)
@@ -210,7 +210,7 @@ module Mail
210
210
  str = strip_domain_comments(format_comments)
211
211
  end
212
212
 
213
- str unless str.blank?
213
+ str unless Utilities.blank?(str)
214
214
  end
215
215
 
216
216
  def get_name
@@ -220,7 +220,7 @@ module Mail
220
220
  str = "(#{comments.join(SPACE).squeeze(SPACE)})"
221
221
  end
222
222
 
223
- unparen(str) unless str.blank?
223
+ unparen(str) unless Utilities.blank?(str)
224
224
  end
225
225
 
226
226
  def format_comments
@@ -47,7 +47,15 @@ module Mail
47
47
  end
48
48
 
49
49
  def Encodings.get_name(enc)
50
- enc = underscoreize(enc).downcase
50
+ underscoreize(enc).downcase
51
+ end
52
+
53
+ def Encodings.transcode_charset(str, from_charset, to_charset = 'UTF-8')
54
+ if from_charset
55
+ RubyVer.transcode_charset str, from_charset, to_charset
56
+ else
57
+ str
58
+ end
51
59
  end
52
60
 
53
61
  # Encodes a parameter value using URI Escaping, note the language field 'en' can
@@ -121,7 +129,7 @@ module Mail
121
129
  # Split on white-space boundaries with capture, so we capture the white-space as well
122
130
  lines.each do |line|
123
131
  line.gsub!(ENCODED_VALUE) do |string|
124
- case $1
132
+ case $2
125
133
  when *B_VALUES then b_value_decode(string)
126
134
  when *Q_VALUES then q_value_decode(string)
127
135
  end
@@ -240,27 +248,13 @@ module Mail
240
248
  RubyVer.q_value_decode(str)
241
249
  end
242
250
 
243
- def Encodings.split_encoding_from_string( str )
244
- match = str.match(/\=\?([^?]+)?\?[QB]\?(.*)\?\=/mi)
245
- if match
246
- match[1]
247
- else
248
- nil
249
- end
250
- end
251
-
252
251
  def Encodings.find_encoding(str)
253
252
  RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
254
253
  end
255
254
 
256
255
  # Gets the encoding type (Q or B) from the string.
257
- def Encodings.split_value_encoding_from_string(str)
258
- match = str.match(/\=\?[^?]+?\?([QB])\?(.*)\?\=/mi)
259
- if match
260
- match[1]
261
- else
262
- nil
263
- end
256
+ def Encodings.value_encoding_from_string(str)
257
+ str[ENCODED_VALUE, 1]
264
258
  end
265
259
 
266
260
  # When the encoded string consists of multiple lines, lines with the same
@@ -268,19 +262,22 @@ module Mail
268
262
  #
269
263
  # String has to be of the format =?<encoding>?[QB]?<string>?=
270
264
  def Encodings.collapse_adjacent_encodings(str)
271
- lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
272
265
  results = []
273
266
  previous_encoding = nil
274
-
275
- lines.each do |line|
276
- encoding = split_value_encoding_from_string(line)
277
-
278
- if encoding == previous_encoding
279
- line = results.pop + line
267
+ lines = str.split(FULL_ENCODED_VALUE)
268
+ lines.each_slice(2) do |unencoded, encoded|
269
+ if encoded
270
+ encoding = value_encoding_from_string(encoded)
271
+ if encoding == previous_encoding && Utilities.blank?(unencoded)
272
+ results.last << encoded
273
+ else
274
+ results << unencoded unless unencoded == EMPTY
275
+ results << encoded
276
+ end
277
+ previous_encoding = encoding
278
+ else
279
+ results << unencoded
280
280
  end
281
-
282
- previous_encoding = encoding
283
- results << line
284
281
  end
285
282
 
286
283
  results
@@ -25,6 +25,11 @@ module Mail
25
25
  1.0
26
26
  end
27
27
 
28
+ # Per RFC 2821 4.5.3.1, SMTP lines may not be longer than 1000 octets including the <CRLF>.
29
+ def self.compatible_input?(str)
30
+ !str.lines.find { |line| line.length > 998 }
31
+ end
32
+
28
33
  Encodings.register(NAME, self)
29
34
  end
30
35
  end
@@ -27,6 +27,11 @@ module Mail
27
27
  4.0/3
28
28
  end
29
29
 
30
+ # Base64 inserts newlines automatically and cannot violate the SMTP spec.
31
+ def self.compatible_input?(str)
32
+ true
33
+ end
34
+
30
35
  Encodings.register(NAME, self)
31
36
  end
32
37
  end
@@ -30,7 +30,12 @@ module Mail
30
30
  total = (str.bytesize - c)*3 + c
31
31
  total.to_f/str.bytesize
32
32
  end
33
-
33
+
34
+ # QP inserts newlines automatically and cannot violate the SMTP spec.
35
+ def self.compatible_input?(str)
36
+ true
37
+ end
38
+
34
39
  private
35
40
 
36
41
  Encodings.register(NAME, self)