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
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)