mail 2.7.1 → 2.8.1

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +59 -28
  3. data/lib/mail/attachments_list.rb +2 -5
  4. data/lib/mail/body.rb +24 -47
  5. data/lib/mail/check_delivery_params.rb +21 -16
  6. data/lib/mail/constants.rb +27 -5
  7. data/lib/mail/elements/address.rb +27 -27
  8. data/lib/mail/elements/address_list.rb +1 -1
  9. data/lib/mail/elements/content_disposition_element.rb +1 -1
  10. data/lib/mail/elements/content_location_element.rb +1 -1
  11. data/lib/mail/elements/content_transfer_encoding_element.rb +1 -1
  12. data/lib/mail/elements/content_type_element.rb +8 -4
  13. data/lib/mail/elements/date_time_element.rb +1 -1
  14. data/lib/mail/elements/envelope_from_element.rb +13 -7
  15. data/lib/mail/elements/message_ids_element.rb +14 -5
  16. data/lib/mail/elements/mime_version_element.rb +1 -1
  17. data/lib/mail/elements/phrase_list.rb +7 -2
  18. data/lib/mail/elements/received_element.rb +20 -6
  19. data/lib/mail/encodings/7bit.rb +5 -0
  20. data/lib/mail/encodings/base64.rb +2 -2
  21. data/lib/mail/encodings/quoted_printable.rb +2 -2
  22. data/lib/mail/encodings.rb +30 -59
  23. data/lib/mail/envelope.rb +11 -14
  24. data/lib/mail/field.rb +37 -53
  25. data/lib/mail/field_list.rb +60 -7
  26. data/lib/mail/fields/bcc_field.rb +34 -52
  27. data/lib/mail/fields/cc_field.rb +28 -49
  28. data/lib/mail/fields/comments_field.rb +27 -37
  29. data/lib/mail/fields/common_address_field.rb +170 -0
  30. data/lib/mail/fields/common_date_field.rb +58 -0
  31. data/lib/mail/fields/common_field.rb +77 -0
  32. data/lib/mail/fields/common_message_id_field.rb +42 -0
  33. data/lib/mail/fields/content_description_field.rb +7 -14
  34. data/lib/mail/fields/content_disposition_field.rb +13 -38
  35. data/lib/mail/fields/content_id_field.rb +24 -51
  36. data/lib/mail/fields/content_location_field.rb +11 -25
  37. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  38. data/lib/mail/fields/content_type_field.rb +46 -71
  39. data/lib/mail/fields/date_field.rb +23 -51
  40. data/lib/mail/fields/from_field.rb +28 -49
  41. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  42. data/lib/mail/fields/keywords_field.rb +18 -31
  43. data/lib/mail/fields/message_id_field.rb +25 -71
  44. data/lib/mail/fields/mime_version_field.rb +19 -30
  45. data/lib/mail/fields/named_structured_field.rb +11 -0
  46. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  47. data/lib/mail/fields/optional_field.rb +5 -6
  48. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
  49. data/lib/mail/fields/received_field.rb +43 -57
  50. data/lib/mail/fields/references_field.rb +35 -49
  51. data/lib/mail/fields/reply_to_field.rb +28 -49
  52. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  53. data/lib/mail/fields/resent_cc_field.rb +28 -49
  54. data/lib/mail/fields/resent_date_field.rb +5 -29
  55. data/lib/mail/fields/resent_from_field.rb +28 -49
  56. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  57. data/lib/mail/fields/resent_sender_field.rb +27 -56
  58. data/lib/mail/fields/resent_to_field.rb +28 -49
  59. data/lib/mail/fields/return_path_field.rb +50 -54
  60. data/lib/mail/fields/sender_field.rb +34 -55
  61. data/lib/mail/fields/structured_field.rb +3 -30
  62. data/lib/mail/fields/subject_field.rb +9 -11
  63. data/lib/mail/fields/to_field.rb +28 -49
  64. data/lib/mail/fields/unstructured_field.rb +16 -48
  65. data/lib/mail/header.rb +69 -110
  66. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  67. data/lib/mail/message.rb +52 -66
  68. data/lib/mail/multibyte/chars.rb +8 -166
  69. data/lib/mail/multibyte/utils.rb +26 -43
  70. data/lib/mail/multibyte.rb +1 -11
  71. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  72. data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
  73. data/lib/mail/network/delivery_methods/logger_delivery.rb +2 -5
  74. data/lib/mail/network/delivery_methods/sendmail.rb +56 -18
  75. data/lib/mail/network/delivery_methods/smtp.rb +25 -9
  76. data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -12
  77. data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
  78. data/lib/mail/network/retriever_methods/base.rb +8 -8
  79. data/lib/mail/network/retriever_methods/imap.rb +2 -2
  80. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  81. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  82. data/lib/mail/parsers/address_lists_parser.rb +33070 -33064
  83. data/lib/mail/parsers/address_lists_parser.rl +7 -0
  84. data/lib/mail/parsers/content_disposition_parser.rb +833 -827
  85. data/lib/mail/parsers/content_disposition_parser.rl +7 -0
  86. data/lib/mail/parsers/content_location_parser.rb +770 -764
  87. data/lib/mail/parsers/content_location_parser.rl +7 -0
  88. data/lib/mail/parsers/content_transfer_encoding_parser.rb +474 -468
  89. data/lib/mail/parsers/content_transfer_encoding_parser.rl +7 -0
  90. data/lib/mail/parsers/content_type_parser.rb +971 -965
  91. data/lib/mail/parsers/content_type_parser.rl +7 -0
  92. data/lib/mail/parsers/date_time_parser.rb +838 -832
  93. data/lib/mail/parsers/date_time_parser.rl +7 -0
  94. data/lib/mail/parsers/envelope_from_parser.rb +3623 -3529
  95. data/lib/mail/parsers/envelope_from_parser.rl +7 -0
  96. data/lib/mail/parsers/message_ids_parser.rb +5107 -2800
  97. data/lib/mail/parsers/message_ids_parser.rl +12 -1
  98. data/lib/mail/parsers/mime_version_parser.rb +463 -457
  99. data/lib/mail/parsers/mime_version_parser.rl +7 -0
  100. data/lib/mail/parsers/phrase_lists_parser.rb +836 -830
  101. data/lib/mail/parsers/phrase_lists_parser.rl +8 -1
  102. data/lib/mail/parsers/received_parser.rb +8688 -8682
  103. data/lib/mail/parsers/received_parser.rl +7 -0
  104. data/lib/mail/parsers/rfc5322.rl +28 -13
  105. data/lib/mail/parsers.rb +11 -17
  106. data/lib/mail/part.rb +5 -9
  107. data/lib/mail/parts_list.rb +57 -0
  108. data/lib/mail/smtp_envelope.rb +57 -0
  109. data/lib/mail/utilities.rb +307 -69
  110. data/lib/mail/version.rb +1 -1
  111. data/lib/mail/yaml.rb +30 -0
  112. data/lib/mail.rb +3 -20
  113. metadata +72 -18
  114. data/lib/mail/core_extensions/smtp.rb +0 -28
  115. data/lib/mail/core_extensions/string.rb +0 -17
  116. data/lib/mail/fields/common/address_container.rb +0 -17
  117. data/lib/mail/fields/common/common_address.rb +0 -161
  118. data/lib/mail/fields/common/common_date.rb +0 -36
  119. data/lib/mail/fields/common/common_field.rb +0 -52
  120. data/lib/mail/fields/common/common_message_id.rb +0 -49
  121. data/lib/mail/version_specific/ruby_1_8.rb +0 -163
  122. data/lib/mail/version_specific/ruby_1_9.rb +0 -278
