mail 2.5.5 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
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