mail 2.6.4 → 2.8.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 (179) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +111 -118
  3. data/lib/mail/attachments_list.rb +11 -10
  4. data/lib/mail/body.rb +73 -84
  5. data/lib/mail/check_delivery_params.rb +54 -10
  6. data/lib/mail/configuration.rb +2 -0
  7. data/lib/mail/constants.rb +27 -5
  8. data/lib/mail/elements/address.rb +61 -50
  9. data/lib/mail/elements/address_list.rb +11 -19
  10. data/lib/mail/elements/content_disposition_element.rb +9 -16
  11. data/lib/mail/elements/content_location_element.rb +6 -11
  12. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  13. data/lib/mail/elements/content_type_element.rb +16 -23
  14. data/lib/mail/elements/date_time_element.rb +7 -15
  15. data/lib/mail/elements/envelope_from_element.rb +22 -23
  16. data/lib/mail/elements/message_ids_element.rb +18 -13
  17. data/lib/mail/elements/mime_version_element.rb +7 -15
  18. data/lib/mail/elements/phrase_list.rb +12 -10
  19. data/lib/mail/elements/received_element.rb +27 -19
  20. data/lib/mail/encodings/7bit.rb +9 -14
  21. data/lib/mail/encodings/8bit.rb +2 -21
  22. data/lib/mail/encodings/base64.rb +11 -12
  23. data/lib/mail/encodings/binary.rb +3 -22
  24. data/lib/mail/encodings/identity.rb +24 -0
  25. data/lib/mail/encodings/quoted_printable.rb +6 -6
  26. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  27. data/lib/mail/encodings/unix_to_unix.rb +3 -1
  28. data/lib/mail/encodings.rb +81 -54
  29. data/lib/mail/envelope.rb +11 -14
  30. data/lib/mail/field.rb +119 -98
  31. data/lib/mail/field_list.rb +60 -7
  32. data/lib/mail/fields/bcc_field.rb +34 -52
  33. data/lib/mail/fields/cc_field.rb +28 -49
  34. data/lib/mail/fields/comments_field.rb +27 -37
  35. data/lib/mail/fields/common_address_field.rb +170 -0
  36. data/lib/mail/fields/common_date_field.rb +58 -0
  37. data/lib/mail/fields/common_field.rb +77 -0
  38. data/lib/mail/fields/common_message_id_field.rb +42 -0
  39. data/lib/mail/fields/content_description_field.rb +7 -14
  40. data/lib/mail/fields/content_disposition_field.rb +13 -38
  41. data/lib/mail/fields/content_id_field.rb +24 -51
  42. data/lib/mail/fields/content_location_field.rb +11 -25
  43. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  44. data/lib/mail/fields/content_type_field.rb +50 -80
  45. data/lib/mail/fields/date_field.rb +23 -52
  46. data/lib/mail/fields/from_field.rb +28 -49
  47. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  48. data/lib/mail/fields/keywords_field.rb +18 -31
  49. data/lib/mail/fields/message_id_field.rb +25 -71
  50. data/lib/mail/fields/mime_version_field.rb +19 -30
  51. data/lib/mail/fields/named_structured_field.rb +11 -0
  52. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  53. data/lib/mail/fields/optional_field.rb +9 -7
  54. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  55. data/lib/mail/fields/received_field.rb +43 -57
  56. data/lib/mail/fields/references_field.rb +35 -49
  57. data/lib/mail/fields/reply_to_field.rb +28 -49
  58. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  59. data/lib/mail/fields/resent_cc_field.rb +28 -49
  60. data/lib/mail/fields/resent_date_field.rb +5 -30
  61. data/lib/mail/fields/resent_from_field.rb +28 -49
  62. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  63. data/lib/mail/fields/resent_sender_field.rb +27 -56
  64. data/lib/mail/fields/resent_to_field.rb +28 -49
  65. data/lib/mail/fields/return_path_field.rb +50 -54
  66. data/lib/mail/fields/sender_field.rb +34 -55
  67. data/lib/mail/fields/structured_field.rb +3 -30
  68. data/lib/mail/fields/subject_field.rb +9 -11
  69. data/lib/mail/fields/to_field.rb +28 -49
  70. data/lib/mail/fields/unstructured_field.rb +32 -47
  71. data/lib/mail/header.rb +71 -110
  72. data/lib/mail/mail.rb +2 -10
  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 +113 -117
  76. data/lib/mail/multibyte/chars.rb +23 -180
  77. data/lib/mail/multibyte/unicode.rb +10 -10
  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 +13 -16
  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 +76 -54
  85. data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -9
  86. data/lib/mail/network/delivery_methods/test_mailer.rb +8 -9
  87. data/lib/mail/network/retriever_methods/base.rb +8 -8
  88. data/lib/mail/network/retriever_methods/imap.rb +20 -7
  89. data/lib/mail/network/retriever_methods/pop3.rb +5 -3
  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 +33225 -116
  94. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  95. data/lib/mail/parsers/content_disposition_parser.rb +882 -49
  96. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  97. data/lib/mail/parsers/content_location_parser.rb +809 -23
  98. data/lib/mail/parsers/content_location_parser.rl +78 -0
  99. data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  101. data/lib/mail/parsers/content_type_parser.rb +1037 -56
  102. data/lib/mail/parsers/content_type_parser.rl +90 -0
  103. data/lib/mail/parsers/date_time_parser.rb +877 -25
  104. data/lib/mail/parsers/date_time_parser.rl +69 -0
  105. data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
  106. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  107. data/lib/mail/parsers/message_ids_parser.rb +5146 -25
  108. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  109. data/lib/mail/parsers/mime_version_parser.rb +497 -26
  110. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  111. data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
  112. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  113. data/lib/mail/parsers/received_parser.rb +8776 -43
  114. data/lib/mail/parsers/received_parser.rl +91 -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 +6 -10
  127. data/lib/mail/parts_list.rb +62 -6
  128. data/lib/mail/smtp_envelope.rb +57 -0
  129. data/lib/mail/utilities.rb +357 -74
  130. data/lib/mail/version.rb +2 -2
  131. data/lib/mail/yaml.rb +30 -0
  132. data/lib/mail.rb +5 -35
  133. metadata +111 -66
  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/core_extensions/smtp.rb +0 -25
  141. data/lib/mail/core_extensions/string/access.rb +0 -146
  142. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  143. data/lib/mail/core_extensions/string.rb +0 -21
  144. data/lib/mail/fields/common/address_container.rb +0 -17
  145. data/lib/mail/fields/common/common_address.rb +0 -136
  146. data/lib/mail/fields/common/common_date.rb +0 -36
  147. data/lib/mail/fields/common/common_field.rb +0 -61
  148. data/lib/mail/fields/common/common_message_id.rb +0 -49
  149. data/lib/mail/multibyte/exceptions.rb +0 -9
  150. data/lib/mail/parsers/ragel/common.rl +0 -185
  151. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  152. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  154. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  156. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  158. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  160. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  162. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  164. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  166. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  168. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  170. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  172. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  173. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  175. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  176. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  177. data/lib/mail/parsers/ragel.rb +0 -18
  178. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  179. 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 = subject =~ /^Re:/i ? subject : "Re: #{subject}"