data/lib/mail/header.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+
3
6
  module Mail
4
-
5
7
  # Provides access to a header object.
6
- #
8
+ #
7
9
  # ===Per RFC2822
8
- #
10
+ #
9
11
  # 2.2. Header Fields
10
- #
12
+ #
11
13
  # Header fields are lines composed of a field name, followed by a colon
12
14
  # (":"), followed by a field body, and terminated by CRLF. A field
13
15
  # name MUST be composed of printable US-ASCII characters (i.e.,
@@ -18,14 +20,12 @@ module Mail
18
20
  # 2.2.3. All field bodies MUST conform to the syntax described in
19
21
  # sections 3 and 4 of this standard.
20
22
  class Header
21
- include Constants
22
- include Utilities
23
23
  include Enumerable
24
-
24
+
25
25
  @@maximum_amount = 1000
26
26
 
27
27
  # Large amount of headers in Email might create extra high CPU load
28
- # Use this parameter to limit number of headers that will be parsed by
28
+ # Use this parameter to limit number of headers that will be parsed by
29
29
  # mail library.
30
30
  # Default: 1000
31
31
  def self.maximum_amount
@@ -36,12 +36,14 @@ module Mail
36
36
  @@maximum_amount = value
37
37
  end
38
38
 
39
+ attr_reader :raw_source, :charset
40
+
39
41
  # Creates a new header object.
