mail 2.5.2 → 2.6.4

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 (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +124 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +7 -22
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +86 -31
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +4 -2
  10. data/lib/mail/body.rb +36 -11
  11. data/lib/mail/check_delivery_params.rb +13 -22
  12. data/lib/mail/configuration.rb +1 -0
  13. data/lib/mail/{patterns.rb → constants.rb} +29 -6
  14. data/lib/mail/core_extensions/smtp.rb +13 -13
  15. data/lib/mail/core_extensions/string/access.rb +1 -0
  16. data/lib/mail/core_extensions/string/multibyte.rb +1 -0
  17. data/lib/mail/core_extensions/string.rb +5 -17
  18. data/lib/mail/elements/address.rb +86 -145
  19. data/lib/mail/elements/address_list.rb +10 -42
  20. data/lib/mail/elements/content_disposition_element.rb +4 -7
  21. data/lib/mail/elements/content_location_element.rb +8 -11
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +4 -10
  23. data/lib/mail/elements/content_type_element.rb +5 -8
  24. data/lib/mail/elements/date_time_element.rb +4 -7
  25. data/lib/mail/elements/envelope_from_element.rb +19 -13
  26. data/lib/mail/elements/message_ids_element.rb +8 -11
  27. data/lib/mail/elements/mime_version_element.rb +4 -7
  28. data/lib/mail/elements/phrase_list.rb +3 -7
  29. data/lib/mail/elements/received_element.rb +4 -7
  30. data/lib/mail/elements.rb +1 -0
  31. data/lib/mail/encodings/7bit.rb +1 -0
  32. data/lib/mail/encodings/8bit.rb +8 -2
  33. data/lib/mail/encodings/base64.rb +7 -1
  34. data/lib/mail/encodings/binary.rb +1 -0
  35. data/lib/mail/encodings/quoted_printable.rb +11 -4
  36. data/lib/mail/encodings/transfer_encoding.rb +27 -17
  37. data/lib/mail/encodings/unix_to_unix.rb +18 -0
  38. data/lib/mail/encodings.rb +54 -39
  39. data/lib/mail/envelope.rb +1 -5
  40. data/lib/mail/field.rb +81 -31
  41. data/lib/mail/field_list.rb +19 -18
  42. data/lib/mail/fields/bcc_field.rb +17 -5
  43. data/lib/mail/fields/cc_field.rb +3 -3
  44. data/lib/mail/fields/comments_field.rb +2 -1
  45. data/lib/mail/fields/common/address_container.rb +1 -0
  46. data/lib/mail/fields/common/common_address.rb +34 -28
  47. data/lib/mail/fields/common/common_date.rb +2 -8
  48. data/lib/mail/fields/common/common_field.rb +9 -5
  49. data/lib/mail/fields/common/common_message_id.rb +3 -2
  50. data/lib/mail/fields/common/parameter_hash.rb +4 -3
  51. data/lib/mail/fields/content_description_field.rb +1 -0
  52. data/lib/mail/fields/content_disposition_field.rb +13 -12
  53. data/lib/mail/fields/content_id_field.rb +4 -4
  54. data/lib/mail/fields/content_location_field.rb +2 -1
  55. data/lib/mail/fields/content_transfer_encoding_field.rb +4 -9
  56. data/lib/mail/fields/content_type_field.rb +4 -3
  57. data/lib/mail/fields/date_field.rb +16 -15
  58. data/lib/mail/fields/from_field.rb +3 -3
  59. data/lib/mail/fields/in_reply_to_field.rb +3 -1
  60. data/lib/mail/fields/keywords_field.rb +2 -2
  61. data/lib/mail/fields/message_id_field.rb +4 -4
  62. data/lib/mail/fields/mime_version_field.rb +3 -2
  63. data/lib/mail/fields/optional_field.rb +1 -0
  64. data/lib/mail/fields/received_field.rb +4 -3
  65. data/lib/mail/fields/references_field.rb +3 -1
  66. data/lib/mail/fields/reply_to_field.rb +3 -3
  67. data/lib/mail/fields/resent_bcc_field.rb +3 -3
  68. data/lib/mail/fields/resent_cc_field.rb +3 -3
  69. data/lib/mail/fields/resent_date_field.rb +2 -1
  70. data/lib/mail/fields/resent_from_field.rb +3 -3
  71. data/lib/mail/fields/resent_message_id_field.rb +1 -0
  72. data/lib/mail/fields/resent_sender_field.rb +4 -4
  73. data/lib/mail/fields/resent_to_field.rb +3 -3
  74. data/lib/mail/fields/return_path_field.rb +1 -1
  75. data/lib/mail/fields/sender_field.rb +9 -9
  76. data/lib/mail/fields/structured_field.rb +1 -0
  77. data/lib/mail/fields/subject_field.rb +1 -0
  78. data/lib/mail/fields/to_field.rb +3 -3
  79. data/lib/mail/fields/unstructured_field.rb +15 -8
  80. data/lib/mail/fields.rb +1 -0
  81. data/lib/mail/header.rb +21 -29
  82. data/lib/mail/indifferent_hash.rb +1 -0
  83. data/lib/mail/mail.rb +16 -1
  84. data/lib/mail/matchers/attachment_matchers.rb +29 -0
  85. data/lib/mail/matchers/has_sent_mail.rb +61 -4
  86. data/lib/mail/message.rb +175 -72
  87. data/lib/mail/multibyte/chars.rb +3 -2
  88. data/lib/mail/multibyte/exceptions.rb +1 -0
  89. data/lib/mail/multibyte/unicode.rb +22 -15
  90. data/lib/mail/multibyte/utils.rb +1 -0
  91. data/lib/mail/multibyte.rb +1 -0
  92. data/lib/mail/network/delivery_methods/exim.rb +3 -7
  93. data/lib/mail/network/delivery_methods/file_delivery.rb +2 -1
  94. data/lib/mail/network/delivery_methods/sendmail.rb +33 -10
  95. data/lib/mail/network/delivery_methods/smtp.rb +32 -34
  96. data/lib/mail/network/delivery_methods/smtp_connection.rb +7 -6
  97. data/lib/mail/network/delivery_methods/test_mailer.rb +3 -2
  98. data/lib/mail/network/retriever_methods/base.rb +1 -0
  99. data/lib/mail/network/retriever_methods/imap.rb +19 -13
  100. data/lib/mail/network/retriever_methods/pop3.rb +1 -0
  101. data/lib/mail/network/retriever_methods/test_retriever.rb +1 -0
  102. data/lib/mail/network.rb +1 -0
  103. data/lib/mail/parsers/address_lists_parser.rb +133 -0
  104. data/lib/mail/parsers/content_disposition_parser.rb +68 -0
  105. data/lib/mail/parsers/content_location_parser.rb +36 -0
  106. data/lib/mail/parsers/content_transfer_encoding_parser.rb +34 -0
  107. data/lib/mail/parsers/content_type_parser.rb +67 -0
  108. data/lib/mail/parsers/date_time_parser.rb +39 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +46 -0
  110. data/lib/mail/parsers/message_ids_parser.rb +40 -0
  111. data/lib/mail/parsers/mime_version_parser.rb +42 -0
  112. data/lib/mail/parsers/phrase_lists_parser.rb +36 -0
  113. data/lib/mail/parsers/ragel/common.rl +185 -0
  114. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  115. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  117. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  118. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  119. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  120. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  121. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  122. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  123. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  124. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  125. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  126. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  127. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  128. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  129. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  130. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  131. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  132. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  133. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  134. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  135. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  136. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  137. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  138. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  139. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  140. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  141. data/lib/mail/parsers/ragel.rb +18 -0
  142. data/lib/mail/parsers/received_parser.rb +49 -0
  143. data/lib/mail/parsers.rb +27 -0
  144. data/lib/mail/part.rb +11 -4
  145. data/lib/mail/parts_list.rb +30 -10
  146. data/lib/mail/utilities.rb +127 -63
  147. data/lib/mail/values/unicode_tables.dat +0 -0
  148. data/lib/mail/version.rb +8 -15
  149. data/lib/mail/version_specific/ruby_1_8.rb +18 -7
  150. data/lib/mail/version_specific/ruby_1_9.rb +136 -32
  151. data/lib/mail.rb +12 -6
  152. metadata +115 -77
  153. data/lib/VERSION +0 -4
  154. data/lib/load_parsers.rb +0 -41
  155. data/lib/mail/core_extensions/nil.rb +0 -19
  156. data/lib/mail/core_extensions/object.rb +0 -13
  157. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  158. data/lib/mail/parsers/address_lists.rb +0 -64
  159. data/lib/mail/parsers/address_lists.treetop +0 -19
  160. data/lib/mail/parsers/content_disposition.rb +0 -535
  161. data/lib/mail/parsers/content_disposition.treetop +0 -46
  162. data/lib/mail/parsers/content_location.rb +0 -139
  163. data/lib/mail/parsers/content_location.treetop +0 -20
  164. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  165. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  166. data/lib/mail/parsers/content_type.rb +0 -967
  167. data/lib/mail/parsers/content_type.treetop +0 -68
  168. data/lib/mail/parsers/date_time.rb +0 -114
  169. data/lib/mail/parsers/date_time.treetop +0 -11
  170. data/lib/mail/parsers/envelope_from.rb +0 -194
  171. data/lib/mail/parsers/envelope_from.treetop +0 -32
  172. data/lib/mail/parsers/message_ids.rb +0 -45
  173. data/lib/mail/parsers/message_ids.treetop +0 -15
  174. data/lib/mail/parsers/mime_version.rb +0 -144
  175. data/lib/mail/parsers/mime_version.treetop +0 -19
  176. data/lib/mail/parsers/phrase_lists.rb +0 -45
  177. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  178. data/lib/mail/parsers/received.rb +0 -71
  179. data/lib/mail/parsers/received.treetop +0 -11
  180. data/lib/mail/parsers/rfc2045.rb +0 -464
  181. data/lib/mail/parsers/rfc2045.treetop +0 -36
  182. data/lib/mail/parsers/rfc2822.rb +0 -5397
  183. data/lib/mail/parsers/rfc2822.treetop +0 -410
  184. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  185. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  186. data/lib/tasks/corpus.rake +0 -125
  187. data/lib/tasks/treetop.rake +0 -10
data/lib/mail/message.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  require "yaml"
3
4
 
4
5
  module Mail
@@ -46,7 +47,7 @@ module Mail
46
47
  # (i.e., a line with nothing preceding the CRLF).
47
48
  class Message
48
49
 
49
- include Patterns
50
+ include Constants
50
51
  include Utilities
51
52
 
52
53
  # ==Making an email
@@ -105,9 +106,12 @@ module Mail
105
106
  @html_part = nil
106
107
  @errors = nil
107
108
  @header = nil
108
- @charset = 'UTF-8'
109
+ @charset = self.class.default_charset
109
110
  @defaulted_charset = true
110
111
 
112
+ @smtp_envelope_from = nil
113
+ @smtp_envelope_to = nil
114
+
111
115
  @perform_deliveries = true
112
116
  @raise_delivery_errors = true
113
117
 
@@ -122,7 +126,7 @@ module Mail
122
126
  if args.flatten.first.respond_to?(:each_pair)
123
127
  init_with_hash(args.flatten.first)
124
128
  else
125
- init_with_string(args.flatten[0].to_s.strip)
129
+ init_with_string(args.flatten[0].to_s)
126
130
  end
127
131
 
128
132
  if block_given?
@@ -169,7 +173,7 @@ module Mail
169
173
  # obj.mail.deliver
170
174
  #
171
175
  # Would cause Mail to call obj.deliver_mail passing itself as a parameter,
172
- # which then can just yield and let Mail do it's own private do_delivery
176
+ # which then can just yield and let Mail do its own private do_delivery
173
177
  # method.
174
178
  attr_accessor :delivery_handler
175
179
 
@@ -204,6 +208,10 @@ module Mail
204
208
  # define a delivery_handler
205
209
  attr_accessor :raise_delivery_errors
206
210
 
211
+ def self.default_charset; @@default_charset; end
212
+ def self.default_charset=(charset); @@default_charset = charset; end
213
+ self.default_charset = 'UTF-8'
214
+
207
215
  def register_for_delivery_notification(observer)
208
216
  STDERR.puts("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
209
217
  Mail.register_observer(observer)
@@ -237,7 +245,7 @@ module Mail
237
245
  # This method bypasses checking perform_deliveries and raise_delivery_errors,
238
246
  # so use with caution.
239
247
  #
240
- # It still however fires off the intercepters and calls the observers callbacks if they are defined.
248
+ # It still however fires off the interceptors and calls the observers callbacks if they are defined.
241
249
  #
242
250
  # Returns self
243
251
  def deliver!
@@ -350,17 +358,23 @@ module Mail
350
358
  return false unless other.respond_to?(:encoded)
351
359
 
352
360
  if self.message_id && other.message_id
353
- result = (self.encoded == other.encoded)
361
+ self.encoded == other.encoded
354
362
  else
355
363
  self_message_id, other_message_id = self.message_id, other.message_id
356
- self.message_id, other.message_id = '<temp@test>', '<temp@test>'
357
- result = self.encoded == other.encoded
358
- self.message_id = "<#{self_message_id}>" if self_message_id
359
- other.message_id = "<#{other_message_id}>" if other_message_id
360
- result
364
+ begin
365
+ self.message_id, other.message_id = '<temp@test>', '<temp@test>'
366
+ self.encoded == other.encoded
367
+ ensure
368
+ self.message_id, other.message_id = self_message_id, other_message_id
369
+ end
361
370
  end
362
371
  end
363
372
 
373
+ def initialize_copy(original)
374
+ super
375
+ @header = @header.dup
376
+ end
377
+
364
378
  # Provides access to the raw source of the message as it was when it
365
379
  # was instantiated. This is set at initialization and so is untouched
366
380
  # by the parsers or decoder / encoders
@@ -938,8 +952,8 @@ module Mail
938
952
  #
939
953
  # Example:
940
954
  #
941
- # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
942
- # mail.sender #=> 'mikel@test.lindsaar.net'
955
+ # mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
956
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
943
957
  def resent_sender=( val )
944
958
  header[:resent_sender] = val
945
959
  end
@@ -1023,6 +1037,82 @@ module Mail
1023
1037
  header[:sender] = val
1024
1038
  end
1025
1039
 
1040
+ # Returns the SMTP Envelope From value of the mail object, as a single
1041
+ # string of an address spec.
1042
+ #
1043
+ # Defaults to Return-Path, Sender, or the first From address.
1044
+ #
1045
+ # Example:
1046
+ #
1047
+ # mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
1048
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1049
+ #
1050
+ # Also allows you to set the value by passing a value as a parameter
1051
+ #
1052
+ # Example:
1053
+ #
1054
+ # mail.smtp_envelope_from 'Mikel <mikel@test.lindsaar.net>'
1055
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1056
+ def smtp_envelope_from( val = nil )
1057
+ if val
1058
+ self.smtp_envelope_from = val
1059
+ else
1060
+ @smtp_envelope_from || return_path || sender || from_addrs.first
1061
+ end
1062
+ end
1063
+
1064
+ # Sets the From address on the SMTP Envelope.
1065
+ #
1066
+ # Example:
1067
+ #
1068
+ # mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
1069
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1070
+ def smtp_envelope_from=( val )
1071
+ @smtp_envelope_from = val
1072
+ end
1073
+
1074
+ # Returns the SMTP Envelope To value of the mail object.
1075
+ #
1076
+ # Defaults to #destinations: To, Cc, and Bcc addresses.
1077
+ #
1078
+ # Example:
1079
+ #
1080
+ # mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
1081
+ # mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
1082
+ #
1083
+ # Also allows you to set the value by passing a value as a parameter
1084
+ #
1085
+ # Example:
1086
+ #
1087
+ # mail.smtp_envelope_to ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
1088
+ # mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
1089
+ def smtp_envelope_to( val = nil )
1090
+ if val
1091
+ self.smtp_envelope_to = val
1092
+ else
1093
+ @smtp_envelope_to || destinations
1094
+ end
1095
+ end
1096
+
1097
+ # Sets the To addresses on the SMTP Envelope.
1098
+ #
1099
+ # Example:
1100
+ #
1101
+ # mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
1102
+ # mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
1103
+ #
1104
+ # mail.smtp_envelope_to = ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
1105
+ # mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
1106
+ def smtp_envelope_to=( val )
1107
+ @smtp_envelope_to =
1108
+ case val
1109
+ when Array, NilClass
1110
+ val
1111
+ else
1112
+ [val]
1113
+ end
1114
+ end
1115
+
1026
1116
  # Returns the decoded value of the subject field, as a single string.
1027
1117
  #
1028
1118
  # Example:
@@ -1309,7 +1399,7 @@ module Mail
1309
1399
  header.has_date?
1310
1400
  end
1311
1401
 
1312
- # Returns true if the message has a Date field, the field may or may
1402
+ # Returns true if the message has a Mime-Version field, the field may or may
1313
1403
  # not have a value, but the field exists or not.
1314
1404
  def has_mime_version?
1315
1405
  header.has_mime_version?
@@ -1326,7 +1416,7 @@ module Mail
1326
1416
  end
1327
1417
 
1328
1418
  def has_content_transfer_encoding?
1329
- header[:content_transfer_encoding] && header[:content_transfer_encoding].errors.blank?
1419
+ header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
1330
1420
  end
1331
1421
 
1332
1422
  def has_transfer_encoding? # :nodoc:
@@ -1515,8 +1605,8 @@ module Mail
1515
1605
  end
1516
1606
 
1517
1607
  # Returns an AttachmentsList object, which holds all of the attachments in
1518
- # the receiver object (either the entier email or a part within) and all
1519
- # of it's descendants.
1608
+ # the receiver object (either the entire email or a part within) and all
1609
+ # of its descendants.
1520
1610
  #
1521
1611
  # It also allows you to add attachments to the mail object directly, like so:
1522
1612
  #
@@ -1559,9 +1649,7 @@ module Mail
1559
1649
  # Accessor for html_part
1560
1650
  def html_part(&block)
1561
1651
  if block_given?
1562
- @html_part = Mail::Part.new(&block)
1563
- add_multipart_alternate_header unless html_part.blank?
1564
- add_part(@html_part)
1652
+ self.html_part = Mail::Part.new(:content_type => 'text/html', &block)
1565
1653
  else
1566
1654
  @html_part || find_first_mime_type('text/html')
1567
1655
  end
@@ -1570,9 +1658,7 @@ module Mail
1570
1658
  # Accessor for text_part
1571
1659
  def text_part(&block)
1572
1660
  if block_given?
1573
- @text_part = Mail::Part.new(&block)
1574
- add_multipart_alternate_header unless html_part.blank?
1575
- add_part(@text_part)
1661
+ self.text_part = Mail::Part.new(:content_type => 'text/plain', &block)
1576
1662
  else
1577
1663
  @text_part || find_first_mime_type('text/plain')
1578
1664
  end
@@ -1581,36 +1667,58 @@ module Mail
1581
1667
  # Helper to add a html part to a multipart/alternative email. If this and
1582
1668
  # text_part are both defined in a message, then it will be a multipart/alternative
1583
1669
  # message and set itself that way.
1584
- def html_part=(msg = nil)
1670
+ def html_part=(msg)
1671
+ # Assign the html part and set multipart/alternative if there's a text part.
1585
1672
  if msg
1673
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1674
+
1586
1675
  @html_part = msg
1587
- else
1588
- @html_part = Mail::Part.new('Content-Type: text/html;')
1676
+ @html_part.content_type = 'text/html' unless @html_part.has_content_type?
1677
+ add_multipart_alternate_header if text_part
1678
+ add_part @html_part
1679
+
1680
+ # If nil, delete the html part and back out of multipart/alternative.
1681
+ elsif @html_part
1682
+ parts.delete_if { |p| p.object_id == @html_part.object_id }
1683
+ @html_part = nil
1684
+ if text_part
1685
+ self.content_type = nil
1686
+ body.boundary = nil
1687
+ end
1589
1688
  end
1590
- add_multipart_alternate_header unless text_part.blank?
1591
- add_part(@html_part)
1592
1689
  end
1593
1690
 
1594
1691
  # Helper to add a text part to a multipart/alternative email. If this and
1595
1692
  # html_part are both defined in a message, then it will be a multipart/alternative
1596
1693
  # message and set itself that way.
1597
- def text_part=(msg = nil)
1694
+ def text_part=(msg)
1695
+ # Assign the text part and set multipart/alternative if there's an html part.
1598
1696
  if msg
1697
+ msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
1698
+
1599
1699
  @text_part = msg
1600
- else
1601
- @text_part = Mail::Part.new('Content-Type: text/plain;')
1700
+ @text_part.content_type = 'text/plain' unless @text_part.has_content_type?
1701
+ add_multipart_alternate_header if html_part
1702
+ add_part @text_part
1703
+
1704
+ # If nil, delete the text part and back out of multipart/alternative.
1705
+ elsif @text_part
1706
+ parts.delete_if { |p| p.object_id == @text_part.object_id }
1707
+ @text_part = nil
1708
+ if html_part
1709
+ self.content_type = nil
1710
+ body.boundary = nil
1711
+ end
1602
1712
  end
1603
- add_multipart_alternate_header unless html_part.blank?
1604
- add_part(@text_part)
1605
1713
  end
1606
1714
 
1607
1715
  # Adds a part to the parts list or creates the part list
1608
1716
  def add_part(part)
1609
- if !body.multipart? && !self.body.decoded.blank?
1610
- @text_part = Mail::Part.new('Content-Type: text/plain;')
1611
- @text_part.body = body.decoded
1612
- self.body << @text_part
1613
- add_multipart_alternate_header
1717
+ if !body.multipart? && !Utilities.blank?(self.body.decoded)
1718
+ @text_part = Mail::Part.new('Content-Type: text/plain;')
1719
+ @text_part.body = body.decoded
1720
+ self.body << @text_part
1721
+ add_multipart_alternate_header
1614
1722
  end
1615
1723
  add_boundary
1616
1724
  self.body << part
@@ -1646,7 +1754,7 @@ module Mail
1646
1754
  # m.add_file(:filename => 'filename.png', :content => File.read('/path/to/file.jpg'))
1647
1755
  #
1648
1756
  # Note also that if you add a file to an existing message, Mail will convert that message
1649
- # to a MIME multipart email, moving whatever plain text body you had into it's own text
1757
+ # to a MIME multipart email, moving whatever plain text body you had into its own text
1650
1758
  # plain part.
1651
1759
  #
1652
1760
  # Example:
@@ -1662,14 +1770,17 @@ module Mail
1662
1770
  #
1663
1771
  # See also #attachments
1664
1772
  def add_file(values)
1665
- convert_to_multipart unless self.multipart? || self.body.decoded.blank?
1773
+ convert_to_multipart unless self.multipart? || Utilities.blank?(self.body.decoded)
1666
1774
  add_multipart_mixed_header
1667
1775
  if values.is_a?(String)
1668
1776
  basename = File.basename(values)
1669
1777
  filedata = File.open(values, 'rb') { |f| f.read }
1670
1778
  else
1671
1779
  basename = values[:filename]
1672
- filedata = values[:content] || File.open(values[:filename], 'rb') { |f| f.read }
1780
+ filedata = values
1781
+ unless filedata[:content]
1782
+ filedata = values.merge(:content=>File.open(values[:filename], 'rb') { |f| f.read })
1783
+ end
1673
1784
  end
1674
1785
  self.attachments[basename] = filedata
1675
1786
  end
@@ -1683,7 +1794,7 @@ module Mail
1683
1794
  self.body << text_part
1684
1795
  end
1685
1796
 
1686
- # Encodes the message, calls encode on all it's parts, gets an email message
1797
+ # Encodes the message, calls encode on all its parts, gets an email message
1687
1798
  # ready to send
1688
1799
  def ready_to_send!
1689
1800
  identify_and_set_transfer_encoding
@@ -1754,7 +1865,7 @@ module Mail
1754
1865
  case
1755
1866
  when k == 'delivery_handler'
1756
1867
  begin
1757
- m.delivery_handler = Object.const_get(v) unless v.blank?
1868
+ m.delivery_handler = Object.const_get(v) unless Utilities.blank?(v)
1758
1869
  rescue NameError
1759
1870
  end
1760
1871
  when k == 'transport_encoding'
@@ -1864,6 +1975,8 @@ module Mail
1864
1975
 
1865
1976
  private
1866
1977
 
1978
+ HEADER_SEPARATOR = /#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m
1979
+
1867
1980
  # 2.1. General Description
1868
1981
  # A message consists of header fields (collectively called "the header
1869
1982
  # of the message") followed, optionally, by a body. The header is a
@@ -1875,14 +1988,14 @@ module Mail
1875
1988
  # Additionally, I allow for the case where someone might have put whitespace
1876
1989
  # on the "gap line"
1877
1990
  def parse_message
1878
- header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
1991
+ header_part, body_part = raw_source.lstrip.split(HEADER_SEPARATOR, 2)
1879
1992
  self.header = header_part
1880
1993
  self.body = body_part
1881
1994
  end
1882
1995
 
1883
1996
  def raw_source=(value)
1884
- value.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1885
- @raw_source = value.to_crlf
1997
+ value = value.dup.force_encoding(Encoding::BINARY) if RUBY_VERSION >= "1.9.1"
1998
+ @raw_source = ::Mail::Utilities.to_crlf(value)
1886
1999
  end
1887
2000
 
1888
2001
  # see comments to body=. We take data and process it lazily
@@ -1904,18 +2017,18 @@ module Mail
1904
2017
 
1905
2018
 
1906
2019
  def process_body_raw
1907
- @body = Mail::Body.new(@body_raw)
1908
- @body_raw = nil
1909
- separate_parts if @separate_parts
2020
+ @body = Mail::Body.new(@body_raw)
2021
+ @body_raw = nil
2022
+ separate_parts if @separate_parts
1910
2023
 
1911
- add_encoding_to_body
2024
+ add_encoding_to_body
1912
2025
  end
1913
2026
 
1914
2027
  def set_envelope_header
1915
2028
  raw_string = raw_source.to_s
1916
2029
  if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
1917
2030
  set_envelope(match_data[1])
1918
- self.raw_source = raw_string.sub(match_data[0], "")
2031
+ self.raw_source = raw_string.sub(match_data[0], "")
1919
2032
  end
1920
2033
  end
1921
2034
 
@@ -1938,16 +2051,19 @@ module Mail
1938
2051
  end
1939
2052
 
1940
2053
  def add_required_fields
1941
- add_multipart_mixed_header unless !body.multipart?
1942
- body = nil if body.nil?
1943
- add_message_id unless (has_message_id? || self.class == Mail::Part)
1944
- add_date unless has_date?
1945
- add_mime_version unless has_mime_version?
2054
+ add_required_message_fields
2055
+ add_multipart_mixed_header if body.multipart?
1946
2056
  add_content_type unless has_content_type?
1947
- add_charset unless has_charset?
2057
+ add_charset if text? && !has_charset?
1948
2058
  add_content_transfer_encoding unless has_content_transfer_encoding?
1949
2059
  end
1950
2060
 
2061
+ def add_required_message_fields
2062
+ add_date unless has_date?
2063
+ add_mime_version unless has_mime_version?
2064
+ add_message_id unless has_message_id?
2065
+ end
2066
+
1951
2067
  def add_multipart_alternate_header
1952
2068
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
1953
2069
  header['content_type'].parameters[:charset] = @charset
@@ -2032,26 +2148,13 @@ module Mail
2032
2148
  if perform_deliveries
2033
2149
  delivery_method.deliver!(self)
2034
2150
  end
2035
- rescue Exception => e # Net::SMTP errors or sendmail pipe errors
2151
+ rescue => e # Net::SMTP errors or sendmail pipe errors
2036
2152
  raise e if raise_delivery_errors
2037
2153
  end
2038
2154
  end
2039
2155
 
2040
2156
  def decode_body_as_text
2041
- body_text = decode_body
2042
- if charset
2043
- if RUBY_VERSION < '1.9'
2044
- require 'iconv'
2045
- return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
2046
- else
2047
- if encoding = Encoding.find(charset) rescue nil
2048
- body_text.force_encoding(encoding)
2049
- return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
2050
- end
2051
- end
2052
- end
2053
- body_text
2157
+ Encodings.transcode_charset decode_body, charset, 'UTF-8'
2054
2158
  end
2055
-
2056
2159
  end
2057
2160
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail #:nodoc:
4
5
  module Multibyte #:nodoc:
@@ -364,7 +365,7 @@ module Mail #:nodoc:
364
365
  # "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
365
366
  # "日本語".mb_chars.titleize # => "日本語"
366
367
  def titleize
367
- chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.apply_mapping $1, :uppercase_mapping })
368
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping })
368
369
  end