282
298
  end
283
299
  if reply_to || from
284
300
  reply.to = self[reply_to ? :reply_to : :from].to_s
@@ -360,13 +376,8 @@ module Mail
360
376
  if self.message_id && other.message_id
361
377
  self.encoded == other.encoded
362
378
  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
379
+ dup.tap { |m| m.message_id = '<temp@test>' }.encoded ==
380
+ other.dup.tap { |m| m.message_id = '<temp@test>' }.encoded
370
381
  end
371
382
  end
372
383
 
@@ -388,9 +399,9 @@ module Mail
388
399
  end
389
400
 
390
401
  # Sets the envelope from for the email
391
- def set_envelope( val )
402
+ def set_envelope(val)
392
403
  @raw_envelope = val
393
- @envelope = Mail::Envelope.new( val )
404
+ @envelope = Mail::Envelope.parse(val) rescue nil
394
405
  end
395
406
 
396
407
  # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
@@ -1193,8 +1204,8 @@ module Mail
1193
1204
  def default( sym, val = nil )
1194
1205
  if val
1195
1206
  header[sym] = val
1196
- else
1197
- header[sym].default if header[sym]
1207
+ elsif field = header[sym]
1208
+ field.default
1198
1209
  end
1199
1210
  end
1200
1211
 
@@ -1240,14 +1251,13 @@ module Mail
1240
1251
  def body(value = nil)
