mail 2.6.1 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +150 -107
  4. data/lib/mail/attachments_list.rb +13 -10
  5. data/lib/mail/body.rb +104 -90
  6. data/lib/mail/check_delivery_params.rb +55 -10
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +79 -0
  9. data/lib/mail/elements/address.rb +96 -108
  10. data/lib/mail/elements/address_list.rb +13 -30
  11. data/lib/mail/elements/content_disposition_element.rb +10 -16
  12. data/lib/mail/elements/content_location_element.rb +9 -13
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +7 -11
  14. data/lib/mail/elements/content_type_element.rb +17 -23
  15. data/lib/mail/elements/date_time_element.rb +8 -15
  16. data/lib/mail/elements/envelope_from_element.rb +23 -23
  17. data/lib/mail/elements/message_ids_element.rb +22 -15
  18. data/lib/mail/elements/mime_version_element.rb +8 -15
  19. data/lib/mail/elements/phrase_list.rb +13 -10
  20. data/lib/mail/elements/received_element.rb +28 -19
  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 -92
  31. data/lib/mail/envelope.rb +12 -14
  32. data/lib/mail/field.rb +121 -85
  33. data/lib/mail/field_list.rb +62 -8
  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 +32 -31
  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 +36 -50
  73. data/lib/mail/fields.rb +1 -0
  74. data/lib/mail/header.rb +73 -110
  75. data/lib/mail/indifferent_hash.rb +1 -0
  76. data/lib/mail/mail.rb +6 -11
  77. data/lib/mail/matchers/attachment_matchers.rb +44 -0
  78. data/lib/mail/matchers/has_sent_mail.rb +53 -9
  79. data/lib/mail/message.rb +132 -136
  80. data/lib/mail/multibyte/chars.rb +24 -180
  81. data/lib/mail/multibyte/unicode.rb +31 -26
  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 +9 -11
  85. data/lib/mail/network/delivery_methods/file_delivery.rb +14 -16
  86. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  87. data/lib/mail/network/delivery_methods/sendmail.rb +68 -24
  88. data/lib/mail/network/delivery_methods/smtp.rb +77 -54
  89. data/lib/mail/network/delivery_methods/smtp_connection.rb +5 -9
  90. data/lib/mail/network/delivery_methods/test_mailer.rb +9 -9
  91. data/lib/mail/network/retriever_methods/base.rb +9 -8
  92. data/lib/mail/network/retriever_methods/imap.rb +21 -7
  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 +33226 -116
  98. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  99. data/lib/mail/parsers/content_disposition_parser.rb +883 -49
  100. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  101. data/lib/mail/parsers/content_location_parser.rb +810 -23
  102. data/lib/mail/parsers/content_location_parser.rl +78 -0
  103. data/lib/mail/parsers/content_transfer_encoding_parser.rb +510 -21
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  105. data/lib/mail/parsers/content_type_parser.rb +1031 -47
  106. data/lib/mail/parsers/content_type_parser.rl +90 -0
  107. data/lib/mail/parsers/date_time_parser.rb +879 -24
  108. data/lib/mail/parsers/date_time_parser.rl +69 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +3670 -40
  110. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  111. data/lib/mail/parsers/message_ids_parser.rb +5147 -25
  112. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +498 -26
  114. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  115. data/lib/mail/parsers/phrase_lists_parser.rb +872 -21
  116. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  117. data/lib/mail/parsers/received_parser.rb +8777 -42
  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/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  128. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  129. data/lib/mail/parsers.rb +12 -25
  130. data/lib/mail/part.rb +11 -12
  131. data/lib/mail/parts_list.rb +88 -14
  132. data/lib/mail/smtp_envelope.rb +57 -0
  133. data/lib/mail/utilities.rb +377 -40
  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 +138 -94
  139. data/CHANGELOG.rdoc +0 -752
  140. data/CONTRIBUTING.md +0 -60
  141. data/Dependencies.txt +0 -2
  142. data/Gemfile +0 -15
  143. data/Rakefile +0 -29
  144. data/TODO.rdoc +0 -9
  145. data/VERSION +0 -4
  146. data/lib/mail/core_extensions/nil.rb +0 -19
  147. data/lib/mail/core_extensions/object.rb +0 -13
  148. data/lib/mail/core_extensions/smtp.rb +0 -24
  149. data/lib/mail/core_extensions/string/access.rb +0 -145
  150. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  151. data/lib/mail/core_extensions/string.rb +0 -43
  152. data/lib/mail/fields/common/address_container.rb +0 -16
  153. data/lib/mail/fields/common/common_address.rb +0 -135
  154. data/lib/mail/fields/common/common_date.rb +0 -35
  155. data/lib/mail/fields/common/common_field.rb +0 -57
  156. data/lib/mail/fields/common/common_message_id.rb +0 -48
  157. data/lib/mail/multibyte/exceptions.rb +0 -8
  158. data/lib/mail/parsers/ragel/common.rl +0 -184
  159. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  160. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  161. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  162. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  163. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  164. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  165. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  166. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  167. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  168. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  169. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  170. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  171. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  172. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2129
  173. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  174. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  175. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  177. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  178. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  179. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  180. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  181. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  182. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  183. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  184. data/lib/mail/parsers/ragel/ruby.rb +0 -39
  185. data/lib/mail/parsers/ragel.rb +0 -17
  186. data/lib/mail/patterns.rb +0 -37
  187. data/lib/mail/version_specific/ruby_1_8.rb +0 -119
  188. data/lib/mail/version_specific/ruby_1_9.rb +0 -159
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
  #