369
370
  alias_method :titlecase, :titleize
370
371
 
@@ -412,7 +413,7 @@ module Mail #:nodoc:
412
413
  chars(Unicode.tidy_bytes(@wrapped_string, force))
413
414
  end
414
415
 
415
- %w(capitalize downcase lstrip reverse rstrip slice strip tidy_bytes upcase).each do |method|
416
+ %w(capitalize downcase lstrip reverse rstrip slice strip tidy_bytes upcase).each do |method|
416
417
  # Only define a corresponding bang method for methods defined in the proxy; On 1.9 the proxy will
417
418
  # exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
418
419
  if public_method_defined?(method)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail #:nodoc:
4
5
  module Multibyte #:nodoc:
@@ -1,6 +1,27 @@
1
+ # frozen_string_literal: true
1
2
  module Mail
2
3
  module Multibyte
3
4
  module Unicode
5
+ # Adapted from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/multibyte/unicode.rb
6
+ # under the MIT license
7
+ # The Unicode version that is supported by the implementation
8
+ UNICODE_VERSION = '7.0.0'
9
+
10
+ # Holds data about a codepoint in the Unicode database.
11
+ class Codepoint
12
+ attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
13
+
14
+ # Initializing Codepoint object with default values
15
+ def initialize
16
+ @combining_class = 0
17
+ @uppercase_mapping = 0
18
+ @lowercase_mapping = 0
19
+ end
20
+
21
+ def swapcase_mapping
22
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
23
+ end
24
+ end
4
25
 
