mail 2.4.4 → 2.5.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mail might be problematic. Click here for more details.

Files changed (45) hide show
  1. data/CHANGELOG.rdoc +60 -0
  2. data/Gemfile +1 -1
  3. data/README.md +11 -10
  4. data/lib/VERSION +2 -2
  5. data/lib/load_parsers.rb +41 -0
  6. data/lib/mail.rb +18 -17
  7. data/lib/mail/body.rb +2 -2
  8. data/lib/mail/check_delivery_params.rb +30 -0
  9. data/lib/mail/configuration.rb +1 -1
  10. data/lib/mail/core_extensions/nil.rb +4 -2
  11. data/lib/mail/core_extensions/shell_escape.rb +1 -1
  12. data/lib/mail/elements.rb +12 -12
  13. data/lib/mail/encodings.rb +8 -10
  14. data/lib/mail/encodings/quoted_printable.rb +1 -1
  15. data/lib/mail/field.rb +73 -95
  16. data/lib/mail/fields.rb +32 -32
  17. data/lib/mail/fields/common/common_address.rb +6 -1
  18. data/lib/mail/fields/common/common_field.rb +7 -1
  19. data/lib/mail/fields/common/common_message_id.rb +9 -5
  20. data/lib/mail/fields/content_disposition_field.rb +1 -0
  21. data/lib/mail/fields/content_type_field.rb +4 -1
  22. data/lib/mail/fields/keywords_field.rb +1 -1
  23. data/lib/mail/fields/unstructured_field.rb +33 -26
  24. data/lib/mail/header.rb +32 -13
  25. data/lib/mail/message.rb +8 -9
  26. data/lib/mail/multibyte/chars.rb +2 -2
  27. data/lib/mail/multibyte/unicode.rb +6 -0
  28. data/lib/mail/network.rb +9 -9
  29. data/lib/mail/network/delivery_methods/exim.rb +5 -1
  30. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  31. data/lib/mail/network/delivery_methods/sendmail.rb +5 -0
  32. data/lib/mail/network/delivery_methods/smtp.rb +11 -19
  33. data/lib/mail/network/delivery_methods/smtp_connection.rb +5 -18
  34. data/lib/mail/network/delivery_methods/test_mailer.rb +5 -1
  35. data/lib/mail/network/retriever_methods/imap.rb +14 -6
  36. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  37. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  38. data/lib/mail/parsers/rfc2822.rb +77 -21
  39. data/lib/mail/parsers/rfc2822.treetop +3 -3
  40. data/lib/mail/patterns.rb +0 -1
  41. data/lib/mail/values/unicode_tables.dat +0 -0
  42. data/lib/mail/version_specific/ruby_1_8.rb +18 -1
  43. data/lib/mail/version_specific/ruby_1_9.rb +7 -1
  44. metadata +27 -10
  45. data/Gemfile.lock +0 -36
@@ -1,35 +1,35 @@
1
1
  module Mail
2
- autoload :UnstructuredField, 'mail/fields/unstructured_field'
3
- autoload :StructuredField, 'mail/fields/structured_field'
4
- autoload :OptionalField, 'mail/fields/optional_field'
2
+ register_autoload :UnstructuredField, 'mail/fields/unstructured_field'
3
+ register_autoload :StructuredField, 'mail/fields/structured_field'
4
+ register_autoload :OptionalField, 'mail/fields/optional_field'
5
5
 