@@ -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
@@ -355,13 +376,8 @@ module Mail
355
376
  if self.message_id && other.message_id
356
377
  self.encoded == other.encoded
357
378
  else
358
- self_message_id, other_message_id = self.message_id, other.message_id
359
- begin
360
- self.message_id, other.message_id = '<temp@test>', '<temp@test>'
361
- self.encoded == other.encoded
362
- ensure
363
- self.message_id, other.message_id = self_message_id, other_message_id
364
- end
379
+ dup.tap { |m| m.message_id = '<temp@test>' }.encoded ==
380
+ other.dup.tap { |m| m.message_id = '<temp@test>' }.encoded
365
381
  end
366
382
  end
367
383
 
@@ -383,9 +399,9 @@ module Mail
383
399
  end
384
400
 
385
401
  # Sets the envelope from for the email
386
- def set_envelope( val )
402
+ def set_envelope(val)
387
403
  @raw_envelope = val
388
- @envelope = Mail::Envelope.new( val )
404
+ @envelope = Mail::Envelope.parse(val) rescue nil
389
405
  end
390
406
 
391
407
  # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
@@ -1188,8 +1204,8 @@ module Mail
1188
1204
  def default( sym, val = nil )
1189
1205
  if val
1190
1206
  header[sym] = val
1191
- else
1192
- header[sym].default if header[sym]
1207
+ elsif field = header[sym]
1208
+ field.default
1193
1209
  end
1194
1210
  end
1195
1211
 
@@ -1235,14 +1251,13 @@ module Mail
1235
1251
  def body(value = nil)
1236
1252
  if value
1237
1253
  self.body = value
1238
- # add_encoding_to_body
1239
1254
  else
1240
1255
  process_body_raw if @body_raw
1241
1256
  @body
1242
1257
  end
1243
1258
  end
1244
1259
 
1245
- def body_encoding(value)
1260
+ def body_encoding(value = nil)
1246
1261
  if value.nil?
1247
1262
  body.encoding
1248
1263
  else
@@ -1251,7 +1266,7 @@ module Mail
1251
1266
  end
1252
1267
 
1253
1268
  def body_encoding=(value)
1254
- body.encoding = value
1269
+ body.encoding = value
1255
1270
  end
1256
1271
 