5
26
  extend self
6
27
 
@@ -8,9 +29,6 @@ module Mail
8
29
  # information about normalization.
9
30
  NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
10
31
 
11
- # The Unicode version that is supported by the implementation
12
- UNICODE_VERSION = '5.2.0'
13
-
14
32
  # The default normalization used for operations that require normalization. It can be set to any of the
15
33
  # normalizations in NORMALIZATION_FORMS.
16
34
  #
@@ -308,11 +326,6 @@ module Mail
308
326
  end.pack('U*')
309
327
  end
310
328
 
311
- # Holds data about a codepoint in the Unicode database
312
- class Codepoint
313
- attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
314
- end
315
-
316
329
  # Holds static data from the Unicode database
317
330
  class UnicodeDatabase
318
331
  ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
@@ -341,7 +354,7 @@ module Mail
341
354
  def load
342
355
  begin
343
356
  @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
344
- rescue Exception => e
357
+ rescue => e
345
358
  raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), Mail::Multibyte is unusable")
346
359
  end
347
360
 
@@ -390,9 +403,3 @@ module Mail
390
403
  end
391
404
  end
392
405
  end
393
-
394
- module ActiveSupport
395
- unless const_defined?(:Multibyte)
396
- Multibyte = Mail::Multibyte
397
- end
398
- end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail #:nodoc:
4
5
  module Multibyte #:nodoc:
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail #:nodoc:
3
4
  module Multibyte