6
- autoload :BccField, 'mail/fields/bcc_field'
7
- autoload :CcField, 'mail/fields/cc_field'
8
- autoload :CommentsField, 'mail/fields/comments_field'
9
- autoload :ContentDescriptionField, 'mail/fields/content_description_field'
10
- autoload :ContentDispositionField, 'mail/fields/content_disposition_field'
11
- autoload :ContentIdField, 'mail/fields/content_id_field'
12
- autoload :ContentLocationField, 'mail/fields/content_location_field'
13
- autoload :ContentTransferEncodingField, 'mail/fields/content_transfer_encoding_field'
14
- autoload :ContentTypeField, 'mail/fields/content_type_field'
15
- autoload :DateField, 'mail/fields/date_field'
16
- autoload :FromField, 'mail/fields/from_field'
17
- autoload :InReplyToField, 'mail/fields/in_reply_to_field'
18
- autoload :KeywordsField, 'mail/fields/keywords_field'
19
- autoload :MessageIdField, 'mail/fields/message_id_field'
20
- autoload :MimeVersionField, 'mail/fields/mime_version_field'
21
- autoload :ReceivedField, 'mail/fields/received_field'
22
- autoload :ReferencesField, 'mail/fields/references_field'
23
- autoload :ReplyToField, 'mail/fields/reply_to_field'
24
- autoload :ResentBccField, 'mail/fields/resent_bcc_field'
25
- autoload :ResentCcField, 'mail/fields/resent_cc_field'
26
- autoload :ResentDateField, 'mail/fields/resent_date_field'
27
- autoload :ResentFromField, 'mail/fields/resent_from_field'
28
- autoload :ResentMessageIdField, 'mail/fields/resent_message_id_field'
29
- autoload :ResentSenderField, 'mail/fields/resent_sender_field'
30
- autoload :ResentToField, 'mail/fields/resent_to_field'
31
- autoload :ReturnPathField, 'mail/fields/return_path_field'
32
- autoload :SenderField, 'mail/fields/sender_field'
33
- autoload :SubjectField, 'mail/fields/subject_field'
34
- autoload :ToField, 'mail/fields/to_field'
6
+ register_autoload :BccField, 'mail/fields/bcc_field'
7
+ register_autoload :CcField, 'mail/fields/cc_field'
8
+ register_autoload :CommentsField, 'mail/fields/comments_field'
9
+ register_autoload :ContentDescriptionField, 'mail/fields/content_description_field'
10
+ register_autoload :ContentDispositionField, 'mail/fields/content_disposition_field'
11
+ register_autoload :ContentIdField, 'mail/fields/content_id_field'
12
+ register_autoload :ContentLocationField, 'mail/fields/content_location_field'
13
+ register_autoload :ContentTransferEncodingField, 'mail/fields/content_transfer_encoding_field'
14
+ register_autoload :ContentTypeField, 'mail/fields/content_type_field'
15
+ register_autoload :DateField, 'mail/fields/date_field'
16
+ register_autoload :FromField, 'mail/fields/from_field'
17
+ register_autoload :InReplyToField, 'mail/fields/in_reply_to_field'
18
+ register_autoload :KeywordsField, 'mail/fields/keywords_field'
19
+ register_autoload :MessageIdField, 'mail/fields/message_id_field'
20
+ register_autoload :MimeVersionField, 'mail/fields/mime_version_field'
21
+ register_autoload :ReceivedField, 'mail/fields/received_field'
22
+ register_autoload :ReferencesField, 'mail/fields/references_field'
23
+ register_autoload :ReplyToField, 'mail/fields/reply_to_field'
24
+ register_autoload :ResentBccField, 'mail/fields/resent_bcc_field'
25
+ register_autoload :ResentCcField, 'mail/fields/resent_cc_field'
26
+ register_autoload :ResentDateField, 'mail/fields/resent_date_field'
27
+ register_autoload :ResentFromField, 'mail/fields/resent_from_field'
28
+ register_autoload :ResentMessageIdField, 'mail/fields/resent_message_id_field'
29
+ register_autoload :ResentSenderField, 'mail/fields/resent_sender_field'
30
+ register_autoload :ResentToField, 'mail/fields/resent_to_field'
31
+ register_autoload :ReturnPathField, 'mail/fields/return_path_field'
32
+ register_autoload :SenderField, 'mail/fields/sender_field'
33
+ register_autoload :SubjectField, 'mail/fields/subject_field'
34
+ register_autoload :ToField, 'mail/fields/to_field'
35
35
  end
@@ -81,9 +81,14 @@ module Mail
81
81
  when val.blank?
82
82
  parse(encoded)
83
83
  else
84
- parse((formatted + [val]).join(", "))
84
+ self.value = [self.value, val].reject {|a| a.blank? }.join(", ")
85
85
  end
86
86
  end
87
+
88
+ def value=(val)
89
+ super
90
+ parse(self.value)
91
+ end
87
92
 
88
93
  private
89
94
 
@@ -34,7 +34,7 @@ module Mail
34
34
  end
35
35
 
36
36
  def responsible_for?( val )
37
- name.to_s.downcase == val.to_s.downcase
37
+ name.to_s.casecmp(val.to_s) == 0
38
38
  end
39
39
 
40
40
  private
@@ -47,5 +47,11 @@ module Mail
47
47
  end
48
48
  end
49
49
 