40
- #
42
+ #
41
43
  # Accepts raw text or nothing. If given raw text will attempt to parse
42
44
  # it and split it into the various fields, instantiating each field as
43
45
  # it goes.
44
- #
46
+ #
45
47
  # If it finds a field that should be a structured field (such as content
46
48
  # type), but it fails to parse it, it will simply make it an unstructured
47
49
  # field and leave it alone. This will mean that the data is preserved but
@@ -50,7 +52,7 @@ module Mail
50
52
  # me the example so we can fix it.
51
53
  def initialize(header_text = nil, charset = nil)
52
54
  @charset = charset
53
- self.raw_source = header_text
55
+ @raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
54
56
  split_header if header_text
55
57
  end
56
58
 
@@ -59,21 +61,15 @@ module Mail
59
61
  @fields = @fields.dup
60
62
  @fields.map!(&:dup)
61
63
  end
62
-
63
- # The preserved raw source of the header as you passed it in, untouched
64
- # for your Regexing glory.
65
- def raw_source
66
- @raw_source
67
- end
68
-
64
+
69
65
  # Returns an array of all the fields in the header in order that they
70
66
  # were read in.
71
67
  def fields
72
68
  @fields ||= FieldList.new
73
69
  end
74
-
70
+
75
71
  # 3.6. Field definitions
76
- #
72
+ #
77
73
  # It is important to note that the header fields are not guaranteed to
78
74
  # be in a particular order. They may appear in any order, and they
79
75
  # have been known to be reordered occasionally when transported over
@@ -83,36 +79,35 @@ module Mail
83
79
  # header fields MUST NOT be reordered, and SHOULD be kept in blocks
84
80
  # prepended to the message. See sections 3.6.6 and 3.6.7 for more
85
81
  # information.
86
- #
82
+ #
87
83
  # Populates the fields container with Field objects in the order it
88
84
  # receives them in.
89
85
  #
90
86
  # Acceps an array of field string values, for example:
91
- #
87
+ #
92
88
  # h = Header.new
93
89
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
94
90
  def fields=(unfolded_fields)
95
91
  @fields = Mail::FieldList.new
96
- Kernel.warn "WARNING: More than #{self.class.maximum_amount} header fields; only using the first #{self.class.maximum_amount} and ignoring the rest" if unfolded_fields.length > self.class.maximum_amount
97
- unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
98
92
 
93
+ if unfolded_fields.size > self.class.maximum_amount
94
+ Kernel.warn "WARNING: More than #{self.class.maximum_amount} header fields; only using the first #{self.class.maximum_amount} and ignoring the rest"
95
+ unfolded_fields = unfolded_fields.slice(0...self.class.maximum_amount)
96
+ end
97
+
98
+ unfolded_fields.each do |field|
99
99
  if field = Field.parse(field, charset)
