mail 2.6.4 → 2.9.0

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 (180) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +208 -156
  3. data/lib/mail/attachments_list.rb +13 -10
  4. data/lib/mail/body.rb +96 -107
  5. data/lib/mail/configuration.rb +2 -0
  6. data/lib/mail/constants.rb +27 -5
  7. data/lib/mail/elements/address.rb +61 -50
  8. data/lib/mail/elements/address_list.rb +11 -19
  9. data/lib/mail/elements/content_disposition_element.rb +9 -16
  10. data/lib/mail/elements/content_location_element.rb +6 -11
  11. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  12. data/lib/mail/elements/content_type_element.rb +16 -23
  13. data/lib/mail/elements/date_time_element.rb +7 -15
  14. data/lib/mail/elements/envelope_from_element.rb +22 -23
  15. data/lib/mail/elements/message_ids_element.rb +18 -13
  16. data/lib/mail/elements/mime_version_element.rb +7 -15
  17. data/lib/mail/elements/phrase_list.rb +12 -10
  18. data/lib/mail/elements/received_element.rb +27 -19
  19. data/lib/mail/encodings/7bit.rb +9 -14
  20. data/lib/mail/encodings/8bit.rb +2 -21
  21. data/lib/mail/encodings/base64.rb +11 -12
  22. data/lib/mail/encodings/binary.rb +3 -22
  23. data/lib/mail/encodings/identity.rb +24 -0
  24. data/lib/mail/encodings/quoted_printable.rb +6 -6
  25. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  26. data/lib/mail/encodings/unix_to_unix.rb +4 -2
  27. data/lib/mail/encodings.rb +83 -56
  28. data/lib/mail/envelope.rb +11 -14
  29. data/lib/mail/field.rb +181 -130
  30. data/lib/mail/field_list.rb +61 -8
  31. data/lib/mail/fields/bcc_field.rb +33 -52
  32. data/lib/mail/fields/cc_field.rb +27 -49
  33. data/lib/mail/fields/comments_field.rb +26 -37
  34. data/lib/mail/fields/common_address_field.rb +162 -0
  35. data/lib/mail/fields/common_date_field.rb +56 -0
  36. data/lib/mail/fields/common_field.rb +77 -0
  37. data/lib/mail/fields/common_message_id_field.rb +41 -0
  38. data/lib/mail/fields/content_description_field.rb +6 -14
  39. data/lib/mail/fields/content_disposition_field.rb +11 -38
  40. data/lib/mail/fields/content_id_field.rb +23 -51
  41. data/lib/mail/fields/content_location_field.rb +10 -25
  42. data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
  43. data/lib/mail/fields/content_type_field.rb +53 -84
  44. data/lib/mail/fields/date_field.rb +22 -52
  45. data/lib/mail/fields/from_field.rb +27 -49
  46. data/lib/mail/fields/in_reply_to_field.rb +37 -49
  47. data/lib/mail/fields/keywords_field.rb +17 -31
  48. data/lib/mail/fields/message_id_field.rb +24 -71
  49. data/lib/mail/fields/mime_version_field.rb +18 -30
  50. data/lib/mail/fields/named_structured_field.rb +10 -0
  51. data/lib/mail/fields/named_unstructured_field.rb +10 -0
  52. data/lib/mail/fields/optional_field.rb +9 -8
  53. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  54. data/lib/mail/fields/received_field.rb +42 -57
  55. data/lib/mail/fields/references_field.rb +34 -49
  56. data/lib/mail/fields/reply_to_field.rb +27 -49
  57. data/lib/mail/fields/resent_bcc_field.rb +27 -49
  58. data/lib/mail/fields/resent_cc_field.rb +27 -49
  59. data/lib/mail/fields/resent_date_field.rb +4 -30
  60. data/lib/mail/fields/resent_from_field.rb +27 -49
  61. data/lib/mail/fields/resent_message_id_field.rb +4 -29
  62. data/lib/mail/fields/resent_sender_field.rb +26 -56
  63. data/lib/mail/fields/resent_to_field.rb +27 -49
  64. data/lib/mail/fields/return_path_field.rb +49 -54
  65. data/lib/mail/fields/sender_field.rb +33 -55
  66. data/lib/mail/fields/structured_field.rb +2 -30
  67. data/lib/mail/fields/subject_field.rb +8 -11
  68. data/lib/mail/fields/to_field.rb +27 -49
  69. data/lib/mail/fields/unstructured_field.rb +31 -47
  70. data/lib/mail/fields.rb +9 -0
  71. data/lib/mail/header.rb +71 -110
  72. data/lib/mail/mail.rb +34 -37
  73. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  74. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  75. data/lib/mail/message.rb +126 -127
  76. data/lib/mail/multibyte/chars.rb +24 -181
  77. data/lib/mail/multibyte/unicode.rb +11 -11
  78. data/lib/mail/multibyte/utils.rb +26 -43
  79. data/lib/mail/multibyte.rb +55 -16
  80. data/lib/mail/network/delivery_methods/exim.rb +8 -11
  81. data/lib/mail/network/delivery_methods/file_delivery.rb +15 -18
  82. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  83. data/lib/mail/network/delivery_methods/sendmail.rb +32 -35
  84. data/lib/mail/network/delivery_methods/smtp.rb +125 -68
  85. data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -16
  86. data/lib/mail/network/delivery_methods/test_mailer.rb +12 -13
  87. data/lib/mail/network/retriever_methods/base.rb +13 -13
  88. data/lib/mail/network/retriever_methods/imap.rb +25 -9
  89. data/lib/mail/network/retriever_methods/pop3.rb +25 -23
  90. data/lib/mail/network/retriever_methods/test_retriever.rb +3 -2
  91. data/lib/mail/network.rb +1 -0
  92. data/lib/mail/parser_tools.rb +15 -0
  93. data/lib/mail/parsers/address_lists_parser.rb +33228 -116
  94. data/lib/mail/parsers/address_lists_parser.rl +183 -0
  95. data/lib/mail/parsers/content_disposition_parser.rb +885 -49
  96. data/lib/mail/parsers/content_disposition_parser.rl +93 -0
  97. data/lib/mail/parsers/content_location_parser.rb +812 -23
  98. data/lib/mail/parsers/content_location_parser.rl +82 -0
  99. data/lib/mail/parsers/content_transfer_encoding_parser.rb +512 -21
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
  101. data/lib/mail/parsers/content_type_parser.rb +1039 -55
  102. data/lib/mail/parsers/content_type_parser.rl +94 -0
  103. data/lib/mail/parsers/date_time_parser.rb +880 -25
  104. data/lib/mail/parsers/date_time_parser.rl +73 -0
  105. data/lib/mail/parsers/envelope_from_parser.rb +3672 -40
  106. data/lib/mail/parsers/envelope_from_parser.rl +93 -0
  107. data/lib/mail/parsers/message_ids_parser.rb +5149 -25
  108. data/lib/mail/parsers/message_ids_parser.rl +97 -0
  109. data/lib/mail/parsers/mime_version_parser.rb +500 -26
  110. data/lib/mail/parsers/mime_version_parser.rl +72 -0
  111. data/lib/mail/parsers/phrase_lists_parser.rb +873 -22
  112. data/lib/mail/parsers/phrase_lists_parser.rl +94 -0
  113. data/lib/mail/parsers/received_parser.rb +8779 -43
  114. data/lib/mail/parsers/received_parser.rl +95 -0
  115. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  116. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  117. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  118. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  119. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  120. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  121. data/lib/mail/parsers/rfc5322.rl +74 -0
  122. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  123. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  124. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  125. data/lib/mail/parsers.rb +11 -25
  126. data/lib/mail/part.rb +25 -29
  127. data/lib/mail/parts_list.rb +62 -6
  128. data/lib/mail/smtp_envelope.rb +57 -0
  129. data/lib/mail/utilities.rb +361 -74
  130. data/lib/mail/version.rb +2 -2
  131. data/lib/mail/yaml.rb +30 -0
  132. data/lib/mail.rb +4 -37
  133. metadata +125 -67
  134. data/CHANGELOG.rdoc +0 -787
  135. data/CONTRIBUTING.md +0 -60
  136. data/Dependencies.txt +0 -2
  137. data/Gemfile +0 -11
  138. data/Rakefile +0 -29
  139. data/TODO.rdoc +0 -9
  140. data/lib/mail/check_delivery_params.rb +0 -21
  141. data/lib/mail/core_extensions/smtp.rb +0 -25
  142. data/lib/mail/core_extensions/string/access.rb +0 -146
  143. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  144. data/lib/mail/core_extensions/string.rb +0 -21
  145. data/lib/mail/fields/common/address_container.rb +0 -17
  146. data/lib/mail/fields/common/common_address.rb +0 -136
  147. data/lib/mail/fields/common/common_date.rb +0 -36
  148. data/lib/mail/fields/common/common_field.rb +0 -61
  149. data/lib/mail/fields/common/common_message_id.rb +0 -49
  150. data/lib/mail/multibyte/exceptions.rb +0 -9
  151. data/lib/mail/parsers/ragel/common.rl +0 -185
  152. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  173. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  177. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  178. data/lib/mail/parsers/ragel.rb +0 -18
  179. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  180. data/lib/mail/version_specific/ruby_1_9.rb +0 -223
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,25 +48,29 @@ 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.
56
54
  #