4
5
  require 'mail/multibyte/exceptions'
@@ -1,5 +1,4 @@
1
- require 'mail/check_delivery_params'
2
-
1
+ # frozen_string_literal: true
3
2
  module Mail
4
3
 
5
4
  # A delivery method implementation which sends via exim.
@@ -38,17 +37,14 @@ module Mail
38
37
  #
39
38
  # mail.deliver!
40
39
  class Exim < Sendmail
41
- include Mail::CheckDeliveryParams
42
-
43
40
  def initialize(values)
44
41
  self.settings = { :location => '/usr/sbin/exim',
45
42
  :arguments => '-i -t' }.merge(values)
46
43
  end
47
44
 
48
45
  def self.call(path, arguments, destinations, mail)
49
- check_params(mail)
50
- IO.popen("#{path} #{arguments}", "w+") do |io|
51
- io.puts mail.encoded.to_lf
46
+ popen "#{path} #{arguments}" do |io|
47
+ io.puts ::Mail::Utilities.to_lf(mail.encoded)
52
48
  io.flush
53
49
  end
54
50
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'mail/check_delivery_params'
2
3
 
3
4
  module Mail
@@ -28,7 +29,7 @@ module Mail
28
29
  attr_accessor :settings
29
30
 
30
31
  def deliver!(mail)