100
- if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
101
- selected.first.update(field.name, field.value)
102
- else
103
- @fields << field
104
- end
100
+ @fields.add_field field
105
101
  end
106
102
  end
107
-
108
103
  end
109
-
104
+
110
105
  def errors
111
106
  @fields.map(&:errors).flatten(1)
112
107
  end
113
-
108
+
114
109
  # 3.6. Field definitions
115
- #
110
+ #
116
111
  # The following table indicates limits on the number of times each
117
112
  # field may occur in a message header as well as any special
118
113
  # limitations on the use of those fields. An asterisk next to a value
@@ -122,35 +117,25 @@ module Mail
122
117
  # <snip table from 3.6>
123
118
  #
124
119
  # As per RFC, many fields can appear more than once, we will return a string
125
- # of the value if there is only one header, or if there is more than one
120
+ # of the value if there is only one header, or if there is more than one
126
121
  # matching header, will return an array of values in order that they appear
127
122
  # in the header ordered from top to bottom.
128
- #
123
+ #
129
124
  # Example:
130
- #
125
+ #
131
126
  # h = Header.new
132
127
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
133
128
  # h['To'] #=> 'mikel@me.com'
134
129
  # h['X-Mail-SPAM'] #=> ['15', '20']
135
130
  def [](name)
136
- name = dasherize(name)
137
- name.downcase!
138
- selected = select_field_for(name)
139
- case
140
- when selected.length > 1
141
- selected.map { |f| f }
142
- when !Utilities.blank?(selected)
143
- selected.first
144
- else
145
- nil
146
- end
131
+ fields.get_field(Utilities.dasherize(name))
147
132
  end
148
-
133
+
149
134
  # Sets the FIRST matching field in the header to passed value, or deletes
150
135
  # the FIRST field matched from the header if passed nil
151
- #
136
+ #
152
137
  # Example:
153
- #
138
+ #
154
139
  # h = Header.new
155
140
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
156
141
  # h['To'] = 'bob@you.com'
@@ -160,51 +145,39 @@ module Mail
160
145
  # h['X-Mail-SPAM'] = nil
161
146
  # h['X-Mail-SPAM'] # => nil
162
147
  def []=(name, value)
163
- name = dasherize(name)
164
- if name.include?(':')
148
+ name = name.to_s
149
+ if name.include?(Constants::COLON)
165
150
  raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
166
151
  end
167
- fn = name.downcase
168
- selected = select_field_for(fn)
169
-
170
- case
171
- # User wants to delete the field
172
- when !Utilities.blank?(selected) && value == nil
173
- fields.delete_if { |f| selected.include?(f) }
174
-
175
- # User wants to change the field
176
- when !Utilities.blank?(selected) && limited_field?(fn)
177
- selected.first.update(fn, value)
178
-
179
- # User wants to create the field
152
+
153
+ name = Utilities.dasherize(name)
154
+
155
+ # Assign nil to delete the field
156
+ if value.nil?
157
+ fields.delete_field name
180
158
  else
181
- # Need to insert in correct order for trace fields
182
- self.fields << Field.new(name.to_s, value, charset)
183
- end
184
- if dasherize(fn) == "content-type"
159
+ fields.add_field Field.new(name.to_s, value, charset)
160
+
185
161
  # Update charset if specified in Content-Type
186
- params = self[:content_type].parameters rescue nil
187
- @charset = params[:charset] if params && params[:charset]
162
+ if name == 'content-type'
163
+ params = self[:content_type].parameters rescue nil
164
+ @charset = params[:charset] if params && params[:charset]
165
+ end
188
166
  end
189
167
  end
190
-
191
- def charset
192
- @charset
193
- end
194
-
168
+
195
169
  def charset=(val)
