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
@@ -23,30 +23,39 @@ module Mail
23
23
  raise "Unimplemented"
24
24
  end
25
25
 
26
+ def self.compatible_input?(str)
27
+ true
28
+ end
29
+
26
30
  def self.to_s
27
31
  self::NAME
28
32
  end
29
33
 
30
34
  def self.get_best_compatible(source_encoding, str)
31
- if self.can_transport? source_encoding then
32
- source_encoding
35
+ if self.can_transport?(source_encoding) && self.compatible_input?(str)
36
+ source_encoding
33
37
  else
34
- choices = []
35
- Encodings.get_all.each do |enc|
36
- choices << enc if self.can_transport? enc and enc.can_encode? source_encoding
37
- end
38
- best = nil
39
- best_cost = 100
40
- choices.each do |enc|
41
- this_cost = enc.cost str
42
- if this_cost < best_cost then
43
- best_cost = this_cost
44
- best = enc
45
- elsif this_cost == best_cost then
46
- best = enc if enc::PRIORITY < best::PRIORITY
47
- end
38
+ choices = Encodings.get_all.select do |enc|
39
+ self.can_transport?(enc) && enc.can_encode?(source_encoding)
40
+ end
41
+
42
+ best = nil
43
+ best_cost = nil
44
+
45
+ choices.each do |enc|
46
+ # If the current choice cannot be transported safely,
47
+ # give priority to other choices but allow it to be used as a fallback.
48
+ this_cost = enc.cost(str) if enc.compatible_input?(str)
49
+
50
+ if !best_cost || (this_cost && this_cost < best_cost)
51
+ best_cost = this_cost
52
+ best = enc
53
+ elsif this_cost == best_cost
54
+ best = enc if enc::PRIORITY < best::PRIORITY
48
55
  end
49
- best
56
+ end
57
+
58
+ best
50
59
  end
51
60
  end
52
61
 
@@ -114,11 +114,11 @@ module Mail
114
114
  def initialize(name, value = nil, charset = 'utf-8')
115
115
  case
116
116
  when name.index(COLON) # Field.new("field-name: field data")
117
- @charset = value.blank? ? charset : value
117
+ @charset = Utilities.blank?(value) ? charset : value
118
118
  @name = name[FIELD_PREFIX]
119
119
  @raw_value = name
120
120
  @value = nil
121
- when value.blank? # Field.new("field-name")
121
+ when Utilities.blank?(value) # Field.new("field-name")
122
122
  @name = name
123
123
  @value = nil
124
124
  @raw_value = nil
@@ -168,15 +168,19 @@ module Mail
168
168
  end
169
169
 
170
170
  def same( other )
171
+ return false unless other.kind_of?(self.class)
171
172
  match_to_s(other.name, self.name)
172
173
  end
173
174
 
175
+ def ==( other )
176
+ return false unless other.kind_of?(self.class)
177
+ match_to_s(other.name, self.name) && match_to_s(other.value, self.value)
178
+ end
179
+
174
180
  def responsible_for?( val )
175
181
  name.to_s.casecmp(val.to_s) == 0
176
182
  end
177
183
 
178
- alias_method :==, :same
179
-
180
184
  def <=>( other )
181
185
  self.field_order_id <=> other.field_order_id
182
186
  end
@@ -189,6 +193,16 @@ module Mail
189
193
  field.send(name, *args, &block)
190
194
  end
191
195
 