57
55
  # ===Making an email via a block
58
56
  #
59
- # mail = Mail.new do
60
- # from 'mikel@test.lindsaar.net'
61
- # to 'you@test.lindsaar.net'
62
- # subject 'This is a test email'
63
- # body File.read('body.txt')
57
+ # mail = Mail.new do |m|
58
+ # m.from 'mikel@test.lindsaar.net'
59
+ # m.to 'you@test.lindsaar.net'
60
+ # m.subject 'This is a test email'
61
+ # m.body File.read('body.txt')
64
62
  # end
65
63
  #
66
64
  # mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
67
65
  #
66
+ # If may also pass a block with no arguments, in which case it will
67
+ # be evaluated in the scope of the new message instance:
68
+ #
69
+ # mail = Mail.new do
70
+ # from 'mikel@test.lindsaar.net'
71
+ # # …
72
+ # end
73
+ #
68
74
  # ===Making an email via passing a string
69
75
  #
70
76
  # mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
@@ -129,8 +135,23 @@ module Mail
129
135
  init_with_string(args.flatten[0].to_s)
130
136
  end
131
137
 
138
+ # Support both builder styles:
139
+ #
140
+ # Mail.new do
141
+ # to 'recipient@example.com'
142
+ # end
143
+ #
144
+ # and
145
+ #
146
+ # Mail.new do |m|
147
+ # m.to 'recipient@example.com'
148
+ # end
132
149
  if block_given?
