mail 2.5.5 → 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 (191) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +170 -108
  4. data/lib/mail/attachments_list.rb +13 -10
  5. data/lib/mail/body.rb +105 -91
  6. data/lib/mail/check_delivery_params.rb +30 -22
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +79 -0
  9. data/lib/mail/elements/address.rb +118 -174
  10. data/lib/mail/elements/address_list.rb +16 -56
  11. data/lib/mail/elements/content_disposition_element.rb +12 -22
  12. data/lib/mail/elements/content_location_element.rb +9 -17
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +8 -19
  14. data/lib/mail/elements/content_type_element.rb +20 -30
  15. data/lib/mail/elements/date_time_element.rb +10 -21
  16. data/lib/mail/elements/envelope_from_element.rb +23 -31
  17. data/lib/mail/elements/message_ids_element.rb +22 -20
  18. data/lib/mail/elements/mime_version_element.rb +10 -21
  19. data/lib/mail/elements/phrase_list.rb +13 -15
  20. data/lib/mail/elements/received_element.rb +26 -21
  21. data/lib/mail/elements.rb +1 -0
  22. data/lib/mail/encodings/7bit.rb +10 -14
  23. data/lib/mail/encodings/8bit.rb +5 -18
  24. data/lib/mail/encodings/base64.rb +15 -10
  25. data/lib/mail/encodings/binary.rb +4 -22
  26. data/lib/mail/encodings/identity.rb +24 -0
  27. data/lib/mail/encodings/quoted_printable.rb +13 -7
  28. data/lib/mail/encodings/transfer_encoding.rb +47 -28
  29. data/lib/mail/encodings/unix_to_unix.rb +20 -0
  30. data/lib/mail/encodings.rb +102 -93
  31. data/lib/mail/envelope.rb +12 -19
  32. data/lib/mail/field.rb +143 -71
  33. data/lib/mail/field_list.rb +73 -19
  34. data/lib/mail/fields/bcc_field.rb +42 -48
  35. data/lib/mail/fields/cc_field.rb +29 -50
  36. data/lib/mail/fields/comments_field.rb +28 -37
  37. data/lib/mail/fields/common_address_field.rb +170 -0
  38. data/lib/mail/fields/common_date_field.rb +58 -0
  39. data/lib/mail/fields/common_field.rb +77 -0
  40. data/lib/mail/fields/common_message_id_field.rb +42 -0
  41. data/lib/mail/fields/content_description_field.rb +8 -14
  42. data/lib/mail/fields/content_disposition_field.rb +20 -44
  43. data/lib/mail/fields/content_id_field.rb +25 -51
  44. data/lib/mail/fields/content_location_field.rb +12 -25
  45. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -36
  46. data/lib/mail/fields/content_type_field.rb +51 -80
  47. data/lib/mail/fields/date_field.rb +24 -52
  48. data/lib/mail/fields/from_field.rb +29 -50
  49. data/lib/mail/fields/in_reply_to_field.rb +39 -49
  50. data/lib/mail/fields/keywords_field.rb +19 -32
  51. data/lib/mail/fields/message_id_field.rb +26 -71
  52. data/lib/mail/fields/mime_version_field.rb +20 -30
  53. data/lib/mail/fields/named_structured_field.rb +11 -0
  54. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  55. data/lib/mail/fields/optional_field.rb +10 -7
  56. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
  57. data/lib/mail/fields/received_field.rb +44 -57
  58. data/lib/mail/fields/references_field.rb +36 -49
  59. data/lib/mail/fields/reply_to_field.rb +29 -50
  60. data/lib/mail/fields/resent_bcc_field.rb +29 -50
  61. data/lib/mail/fields/resent_cc_field.rb +29 -50
  62. data/lib/mail/fields/resent_date_field.rb +6 -30
  63. data/lib/mail/fields/resent_from_field.rb +29 -50
  64. data/lib/mail/fields/resent_message_id_field.rb +6 -29
  65. data/lib/mail/fields/resent_sender_field.rb +28 -57
  66. data/lib/mail/fields/resent_to_field.rb +29 -50
  67. data/lib/mail/fields/return_path_field.rb +51 -55
  68. data/lib/mail/fields/sender_field.rb +35 -56
  69. data/lib/mail/fields/structured_field.rb +4 -30
  70. data/lib/mail/fields/subject_field.rb +10 -11
  71. data/lib/mail/fields/to_field.rb +29 -50
  72. data/lib/mail/fields/unstructured_field.rb +43 -51
  73. data/lib/mail/fields.rb +1 -0
  74. data/lib/mail/header.rb +78 -129
  75. data/lib/mail/indifferent_hash.rb +1 -0
  76. data/lib/mail/mail.rb +18 -11
  77. data/lib/mail/matchers/attachment_matchers.rb +44 -0
  78. data/lib/mail/matchers/has_sent_mail.rb +81 -4
  79. data/lib/mail/message.rb +142 -139
  80. data/lib/mail/multibyte/chars.rb +24 -180
  81. data/lib/mail/multibyte/unicode.rb +32 -27
  82. data/lib/mail/multibyte/utils.rb +27 -43
  83. data/lib/mail/multibyte.rb +56 -16
  84. data/lib/mail/network/delivery_methods/exim.rb +6 -4
  85. data/lib/mail/network/delivery_methods/file_delivery.rb +12 -10
  86. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  87. data/lib/mail/network/delivery_methods/sendmail.rb +63 -21
  88. data/lib/mail/network/delivery_methods/smtp.rb +76 -50
  89. data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -4
  90. data/lib/mail/network/delivery_methods/test_mailer.rb +5 -2
  91. data/lib/mail/network/retriever_methods/base.rb +9 -8
  92. data/lib/mail/network/retriever_methods/imap.rb +37 -18
  93. data/lib/mail/network/retriever_methods/pop3.rb +6 -3
  94. data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
  95. data/lib/mail/network.rb +2 -0
  96. data/lib/mail/parser_tools.rb +15 -0
  97. data/lib/mail/parsers/address_lists_parser.rb +33242 -0
  98. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  99. data/lib/mail/parsers/content_disposition_parser.rb +901 -0
  100. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  101. data/lib/mail/parsers/content_location_parser.rb +822 -0
  102. data/lib/mail/parsers/content_location_parser.rl +78 -0
  103. data/lib/mail/parsers/content_transfer_encoding_parser.rb +522 -0
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  105. data/lib/mail/parsers/content_type_parser.rb +1048 -0
  106. data/lib/mail/parsers/content_type_parser.rl +90 -0
  107. data/lib/mail/parsers/date_time_parser.rb +891 -0
  108. data/lib/mail/parsers/date_time_parser.rl +69 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +3675 -0
  110. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  111. data/lib/mail/parsers/message_ids_parser.rb +5161 -0
  112. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +513 -0
  114. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  115. data/lib/mail/parsers/phrase_lists_parser.rb +884 -0
  116. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  117. data/lib/mail/parsers/received_parser.rb +8782 -0
  118. data/lib/mail/parsers/received_parser.rl +91 -0
  119. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  120. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  121. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  122. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  123. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  124. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  125. data/lib/mail/parsers/rfc5322.rl +74 -0
  126. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  127. data/lib/mail/parsers/rfc5322_date_time.rl +37 -0
  128. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  129. data/lib/mail/parsers.rb +13 -0
  130. data/lib/mail/part.rb +11 -12
  131. data/lib/mail/parts_list.rb +90 -14
  132. data/lib/mail/smtp_envelope.rb +57 -0
  133. data/lib/mail/utilities.rb +415 -76
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +8 -15
  136. data/lib/mail/yaml.rb +30 -0
  137. data/lib/mail.rb +9 -32
  138. metadata +127 -79
  139. data/CHANGELOG.rdoc +0 -742
  140. data/CONTRIBUTING.md +0 -45
  141. data/Dependencies.txt +0 -3
  142. data/Gemfile +0 -32
  143. data/Rakefile +0 -21
  144. data/TODO.rdoc +0 -9
  145. data/lib/VERSION +0 -4
  146. data/lib/load_parsers.rb +0 -35
  147. data/lib/mail/core_extensions/nil.rb +0 -19
  148. data/lib/mail/core_extensions/object.rb +0 -13
  149. data/lib/mail/core_extensions/smtp.rb +0 -24
  150. data/lib/mail/core_extensions/string/access.rb +0 -145
  151. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  152. data/lib/mail/core_extensions/string.rb +0 -33
  153. data/lib/mail/fields/common/address_container.rb +0 -16
  154. data/lib/mail/fields/common/common_address.rb +0 -140
  155. data/lib/mail/fields/common/common_date.rb +0 -42
  156. data/lib/mail/fields/common/common_field.rb +0 -57
  157. data/lib/mail/fields/common/common_message_id.rb +0 -48
  158. data/lib/mail/multibyte/exceptions.rb +0 -8
  159. data/lib/mail/parsers/address_lists.rb +0 -64
  160. data/lib/mail/parsers/address_lists.treetop +0 -19
  161. data/lib/mail/parsers/content_disposition.rb +0 -535
  162. data/lib/mail/parsers/content_disposition.treetop +0 -46
  163. data/lib/mail/parsers/content_location.rb +0 -139
  164. data/lib/mail/parsers/content_location.treetop +0 -20
  165. data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
  166. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
  167. data/lib/mail/parsers/content_type.rb +0 -971
  168. data/lib/mail/parsers/content_type.treetop +0 -68
  169. data/lib/mail/parsers/date_time.rb +0 -114
  170. data/lib/mail/parsers/date_time.treetop +0 -11
  171. data/lib/mail/parsers/envelope_from.rb +0 -194
  172. data/lib/mail/parsers/envelope_from.treetop +0 -32
  173. data/lib/mail/parsers/message_ids.rb +0 -45
  174. data/lib/mail/parsers/message_ids.treetop +0 -15
  175. data/lib/mail/parsers/mime_version.rb +0 -144
  176. data/lib/mail/parsers/mime_version.treetop +0 -19
  177. data/lib/mail/parsers/phrase_lists.rb +0 -45
  178. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  179. data/lib/mail/parsers/received.rb +0 -71
  180. data/lib/mail/parsers/received.treetop +0 -11
  181. data/lib/mail/parsers/rfc2045.rb +0 -421
  182. data/lib/mail/parsers/rfc2045.treetop +0 -35
  183. data/lib/mail/parsers/rfc2822.rb +0 -5397
  184. data/lib/mail/parsers/rfc2822.treetop +0 -408
  185. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  186. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  187. data/lib/mail/patterns.rb +0 -35
  188. data/lib/mail/version_specific/ruby_1_8.rb +0 -119
  189. data/lib/mail/version_specific/ruby_1_9.rb +0 -147
  190. data/lib/tasks/corpus.rake +0 -125
  191. data/lib/tasks/treetop.rake +0 -10