196
+ if RUBY_VERSION >= '1.9.2'
197
+ def respond_to_missing?(method_name, include_private)
198
+ field.respond_to?(method_name, include_private) || super
199
+ end
200
+ else
201
+ def respond_to?(method_name, include_private = false)
202
+ field.respond_to?(method_name, include_private) || super
203
+ end
204
+ end
205
+
192
206
  FIELD_ORDER = %w[ return-path received
193
207
  resent-date resent-from resent-sender resent-to
194
208
  resent-cc resent-bcc resent-message-id
@@ -39,13 +39,24 @@ module Mail
39
39
  def initialize(value = '', charset = 'utf-8')
40
40
  @charset = charset
41
41
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
42
  self
44
43
  end
45
44
 
46
- # Bcc field should never be :encoded
45
+ def include_in_headers=(include_in_headers)
46
+ @include_in_headers = include_in_headers
47
+ end
48
+
49
+ def include_in_headers
50
+ defined?(@include_in_headers) ? @include_in_headers : self.include_in_headers = false
51
+ end
52
+
53
+ # Bcc field should not be :encoded by default
47
54
  def encoded
48
- ''
55
+ if include_in_headers
56
+ do_encode(CAPITALIZED_FIELD)
57
+ else
58
+ ''
59
+ end
49
60
  end
50
61
 
51
62
  def decoded
@@ -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
 
@@ -5,7 +5,7 @@ module Mail
5
5
  module CommonAddress # :nodoc:
6
6
 
7
7
  def parse(val = value)
8
- unless val.blank?
8
+ unless Utilities.blank?(val)
9
9
  @address_list = AddressList.new(encode_if_needed(val))
10
10
  else
11
11
  nil
@@ -84,10 +84,10 @@ module Mail
84
84
  case
85
85
  when val.nil?
86
86
  raise ArgumentError, "Need to pass an address to <<"
87
- when val.blank?
87
+ when Utilities.blank?(val)
88
88
  parse(encoded)
89
89
  else
90
- self.value = [self.value, val].reject {|a| a.blank? }.join(", ")
90
+ self.value = [self.value, val].reject {|a| Utilities.blank?(a) }.join(", ")
91
91
  end
92
92
  end
93
93
 
@@ -99,22 +99,22 @@ module Mail
99
99
  private
100
100
 
101
101
  def do_encode(field_name)
102
- return '' if value.blank?
102
+ return '' if Utilities.blank?(value)
103
103
  address_array = address_list.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
104
104
  address_text = address_array.join(", \r\n\s")
105
105
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
106
106
  group_text = group_array.join(" \r\n\s")
107
- return_array = [address_text, group_text].reject { |a| a.blank? }
107
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
108
108
  "#{field_name}: #{return_array.join(", \r\n\s")}\r\n"
109
109
  end
110
110
 
111
111
  def do_decode
112
- return nil if value.blank?
112
+ return nil if Utilities.blank?(value)
113
113
  address_array = address_list.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
114
114
  address_text = address_array.join(", ")
115
115
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
116
116
  group_text = group_array.join(" ")
117
- return_array = [address_text, group_text].reject { |a| a.blank? }
117
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
118
118
  return_array.join(", ")
119
119
  end
120
120
 
@@ -11,7 +11,7 @@ module Mail
11
11
  end
12
12
 
13
13
  def parse(val = value)
14
- unless val.blank?
14
+ unless Utilities.blank?(val)
15
15
  @element = Mail::DateTimeElement.new(val)
16
16
  else
17
17
  nil
@@ -44,7 +44,7 @@ module Mail
44
44
  if value.is_a?(Array)
45
45
  value
46
46
  else
47
- value.to_s.sub(/#{field_name}:\s+/i, EMPTY)
47
+ value.to_s.sub(/\A#{field_name}:\s+/i, EMPTY)
48
48
  end
49
49
  end
50
50
 
@@ -2,11 +2,11 @@
2
2
  module Mail
3
3
  module CommonMessageId # :nodoc:
4
4
  def element
5
- @element ||= Mail::MessageIdsElement.new(value) unless value.blank?
5
+ @element ||= Mail::MessageIdsElement.new(value) unless Utilities.blank?(value)
6
6
  end
7
7
 
8
8
  def parse(val = value)
9
- unless val.blank?
9
+ unless Utilities.blank?(val)
10
10
  @element = Mail::MessageIdsElement.new(val)
11
11
  else
12
12
  nil
@@ -3,10 +3,10 @@ require 'mail/fields/common/parameter_hash'
3
3
 
4
4
  module Mail
5
5
  class ContentDispositionField < StructuredField
6
-
6
+
7
7
  FIELD_NAME = 'content-disposition'
8
8
  CAPITALIZED_FIELD = 'Content-Disposition'
9
-
9
+
10
10
  def initialize(value = nil, charset = 'utf-8')
11
11
  self.charset = charset
12
12
  ensure_filename_quoted(value)
@@ -14,13 +14,13 @@ module Mail
14
14
  self.parse
15
15
  self
16
16
  end
17
-
17
+
18
18
  def parse(val = value)
19
- unless val.blank?
19
+ unless Utilities.blank?(val)
20
20
  @element = Mail::ContentDispositionElement.new(val)
21
21
  end
22
22
  end
23
-
23
+
24
24
  def element
25
25
  @element ||= Mail::ContentDispositionElement.new(value)
26
26
  end
@@ -28,20 +28,20 @@ module Mail
28
28
  def disposition_type
29
29
  element.disposition_type
30
30
  end
31
-
31
+
32
32
  def parameters
33
33
  @parameters = ParameterHash.new
34
- element.parameters.each { |p| @parameters.merge!(p) }
34
+ element.parameters.each { |p| @parameters.merge!(p) } unless element.parameters.nil?
35
35
  @parameters
36
36
  end
37
37
 
38
38
  def filename
39
39
  case
40
- when !parameters['filename'].blank?
40
+ when !Utilities.blank?(parameters['filename'])
41
41
  @filename = parameters['filename']
42
- when !parameters['name'].blank?
42
+ when !Utilities.blank?(parameters['name'])
43
43
  @filename = parameters['name']
44
- else
44
+ else
45
45
  @filename = nil
46
46
  end
47
47
  @filename
@@ -56,7 +56,7 @@ module Mail
56
56
  end
57
57
  "#{CAPITALIZED_FIELD}: #{disposition_type}" + p
58
58
  end
59
-
59
+
60
60
  def decoded
61
61
  if parameters.length > 0
62
62
  p = "; #{parameters.decoded}"
@@ -11,7 +11,7 @@ module Mail
11
11
  def initialize(value = nil, charset = 'utf-8')
12
12
  self.charset = charset
13
13
  @uniq = 1
14
- if value.blank?
14
+ if Utilities.blank?(value)
15
15
  value = generate_content_id
16
16
  else
17
17
  value = strip_field(FIELD_NAME, value)
@@ -22,7 +22,7 @@ module Mail
22
22
  end
23
23
 
24
24
  def parse(val = value)
25
- unless val.blank?
25
+ unless Utilities.blank?(val)
26
26
  @element = Mail::MessageIdsElement.new(val)
27
27
  end
28
28
  end
@@ -16,7 +16,7 @@ module Mail
16
16
  end
17
17
 
18
18
  def parse(val = value)
19
- unless val.blank?
19
+ unless Utilities.blank?(val)
20
20
  @element = Mail::ContentLocationElement.new(val)
21
21
  end
22
22
  end
@@ -18,7 +18,7 @@ module Mail
18
18
  end
19
19
 
20
20
  def parse(val = value)
21
- unless val.blank?
21
+ unless Utilities.blank?(val)
22
22
  @element = Mail::ContentTransferEncodingElement.new(val)
23
23
  end
24
24
  end
@@ -26,7 +26,7 @@ module Mail
26
26
  end
27
27
 
28
28
  def parse(val = value)
29
- unless val.blank?
29
+ unless Utilities.blank?(val)
30
30
  self.value = val
31
31
  @element = nil
32
32
  element
@@ -33,7 +33,7 @@ module Mail
33
33
 
34
34
  def initialize(value = nil, charset = 'utf-8')
35
35
  self.charset = charset
36
- if value.blank?
36
+ if Utilities.blank?(value)
37
37
  value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
38
38
  else
39
39
  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
 
@@ -10,12 +10,11 @@ module Mail
10
10
  def initialize(value = nil, charset = 'utf-8')
11
11
  self.charset = charset
12
12
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
13
- self.parse
14
13
  self
15
14
  end
16
15
 
17
16
  def parse(val = value)
18
- unless val.blank?
17
+ unless Utilities.blank?(val)
19
18
  @phrase_list ||= PhraseList.new(value)
20
19
  end
21
20
  end
@@ -41,7 +41,7 @@ module Mail
41
41
  def initialize(value = nil, charset = 'utf-8')
42
42
  self.charset = charset
43
43
  @uniq = 1
44
- if value.blank?
44
+ if Utilities.blank?(value)
45
45
  self.name = CAPITALIZED_FIELD
46
46
  self.value = generate_message_id
47
47
  else
@@ -10,7 +10,7 @@ module Mail
10
10
 
11
11
  def initialize(value = nil, charset = 'utf-8')
12
12
  self.charset = charset
13
- if value.blank?
13
+ if Utilities.blank?(value)
14
14
  value = '1.0'
15
15
  end
16
16
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
@@ -20,7 +20,7 @@ module Mail
20
20
  end
21
21
 
22
22
  def parse(val = value)
23
- unless val.blank?
23
+ unless Utilities.blank?(val)
24
24
  @element = Mail::MimeVersionElement.new(val)
25
25
  end
26
26
  end
@@ -34,7 +34,7 @@ module Mail
34
34
  end
35
35
 
36
36
  def parse(val = value)
37
- unless val.blank?
37
+ unless Utilities.blank?(val)
38
38
  @element = Mail::ReceivedElement.new(val)
39
39
  end
40
40
  end
@@ -56,7 +56,7 @@ module Mail
56
56
  end
57
57
 
58
58
  def encoded
59
- if value.blank?
59
+ if Utilities.blank?(value)
60
60
  "#{CAPITALIZED_FIELD}: \r\n"
61
61
  else
62
62
  "#{CAPITALIZED_FIELD}: #{info}; #{formatted_date}\r\n"
@@ -64,7 +64,7 @@ module Mail
64
64
  end
65
65
 
66
66
  def decoded
67
- if value.blank?
67
+ if Utilities.blank?(value)
68
68
  ""
69
69
  else
70
70
  "#{info}; #{formatted_date}"
@@ -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