196
170
  params = self[:content_type].parameters rescue nil
197
171
  if params
198
- params[:charset] = val
172
+ if val
173
+ params[:charset] = val
174
+ else
175
+ params.delete(:charset)
176
+ end
199
177
  end
200
178
  @charset = val
201
179
  end
202
-
203
- LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
204
- message-id in-reply-to references subject
205
- return-path content-type mime-version
206
- content-transfer-encoding content-description
207
- content-id content-disposition content-location]
180
+
208
181
 
209
182
  def encoded
210
183
  buffer = String.new
@@ -218,61 +191,47 @@ module Mail
218
191
  def to_s
219
192
  encoded
220
193
  end
221
-
194
+
222
195
  def decoded
223
- raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts, try calling #decoded on the various fields.'
196
+ raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts. Try calling #decoded on the various fields.'
224
197
  end
225
198
 
226
199
  def field_summary
227
- fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
200
+ fields.summary
228
201
  end
229
202
 
230
203
  # Returns true if the header has a Message-ID defined (empty or not)
231
204
  def has_message_id?
232
- !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
205
+ fields.has_field? 'Message-ID'
233
206
  end
234
207
 
235
208
  # Returns true if the header has a Content-ID defined (empty or not)
236
209
  def has_content_id?
237
- !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
210
+ fields.has_field? 'Content-ID'
238
211
  end
239
212
 
240
213
  # Returns true if the header has a Date defined (empty or not)
241
214
  def has_date?
242
- !fields.select { |f| f.responsible_for?('Date') }.empty?
215
+ fields.has_field? 'Date'
243
216
  end
244
217
 
245
218
  # Returns true if the header has a MIME version defined (empty or not)
246
219
  def has_mime_version?
247
- !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
220
+ fields.has_field? 'Mime-Version'
248
221
  end
249
222
 
250
223
  private
251
-
252
- def raw_source=(val)
253
- @raw_source = ::Mail::Utilities.to_crlf(val).lstrip
254
- end
255
-
224
+
256
225
  # Splits an unfolded and line break cleaned header into individual field
257
226
  # strings.
258
227
  def split_header
259
- self.fields = raw_source.split(HEADER_SPLIT)
260
- end
261
-
262
- def select_field_for(name)
263
- fields.select { |f| f.responsible_for?(name) }
264
- end
265
-
266
- def limited_field?(name)
267
- LIMITED_FIELDS.include?(name.to_s.downcase)
228
+ self.fields = @raw_source.split(Constants::HEADER_SPLIT)
268
229
  end
269
230
 
270
- # Enumerable support; yield each field in order to the block if there is one,
271
- # or return an Enumerator for them if there isn't.
272
- def each( &block )
273
- return self.fields.each( &block ) if block
274
- self.fields.each
275
- end
276
231
 
232
+ # Enumerable support. Yield each field in order.
233
+ def each(&block)
234
+ fields.each(&block)
235
+ end
277
236
  end
278
237
  end
@@ -9,6 +9,10 @@ module Mail
9
9
  AttachmentFilenameMatcher.new(filename)
10
10
  end
11
11
 
12
+ def an_attachment_with_mime_type(filename)
13
+ AttachmentMimeTypeMatcher.new(filename)
14
+ end
15
+
12
16
  class AnyAttachmentMatcher
13
17
  def ===(other)
14
18
  other.attachment?
@@ -25,5 +29,16 @@ module Mail
25
29
  other.attachment? && other.filename == filename
26
30
  end
27
31
  end
32
+
33
+ class AttachmentMimeTypeMatcher
34
+ attr_reader :mime_type
35
+ def initialize(mime_type)
36
+ @mime_type = mime_type
37
+ end
38
+
39
+ def ===(other)
40
+ other.attachment? && other.mime_type == mime_type
41
+ end
42
+ end
28
43
  end
29
44
  end