133
- instance_eval(&block)
150
+ if block.arity.zero?
151
+ instance_eval(&block)
152
+ else
153
+ yield self
154
+ end
134
155
  end
135
156
 
136
157
  self
@@ -212,11 +233,6 @@ module Mail
212
233
  def self.default_charset=(charset); @@default_charset = charset; end
213
234
  self.default_charset = 'UTF-8'
214
235
 
215
- def register_for_delivery_notification(observer)
216
- STDERR.puts("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
217
- Mail.register_observer(observer)
218
- end
219
-
220
236
  def inform_observers
221
237
  Mail.inform_observers(self)
222
238
  end
@@ -225,7 +241,7 @@ module Mail
225
241
  Mail.inform_interceptors(self)
226
242
  end
227
243
 
228
- # Delivers an mail object.
244
+ # Delivers a mail object.
229
245
  #
230
246
  # Examples:
231
247
  #
@@ -278,7 +294,7 @@ module Mail
278
294
  reply.references ||= bracketed_message_id
279
295
  end
280
296
  if subject
281
- reply.subject = subject =~ /^Re:/i ? subject : "RE: #{subject}"
297
+ reply.subject = /^Re:/i.match?(subject) ? subject : "Re: #{subject}"
282
298
  end
283
299
  if reply_to || from
284
300
  reply.to = self[reply_to ? :reply_to : :from].to_s
@@ -333,6 +349,10 @@ module Mail
333
349
  # the same content, ignoring the Message-ID field, unless BOTH emails have a defined and
334
350
  # different Message-ID value, then they are false.
335
351
  #
352
+ # Note that Mail creates Date and Mime-Type fields if they don't exist.
353
+ # The Date field is derived from the current time, so this needs to be allowed for in comparisons.
354
+ # (Mime-type does not depend on dynamic data, so cannot affect equality)
355
+ #
336
356
  # So, in practice the == operator works like this:
337
357
  #
338
358
  # m1 = Mail.new("Subject: Hello\r\n\r\nHello")
@@ -354,19 +374,17 @@ module Mail
354
374
  # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
355
375
  # m2 = Mail.new("Message-ID: <DIFFERENT@test>\r\nSubject: Hello\r\n\r\nHello")
356
376
  # m1 == m2 #=> false
357
- def ==(other)
377
+ def ==(other) # TODO could be more efficient
358
378
  return false unless other.respond_to?(:encoded)
359
379
 
380
+ stamp = Mail::CommonDateField.normalize_datetime('')
381
+ # Note: must always dup the inputs so they are not altered by encoded
360
382
  if self.message_id && other.message_id
361
- self.encoded == other.encoded
383
+ dup.tap { |m| m.date ||= stamp }.encoded ==
384
+ other.dup.tap { |m| m.date ||= stamp }.encoded
362
385
  else
363
- self_message_id, other_message_id = self.message_id, other.message_id
364
- begin
365
- self.message_id, other.message_id = '<temp@test>', '<temp@test>'
366
- self.encoded == other.encoded
367
- ensure
368
- self.message_id, other.message_id = self_message_id, other_message_id
369
- end
386
+ dup.tap { |m| m.message_id = '<temp@test>'; m.date ||= stamp }.encoded ==
387
+ other.dup.tap { |m| m.message_id = '<temp@test>'; m.date ||= stamp }.encoded
370
388
  end
371
389
  end
372
390
 
@@ -388,9 +406,9 @@ module Mail
388
406
  end
389
407
 
390
408
  # Sets the envelope from for the email
391
- def set_envelope( val )
409
+ def set_envelope(val)
392
410
  @raw_envelope = val
393
- @envelope = Mail::Envelope.new( val )
411
+ @envelope = Mail::Envelope.parse(val) rescue nil
394
412
  end
395
413
 
396
414
  # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
@@ -453,7 +471,7 @@ module Mail
453
471
  # message.errors.size #=> 1
454
472
  # message.errors.first[0] #=> "Content-Transfer-Encoding"
455
473
  # message.errors.first[1] #=> "weirdo"
456
- # message.errors.first[3] #=> <The original error message exception>
474
+ # message.errors.first[2] #=> <The original error message exception>
457
475
  #
458
476
  # This is a good first defence on detecting spam by the way. Some spammers send
459
477
  # invalid emails to try and get email parsers to give up parsing them.
@@ -1193,8 +1211,8 @@ module Mail
1193
1211
  def default( sym, val = nil )
1194
1212
  if val
1195
1213
  header[sym] = val
1196
- else
1197
- header[sym].default if header[sym]
1214
+ elsif field = header[sym]
1215
+ field.default
1198
1216
  end
1199
1217
  end
1200
1218
 
@@ -1240,14 +1258,13 @@ module Mail
1240
1258
  def body(value = nil)
1241
1259
  if value
1242
1260
  self.body = value
1243
- # add_encoding_to_body
1244
1261
  else
1245
1262
  process_body_raw if @body_raw
1246
1263
  @body
1247
1264
  end
1248
1265
  end
1249
1266
 
1250
- def body_encoding(value)
1267
+ def body_encoding(value = nil)
1251
1268
  if value.nil?
1252
1269
  body.encoding
1253
1270
  else
@@ -1256,7 +1273,7 @@ module Mail
1256
1273
  end
1257
1274
 
1258
1275
  def body_encoding=(value)
1259
- body.encoding = value
1276
+ body.encoding = value
1260
1277
  end
1261
1278
 
1262
1279
  # Returns the list of addresses this message should be sent to by
@@ -1306,7 +1323,7 @@ module Mail
1306
1323
  def []=(name, value)
1307
1324
  if name.to_s == 'body'
1308
1325
  self.body = value
1309
- elsif name.to_s =~ /content[-_]type/i
1326
+ elsif /content[-_]type/i.match?(name.to_s)
1310
1327
  header[name] = value
1311
1328
  elsif name.to_s == 'charset'
1312
1329
  self.charset = value
@@ -1322,7 +1339,7 @@ module Mail
1322
1339
  # mail['foo'] = '1234'
1323
1340
  # mail['foo'].to_s #=> '1234'
1324
1341
  def [](name)
1325
- header[underscoreize(name)]
1342
+ header[Utilities.underscoreize(name)]
1326
1343
  end
1327
1344
 
1328
1345
  # Method Missing in this implementation allows you to set any of the
@@ -1368,7 +1385,7 @@ module Mail
1368
1385
  #:nodoc:
1369
1386
  # Only take the structured fields, as we could take _anything_ really
1370
1387
  # as it could become an optional field... "but therin lies the dark side"
1371
- field_name = underscoreize(name).chomp("=")
1388
+ field_name = Utilities.underscoreize(name).chomp("=")
1372
1389
  if Mail::Field::KNOWN_FIELDS.include?(field_name)
1373
1390
  if args.empty?
1374
1391
  header[field_name]
@@ -1399,7 +1416,7 @@ module Mail
1399
1416
  header.has_date?
1400
1417
  end
1401
1418
 
1402
- # Returns true if the message has a Mime-Version field, the field may or may
1419
+ # Returns true if the message has a MIME-Version field, the field may or may
1403
1420
  # not have a value, but the field exists or not.
1404
1421
  def has_mime_version?
1405
1422
  header.has_mime_version?
@@ -1419,11 +1436,6 @@ module Mail
1419
1436
  header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1420
1437
  end
1421
1438
 
1422
- def has_transfer_encoding? # :nodoc:
1423
- STDERR.puts(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
1424
- has_content_transfer_encoding?
1425
- end
1426
-
1427
1439
  # Creates a new empty Message-ID field and inserts it in the correct order
1428
1440
  # into the Header. The MessageIdField object will automatically generate
1429
1441
  # a unique message ID if you try and encode it or output it to_s without
@@ -1468,35 +1480,19 @@ module Mail
1468
1480
  if !body.empty?
1469
1481
  # Only give a warning if this isn't an attachment, has non US-ASCII and the user
1470
1482
  # has not specified an encoding explicitly.
1471
- if @defaulted_charset && body.raw_source.not_ascii_only? && !self.attachment?
1483
+ if @defaulted_charset && !body.raw_source.ascii_only? && !self.attachment?
1472
1484
  warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1473
- STDERR.puts(warning)
1485
+ warn(warning)
1486
+ end
1487
+ if @charset
1488
+ header[:content_type].parameters['charset'] = @charset
1474
1489
  end
1475
- header[:content_type].parameters['charset'] = @charset
1476
1490
  end
1477
1491
  end
1478
1492
 
1479
1493
  # Adds a content transfer encoding
1480
- #
1481
- # Otherwise raises a warning
1482
1494
  def add_content_transfer_encoding
1483
- if body.only_us_ascii?
1484
- header[:content_transfer_encoding] = '7bit'
1485
- else
1486
- warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
1487
- STDERR.puts(warning)
1488
- header[:content_transfer_encoding] = '8bit'
1489
- end
1490
- end
1491
-
1492
- def add_transfer_encoding # :nodoc:
1493
- STDERR.puts(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
1494
- add_content_transfer_encoding
1495
- end
1496
-
1497
- def transfer_encoding # :nodoc:
1498
- STDERR.puts(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
1499
- content_transfer_encoding
1495
+ header[:content_transfer_encoding] ||= body.default_encoding
1500
1496
  end
1501
1497
 
1502
1498
  # Returns the MIME media type of part we are on, this is taken from the content-type header
@@ -1504,15 +1500,10 @@ module Mail
1504
1500
  has_content_type? ? header[:content_type].string : nil rescue nil
1505
1501
  end
1506
1502
 
1507
- def message_content_type
1508
- STDERR.puts(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
1509
- mime_type
1510
- end
1511
-
1512
1503
  # Returns the character set defined in the content type field
1513
1504
  def charset
1514
1505
  if @header
1515
- has_content_type? ? content_type_parameters['charset'] : @charset
1506
+ has_content_type? && !multipart? ? content_type_parameters['charset'] : @charset
1516
1507
  else
1517
1508
  @charset
1518
1509
  end
@@ -1535,12 +1526,6 @@ module Mail
1535
1526
  has_content_type? ? header[:content_type].sub_type : nil rescue nil
1536
1527
  end
1537
1528
 
1538
- # Returns the content type parameters
1539
- def mime_parameters
1540
- STDERR.puts(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
1541
- content_type_parameters
1542
- end
1543
-
1544
1529
  # Returns the content type parameters
1545
1530
  def content_type_parameters
1546
1531
  has_content_type? ? header[:content_type].parameters : nil rescue nil
@@ -1548,22 +1533,29 @@ module Mail
1548
1533
 
1549
1534
  # Returns true if the message is multipart
1550
1535
  def multipart?
1551
- has_content_type? ? !!(main_type =~ /^multipart$/i) : false
1536
+ has_content_type? ? /^multipart$/i.match?(main_type) : false
1552
1537
  end
1553
1538
 
1554
1539
  # Returns true if the message is a multipart/report
1555
1540
  def multipart_report?
1556
- multipart? && sub_type =~ /^report$/i
1541
+ multipart? && /^report$/i.match?(sub_type)
1557
1542
  end
1558
1543
 
1559
1544
  # Returns true if the message is a multipart/report; report-type=delivery-status;
1560
1545
  def delivery_status_report?
1561
- multipart_report? && content_type_parameters['report-type'] =~ /^delivery-status$/i
1546
+ multipart_report? && /^delivery-status$/i.match?(content_type_parameters['report-type'])
1562
1547
  end
1563
1548
 
1564
1549
  # returns the part in a multipart/report email that has the content-type delivery-status
1565
1550
  def delivery_status_part
1566
- @delivery_stats_part ||= parts.select { |p| p.delivery_status_report_part? }.first
1551
+ unless defined? @delivery_status_part
1552
+ @delivery_status_part =
1553
+ if delivery_status_report?
1554
+ parts.detect(&:delivery_status_report_part?)
1555
+ end
1556
+ end
1557
+
1558
+ @delivery_status_part
1567
1559
  end
1568
1560
 
1569
1561
  def bounced?
@@ -1778,19 +1770,26 @@ module Mail
1778
1770
  else
1779
1771
  basename = values[:filename]
1780
1772
  filedata = values
1781
- unless filedata[:content]
1782
- filedata = values.merge(:content=>File.open(values[:filename], 'rb') { |f| f.read })
1783
- end
1784
1773
  end
1785
1774
  self.attachments[basename] = filedata
1786
1775
  end
1787
1776
 
1777
+ MULTIPART_CONVERSION_CONTENT_FIELDS = [ :content_description, :content_disposition, :content_transfer_encoding, :content_type ]
1778
+ private_constant :MULTIPART_CONVERSION_CONTENT_FIELDS if respond_to?(:private_constant)
1779
+
1788
1780
  def convert_to_multipart
1789
- text = body.decoded
1790
- self.body = ''
1791
- text_part = Mail::Part.new({:content_type => 'text/plain;',
1792
- :body => text})
1781
+ text_part = Mail::Part.new(:content_type => 'text/plain;', :body => body.decoded)
1782
+
1783
+ MULTIPART_CONVERSION_CONTENT_FIELDS.each do |field_name|
1784
+ if value = send(field_name)
1785
+ writer = :"#{field_name}="
1786
+ text_part.send writer, value
1787
+ send writer, nil
1788
+ end
1789
+ end
1793
1790
  text_part.charset = charset unless @defaulted_charset
1791
+
1792
+ self.body = ''
1794
1793
  self.body << text_part
1795
1794
  end
1796
1795
 
@@ -1798,7 +1797,6 @@ module Mail
1798
1797
  # ready to send
1799
1798
  def ready_to_send!
1800
1799
  identify_and_set_transfer_encoding
1801
- parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
1802
1800
  parts.each do |part|
1803
1801
  part.transport_encoding = transport_encoding
1804
1802
  part.ready_to_send!
@@ -1806,11 +1804,6 @@ module Mail
1806
1804
  add_required_fields
1807
1805
  end
1808
1806
 
1809
- def encode!
1810
- STDERR.puts("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
1811
- ready_to_send!
1812
- end
1813
-
1814
1807
  # Outputs an encoded string representation of the mail message including
1815
1808
  # all headers, attachments, etc. This is an encoded email in US-ASCII,
1816
1809
  # so it is able to be directly sent to an email server.
@@ -1823,16 +1816,13 @@ module Mail
1823
1816
  end
1824
1817
 
1825
1818
  def without_attachments!
1826
- return self unless has_attachments?
1827
-
1828
- parts.delete_if { |p| p.attachment? }
1829
- body_raw = if parts.empty?
1830
- ''
1831
- else
1832
- body.encoded
1833
- end
1819
+ if has_attachments?
1820
+ parts.delete_attachments
1834
1821
 
1835
- @body = Mail::Body.new(body_raw)
1822
+ reencoded = parts.empty? ? '' : body.encoded(content_transfer_encoding)
1823
+ @body = nil # So the new parts won't be added to the existing body
1824
+ self.body = reencoded
1825
+ end
1836
1826
 
1837
1827
  self
1838
1828
  end
@@ -1858,7 +1848,7 @@ module Mail
1858
1848
  end
1859
1849
 
1860
1850
  def self.from_yaml(str)
1861
- hash = YAML.load(str)
1851
+ hash = Mail::YAML.load(str)
1862
1852
  m = self.new(:headers => hash['headers'])
1863
1853
  hash.delete('headers')
1864
1854
  hash.each do |k,v|
@@ -1872,7 +1862,7 @@ module Mail
1872
1862
  m.transport_encoding(v)
1873
1863
  when k == 'multipart_body'
1874
1864
  v.map {|part| m.add_part Mail::Part.from_yaml(part) }
1875
- when k =~ /^@/
1865
+ when k.start_with?('@')
1876
1866
  m.instance_variable_set(k.to_sym, v)
1877
1867
  end
1878
1868
  end
@@ -1891,6 +1881,15 @@ module Mail
1891
1881
  "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1892
1882
  end
1893
1883
 
1884
+ def inspect_structure
1885
+ inspect +
1886
+ if self.multipart?
1887
+ "\n" + parts.inspect_structure
1888
+ else
1889
+ ''
1890
+ end
1891
+ end
1892
+
1894
1893
  def decoded
1895
1894
  case
1896
1895
  when self.text?
@@ -1900,7 +1899,7 @@ module Mail
1900
1899
  when !self.multipart?
1901
1900
  body.decoded
1902
1901
  else
1903
- raise NoMethodError, 'Can not decode an entire message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
1902
+ raise NoMethodError, 'This message cannot be decoded as _entire_ message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
1904
1903
  end
1905
1904
  end
1906
1905
 
@@ -1970,12 +1969,12 @@ module Mail
1970
1969
  end
1971
1970
 
1972
1971
  def text?
1973
- has_content_type? ? !!(main_type =~ /^text$/i) : false
1972
+ has_content_type? ? /^text$/i.match?(main_type) : false
1974
1973
  end
1975
1974
 
1976
1975
  private
1977
1976
 
1978
- HEADER_SEPARATOR = /#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m
1977
+ HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
1979
1978
 
1980
1979
  # 2.1. General Description
1981
1980
  # A message consists of header fields (collectively called "the header
@@ -1984,9 +1983,6 @@ module Mail
1984
1983
  # this standard. The body is simply a sequence of characters that
1985
1984
  # follows the header and is separated from the header by an empty line
1986
1985
  # (i.e., a line with nothing preceding the CRLF).
1987
- #
1988
- # Additionally, I allow for the case where someone might have put whitespace
1989
- # on the "gap line"
1990
1986
  def parse_message
1991
1987
  header_part, body_part = raw_source.lstrip.split(HEADER_SEPARATOR, 2)
1992
1988
  self.header = header_part
@@ -1994,8 +1990,7 @@ module Mail
1994
1990
  end
1995
1991
 
1996
1992
  def raw_source=(value)
1997
- value = value.dup.force_encoding(Encoding::BINARY) if RUBY_VERSION >= "1.9.1"
1998
- @raw_source = ::Mail::Utilities.to_crlf(value)
1993
+ @raw_source = value
1999
1994
  end
2000
1995
 
2001
1996
  # see comments to body=. We take data and process it lazily
@@ -2007,11 +2002,9 @@ module Mail
2007
2002
  @body_raw = nil
2008
2003
  add_encoding_to_body
2009
2004
  when @body && @body.multipart?
2010
- @body << Mail::Part.new(value)
2011
- add_encoding_to_body
2005
+ self.text_part = value
2012
2006
  else
2013
2007
  @body_raw = value
2014
- # process_body_raw
2015
2008
  end
2016
2009
  end
2017
2010
 
@@ -2026,7 +2019,7 @@ module Mail
2026
2019
 
2027
2020
  def set_envelope_header
2028
2021
  raw_string = raw_source.to_s
2029
- if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
2022
+ if match_data = raw_string.match(/\AFrom\s+([^:\s]#{Constants::TEXT}*)#{Constants::LAX_CRLF}/m)
2030
2023
  set_envelope(match_data[1])
2031
2024
  self.raw_source = raw_string.sub(match_data[0], "")
2032
2025
  end
@@ -2036,6 +2029,13 @@ module Mail
2036
2029
  body.split!(boundary)
2037
2030
  end
2038
2031
 
2032
+ def allowed_encodings
2033
+ case mime_type
2034
+ when 'message/rfc822'
2035
+ [Encodings::SevenBit, Encodings::EightBit, Encodings::Binary]
2036
+ end
2037
+ end
2038
+
2039
2039
  def add_encoding_to_body
2040
2040
  if has_content_transfer_encoding?
2041
2041
  @body.encoding = content_transfer_encoding
@@ -2043,11 +2043,13 @@ module Mail
2043
2043
  end
2044
2044
 
2045
2045
  def identify_and_set_transfer_encoding
2046
- if body && body.multipart?
2047
- self.content_transfer_encoding = @transport_encoding
2046
+ if body
2047
+ if body.multipart?
2048
+ self.content_transfer_encoding = @transport_encoding
2048
2049
  else
2049
- self.content_transfer_encoding = body.get_best_encoding(@transport_encoding)
2050
+ self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
2050
2051
  end
2052
+ end
2051
2053
  end
2052
2054
 
2053
2055
  def add_required_fields
@@ -2066,7 +2068,6 @@ module Mail
2066
2068
 
2067
2069
  def add_multipart_alternate_header
2068
2070
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2069
- header['content_type'].parameters[:charset] = @charset
2070
2071
  body.boundary = boundary
2071
2072
  end
2072
2073
 
@@ -2074,7 +2075,6 @@ module Mail
2074
2075
  unless body.boundary && boundary
2075
2076
  header['content-type'] = 'multipart/mixed' unless header['content-type']
2076
2077
  header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2077
- header['content_type'].parameters[:charset] = @charset
2078
2078
  body.boundary = boundary
2079
2079
  end
2080
2080
  end
@@ -2082,7 +2082,6 @@ module Mail
2082
2082
  def add_multipart_mixed_header
2083
2083
  unless header['content-type']
2084
2084
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2085
- header['content_type'].parameters[:charset] = @charset
2086
2085
  body.boundary = boundary
2087
2086
  end
2088
2087
  end
@@ -2099,7 +2098,7 @@ module Mail
2099
2098
  body_content = nil
2100
2099
 
2101
2100
  passed_in_options.each_pair do |k,v|
2102
- k = underscoreize(k).to_sym if k.class == String
2101
+ k = Utilities.underscoreize(k).to_sym if k.class == String
2103
2102
  if k == :headers
2104
2103
  self.headers(v)
2105
2104
  elsif k == :body
@@ -2130,10 +2129,10 @@ module Mail
2130
2129
  content_disp_name = header[:content_disposition].filename rescue nil
2131
2130
  content_loc_name = header[:content_location].location rescue nil
2132
2131
  case
2133
- when content_type && content_type_name
2134
- filename = content_type_name
2135
2132
  when content_disposition && content_disp_name
2136
2133
  filename = content_disp_name
2134
+ when content_type && content_type_name
2135
+ filename = content_type_name
2137
2136
  when content_location && content_loc_name
2138
2137
  filename = content_loc_name
2139
2138
  else