31
- check_params(mail)
32
+ check_delivery_params(mail)
32
33
 
33
34
  if ::File.respond_to?(:makedirs)
34
35
  ::File.makedirs settings[:location]
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'mail/check_delivery_params'
2
3
 
3
4
  module Mail
@@ -41,27 +42,49 @@ module Mail
41
42
 
42
43
  def initialize(values)
43
44
  self.settings = { :location => '/usr/sbin/sendmail',
44
- :arguments => '-i -t' }.merge(values)
45
+ :arguments => '-i' }.merge(values)
45
46
  end
46
47
 
47
48
  attr_accessor :settings
48
49
 
49
50
  def deliver!(mail)
50
- check_params(mail)
51
+ smtp_from, smtp_to, message = check_delivery_params(mail)
51
52
 
52
- envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
53
- return_path = "-f " + '"' + envelope_from.escape_for_shell + '"' if envelope_from
53
+ from = "-f #{self.class.shellquote(smtp_from)}"
54
+ to = smtp_to.map { |_to| self.class.shellquote(_to) }.join(' ')
54
55
 
55
- arguments = [settings[:arguments], return_path].compact.join(" ")
56
-
57
- self.class.call(settings[:location], arguments, mail.destinations.collect(&:escape_for_shell).join(" "), mail)
56
+ arguments = "#{settings[:arguments]} #{from} --"
57
+ self.class.call(settings[:location], arguments, to, message)
58
58
  end