1241
1252
  if value
1242
1253
  self.body = value
1243
- # add_encoding_to_body
1244
1254
  else
1245
1255
  process_body_raw if @body_raw
1246
1256
  @body
1247
1257
  end
1248
1258
  end
1249
1259
 
1250
- def body_encoding(value)
1260
+ def body_encoding(value = nil)
1251
1261
  if value.nil?
1252
1262
  body.encoding
1253
1263
  else
@@ -1256,7 +1266,7 @@ module Mail
1256
1266
  end
1257
1267
 
1258
1268
  def body_encoding=(value)
1259
- body.encoding = value
1269
+ body.encoding = value
1260
1270
  end
1261
1271
 
1262
1272
  # Returns the list of addresses this message should be sent to by
@@ -1322,7 +1332,7 @@ module Mail
1322
1332
  # mail['foo'] = '1234'
1323
1333
  # mail['foo'].to_s #=> '1234'
1324
1334
  def [](name)
1325
- header[underscoreize(name)]
1335
+ header[Utilities.underscoreize(name)]
1326
1336
  end
1327
1337
 
1328
1338
  # Method Missing in this implementation allows you to set any of the
@@ -1368,7 +1378,7 @@ module Mail
1368
1378
  #:nodoc:
1369
1379
  # Only take the structured fields, as we could take _anything_ really
1370
1380
  # as it could become an optional field... "but therin lies the dark side"
1371
- field_name = underscoreize(name).chomp("=")
1381
+ field_name = Utilities.underscoreize(name).chomp("=")
1372
1382
  if Mail::Field::KNOWN_FIELDS.include?(field_name)
1373
1383
  if args.empty?
1374
1384
  header[field_name]
@@ -1419,11 +1429,6 @@ module Mail
1419
1429
  header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1420
1430
  end
1421
1431
 
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
1432
  # Creates a new empty Message-ID field and inserts it in the correct order
1428
1433
  # into the Header. The MessageIdField object will automatically generate
1429
1434
  # a unique message ID if you try and encode it or output it to_s without
@@ -1468,35 +1473,19 @@ module Mail
1468
1473
  if !body.empty?
1469
1474
  # Only give a warning if this isn't an attachment, has non US-ASCII and the user
1470
1475
  # has not specified an encoding explicitly.
1471
- if @defaulted_charset && body.raw_source.not_ascii_only? && !self.attachment?
1476
+ if @defaulted_charset && !body.raw_source.ascii_only? && !self.attachment?
1472
1477
  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)
1478
+ warn(warning)
1479
+ end
1480
+ if @charset
1481
+ header[:content_type].parameters['charset'] = @charset
1474
1482
  end
1475
- header[:content_type].parameters['charset'] = @charset
1476
1483
  end
1477
1484
  end
1478
1485
 
1479
1486
  # Adds a content transfer encoding
1480
- #
1481
- # Otherwise raises a warning
1482
1487
  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
1488
+ header[:content_transfer_encoding] ||= body.default_encoding
1500
1489
  end
1501
1490
 
1502
1491
  # Returns the MIME media type of part we are on, this is taken from the content-type header
@@ -1504,15 +1493,10 @@ module Mail
1504
1493
  has_content_type? ? header[:content_type].string : nil rescue nil
1505
1494
  end
1506
1495
 
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
1496
  # Returns the character set defined in the content type field
1513
1497
  def charset
1514
1498
  if @header
1515
- has_content_type? ? content_type_parameters['charset'] : @charset
1499
+ has_content_type? && !multipart? ? content_type_parameters['charset'] : @charset
1516
1500
  else
1517
1501
  @charset
1518
1502
  end
@@ -1535,12 +1519,6 @@ module Mail
1535
1519
  has_content_type? ? header[:content_type].sub_type : nil rescue nil
1536
1520
  end
1537
1521
 
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
1522
  # Returns the content type parameters
1545
1523
  def content_type_parameters
1546
1524
  has_content_type? ? header[:content_type].parameters : nil rescue nil
@@ -1563,7 +1541,14 @@ module Mail
1563
1541
 