50
+ def ensure_filename_quoted(value)
51
+ if !value.is_a?(Array) and /(.)*\s(filename|name)=[^"](.+\s)+[^"]/.match value
52
+ value.gsub!(/[^=]+$/, '"\\0"')
53
+ end
54
+ end
55
+
50
56
  end
51
57
  end
@@ -31,14 +31,18 @@ module Mail
31
31
  end
32
32
 
33
33
  private
34
-
34
+
35
35
  def do_encode(field_name)
36
- %Q{#{field_name}: #{do_decode}\r\n}
36
+ %Q{#{field_name}: #{formated_message_ids("\r\n ")}\r\n}
37
37
  end
38
-
38
+
39
39
  def do_decode
40
- "#{message_ids.map { |m| "<#{m}>" }.join(' ')}" if message_ids
40
+ formated_message_ids(' ')
41
41
  end
42
-
42
+
43
+ def formated_message_ids(join)
44
+ message_ids.map{ |m| "<#{m}>" }.join(join) if message_ids
45
+ end
46
+
43
47
  end
44
48
  end
@@ -9,6 +9,7 @@ module Mail
9
9
 
10
10
  def initialize(value = nil, charset = 'utf-8')
11
11
  self.charset = charset
12
+ ensure_filename_quoted(value)
12
13
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
13
14
  self.parse
14
15
  self
@@ -19,6 +19,7 @@ module Mail
19
19
  @parameters = nil
20
20
  value = strip_field(FIELD_NAME, value)
21
21
  end
22
+ ensure_filename_quoted(value)
22
23
  super(CAPITALIZED_FIELD, value, charset)
23
24
  self.parse
24
25
  self
@@ -141,7 +142,9 @@ module Mail
141
142
  def sanatize( val )
142
143
 
143
144
  # TODO: check if there are cases where whitespace is not a separator
144
- val = val.tr(' ',';').
145
+ val = val.
146
+ gsub(/\s*=\s*/,'='). # remove whitespaces around equal sign
147
+ tr(' ',';').
145
148
  squeeze(';').
146
149
  gsub(';', '; '). #use '; ' as a separator (or EOL)
147
150
  gsub(/;\s*$/,'') #remove trailing to keep examples below
@@ -29,7 +29,7 @@ module Mail
29
29
  end
30
30
 
31
31
  def encoded
32
- "#{CAPITALIZED_FIELD}: #{keywords.join(', ')}\r\n"
32
+ "#{CAPITALIZED_FIELD}: #{keywords.join(",\r\n ")}\r\n"
33
33
  end
34
34
 
35
35
  def decoded
@@ -6,7 +6,7 @@ module Mail
6
6
  #
7
7
  # ===Per RFC 2822:
8
8
  # 2.2.1. Unstructured Header Field Bodies
9
- #
9
+ #
10
10
  # Some field bodies in this standard are defined simply as
11
11
  # "unstructured" (which is specified below as any US-ASCII characters,
12
12
  # except for CR and LF) with no further restrictions. These are
@@ -15,20 +15,29 @@ module Mail
15
15
  # with no further processing (except for header "folding" and
16
16
  # "unfolding" as described in section 2.2.3).
17
17
  class UnstructuredField
18
-
18
+
19
19
  include Mail::CommonField
20
20
  include Mail::Utilities
21
-
21
+
22
22
  attr_accessor :charset
23
23
  attr_reader :errors
24
-
24
+
25
25
  def initialize(name, value, charset = nil)
26
26
  @errors = []
27
+
28
+ if value.is_a?(Array)
29
+ # Probably has arrived here from a failed parse of an AddressList Field
30
+ value = value.join(', ')
31
+ else
32
+ # Ensure we are dealing with a string
33
+ value = value.to_s
34
+ end
35
+
27
36
  if charset
28
37
  self.charset = charset
29
38
  else
30
- if value.to_s.respond_to?(:encoding)
31
- self.charset = value.to_s.encoding
39
+ if value.respond_to?(:encoding)
40
+ self.charset = value.encoding
32
41
  else
33
42
  self.charset = $KCODE
34
43
  end
@@ -37,11 +46,11 @@ module Mail
37
46
  self.value = value
38
47
  self
39
48
  end
40
-
49
+
41
50
  def encoded
42
51
  do_encode
43
52
  end
44
-
53
+
45
54
  def decoded
46
55
  do_decode
47
56
  end
@@ -49,25 +58,23 @@ module Mail
49
58
  def default
50
59
  decoded
51
60
  end
52
-
61
+
53
62
  def parse # An unstructured field does not parse
54
63
  self
55
64
  end
56
65
 
57
66
  private
58
-
67
+
59
68
  def do_encode
60
69
  value.nil? ? '' : "#{wrapped_value}\r\n"
61
70
  end
62
-
71
+
63
72
  def do_decode
64
- result = value.blank? ? nil : Encodings.decode_encode(value, :decode)
65
- result.encode!(value.encoding || "UTF-8") if RUBY_VERSION >= '1.9' && !result.blank?
66
- result
73
+ value.blank? ? nil : Encodings.decode_encode(value, :decode)
67
74
  end
68
-
75
+
69
76
  # 2.2.3. Long Header Fields
70
- #
77
+ #
71
78
  # Each header field is logically a single line of characters comprising
72
79
  # the field name, the colon, and the field body. For convenience
73
80
  # however, and to deal with the 998/78 character limitations per line,
@@ -76,14 +83,14 @@ module Mail
76
83
  # that wherever this standard allows for folding white space (not
77
84
  # simply WSP characters), a CRLF may be inserted before any WSP. For
78
85
  # example, the header field:
79
- #
86
+ #
80
87
  # Subject: This is a test
81
- #
88
+ #
82
89
  # can be represented as:
83
- #
90
+ #
84
91
  # Subject: This
85
92
  # is a test
86
- #
93
+ #
87
94
  # Note: Though structured field bodies are defined in such a way that
88
95
  # folding can take place between many of the lexical tokens (and even
89
96
  # within some of the lexical tokens), folding SHOULD be limited to
@@ -95,9 +102,9 @@ module Mail
95
102
  def wrapped_value # :nodoc:
96
103
  wrap_lines(name, fold("#{name}: ".length))
97
104
  end
98
-
105
+
99
106
  # 6.2. Display of 'encoded-word's
100
- #
107
+ #
101
108
  # When displaying a particular header field that contains multiple
102
109
  # 'encoded-word's, any 'linear-white-space' that separates a pair of
103
110
  # adjacent 'encoded-word's is ignored. (This is to allow the use of
@@ -131,7 +138,7 @@ module Mail
131
138
  else
132
139
  words = decoded_string.split(/[ \t]/)
133
140
  end
134
-
141
+
135
142
  folded_lines = []
136
143
  while !words.empty?
137
144
  limit = 78 - prepend
@@ -145,7 +152,7 @@ module Mail
145
152
  # Skip to next line if we're going to go past the limit
146
153
  # Unless this is the first word, in which case we're going to add it anyway
147
154
  # Note: This means that a word that's longer than 998 characters is going to break the spec. Please fix if this is a problem for you.
148
- # (The fix, it seems, would be to use encoded-word encoding on it, because that way you can break it across multiple lines and
155
+ # (The fix, it seems, would be to use encoded-word encoding on it, because that way you can break it across multiple lines and
149
156
  # the linebreak will be ignored)
150
157
  break if !line.empty? && (line.length + word.length + 1 > limit)
151
158
  # Remove the word from the queue ...
@@ -153,7 +160,7 @@ module Mail
153
160
  # Add word separator
154
161
  line << " " unless (line.empty? || should_encode)
155
162
  # ... add it in encoded form to the current line
156
- line << word
163
+ line << word
157
164
  end
158
165
  # Encode the line if necessary
159
166
  line = "=?#{encoding}?Q?#{line}?=" if should_encode
@@ -163,7 +170,7 @@ module Mail
163
170
  end
164
171
  folded_lines
165
172
  end
166
-
173
+
167
174
  def encode(value)
168
175
  value = [value].pack("M").gsub("=\n", '')
169
176
  value.gsub!(/"/, '=22')
@@ -21,6 +21,20 @@ module Mail
21
21
  include Utilities
22
22
  include Enumerable
23
23
 
24
+ @@maximum_amount = 1000
25
+
26
+ # Large amount of headers in Email might create extra high CPU load
27
+ # Use this parameter to limit number of headers that will be parsed by
28
+ # mail library.
29
+ # Default: 1000
30
+ def self.maximum_amount
31
+ @@maximum_amount
32
+ end
33
+
34
+ def self.maximum_amount=(value)
35
+ @@maximum_amount = value
36
+ end
37
+
24
38
  # Creates a new header object.
25
39
  #
26
40
  # Accepts raw text or nothing. If given raw text will attempt to parse
@@ -73,14 +87,12 @@ module Mail
73
87
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
74
88
  def fields=(unfolded_fields)
75
89
  @fields = Mail::FieldList.new
76
- warn "Warning: more than 1000 header fields only using the first 1000" if unfolded_fields.length > 1000
77
- unfolded_fields[0..1000].each do |field|
90
+ warn "Warning: more than #{self.class.maximum_amount} header fields only using the first #{self.class.maximum_amount}" if unfolded_fields.length > self.class.maximum_amount
91
+ unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
78
92
 
79
93
  field = Field.new(field, nil, charset)
80
94
  field.errors.each { |error| self.errors << error }
81
- selected = select_field_for(field.name)
82
-
83
- if selected.any? && limited_field?(field.name)
95
+ if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
84
96
  selected.first.update(field.name, field.value)
85
97
  else
86
98
  @fields << field
@@ -159,15 +171,15 @@ module Mail
159
171
  # Need to insert in correct order for trace fields
160
172
  self.fields << Field.new(name.to_s, value, charset)
161
173
  end
174
+ if dasherize(fn) == "content-type"
175
+ # Update charset if specified in Content-Type
176
+ params = self[:content_type].parameters rescue nil
177
+ @charset = params && params[:charset]
178
+ end
162
179
  end
163
180
 
164
181
  def charset
165
- params = self[:content_type].parameters rescue nil
166
- if params
167
- params[:charset]
168
- else
169
- @charset
170
- end
182
+ @charset
171
183
  end
172
184
 
173
185
  def charset=(val)
@@ -254,12 +266,19 @@ module Mail
254
266
  end
255
267
 
256
268
  def select_field_for(name)
257
- fields.select { |f| f.responsible_for?(name.to_s) }
269
+ fields.select { |f| f.responsible_for?(name) }
258
270
  end
259
271
 
260
272
  def limited_field?(name)
261
273
  LIMITED_FIELDS.include?(name.to_s.downcase)
262
274
  end
263
-
275
+
276
+ # Enumerable support; yield each field in order to the block if there is one,
277
+ # or return an Enumerator for them if there isn't.
278
+ def each( &block )
279
+ return self.fields.each( &block ) if block
280
+ self.fields.each
281
+ end
282
+
264
283
  end
265
284
  end
@@ -1411,7 +1411,7 @@ module Mail
1411
1411
 
1412
1412
  # Returns the MIME media type of part we are on, this is taken from the content-type header
1413
1413
  def mime_type
1414
- content_type ? header[:content_type].string : nil rescue nil
1414
+ has_content_type? ? header[:content_type].string : nil rescue nil
1415
1415
  end
1416
1416
 
1417
1417
  def message_content_type
@@ -1422,7 +1422,7 @@ module Mail
1422
1422
  # Returns the character set defined in the content type field
1423
1423
  def charset
1424
1424
  if @header
1425
- content_type ? content_type_parameters['charset'] : @charset
1425
+ has_content_type? ? content_type_parameters['charset'] : @charset
1426
1426
  else
1427
1427
  @charset
1428
1428
  end
@@ -1875,15 +1875,13 @@ module Mail
1875
1875
  # Additionally, I allow for the case where someone might have put whitespace
1876
1876
  # on the "gap line"
1877
1877
  def parse_message
1878
- header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
1879
- # index = raw_source.index(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
1880
- # self.header = (index) ? header_part[0,index] : nil
1881
- # lazy_body ( [raw_source, index+1])
1878
+ header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
1882
1879
  self.header = header_part
1883
1880
  self.body = body_part
1884
1881
  end
1885
1882
 
1886
1883
  def raw_source=(value)
1884
+ value.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1887
1885
  @raw_source = value.to_crlf
1888
1886
  end
1889
1887
 
@@ -1914,9 +1912,10 @@ module Mail
1914
1912
  end
1915
1913
 
1916
1914
  def set_envelope_header
1917
- if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}(.*)/m)
1915
+ raw_string = raw_source.to_s
1916
+ if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
1918
1917
  set_envelope(match_data[1])
1919
- self.raw_source = match_data[2]
1918
+ self.raw_source = raw_string.sub(match_data[0], "")
1920
1919
  end
1921
1920
  end
1922
1921
 
@@ -2047,7 +2046,7 @@ module Mail
2047
2046
  else
2048
2047
  if encoding = Encoding.find(charset) rescue nil
2049
2048
  body_text.force_encoding(encoding)
2050
- return body_text.encode(Encoding::UTF_8)
2049
+ return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
2051
2050
  end
2052
2051
  end
2053
2052
  end