data/lib/mail/message.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # encoding: utf-8
2
- require "yaml"
2
+ # frozen_string_literal: true
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+ require 'mail/yaml'
3
6
 
4
7
  module Mail
5
8
  # The Message class provides a single point of access to all things to do with an
@@ -45,25 +48,29 @@ module Mail
45
48
  # follows the header and is separated from the header by an empty line
46
49
  # (i.e., a line with nothing preceding the CRLF).
47
50
  class Message
48
-
49
- include Patterns
50
- include Utilities
51
-
52
51
  # ==Making an email
53
52
  #
54
53
  # You can make an new mail object via a block, passing a string, file or direct assignment.
55
54
  #
56
55
  # ===Making an email via a block
57
56
  #
58
- # mail = Mail.new do
59
- # from 'mikel@test.lindsaar.net'
60
- # to 'you@test.lindsaar.net'
61
- # subject 'This is a test email'
62
- # 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')
63
62
  # end
64
63
  #
65
64
  # mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
66
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
+ #
67
74
  # ===Making an email via passing a string
68
75
  #
69
76
  # mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
@@ -105,7 +112,7 @@ module Mail
105
112
  @html_part = nil
106
113
  @errors = nil
107
114
  @header = nil
108
- @charset = 'UTF-8'
115
+ @charset = self.class.default_charset
109
116
  @defaulted_charset = true