1257
1272
  # Returns the list of addresses this message should be sent to by
@@ -1317,7 +1332,7 @@ module Mail
1317
1332
  # mail['foo'] = '1234'
1318
1333
  # mail['foo'].to_s #=> '1234'
1319
1334
  def [](name)
1320
- header[underscoreize(name)]
1335
+ header[Utilities.underscoreize(name)]
1321
1336
  end
1322
1337
 
1323
1338
  # Method Missing in this implementation allows you to set any of the
@@ -1363,7 +1378,7 @@ module Mail
1363
1378
  #:nodoc:
1364
1379
  # Only take the structured fields, as we could take _anything_ really
1365
1380
  # as it could become an optional field... "but therin lies the dark side"
1366
- field_name = underscoreize(name).chomp("=")
1381
+ field_name = Utilities.underscoreize(name).chomp("=")
1367
1382
  if Mail::Field::KNOWN_FIELDS.include?(field_name)
1368
1383
  if args.empty?
1369
1384
  header[field_name]
@@ -1411,12 +1426,7 @@ module Mail
1411
1426
  end
1412
1427
 
1413
1428
  def has_content_transfer_encoding?
1414
- header[:content_transfer_encoding] && header[:content_transfer_encoding].errors.blank?
1415
- end
1416
-
1417
- def has_transfer_encoding? # :nodoc:
1418
- STDERR.puts(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
1419
- has_content_transfer_encoding?
1429
+ header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1420
1430
  end
1421
1431
 
1422
1432
  # Creates a new empty Message-ID field and inserts it in the correct order
@@ -1463,35 +1473,19 @@ module Mail
1463
1473
  if !body.empty?
1464
1474
  # Only give a warning if this isn't an attachment, has non US-ASCII and the user
1465
1475
  # has not specified an encoding explicitly.
1466
- if @defaulted_charset && body.raw_source.not_ascii_only? && !self.attachment?
1476
+ if @defaulted_charset && !body.raw_source.ascii_only? && !self.attachment?
1467
1477
  warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1468
- STDERR.puts(warning)
1478
+ warn(warning)
1479
+ end
1480
+ if @charset
1481
+ header[:content_type].parameters['charset'] = @charset
1469
1482
  end
1470
- header[:content_type].parameters['charset'] = @charset
1471
1483
  end
1472
1484
  end
1473
1485
 
1474
1486
  # Adds a content transfer encoding
1475
- #
1476
- # Otherwise raises a warning
1477
1487
  def add_content_transfer_encoding
1478
- if body.only_us_ascii?
1479
- header[:content_transfer_encoding] = '7bit'
1480
- else
1481
- warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
1482
- STDERR.puts(warning)
1483
- header[:content_transfer_encoding] = '8bit'
1484
- end
1485
- end
1486
-
1487
- def add_transfer_encoding # :nodoc:
1488
- STDERR.puts(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
1489
- add_content_transfer_encoding
1490
- end
1491
-
1492
- def transfer_encoding # :nodoc:
1493
- STDERR.puts(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
1494
- content_transfer_encoding
1488
+ header[:content_transfer_encoding] ||= body.default_encoding
1495
1489
  end
1496
1490
 
1497
1491
  # Returns the MIME media type of part we are on, this is taken from the content-type header
@@ -1499,15 +1493,10 @@ module Mail
1499
1493
  has_content_type? ? header[:content_type].string : nil rescue nil
1500
1494
  end
1501
1495
 
1502
- def message_content_type
1503
- STDERR.puts(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
1504
- mime_type
1505
- end
1506
-
1507
1496
  # Returns the character set defined in the content type field
1508
1497
  def charset
1509
1498
  if @header
1510
- has_content_type? ? content_type_parameters['charset'] : @charset
1499
+ has_content_type? && !multipart? ? content_type_parameters['charset'] : @charset
1511
1500
  else
1512
1501
  @charset
1513
1502
  end
@@ -1530,12 +1519,6 @@ module Mail
1530
1519
  has_content_type? ? header[:content_type].sub_type : nil rescue nil
1531
1520
  end
1532
1521
 
1533
- # Returns the content type parameters
1534
- def mime_parameters
1535
- STDERR.puts(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
1536
- content_type_parameters
1537
- end
1538
-
1539
1522
  # Returns the content type parameters
1540
1523
  def content_type_parameters
1541
1524
  has_content_type? ? header[:content_type].parameters : nil rescue nil
@@ -1558,7 +1541,14 @@ module Mail
1558
1541
 
1559
1542
  # returns the part in a multipart/report email that has the content-type delivery-status
1560
1543
  def delivery_status_part
1561
- @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
1562
1552
  end
1563
1553
 
1564
1554
  def bounced?
@@ -1665,6 +1655,8 @@ module Mail
1665
1655
  def html_part=(msg)
1666
1656
  # Assign the html part and set multipart/alternative if there's a text part.
1667
1657
  if msg
1658
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1659
+
1668
1660
  @html_part = msg
1669
1661
  @html_part.content_type = 'text/html' unless @html_part.has_content_type?
1670
1662
  add_multipart_alternate_header if text_part
@@ -1687,6 +1679,8 @@ module Mail
1687
1679
  def text_part=(msg)
1688
1680
  # Assign the text part and set multipart/alternative if there's an html part.
1689
1681
  if msg
1682
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1683
+
1690
1684
  @text_part = msg
1691
1685
  @text_part.content_type = 'text/plain' unless @text_part.has_content_type?
1692
1686
  add_multipart_alternate_header if html_part
@@ -1705,7 +1699,7 @@ module Mail
1705
1699
 
1706
1700
  # Adds a part to the parts list or creates the part list
1707
1701
  def add_part(part)
1708
- if !body.multipart? && !self.body.decoded.blank?
1702
+ if !body.multipart? && !Utilities.blank?(self.body.decoded)
1709
1703
  @text_part = Mail::Part.new('Content-Type: text/plain;')
1710
1704
  @text_part.body = body.decoded
1711
1705
  self.body << @text_part
@@ -1761,24 +1755,34 @@ module Mail
1761
1755
  #
1762
1756
  # See also #attachments
1763
1757
  def add_file(values)
1764
- convert_to_multipart unless self.multipart? || self.body.decoded.blank?
1758
+ convert_to_multipart unless self.multipart? || Utilities.blank?(self.body.decoded)
1765
1759
  add_multipart_mixed_header
1766
1760
  if values.is_a?(String)
1767
1761
  basename = File.basename(values)
1768
1762
  filedata = File.open(values, 'rb') { |f| f.read }
1769
1763
  else
1770
1764
  basename = values[:filename]
1771
- filedata = values[:content] || File.open(values[:filename], 'rb') { |f| f.read }
1765
+ filedata = values
1772
1766
  end
1773
1767
  self.attachments[basename] = filedata
1774
1768
  end
1775
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
+
1776
1773
  def convert_to_multipart
1777
- text = body.decoded
1778
- self.body = ''
1779
- text_part = Mail::Part.new({:content_type => 'text/plain;',
1780
- :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
1781
1783
  text_part.charset = charset unless @defaulted_charset
1784
+
1785
+ self.body = ''
1782
1786
  self.body << text_part
1783
1787
  end
1784
1788
 
@@ -1786,7 +1790,6 @@ module Mail
1786
1790
  # ready to send
1787
1791
  def ready_to_send!
1788
1792
  identify_and_set_transfer_encoding
1789
- parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
1790
1793
  parts.each do |part|
1791
1794
  part.transport_encoding = transport_encoding
1792
1795
  part.ready_to_send!
@@ -1794,11 +1797,6 @@ module Mail
1794
1797
  add_required_fields
1795
1798
  end
1796
1799
 
1797
- def encode!
1798
- STDERR.puts("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
1799
- ready_to_send!
1800
- end
1801
-
1802
1800
  # Outputs an encoded string representation of the mail message including
1803
1801
  # all headers, attachments, etc. This is an encoded email in US-ASCII,
1804
1802
  # so it is able to be directly sent to an email server.
@@ -1811,16 +1809,13 @@ module Mail
1811
1809
  end
1812
1810
 
1813
1811
  def without_attachments!
1814
- return self unless has_attachments?
1815
-
1816
- parts.delete_if { |p| p.attachment? }
1817
- body_raw = if parts.empty?
1818
- ''
1819
- else
1820
- body.encoded
1821
- end
1812
+ if has_attachments?
1813
+ parts.delete_attachments
1822
1814
 
1823
- @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
1824
1819
 
1825
1820
  self
1826
1821
  end
@@ -1846,14 +1841,14 @@ module Mail
1846
1841
  end
1847
1842
 
1848
1843
  def self.from_yaml(str)
1849
- hash = YAML.load(str)
1844
+ hash = Mail::YAML.load(str)
1850
1845
  m = self.new(:headers => hash['headers'])
1851
1846
  hash.delete('headers')
1852
1847
  hash.each do |k,v|
1853
1848
  case
1854
1849
  when k == 'delivery_handler'
1855
1850
  begin
1856
- m.delivery_handler = Object.const_get(v) unless v.blank?
1851
+ m.delivery_handler = Object.const_get(v) unless Utilities.blank?(v)
1857
1852
  rescue NameError
1858
1853
  end
1859
1854
  when k == 'transport_encoding'
@@ -1879,6 +1874,15 @@ module Mail
1879
1874
  "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1880
1875
  end
1881
1876
 
1877
+ def inspect_structure
1878
+ inspect +
1879
+ if self.multipart?
1880
+ "\n" + parts.inspect_structure
1881
+ else
1882
+ ''
1883
+ end
1884
+ end
1885
+
1882
1886
  def decoded
1883
1887
  case
1884
1888
  when self.text?
@@ -1963,6 +1967,8 @@ module Mail
1963
1967
 
1964
1968
  private
1965
1969
 
1970
+ HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
1971
+
1966
1972
  # 2.1. General Description
1967
1973
  # A message consists of header fields (collectively called "the header
1968
1974
  # of the message") followed, optionally, by a body. The header is a
@@ -1970,19 +1976,14 @@ module Mail
1970
1976
  # this standard. The body is simply a sequence of characters that
1971
1977
  # follows the header and is separated from the header by an empty line
1972
1978
  # (i.e., a line with nothing preceding the CRLF).
1973
- #
1974
- # Additionally, I allow for the case where someone might have put whitespace
1975
- # on the "gap line"
1976
1979
  def parse_message
1977
- 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)
1978
1981
  self.header = header_part
1979
1982
  self.body = body_part
1980
1983
  end
1981
1984
 
1982
1985
  def raw_source=(value)
1983
- @raw_source = value.to_crlf
1984
- @raw_source.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1985
- @raw_source
1986
+ @raw_source = value
1986
1987
  end
1987
1988
 
1988
1989
  # see comments to body=. We take data and process it lazily
@@ -1994,11 +1995,9 @@ module Mail
1994
1995
  @body_raw = nil
1995
1996
  add_encoding_to_body
1996
1997
  when @body && @body.multipart?
1997
- @body << Mail::Part.new(value)
1998
- add_encoding_to_body
1998
+ self.text_part = value
1999
1999
  else
2000
2000
  @body_raw = value
2001
- # process_body_raw
2002
2001
  end
2003
2002
  end
2004
2003
 
@@ -2013,9 +2012,9 @@ module Mail
2013
2012
 
2014
2013
  def set_envelope_header
2015
2014
  raw_string = raw_source.to_s
2016
- 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)
2017
2016
  set_envelope(match_data[1])
2018
- self.raw_source = raw_string.sub(match_data[0], "")
2017
+ self.raw_source = raw_string.sub(match_data[0], "")
2019
2018
  end
2020
2019
  end
2021
2020
 
@@ -2023,6 +2022,13 @@ module Mail
2023
2022
  body.split!(boundary)
2024
2023
  end
2025
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
+
2026
2032
  def add_encoding_to_body
2027
2033
  if has_content_transfer_encoding?
2028
2034
  @body.encoding = content_transfer_encoding
@@ -2030,18 +2036,20 @@ module Mail
2030
2036
  end
2031
2037
 
2032
2038
  def identify_and_set_transfer_encoding
2033
- if body && body.multipart?
2034
- self.content_transfer_encoding = @transport_encoding
2039
+ if body
2040
+ if body.multipart?
2041
+ self.content_transfer_encoding = @transport_encoding
2035
2042
  else
2036
- 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
2037
2044
  end
2045
+ end
2038
2046
  end
2039
2047
 
2040
2048
  def add_required_fields
2041
2049
  add_required_message_fields
2042
2050
  add_multipart_mixed_header if body.multipart?
2043
2051
  add_content_type unless has_content_type?
2044
- add_charset unless has_charset?
2052
+ add_charset if text? && !has_charset?
2045
2053
  add_content_transfer_encoding unless has_content_transfer_encoding?
2046
2054
  end
2047
2055
 
@@ -2053,15 +2061,17 @@ module Mail
2053
2061
 
2054
2062
  def add_multipart_alternate_header
2055
2063
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2056
- header['content_type'].parameters[:charset] = @charset
2057
2064
  body.boundary = boundary
2058
2065
  end
2059
2066
 
2060
2067
  def add_boundary
2061
2068
  unless body.boundary && boundary
2062
- 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
2063
2074
  header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2064
- header['content_type'].parameters[:charset] = @charset
2065
2075
  body.boundary = boundary
2066
2076
  end
2067
2077
  end
@@ -2069,7 +2079,6 @@ module Mail
2069
2079
  def add_multipart_mixed_header
2070
2080
  unless header['content-type']
2071
2081
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2072
- header['content_type'].parameters[:charset] = @charset
2073
2082
  body.boundary = boundary
2074
2083
  end
2075
2084
  end
@@ -2086,7 +2095,7 @@ module Mail
2086
2095
  body_content = nil
2087
2096
 
2088
2097
  passed_in_options.each_pair do |k,v|
2089
- k = underscoreize(k).to_sym if k.class == String
2098
+ k = Utilities.underscoreize(k).to_sym if k.class == String
2090
2099
  if k == :headers
2091
2100
  self.headers(v)
2092
2101
  elsif k == :body
@@ -2117,10 +2126,10 @@ module Mail
2117
2126
  content_disp_name = header[:content_disposition].filename rescue nil
2118
2127
  content_loc_name = header[:content_location].location rescue nil
2119
2128
  case
2120
- when content_type && content_type_name
2121
- filename = content_type_name
2122
2129
  when content_disposition && content_disp_name
2123
2130
  filename = content_disp_name
2131
+ when content_type && content_type_name
2132
+ filename = content_type_name
2124
2133
  when content_location && content_loc_name
2125
2134
  filename = content_loc_name
2126
2135
  else
@@ -2141,20 +2150,7 @@ module Mail
2141
2150
  end
2142
2151
 
2143
2152
  def decode_body_as_text
2144
- body_text = decode_body
2145
- if charset
2146
- if RUBY_VERSION < '1.9'
2147
- require 'iconv'
2148
- return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
2149
- else
2150
- if encoding = Encoding.find(charset) rescue nil
2151
- body_text.force_encoding(encoding)
2152
- return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
2153
- end
2154
- end
2155
- end
2156
- body_text
2153
+ Encodings.transcode_charset decode_body, charset, 'UTF-8'
2157
2154
  end
2158
-
2159
2155
  end
2160
2156
  end