data/lib/mail/message.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- require "yaml"
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+ require 'mail/yaml'
4
6
 
5
7
  module Mail
6
8
  # The Message class provides a single point of access to all things to do with an
@@ -46,10 +48,6 @@ module Mail
46
48
  # follows the header and is separated from the header by an empty line
47
49
  # (i.e., a line with nothing preceding the CRLF).
48
50
  class Message
49
-
50
- include Constants
51
- include Utilities
52
-
53
51
  # ==Making an email
54
52
  #
55
53
  # You can make an new mail object via a block, passing a string, file or direct assignment.
@@ -149,7 +147,7 @@ module Mail
149
147
  # m.to 'recipient@example.com'
150
148
  # end
151
149
  if block_given?
152
- if block.arity.zero? || (RUBY_VERSION < '1.9' && block.arity < 1)
150
+ if block.arity.zero?
153
151
  instance_eval(&block)
154
152
  else
155
153
  yield self
@@ -235,11 +233,6 @@ module Mail
235
233
  def self.default_charset=(charset); @@default_charset = charset; end
236
234
  self.default_charset = 'UTF-8'
237
235
 
238
- def register_for_delivery_notification(observer)
239
- warn("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
240
- Mail.register_observer(observer)
241
- end
242
-
243
236
  def inform_observers
244
237
  Mail.inform_observers(self)
245
238
  end
@@ -301,7 +294,7 @@ module Mail
301
294
  reply.references ||= bracketed_message_id
302
295
  end
303
296
  if subject
304
- reply.subject = subject =~ /^Re:/i ? subject : "RE: #{subject}"
297
+ reply.subject = subject =~ /^Re:/i ? subject : "Re: #{subject}"
305
298
  end
306
299
  if reply_to || from
307
300
  reply.to = self[reply_to ? :reply_to : :from].to_s
@@ -406,9 +399,9 @@ module Mail
406
399
  end
407
400
 
408
401
  # Sets the envelope from for the email
409
- def set_envelope( val )
402
+ def set_envelope(val)
410
403
  @raw_envelope = val
411
- @envelope = Mail::Envelope.new( val )
404
+ @envelope = Mail::Envelope.parse(val) rescue nil
412
405
  end
413
406
 
414
407
  # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
@@ -1339,7 +1332,7 @@ module Mail
1339
1332
  # mail['foo'] = '1234'
1340
1333
  # mail['foo'].to_s #=> '1234'
1341
1334
  def [](name)
1342
- header[underscoreize(name)]
1335
+ header[Utilities.underscoreize(name)]
1343
1336
  end
1344
1337
 
1345
1338
  # Method Missing in this implementation allows you to set any of the
@@ -1385,7 +1378,7 @@ module Mail
1385
1378
  #:nodoc:
1386
1379
  # Only take the structured fields, as we could take _anything_ really
1387
1380
  # as it could become an optional field... "but therin lies the dark side"
1388
- field_name = underscoreize(name).chomp("=")
1381
+ field_name = Utilities.underscoreize(name).chomp("=")
1389
1382
  if Mail::Field::KNOWN_FIELDS.include?(field_name)
1390
1383
  if args.empty?
1391
1384
  header[field_name]
@@ -1436,11 +1429,6 @@ module Mail
1436
1429
  header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1437
1430
  end
1438
1431
 
1439
- def has_transfer_encoding? # :nodoc:
1440
- warn(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
1441
- has_content_transfer_encoding?
1442
- end
1443
-
1444
1432
  # Creates a new empty Message-ID field and inserts it in the correct order
1445
1433
  # into the Header. The MessageIdField object will automatically generate
1446
1434
  # a unique message ID if you try and encode it or output it to_s without
@@ -1489,7 +1477,9 @@ module Mail
1489
1477
  warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1490
1478
  warn(warning)
1491
1479
  end
1492
- header[:content_type].parameters['charset'] = @charset
1480
+ if @charset
1481
+ header[:content_type].parameters['charset'] = @charset
1482
+ end
1493
1483
  end
1494
1484
  end
1495
1485
 
@@ -1498,30 +1488,15 @@ module Mail
1498
1488
  header[:content_transfer_encoding] ||= body.default_encoding
1499
1489
  end
1500
1490
 
1501
- def add_transfer_encoding # :nodoc:
1502
- warn(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
1503
- add_content_transfer_encoding
1504
- end
1505
-
1506
- def transfer_encoding # :nodoc:
1507
- warn(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
1508
- content_transfer_encoding
1509
- end
1510
-
1511
1491
  # Returns the MIME media type of part we are on, this is taken from the content-type header
1512
1492
  def mime_type
1513
1493
  has_content_type? ? header[:content_type].string : nil rescue nil
1514
1494
  end
1515
1495
 
1516
- def message_content_type
1517
- warn(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
1518
- mime_type
1519
- end
1520
-
1521
1496
  # Returns the character set defined in the content type field
1522
1497
  def charset
1523
1498
  if @header
1524
- has_content_type? ? content_type_parameters['charset'] : @charset
1499
+ has_content_type? && !multipart? ? content_type_parameters['charset'] : @charset
1525
1500
  else
1526
1501
  @charset
1527
1502
  end
@@ -1544,12 +1519,6 @@ module Mail
1544
1519
  has_content_type? ? header[:content_type].sub_type : nil rescue nil
1545
1520
  end
1546
1521
 
1547
- # Returns the content type parameters
1548
- def mime_parameters
1549
- warn(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
1550
- content_type_parameters
1551
- end
1552
-
1553
1522
  # Returns the content type parameters
1554
1523
  def content_type_parameters
1555
1524
  has_content_type? ? header[:content_type].parameters : nil rescue nil
@@ -1798,12 +1767,22 @@ module Mail
1798
1767
  self.attachments[basename] = filedata
1799
1768
  end
1800
1769
 
1770
+ MULTIPART_CONVERSION_CONTENT_FIELDS = [ :content_description, :content_disposition, :content_transfer_encoding, :content_type ]
1771
+ private_constant :MULTIPART_CONVERSION_CONTENT_FIELDS if respond_to?(:private_constant)
1772
+
1801
1773
  def convert_to_multipart
1802
- text = body.decoded
1803
- self.body = ''
1804
- text_part = Mail::Part.new({:content_type => 'text/plain;',
1805
- :body => text})
1774
+ text_part = Mail::Part.new(:body => body.decoded)
1775
+
1776
+ MULTIPART_CONVERSION_CONTENT_FIELDS.each do |field_name|
1777
+ if value = send(field_name)
1778
+ writer = :"#{field_name}="
1779
+ text_part.send writer, value
1780
+ send writer, nil
1781
+ end
1782
+ end
1806
1783
  text_part.charset = charset unless @defaulted_charset
1784
+
1785
+ self.body = ''
1807
1786
  self.body << text_part
1808
1787
  end
1809
1788
 
@@ -1818,11 +1797,6 @@ module Mail
1818
1797
  add_required_fields
1819
1798
  end
1820
1799
 
1821
- def encode!
1822
- warn("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
1823
- ready_to_send!
1824
- end
1825
-
1826
1800
  # Outputs an encoded string representation of the mail message including
1827
1801
  # all headers, attachments, etc. This is an encoded email in US-ASCII,
1828
1802
  # so it is able to be directly sent to an email server.
@@ -1836,7 +1810,7 @@ module Mail
1836
1810
 
1837
1811
  def without_attachments!
1838
1812
  if has_attachments?
1839
- parts.delete_if { |p| p.attachment? }
1813
+ parts.delete_attachments
1840
1814
 
1841
1815
  reencoded = parts.empty? ? '' : body.encoded(content_transfer_encoding)
1842
1816
  @body = nil # So the new parts won't be added to the existing body
@@ -1867,7 +1841,7 @@ module Mail
1867
1841
  end
1868
1842
 
1869
1843
  def self.from_yaml(str)
1870
- hash = YAML.load(str)
1844
+ hash = Mail::YAML.load(str)
1871
1845
  m = self.new(:headers => hash['headers'])
1872
1846
  hash.delete('headers')
1873
1847
  hash.each do |k,v|
@@ -1900,6 +1874,15 @@ module Mail
1900
1874
  "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1901
1875
  end
1902
1876
 
1877
+ def inspect_structure
1878
+ inspect +
1879
+ if self.multipart?
1880
+ "\n" + parts.inspect_structure
1881
+ else
1882
+ ''
1883
+ end
1884
+ end
1885
+
1903
1886
  def decoded
1904
1887
  case
1905
1888
  when self.text?
@@ -1984,7 +1967,7 @@ module Mail
1984
1967
 
1985
1968
  private
1986
1969
 
1987
- HEADER_SEPARATOR = /#{Constants::CRLF}#{Constants::CRLF}/
1970
+ HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
1988
1971
 
1989
1972
  # 2.1. General Description
1990
1973
  # A message consists of header fields (collectively called "the header
@@ -2029,7 +2012,7 @@ module Mail
2029
2012
 
2030
2013
  def set_envelope_header
2031
2014
  raw_string = raw_source.to_s
2032
- if match_data = raw_string.match(/\AFrom\s(#{TEXT}+)#{Constants::CRLF}/m)
2015
+ if match_data = raw_string.match(/\AFrom\s+([^:\s]#{Constants::TEXT}*)#{Constants::LAX_CRLF}/m)
2033
2016
  set_envelope(match_data[1])
2034
2017
  self.raw_source = raw_string.sub(match_data[0], "")
2035
2018
  end
@@ -2053,10 +2036,12 @@ module Mail
2053
2036
  end
2054
2037
 
2055
2038
  def identify_and_set_transfer_encoding
2056
- if body && body.multipart?
2057
- self.content_transfer_encoding = @transport_encoding
2058
- else
2059
- self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
2039
+ if body
2040
+ if body.multipart?
2041
+ self.content_transfer_encoding = @transport_encoding
2042
+ else
2043
+ self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
2044
+ end
2060
2045
  end
2061
2046
  end
2062
2047
 
@@ -2076,15 +2061,17 @@ module Mail
2076
2061
 
2077
2062
  def add_multipart_alternate_header
2078
2063
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2079
- header['content_type'].parameters[:charset] = @charset
2080
2064
  body.boundary = boundary
2081
2065
  end
2082
2066
 
2083
2067
  def add_boundary
2084
2068
  unless body.boundary && boundary
2085
- header['content-type'] = 'multipart/mixed' unless header['content-type']
2069
+ unless header['content-type']
2070
+ _charset = charset
2071
+ header['content-type'] = 'multipart/mixed'
2072
+ header['content-type'].parameters[:charset] = _charset
2073
+ end
2086
2074
  header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2087
- header['content_type'].parameters[:charset] = @charset
2088
2075
  body.boundary = boundary
2089
2076
  end
2090
2077
  end
@@ -2092,7 +2079,6 @@ module Mail
2092
2079
  def add_multipart_mixed_header
2093
2080
  unless header['content-type']
2094
2081
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2095
- header['content_type'].parameters[:charset] = @charset
2096
2082
  body.boundary = boundary
2097
2083
  end
2098
2084
  end
@@ -2109,7 +2095,7 @@ module Mail
2109
2095
  body_content = nil
2110
2096
 
2111
2097
  passed_in_options.each_pair do |k,v|
2112
- k = underscoreize(k).to_sym if k.class == String
2098
+ k = Utilities.underscoreize(k).to_sym if k.class == String
2113
2099
  if k == :headers
2114
2100
  self.headers(v)
2115
2101
  elsif k == :body