110
117
 
111
118
  @smtp_envelope_from = nil
@@ -128,8 +135,23 @@ module Mail
128
135
  init_with_string(args.flatten[0].to_s)
129
136
  end
130
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
131
149
  if block_given?
132
- instance_eval(&block)
150
+ if block.arity.zero?
151
+ instance_eval(&block)
152
+ else
153
+ yield self
154
+ end
133
155
  end
134
156
 
135
157
  self
@@ -207,10 +229,9 @@ module Mail
207
229
  # define a delivery_handler
208
230
  attr_accessor :raise_delivery_errors
209
231
 
210
- def register_for_delivery_notification(observer)
211
- STDERR.puts("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
212
- Mail.register_observer(observer)
213
- end
232
+ def self.default_charset; @@default_charset; end
233
+ def self.default_charset=(charset); @@default_charset = charset; end
234
+ self.default_charset = 'UTF-8'
214
235
 
215
236
  def inform_observers
216
237
  Mail.inform_observers(self)
@@ -220,7 +241,7 @@ module Mail
220
241
  Mail.inform_interceptors(self)
221
242
  end
222
243
 
223
- # Delivers an mail object.
244
+ # Delivers a mail object.
224
245
  #
225
246
  # Examples:
226
247
  #
@@ -240,7 +261,7 @@ module Mail
240
261
  # This method bypasses checking perform_deliveries and raise_delivery_errors,
241
262
  # so use with caution.
242
263
  #
243
- # It still however fires off the intercepters and calls the observers callbacks if they are defined.
264
+ # It still however fires off the interceptors and calls the observers callbacks if they are defined.
244
265
  #
245
266
  # Returns self
246
267
  def deliver!
@@ -273,7 +294,7 @@ module Mail
273
294
  reply.references ||= bracketed_message_id
274
295
  end
275
296
  if subject
276
- reply.subject = subject =~ /^Re:/i ? subject : "RE: #{subject}"
297
+ reply.subject = subject =~ /^Re:/i ? subject : "Re: #{subject}"
277
298
  end
278
299
  if reply_to || from
279
300
  reply.to = self[reply_to ? :reply_to : :from].to_s
@@ -353,17 +374,18 @@ module Mail
353
374
  return false unless other.respond_to?(:encoded)
354
375
 
355
376
  if self.message_id && other.message_id
356
- result = (self.encoded == other.encoded)
377
+ self.encoded == other.encoded
357
378
  else
358
- self_message_id, other_message_id = self.message_id, other.message_id
359
- self.message_id, other.message_id = '<temp@test>', '<temp@test>'
360
- result = self.encoded == other.encoded
361
- self.message_id = "<#{self_message_id}>" if self_message_id
362
- other.message_id = "<#{other_message_id}>" if other_message_id
363
- result
379
+ dup.tap { |m| m.message_id = '<temp@test>' }.encoded ==
380
+ other.dup.tap { |m| m.message_id = '<temp@test>' }.encoded
364
381
  end
365
382
  end
366
383
 
384
+ def initialize_copy(original)
385
+ super
386
+ @header = @header.dup
387
+ end
388
+
367
389
  # Provides access to the raw source of the message as it was when it
368
390
  # was instantiated. This is set at initialization and so is untouched
369
391
  # by the parsers or decoder / encoders
@@ -377,9 +399,9 @@ module Mail
377
399
  end
378
400
 
379
401
  # Sets the envelope from for the email
380
- def set_envelope( val )
402
+ def set_envelope(val)
381
403
  @raw_envelope = val
382
- @envelope = Mail::Envelope.new( val )
404
+ @envelope = Mail::Envelope.parse(val) rescue nil
383
405
  end
384
406
 
385
407
  # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
@@ -1182,8 +1204,8 @@ module Mail
1182
1204
  def default( sym, val = nil )
1183
1205
  if val
1184
1206
  header[sym] = val
1185
- else
1186
- header[sym].default if header[sym]
1207
+ elsif field = header[sym]
1208
+ field.default
1187
1209
  end
1188
1210
  end
1189
1211
 
@@ -1229,14 +1251,13 @@ module Mail
1229
1251
  def body(value = nil)
1230
1252
  if value
1231
1253
  self.body = value
1232
- # add_encoding_to_body
1233
1254
  else
1234
1255
  process_body_raw if @body_raw
1235
1256
  @body
1236
1257
  end
1237
1258
  end
1238
1259
 
1239
- def body_encoding(value)
1260
+ def body_encoding(value = nil)
1240
1261
  if value.nil?
1241
1262
  body.encoding
1242
1263
  else
@@ -1245,7 +1266,7 @@ module Mail
1245
1266
  end
1246
1267
 
1247
1268
  def body_encoding=(value)
1248
- body.encoding = value
1269
+ body.encoding = value
1249
1270
  end
1250
1271
 
1251
1272
  # Returns the list of addresses this message should be sent to by
@@ -1311,7 +1332,7 @@ module Mail
1311
1332
  # mail['foo'] = '1234'
1312
1333
  # mail['foo'].to_s #=> '1234'
1313
1334
  def [](name)
1314
- header[underscoreize(name)]
1335
+ header[Utilities.underscoreize(name)]
1315
1336
  end
1316
1337
 
1317
1338
  # Method Missing in this implementation allows you to set any of the
@@ -1357,7 +1378,7 @@ module Mail
1357
1378
  #:nodoc:
1358
1379
  # Only take the structured fields, as we could take _anything_ really
1359
1380
  # as it could become an optional field... "but therin lies the dark side"
1360
- field_name = underscoreize(name).chomp("=")
1381
+ field_name = Utilities.underscoreize(name).chomp("=")
1361
1382
  if Mail::Field::KNOWN_FIELDS.include?(field_name)
1362
1383
  if args.empty?
1363
1384
  header[field_name]
@@ -1388,7 +1409,7 @@ module Mail
1388
1409
  header.has_date?
1389
1410
  end
1390
1411
 
1391
- # Returns true if the message has a Date field, the field may or may
1412
+ # Returns true if the message has a Mime-Version field, the field may or may
1392
1413
  # not have a value, but the field exists or not.
1393
1414
  def has_mime_version?
1394
1415
  header.has_mime_version?
@@ -1405,12 +1426,7 @@ module Mail
1405
1426
  end
1406
1427
 
1407
1428
  def has_content_transfer_encoding?
1408
- header[:content_transfer_encoding] && header[:content_transfer_encoding].errors.blank?
1409
- end
1410
-
1411
- def has_transfer_encoding? # :nodoc:
1412
- STDERR.puts(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
1413
- has_content_transfer_encoding?
1429
+ header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1414
1430
  end
1415
1431
 
1416
1432
  # Creates a new empty Message-ID field and inserts it in the correct order
@@ -1457,35 +1473,19 @@ module Mail
1457
1473
  if !body.empty?
1458
1474
  # Only give a warning if this isn't an attachment, has non US-ASCII and the user
1459
1475
  # has not specified an encoding explicitly.
1460
- if @defaulted_charset && body.raw_source.not_ascii_only? && !self.attachment?
1476
+ if @defaulted_charset && !body.raw_source.ascii_only? && !self.attachment?
1461
1477
  warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1462
- STDERR.puts(warning)
1478
+ warn(warning)
1479
+ end
1480
+ if @charset
1481
+ header[:content_type].parameters['charset'] = @charset
1463
1482
  end
1464
- header[:content_type].parameters['charset'] = @charset
1465
1483
  end
1466
1484
  end
1467
1485
 
1468
1486
  # Adds a content transfer encoding
1469
- #
1470
- # Otherwise raises a warning
1471
1487
  def add_content_transfer_encoding
1472
- if body.only_us_ascii?
1473
- header[:content_transfer_encoding] = '7bit'
1474
- else
1475
- warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
1476
- STDERR.puts(warning)
1477
- header[:content_transfer_encoding] = '8bit'
1478
- end
1479
- end
1480
-
1481
- def add_transfer_encoding # :nodoc:
1482
- STDERR.puts(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
1483
- add_content_transfer_encoding
1484
- end
1485
-
1486
- def transfer_encoding # :nodoc:
1487
- STDERR.puts(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
1488
- content_transfer_encoding
1488
+ header[:content_transfer_encoding] ||= body.default_encoding
1489
1489
  end
1490
1490
 
1491
1491
  # Returns the MIME media type of part we are on, this is taken from the content-type header
@@ -1493,15 +1493,10 @@ module Mail
1493
1493
  has_content_type? ? header[:content_type].string : nil rescue nil
1494
1494
  end
1495
1495
 
1496
- def message_content_type
1497
- STDERR.puts(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
1498
- mime_type
1499
- end
1500
-
1501
1496
  # Returns the character set defined in the content type field
1502
1497
  def charset
1503
1498
  if @header
1504
- has_content_type? ? content_type_parameters['charset'] : @charset
1499
+ has_content_type? && !multipart? ? content_type_parameters['charset'] : @charset
1505
1500
  else
1506
1501
  @charset
1507
1502
  end
@@ -1524,12 +1519,6 @@ module Mail
1524
1519
  has_content_type? ? header[:content_type].sub_type : nil rescue nil
1525
1520
  end
1526
1521
 
1527
- # Returns the content type parameters
1528
- def mime_parameters
1529
- STDERR.puts(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
1530
- content_type_parameters
1531
- end
1532
-
1533
1522
  # Returns the content type parameters
1534
1523
  def content_type_parameters
1535
1524
  has_content_type? ? header[:content_type].parameters : nil rescue nil
@@ -1552,7 +1541,14 @@ module Mail
1552
1541
 
1553
1542
  # returns the part in a multipart/report email that has the content-type delivery-status
1554
1543
  def delivery_status_part
1555
- @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
1556
1552
  end
1557
1553
 
1558
1554
  def bounced?
@@ -1594,7 +1590,7 @@ module Mail
1594
1590
  end
1595
1591
 
1596
1592
  # Returns an AttachmentsList object, which holds all of the attachments in
1597
- # the receiver object (either the entier email or a part within) and all
1593
+ # the receiver object (either the entire email or a part within) and all
1598
1594
  # of its descendants.
1599
1595
  #
1600
1596
  # It also allows you to add attachments to the mail object directly, like so:
@@ -1659,6 +1655,8 @@ module Mail
1659
1655
  def html_part=(msg)
1660
1656
  # Assign the html part and set multipart/alternative if there's a text part.
1661
1657
  if msg
1658
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1659
+
1662
1660
  @html_part = msg
1663
1661
  @html_part.content_type = 'text/html' unless @html_part.has_content_type?
1664
1662
  add_multipart_alternate_header if text_part
@@ -1681,6 +1679,8 @@ module Mail
1681
1679
  def text_part=(msg)
1682
1680
  # Assign the text part and set multipart/alternative if there's an html part.
1683
1681
  if msg
1682
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1683
+
1684
1684
  @text_part = msg
1685
1685
  @text_part.content_type = 'text/plain' unless @text_part.has_content_type?
1686
1686
  add_multipart_alternate_header if html_part
@@ -1699,7 +1699,7 @@ module Mail
1699
1699
 
1700
1700
  # Adds a part to the parts list or creates the part list
1701
1701
  def add_part(part)
1702
- if !body.multipart? && !self.body.decoded.blank?
1702
+ if !body.multipart? && !Utilities.blank?(self.body.decoded)
1703
1703
  @text_part = Mail::Part.new('Content-Type: text/plain;')
1704
1704
  @text_part.body = body.decoded
1705
1705
  self.body << @text_part
@@ -1755,24 +1755,34 @@ module Mail
1755
1755
  #
1756
1756
  # See also #attachments
1757
1757
  def add_file(values)
1758
- convert_to_multipart unless self.multipart? || self.body.decoded.blank?
1758
+ convert_to_multipart unless self.multipart? || Utilities.blank?(self.body.decoded)
1759
1759
  add_multipart_mixed_header
1760
1760
  if values.is_a?(String)
1761
1761
  basename = File.basename(values)
1762
1762
  filedata = File.open(values, 'rb') { |f| f.read }
1763
1763
  else
1764
1764
  basename = values[:filename]
1765
- filedata = values[:content] || File.open(values[:filename], 'rb') { |f| f.read }
1765
+ filedata = values
1766
1766
  end
1767
1767
  self.attachments[basename] = filedata
1768
1768
  end
1769
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
+
1770
1773
  def convert_to_multipart
1771
- text = body.decoded
1772
- self.body = ''
1773
- text_part = Mail::Part.new({:content_type => 'text/plain;',
1774
- :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
1775
1783
  text_part.charset = charset unless @defaulted_charset
1784
+
1785
+ self.body = ''
1776
1786
  self.body << text_part
1777
1787
  end
1778
1788
 
@@ -1780,7 +1790,6 @@ module Mail
1780
1790
  # ready to send
1781
1791
  def ready_to_send!
1782
1792
  identify_and_set_transfer_encoding
1783
- parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
1784
1793
  parts.each do |part|
1785
1794
  part.transport_encoding = transport_encoding
1786
1795
  part.ready_to_send!
@@ -1788,11 +1797,6 @@ module Mail
1788
1797
  add_required_fields
1789
1798
  end
1790
1799
 
1791
- def encode!
1792
- STDERR.puts("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
1793
- ready_to_send!
1794
- end
1795
-
1796
1800
  # Outputs an encoded string representation of the mail message including
1797
1801
  # all headers, attachments, etc. This is an encoded email in US-ASCII,
1798
1802
  # so it is able to be directly sent to an email server.
@@ -1805,16 +1809,13 @@ module Mail
1805
1809
  end
1806
1810
 
1807
1811
  def without_attachments!
1808
- return self unless has_attachments?
1812
+ if has_attachments?
1813
+ parts.delete_attachments
1809
1814
 
1810
- parts.delete_if { |p| p.attachment? }
1811
- body_raw = if parts.empty?
1812
- ''
1813
- else
1814
- body.encoded
1815
- end
1816
-
1817
- @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
1818
1819
 
1819
1820
  self
1820
1821
  end
@@ -1840,14 +1841,14 @@ module Mail
1840
1841
  end
1841
1842
 
1842
1843
  def self.from_yaml(str)
1843
- hash = YAML.load(str)
1844
+ hash = Mail::YAML.load(str)
1844
1845
  m = self.new(:headers => hash['headers'])
1845
1846
  hash.delete('headers')
1846
1847
  hash.each do |k,v|
1847
1848
  case
1848
1849
  when k == 'delivery_handler'
1849
1850
  begin
1850
- m.delivery_handler = Object.const_get(v) unless v.blank?
1851
+ m.delivery_handler = Object.const_get(v) unless Utilities.blank?(v)
1851
1852
  rescue NameError
1852
1853
  end
1853
1854
  when k == 'transport_encoding'
@@ -1873,6 +1874,15 @@ module Mail
1873
1874
  "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1874
1875
  end
1875
1876
 
1877
+ def inspect_structure
1878
+ inspect +
1879
+ if self.multipart?
1880
+ "\n" + parts.inspect_structure
1881
+ else
1882
+ ''
1883
+ end
1884
+ end
1885
+
1876
1886
  def decoded
1877
1887
  case
1878
1888
  when self.text?
@@ -1957,6 +1967,8 @@ module Mail
1957
1967
 
1958
1968
  private
1959
1969
 
1970
+ HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
1971
+
1960
1972
  # 2.1. General Description
1961
1973
  # A message consists of header fields (collectively called "the header
1962
1974
  # of the message") followed, optionally, by a body. The header is a
@@ -1964,18 +1976,14 @@ module Mail
1964
1976
  # this standard. The body is simply a sequence of characters that
1965
1977
  # follows the header and is separated from the header by an empty line
1966
1978
  # (i.e., a line with nothing preceding the CRLF).
1967
- #
1968
- # Additionally, I allow for the case where someone might have put whitespace
1969
- # on the "gap line"
1970
1979
  def parse_message
1971
- header_part, body_part = raw_source.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
1980
+ header_part, body_part = raw_source.lstrip.split(HEADER_SEPARATOR, 2)
1972
1981
  self.header = header_part
1973
1982
  self.body = body_part
1974
1983
  end
1975
1984
 
1976
1985
  def raw_source=(value)
1977
- value.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1978
- @raw_source = value.to_crlf
1986
+ @raw_source = value
1979
1987
  end
1980
1988
 
1981
1989
  # see comments to body=. We take data and process it lazily
@@ -1987,11 +1995,9 @@ module Mail
1987
1995
  @body_raw = nil
1988
1996
  add_encoding_to_body
1989
1997
  when @body && @body.multipart?
1990
- @body << Mail::Part.new(value)
1991
- add_encoding_to_body
1998
+ self.text_part = value
1992
1999
  else
1993
2000
  @body_raw = value
1994
- # process_body_raw
1995
2001
  end
1996
2002
  end
1997
2003
 
@@ -2006,9 +2012,9 @@ module Mail
2006
2012
 
2007
2013
  def set_envelope_header
2008
2014
  raw_string = raw_source.to_s
2009
- 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)
2010
2016
  set_envelope(match_data[1])
2011
- self.raw_source = raw_string.sub(match_data[0], "")
2017
+ self.raw_source = raw_string.sub(match_data[0], "")
2012
2018
  end
2013
2019
  end
2014
2020
 
@@ -2016,6 +2022,13 @@ module Mail
2016
2022
  body.split!(boundary)
2017
2023
  end
2018
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
+
2019
2032
  def add_encoding_to_body
2020
2033
  if has_content_transfer_encoding?
2021
2034
  @body.encoding = content_transfer_encoding
@@ -2023,18 +2036,20 @@ module Mail
2023
2036
  end
2024
2037
 
2025
2038
  def identify_and_set_transfer_encoding
2026
- if body && body.multipart?
2027
- self.content_transfer_encoding = @transport_encoding
2039
+ if body
2040
+ if body.multipart?
2041
+ self.content_transfer_encoding = @transport_encoding
2028
2042
  else
2029
- 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
2030
2044
  end
2045
+ end
2031
2046
  end
2032
2047
 
2033
2048
  def add_required_fields
2034
2049
  add_required_message_fields
2035
2050
  add_multipart_mixed_header if body.multipart?
2036
2051
  add_content_type unless has_content_type?
2037
- add_charset unless has_charset?
2052
+ add_charset if text? && !has_charset?
2038
2053
  add_content_transfer_encoding unless has_content_transfer_encoding?
2039
2054
  end
2040
2055
 
@@ -2046,15 +2061,17 @@ module Mail
2046
2061
 
2047
2062
  def add_multipart_alternate_header
2048
2063
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2049
- header['content_type'].parameters[:charset] = @charset
2050
2064
  body.boundary = boundary
2051
2065
  end
2052
2066
 
2053
2067
  def add_boundary
2054
2068
  unless body.boundary && boundary
2055
- 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
2056
2074
  header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2057
- header['content_type'].parameters[:charset] = @charset
2058
2075
  body.boundary = boundary
2059
2076
  end
2060
2077
  end
@@ -2062,7 +2079,6 @@ module Mail
2062
2079
  def add_multipart_mixed_header
2063
2080
  unless header['content-type']
2064
2081
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2065
- header['content_type'].parameters[:charset] = @charset
2066
2082
  body.boundary = boundary
2067
2083
  end
2068
2084
  end
@@ -2079,7 +2095,7 @@ module Mail
2079
2095
  body_content = nil
2080
2096
 
2081
2097
  passed_in_options.each_pair do |k,v|
2082
- k = underscoreize(k).to_sym if k.class == String
2098
+ k = Utilities.underscoreize(k).to_sym if k.class == String
2083
2099
  if k == :headers
2084
2100
  self.headers(v)
2085
2101
  elsif k == :body
@@ -2110,10 +2126,10 @@ module Mail
2110
2126
  content_disp_name = header[:content_disposition].filename rescue nil
2111
2127
  content_loc_name = header[:content_location].location rescue nil
2112
2128
  case
2113
- when content_type && content_type_name
2114
- filename = content_type_name
2115
2129
  when content_disposition && content_disp_name
2116
2130
  filename = content_disp_name
2131
+ when content_type && content_type_name
2132
+ filename = content_type_name
2117
2133
  when content_location && content_loc_name
2118
2134
  filename = content_loc_name
2119
2135
  else
@@ -2128,26 +2144,13 @@ module Mail
2128
2144
  if perform_deliveries
2129
2145
  delivery_method.deliver!(self)
2130
2146
  end
2131
- rescue Exception => e # Net::SMTP errors or sendmail pipe errors
2147
+ rescue => e # Net::SMTP errors or sendmail pipe errors
2132
2148
  raise e if raise_delivery_errors
2133
2149
  end
2134
2150
  end
2135
2151
 
2136
2152
  def decode_body_as_text
2137
- body_text = decode_body
2138
- if charset
2139
- if RUBY_VERSION < '1.9'
2140
- require 'iconv'
2141
- return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
2142
- else
2143
- if encoding = Encoding.find(charset) rescue nil
2144
- body_text.force_encoding(encoding)
2145
- return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
2146
- end
2147
- end
2148
- end
2149
- body_text
2153
+ Encodings.transcode_charset decode_body, charset, 'UTF-8'
2150
2154
  end
2151
-
2152
2155
  end
2153
2156
  end