59
59
 
60
- def self.call(path, arguments, destinations, mail)
61
- IO.popen("#{path} #{arguments} #{destinations}", "w+") do |io|
62
- io.puts mail.encoded.to_lf
60
+ def self.call(path, arguments, destinations, encoded_message)
61
+ popen "#{path} #{arguments} #{destinations}" do |io|
62
+ io.puts ::Mail::Utilities.to_lf(encoded_message)
63
63
  io.flush
64
64
  end
65
65
  end
66
+
67
+ if RUBY_VERSION < '1.9.0'
68
+ def self.popen(command, &block)
69
+ IO.popen "#{command} 2>&1", 'w+', &block
70
+ end
71
+ else
72
+ def self.popen(command, &block)
73
+ IO.popen command, 'w+', :err => :out, &block
74
+ end
75
+ end
76
+
77
+ # The following is an adaptation of ruby 1.9.2's shellwords.rb file,
78
+ # it is modified to include '+' in the allowed list to allow for
79
+ # sendmail to accept email addresses as the sender with a + in them.
80
+ def self.shellquote(address)
81
+ # Process as a single byte sequence because not all shell
82
+ # implementations are multibyte aware.
83
+ #
84
+ # A LF cannot be escaped with a backslash because a backslash + LF
85
+ # combo is regarded as line continuation and simply ignored. Strip it.
86
+ escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@])/n, "\\\\\\1").gsub("\n", '')
87
+ %("#{escaped}")
88
+ end
66
89
  end
67
90
  end