1564
1542
  # returns the part in a multipart/report email that has the content-type delivery-status
1565
1543
  def delivery_status_part
1566
- @delivery_stats_part ||= parts.select { |p| p.delivery_status_report_part? }.first
1544
+ unless defined? @delivery_status_part
1545
+ @delivery_status_part =
1546
+ if delivery_status_report?
1547
+ parts.detect(&:delivery_status_report_part?)
1548
+ end
1549
+ end
1550
+
1551
+ @delivery_status_part
1567
1552
  end
1568
1553
 
1569
1554
  def bounced?
@@ -1778,19 +1763,26 @@ module Mail
1778
1763
  else
1779
1764
  basename = values[:filename]
1780
1765
  filedata = values
1781
- unless filedata[:content]
1782
- filedata = values.merge(:content=>File.open(values[:filename], 'rb') { |f| f.read })
1783
- end
1784
1766
  end
1785
1767
  self.attachments[basename] = filedata
1786
1768
  end
1787
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
+
1788
1773
  def convert_to_multipart
1789
- text = body.decoded
1790
- self.body = ''
1791
- text_part = Mail::Part.new({:content_type => 'text/plain;',
1792
- :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
1793
1783
  text_part.charset = charset unless @defaulted_charset
1784
+
1785
+ self.body = ''
1794
1786
  self.body << text_part
1795
1787
  end
1796
1788
 
@@ -1798,7 +1790,6 @@ module Mail
1798
1790
  # ready to send
1799
1791
  def ready_to_send!
1800
1792
  identify_and_set_transfer_encoding
1801
- parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
1802
1793
  parts.each do |part|
1803
1794
  part.transport_encoding = transport_encoding
1804
1795
  part.ready_to_send!
@@ -1806,11 +1797,6 @@ module Mail
1806
1797
  add_required_fields
1807
1798
  end
1808
1799
 
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
1800
  # Outputs an encoded string representation of the mail message including
1815
1801
  # all headers, attachments, etc. This is an encoded email in US-ASCII,
1816
1802
  # so it is able to be directly sent to an email server.
@@ -1823,16 +1809,13 @@ module Mail
1823
1809
  end
1824
1810
 
1825
1811
  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
1812
+ if has_attachments?
1813
+ parts.delete_attachments
1834
1814
 
1835
- @body = Mail::Body.new(body_raw)
1815
+ reencoded = parts.empty? ? '' : body.encoded(content_transfer_encoding)
1816
+ @body = nil # So the new parts won't be added to the existing body
1817
+ self.body = reencoded
1818
+ end
1836
1819
 
1837
1820
  self
1838
1821
  end
@@ -1858,7 +1841,7 @@ module Mail
1858
1841
  end
1859
1842
 
1860
1843
  def self.from_yaml(str)
1861
- hash = YAML.load(str)
1844
+ hash = Mail::YAML.load(str)
1862
1845
  m = self.new(:headers => hash['headers'])
1863
1846
  hash.delete('headers')
1864
1847
  hash.each do |k,v|
@@ -1891,6 +1874,15 @@ module Mail
1891
1874
  "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1892
1875
  end
1893
1876
 
1877
+ def inspect_structure
1878
+ inspect +
1879
+ if self.multipart?
1880
+ "\n" + parts.inspect_structure
1881
+ else
1882
+ ''
1883
+ end
1884
+ end
1885
+
1894
1886
  def decoded
1895
1887
  case
1896
1888
  when self.text?
@@ -1975,7 +1967,7 @@ module Mail
1975
1967
 
1976
1968
  private
1977
1969
 
1978
- HEADER_SEPARATOR = /#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m
1970
+ HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
1979
1971
 
1980
1972
  # 2.1. General Description
1981
1973
  # A message consists of header fields (collectively called "the header
@@ -1984,9 +1976,6 @@ module Mail
1984
1976
  # this standard. The body is simply a sequence of characters that
1985
1977
  # follows the header and is separated from the header by an empty line
1986
1978
  # (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
1979
  def parse_message
1991
1980
  header_part, body_part = raw_source.lstrip.split(HEADER_SEPARATOR, 2)
1992
1981
  self.header = header_part
@@ -1994,8 +1983,7 @@ module Mail
1994
1983
  end
1995
1984
 
1996
1985
  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)
1986
+ @raw_source = value
1999
1987
  end
2000
1988
 
2001
1989
  # see comments to body=. We take data and process it lazily
@@ -2007,11 +1995,9 @@ module Mail
2007
1995
  @body_raw = nil
2008
1996
  add_encoding_to_body
2009
1997
  when @body && @body.multipart?
2010
- @body << Mail::Part.new(value)
2011
- add_encoding_to_body
1998
+ self.text_part = value
2012
1999
  else
2013
2000
  @body_raw = value
2014
- # process_body_raw
2015
2001
  end
2016
2002
  end
2017
2003
 
@@ -2026,7 +2012,7 @@ module Mail
2026
2012
 
2027
2013
  def set_envelope_header
2028
2014
  raw_string = raw_source.to_s
2029
- if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
2015
+ if match_data = raw_string.match(/\AFrom\s+([^:\s]#{Constants::TEXT}*)#{Constants::LAX_CRLF}/m)
2030
2016
  set_envelope(match_data[1])
2031
2017
  self.raw_source = raw_string.sub(match_data[0], "")
2032
2018
  end
@@ -2036,6 +2022,13 @@ module Mail
2036
2022
  body.split!(boundary)
2037
2023
  end
2038
2024
 
2025
+ def allowed_encodings
2026
+ case mime_type
2027
+ when 'message/rfc822'
2028
+ [Encodings::SevenBit, Encodings::EightBit, Encodings::Binary]
2029
+ end
2030
+ end
2031
+
2039
2032
  def add_encoding_to_body
2040
2033
  if has_content_transfer_encoding?
2041
2034
  @body.encoding = content_transfer_encoding
@@ -2043,11 +2036,13 @@ module Mail
2043
2036
  end
2044
2037
 
2045
2038
  def identify_and_set_transfer_encoding
2046
- if body && body.multipart?
2047
- self.content_transfer_encoding = @transport_encoding
2039
+ if body
2040
+ if body.multipart?
2041
+ self.content_transfer_encoding = @transport_encoding
2048
2042
  else
2049
- self.content_transfer_encoding = body.get_best_encoding(@transport_encoding)
2043
+ self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
2050
2044
  end
2045
+ end
2051
2046
  end
2052
2047
 
2053
2048
  def add_required_fields
@@ -2066,15 +2061,17 @@ module Mail
2066
2061
 
2067
2062
  def add_multipart_alternate_header
2068
2063
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2069
- header['content_type'].parameters[:charset] = @charset
2070
2064
  body.boundary = boundary
2071
2065
  end
2072
2066
 
2073
2067
  def add_boundary
2074
2068
  unless body.boundary && boundary
2075
- 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
2076
2074
  header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2077
- header['content_type'].parameters[:charset] = @charset
2078
2075
  body.boundary = boundary
2079
2076
  end
2080
2077
  end
@@ -2082,7 +2079,6 @@ module Mail
2082
2079
  def add_multipart_mixed_header
2083
2080
  unless header['content-type']
2084
2081
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2085
- header['content_type'].parameters[:charset] = @charset
2086
2082
  body.boundary = boundary
2087
2083
  end
2088
2084
  end
@@ -2099,7 +2095,7 @@ module Mail
2099
2095
  body_content = nil
2100
2096
 
2101
2097
  passed_in_options.each_pair do |k,v|
2102
- k = underscoreize(k).to_sym if k.class == String
2098
+ k = Utilities.underscoreize(k).to_sym if k.class == String
2103
2099
  if k == :headers
2104
2100
  self.headers(v)
2105
2101
  elsif k == :body
@@ -2130,10 +2126,10 @@ module Mail
2130
2126
  content_disp_name = header[:content_disposition].filename rescue nil
2131
2127
  content_loc_name = header[:content_location].location rescue nil
2132
2128
  case
2133
- when content_type && content_type_name
2134
- filename = content_type_name
2135
2129
  when content_disposition && content_disp_name
2136
2130
  filename = content_disp_name
2131
+ when content_type && content_type_name
2132
+ filename = content_type_name
2137
2133
  when content_location && content_loc_name
2138
2134
  filename = content_loc_name
2139
2135
  else