mail 2.4.4 → 2.5.5

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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +140 -1
  3. data/CONTRIBUTING.md +4 -4
  4. data/Gemfile +14 -8
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +24 -23
  7. data/Rakefile +3 -22
  8. data/lib/VERSION +2 -2
  9. data/lib/load_parsers.rb +35 -0
  10. data/lib/mail/attachments_list.rb +2 -2
  11. data/lib/mail/body.rb +5 -5
  12. data/lib/mail/check_delivery_params.rb +57 -0
  13. data/lib/mail/configuration.rb +1 -1
  14. data/lib/mail/core_extensions/nil.rb +4 -2
  15. data/lib/mail/core_extensions/object.rb +8 -8
  16. data/lib/mail/core_extensions/smtp.rb +12 -13
  17. data/lib/mail/core_extensions/string.rb +4 -4
  18. data/lib/mail/elements/address.rb +13 -5
  19. data/lib/mail/elements/envelope_from_element.rb +15 -2
  20. data/lib/mail/elements.rb +12 -12
  21. data/lib/mail/encodings/quoted_printable.rb +4 -3
  22. data/lib/mail/encodings.rb +66 -35
  23. data/lib/mail/field.rb +76 -99
  24. data/lib/mail/fields/bcc_field.rb +2 -2
  25. data/lib/mail/fields/cc_field.rb +2 -2
  26. data/lib/mail/fields/comments_field.rb +1 -1
  27. data/lib/mail/fields/common/common_address.rb +19 -4
  28. data/lib/mail/fields/common/common_field.rb +8 -2
  29. data/lib/mail/fields/common/common_message_id.rb +9 -5
  30. data/lib/mail/fields/content_disposition_field.rb +1 -0
  31. data/lib/mail/fields/content_id_field.rb +1 -2
  32. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -2
  33. data/lib/mail/fields/content_type_field.rb +5 -2
  34. data/lib/mail/fields/date_field.rb +14 -14
  35. data/lib/mail/fields/from_field.rb +2 -2
  36. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  37. data/lib/mail/fields/keywords_field.rb +1 -1
  38. data/lib/mail/fields/message_id_field.rb +2 -3
  39. data/lib/mail/fields/references_field.rb +2 -1
  40. data/lib/mail/fields/reply_to_field.rb +2 -2
  41. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  42. data/lib/mail/fields/resent_cc_field.rb +2 -2
  43. data/lib/mail/fields/resent_from_field.rb +2 -2
  44. data/lib/mail/fields/resent_sender_field.rb +2 -2
  45. data/lib/mail/fields/resent_to_field.rb +2 -2
  46. data/lib/mail/fields/sender_field.rb +7 -7
  47. data/lib/mail/fields/to_field.rb +2 -2
  48. data/lib/mail/fields/unstructured_field.rb +34 -27
  49. data/lib/mail/fields.rb +32 -32
  50. data/lib/mail/header.rb +37 -14
  51. data/lib/mail/message.rb +140 -45
  52. data/lib/mail/multibyte/chars.rb +4 -4
  53. data/lib/mail/multibyte/unicode.rb +8 -0
  54. data/lib/mail/network/delivery_methods/exim.rb +6 -11
  55. data/lib/mail/network/delivery_methods/file_delivery.rb +7 -6
  56. data/lib/mail/network/delivery_methods/sendmail.rb +40 -11
  57. data/lib/mail/network/delivery_methods/smtp.rb +33 -47
  58. data/lib/mail/network/delivery_methods/smtp_connection.rb +7 -24
  59. data/lib/mail/network/delivery_methods/test_mailer.rb +9 -8
  60. data/lib/mail/network/retriever_methods/imap.rb +14 -6
  61. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  62. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  63. data/lib/mail/network.rb +9 -9
  64. data/lib/mail/parsers/content_transfer_encoding.rb +81 -42
  65. data/lib/mail/parsers/content_transfer_encoding.treetop +4 -6
  66. data/lib/mail/parsers/content_type.rb +16 -12
  67. data/lib/mail/parsers/content_type.treetop +2 -2
  68. data/lib/mail/parsers/rfc2045.rb +12 -55
  69. data/lib/mail/parsers/rfc2045.treetop +1 -2
  70. data/lib/mail/parsers/rfc2822.rb +127 -71
  71. data/lib/mail/parsers/rfc2822.treetop +22 -24
  72. data/lib/mail/part.rb +6 -2
  73. data/lib/mail/parts_list.rb +1 -1
  74. data/lib/mail/patterns.rb +1 -1
  75. data/lib/mail/utilities.rb +25 -17
  76. data/lib/mail/values/unicode_tables.dat +0 -0
  77. data/lib/mail/version_specific/ruby_1_8.rb +23 -2
  78. data/lib/mail/version_specific/ruby_1_9.rb +55 -21
  79. data/lib/mail.rb +18 -18
  80. metadata +89 -37
  81. data/Gemfile.lock +0 -36
  82. data/lib/mail/core_extensions/shell_escape.rb +0 -56
data/lib/mail/fields.rb CHANGED
@@ -1,35 +1,35 @@
1
1
  module Mail
2
- autoload :UnstructuredField, 'mail/fields/unstructured_field'
3
- autoload :StructuredField, 'mail/fields/structured_field'
4
- autoload :OptionalField, 'mail/fields/optional_field'
2
+ register_autoload :UnstructuredField, 'mail/fields/unstructured_field'
3
+ register_autoload :StructuredField, 'mail/fields/structured_field'
4
+ register_autoload :OptionalField, 'mail/fields/optional_field'
5
5
 
6
- autoload :BccField, 'mail/fields/bcc_field'
7
- autoload :CcField, 'mail/fields/cc_field'
8
- autoload :CommentsField, 'mail/fields/comments_field'
9
- autoload :ContentDescriptionField, 'mail/fields/content_description_field'
10
- autoload :ContentDispositionField, 'mail/fields/content_disposition_field'
11
- autoload :ContentIdField, 'mail/fields/content_id_field'
12
- autoload :ContentLocationField, 'mail/fields/content_location_field'
13
- autoload :ContentTransferEncodingField, 'mail/fields/content_transfer_encoding_field'
14
- autoload :ContentTypeField, 'mail/fields/content_type_field'
15
- autoload :DateField, 'mail/fields/date_field'
16
- autoload :FromField, 'mail/fields/from_field'
17
- autoload :InReplyToField, 'mail/fields/in_reply_to_field'
18
- autoload :KeywordsField, 'mail/fields/keywords_field'
19
- autoload :MessageIdField, 'mail/fields/message_id_field'
20
- autoload :MimeVersionField, 'mail/fields/mime_version_field'
21
- autoload :ReceivedField, 'mail/fields/received_field'
22
- autoload :ReferencesField, 'mail/fields/references_field'
23
- autoload :ReplyToField, 'mail/fields/reply_to_field'
24
- autoload :ResentBccField, 'mail/fields/resent_bcc_field'
25
- autoload :ResentCcField, 'mail/fields/resent_cc_field'
26
- autoload :ResentDateField, 'mail/fields/resent_date_field'
27
- autoload :ResentFromField, 'mail/fields/resent_from_field'
28
- autoload :ResentMessageIdField, 'mail/fields/resent_message_id_field'
29
- autoload :ResentSenderField, 'mail/fields/resent_sender_field'
30
- autoload :ResentToField, 'mail/fields/resent_to_field'
31
- autoload :ReturnPathField, 'mail/fields/return_path_field'
32
- autoload :SenderField, 'mail/fields/sender_field'
33
- autoload :SubjectField, 'mail/fields/subject_field'
34
- autoload :ToField, 'mail/fields/to_field'
6
+ register_autoload :BccField, 'mail/fields/bcc_field'
7
+ register_autoload :CcField, 'mail/fields/cc_field'
8
+ register_autoload :CommentsField, 'mail/fields/comments_field'
9
+ register_autoload :ContentDescriptionField, 'mail/fields/content_description_field'
10
+ register_autoload :ContentDispositionField, 'mail/fields/content_disposition_field'
11
+ register_autoload :ContentIdField, 'mail/fields/content_id_field'
12
+ register_autoload :ContentLocationField, 'mail/fields/content_location_field'
13
+ register_autoload :ContentTransferEncodingField, 'mail/fields/content_transfer_encoding_field'
14
+ register_autoload :ContentTypeField, 'mail/fields/content_type_field'
15
+ register_autoload :DateField, 'mail/fields/date_field'
16
+ register_autoload :FromField, 'mail/fields/from_field'
17
+ register_autoload :InReplyToField, 'mail/fields/in_reply_to_field'
18
+ register_autoload :KeywordsField, 'mail/fields/keywords_field'
19
+ register_autoload :MessageIdField, 'mail/fields/message_id_field'
20
+ register_autoload :MimeVersionField, 'mail/fields/mime_version_field'
21
+ register_autoload :ReceivedField, 'mail/fields/received_field'
22
+ register_autoload :ReferencesField, 'mail/fields/references_field'
23
+ register_autoload :ReplyToField, 'mail/fields/reply_to_field'
24
+ register_autoload :ResentBccField, 'mail/fields/resent_bcc_field'
25
+ register_autoload :ResentCcField, 'mail/fields/resent_cc_field'
26
+ register_autoload :ResentDateField, 'mail/fields/resent_date_field'
27
+ register_autoload :ResentFromField, 'mail/fields/resent_from_field'
28
+ register_autoload :ResentMessageIdField, 'mail/fields/resent_message_id_field'
29
+ register_autoload :ResentSenderField, 'mail/fields/resent_sender_field'
30
+ register_autoload :ResentToField, 'mail/fields/resent_to_field'
31
+ register_autoload :ReturnPathField, 'mail/fields/return_path_field'
32
+ register_autoload :SenderField, 'mail/fields/sender_field'
33
+ register_autoload :SubjectField, 'mail/fields/subject_field'
34
+ register_autoload :ToField, 'mail/fields/to_field'
35
35
  end
data/lib/mail/header.rb CHANGED
@@ -21,6 +21,20 @@ module Mail
21
21
  include Utilities
22
22
  include Enumerable
23
23
 
24
+ @@maximum_amount = 1000
25
+
26
+ # Large amount of headers in Email might create extra high CPU load
27
+ # Use this parameter to limit number of headers that will be parsed by
28
+ # mail library.
29
+ # Default: 1000
30
+ def self.maximum_amount
31
+ @@maximum_amount
32
+ end
33
+
34
+ def self.maximum_amount=(value)
35
+ @@maximum_amount = value
36
+ end
37
+
24
38
  # Creates a new header object.
25
39
  #
26
40
  # Accepts raw text or nothing. If given raw text will attempt to parse
@@ -36,7 +50,7 @@ module Mail
36
50
  def initialize(header_text = nil, charset = nil)
37
51
  @errors = []
38
52
  @charset = charset
39
- self.raw_source = header_text.to_crlf
53
+ self.raw_source = header_text.to_crlf.lstrip
40
54
  split_header if header_text
41
55
  end
42
56
 
@@ -73,14 +87,12 @@ module Mail
73
87
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
74
88
  def fields=(unfolded_fields)
75
89
  @fields = Mail::FieldList.new
76
- warn "Warning: more than 1000 header fields only using the first 1000" if unfolded_fields.length > 1000
77
- unfolded_fields[0..1000].each do |field|
90
+ warn "Warning: more than #{self.class.maximum_amount} header fields only using the first #{self.class.maximum_amount}" if unfolded_fields.length > self.class.maximum_amount
91
+ unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
78
92
 
79
93
  field = Field.new(field, nil, charset)
80
94
  field.errors.each { |error| self.errors << error }
81
- selected = select_field_for(field.name)
82
-
83
- if selected.any? && limited_field?(field.name)
95
+ if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
84
96
  selected.first.update(field.name, field.value)
85
97
  else
86
98
  @fields << field
@@ -142,6 +154,9 @@ module Mail
142
154
  # h['X-Mail-SPAM'] # => nil
143
155
  def []=(name, value)
144
156
  name = dasherize(name)
157
+ if name.include?(':')
158
+ raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
159
+ end
145
160
  fn = name.downcase
146
161
  selected = select_field_for(fn)
147
162
 
@@ -159,15 +174,15 @@ module Mail
159
174
  # Need to insert in correct order for trace fields
160
175
  self.fields << Field.new(name.to_s, value, charset)
161
176
  end
177
+ if dasherize(fn) == "content-type"
178
+ # Update charset if specified in Content-Type
179
+ params = self[:content_type].parameters rescue nil
180
+ @charset = params && params[:charset]
181
+ end
162
182
  end
163
183
 
164
184
  def charset
165
- params = self[:content_type].parameters rescue nil
166
- if params
167
- params[:charset]
168
- else
169
- @charset
170
- end
185
+ @charset
171
186
  end
172
187
 
173
188
  def charset=(val)
@@ -186,6 +201,7 @@ module Mail
186
201
 
187
202
  def encoded
188
203
  buffer = ''
204
+ buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
189
205
  fields.each do |field|
190
206
  buffer << field.encoded
191
207
  end
@@ -254,12 +270,19 @@ module Mail
254
270
  end
255
271
 
256
272
  def select_field_for(name)
257
- fields.select { |f| f.responsible_for?(name.to_s) }
273
+ fields.select { |f| f.responsible_for?(name) }
258
274
  end
259
275
 
260
276
  def limited_field?(name)
261
277
  LIMITED_FIELDS.include?(name.to_s.downcase)
262
278
  end
263
-
279
+
280
+ # Enumerable support; yield each field in order to the block if there is one,
281
+ # or return an Enumerator for them if there isn't.
282
+ def each( &block )
283
+ return self.fields.each( &block ) if block
284
+ self.fields.each
285
+ end
286
+
264
287
  end
265
288
  end
data/lib/mail/message.rb CHANGED
@@ -108,6 +108,9 @@ module Mail
108
108
  @charset = 'UTF-8'
109
109
  @defaulted_charset = true
110
110
 
111
+ @smtp_envelope_from = nil
112
+ @smtp_envelope_to = nil
113
+
111
114
  @perform_deliveries = true
112
115
  @raise_delivery_errors = true
113
116
 
@@ -122,7 +125,7 @@ module Mail
122
125
  if args.flatten.first.respond_to?(:each_pair)
123
126
  init_with_hash(args.flatten.first)
124
127
  else
125
- init_with_string(args.flatten[0].to_s.strip)
128
+ init_with_string(args.flatten[0].to_s)
126
129
  end
127
130
 
128
131
  if block_given?
@@ -169,7 +172,7 @@ module Mail
169
172
  # obj.mail.deliver
170
173
  #
171
174
  # 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
175
+ # which then can just yield and let Mail do its own private do_delivery
173
176
  # method.
174
177
  attr_accessor :delivery_handler
175
178
 
@@ -938,8 +941,8 @@ module Mail
938
941
  #
939
942
  # Example:
940
943
  #
941
- # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
942
- # mail.sender #=> 'mikel@test.lindsaar.net'
944
+ # mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
945
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
943
946
  def resent_sender=( val )
944
947
  header[:resent_sender] = val
945
948
  end
@@ -1023,6 +1026,82 @@ module Mail
1023
1026
  header[:sender] = val
1024
1027
  end
1025
1028
 
1029
+ # Returns the SMTP Envelope From value of the mail object, as a single
1030
+ # string of an address spec.
1031
+ #
1032
+ # Defaults to Return-Path, Sender, or the first From address.
1033
+ #
1034
+ # Example:
1035
+ #
1036
+ # mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
1037
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1038
+ #
1039
+ # Also allows you to set the value by passing a value as a parameter
1040
+ #
1041
+ # Example:
1042
+ #
1043
+ # mail.smtp_envelope_from 'Mikel <mikel@test.lindsaar.net>'
1044
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1045
+ def smtp_envelope_from( val = nil )
1046
+ if val
1047
+ self.smtp_envelope_from = val
1048
+ else
1049
+ @smtp_envelope_from || return_path || sender || from_addrs.first
1050
+ end
1051
+ end
1052
+
1053
+ # Sets the From address on the SMTP Envelope.
1054
+ #
1055
+ # Example:
1056
+ #
1057
+ # mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
1058
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1059
+ def smtp_envelope_from=( val )
1060
+ @smtp_envelope_from = val
1061
+ end
1062
+
1063
+ # Returns the SMTP Envelope To value of the mail object.
1064
+ #
1065
+ # Defaults to #destinations: To, Cc, and Bcc addresses.
1066
+ #
1067
+ # Example:
1068
+ #
1069
+ # mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
1070
+ # mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
1071
+ #
1072
+ # Also allows you to set the value by passing a value as a parameter
1073
+ #
1074
+ # Example:
1075
+ #
1076
+ # mail.smtp_envelope_to ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
1077
+ # mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
1078
+ def smtp_envelope_to( val = nil )
1079
+ if val
1080
+ self.smtp_envelope_to = val
1081
+ else
1082
+ @smtp_envelope_to || destinations
1083
+ end
1084
+ end
1085
+
1086
+ # Sets the To addresses on the SMTP Envelope.
1087
+ #
1088
+ # Example:
1089
+ #
1090
+ # mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
1091
+ # mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
1092
+ #
1093
+ # mail.smtp_envelope_to = ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
1094
+ # mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
1095
+ def smtp_envelope_to=( val )
1096
+ @smtp_envelope_to =
1097
+ case val
1098
+ when Array, NilClass
1099
+ val
1100
+ else
1101
+ [val]
1102
+ end
1103
+ end
1104
+
1026
1105
  # Returns the decoded value of the subject field, as a single string.
1027
1106
  #
1028
1107
  # Example:
@@ -1411,7 +1490,7 @@ module Mail
1411
1490
 
1412
1491
  # Returns the MIME media type of part we are on, this is taken from the content-type header
1413
1492
  def mime_type
1414
- content_type ? header[:content_type].string : nil rescue nil
1493
+ has_content_type? ? header[:content_type].string : nil rescue nil
1415
1494
  end
1416
1495
 
1417
1496
  def message_content_type
@@ -1422,7 +1501,7 @@ module Mail
1422
1501
  # Returns the character set defined in the content type field
1423
1502
  def charset
1424
1503
  if @header
1425
- content_type ? content_type_parameters['charset'] : @charset
1504
+ has_content_type? ? content_type_parameters['charset'] : @charset
1426
1505
  else
1427
1506
  @charset
1428
1507
  end
@@ -1516,7 +1595,7 @@ module Mail
1516
1595
 
1517
1596
  # Returns an AttachmentsList object, which holds all of the attachments in
1518
1597
  # the receiver object (either the entier email or a part within) and all
1519
- # of it's descendants.
1598
+ # of its descendants.
1520
1599
  #
1521
1600
  # It also allows you to add attachments to the mail object directly, like so:
1522
1601
  #
@@ -1559,9 +1638,7 @@ module Mail
1559
1638
  # Accessor for html_part
1560
1639
  def html_part(&block)
1561
1640
  if block_given?
1562
- @html_part = Mail::Part.new(&block)
1563
- add_multipart_alternate_header unless html_part.blank?
1564
- add_part(@html_part)
1641
+ self.html_part = Mail::Part.new(:content_type => 'text/html', &block)
1565
1642
  else
1566
1643
  @html_part || find_first_mime_type('text/html')
1567
1644
  end
@@ -1570,9 +1647,7 @@ module Mail
1570
1647
  # Accessor for text_part
1571
1648
  def text_part(&block)
1572
1649
  if block_given?
1573
- @text_part = Mail::Part.new(&block)
1574
- add_multipart_alternate_header unless html_part.blank?
1575
- add_part(@text_part)
1650
+ self.text_part = Mail::Part.new(:content_type => 'text/plain', &block)
1576
1651
  else
1577
1652
  @text_part || find_first_mime_type('text/plain')
1578
1653
  end
@@ -1581,36 +1656,54 @@ module Mail
1581
1656
  # Helper to add a html part to a multipart/alternative email. If this and
1582
1657
  # text_part are both defined in a message, then it will be a multipart/alternative
1583
1658
  # message and set itself that way.
1584
- def html_part=(msg = nil)
1659
+ def html_part=(msg)
1660
+ # Assign the html part and set multipart/alternative if there's a text part.
1585
1661
  if msg
1586
1662
  @html_part = msg
1587
- else
1588
- @html_part = Mail::Part.new('Content-Type: text/html;')
1663
+ @html_part.content_type = 'text/html' unless @html_part.has_content_type?
1664
+ add_multipart_alternate_header if text_part
1665
+ add_part @html_part
1666
+
1667
+ # If nil, delete the html part and back out of multipart/alternative.
1668
+ elsif @html_part
1669
+ parts.delete_if { |p| p.object_id == @html_part.object_id }
1670
+ @html_part = nil
1671
+ if text_part
1672
+ self.content_type = nil
1673
+ body.boundary = nil
1674
+ end
1589
1675
  end
1590
- add_multipart_alternate_header unless text_part.blank?
1591
- add_part(@html_part)
1592
1676
  end
1593
1677
 
1594
1678
  # Helper to add a text part to a multipart/alternative email. If this and
1595
1679
  # html_part are both defined in a message, then it will be a multipart/alternative
1596
1680
  # message and set itself that way.
1597
- def text_part=(msg = nil)
1681
+ def text_part=(msg)
1682
+ # Assign the text part and set multipart/alternative if there's an html part.
1598
1683
  if msg
1599
1684
  @text_part = msg
1600
- else
1601
- @text_part = Mail::Part.new('Content-Type: text/plain;')
1685
+ @text_part.content_type = 'text/plain' unless @text_part.has_content_type?
1686
+ add_multipart_alternate_header if html_part
1687
+ add_part @text_part
1688
+
1689
+ # If nil, delete the text part and back out of multipart/alternative.
1690
+ elsif @text_part
1691
+ parts.delete_if { |p| p.object_id == @text_part.object_id }
1692
+ @text_part = nil
1693
+ if html_part
1694
+ self.content_type = nil
1695
+ body.boundary = nil
1696
+ end
1602
1697
  end
1603
- add_multipart_alternate_header unless html_part.blank?
1604
- add_part(@text_part)
1605
1698
  end
1606
1699
 
1607
1700
  # Adds a part to the parts list or creates the part list
1608
1701
  def add_part(part)
1609
1702
  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
1703
+ @text_part = Mail::Part.new('Content-Type: text/plain;')
1704
+ @text_part.body = body.decoded
1705
+ self.body << @text_part
1706
+ add_multipart_alternate_header
1614
1707
  end
1615
1708
  add_boundary
1616
1709
  self.body << part
@@ -1646,7 +1739,7 @@ module Mail
1646
1739
  # m.add_file(:filename => 'filename.png', :content => File.read('/path/to/file.jpg'))
1647
1740
  #
1648
1741
  # 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
1742
+ # to a MIME multipart email, moving whatever plain text body you had into its own text
1650
1743
  # plain part.
1651
1744
  #
1652
1745
  # Example:
@@ -1683,7 +1776,7 @@ module Mail
1683
1776
  self.body << text_part
1684
1777
  end
1685
1778
 
1686
- # Encodes the message, calls encode on all it's parts, gets an email message
1779
+ # Encodes the message, calls encode on all its parts, gets an email message
1687
1780
  # ready to send
1688
1781
  def ready_to_send!
1689
1782
  identify_and_set_transfer_encoding
@@ -1875,15 +1968,13 @@ module Mail
1875
1968
  # Additionally, I allow for the case where someone might have put whitespace
1876
1969
  # on the "gap line"
1877
1970
  def parse_message
1878
- header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
1879
- # index = raw_source.index(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
1880
- # self.header = (index) ? header_part[0,index] : nil
1881
- # lazy_body ( [raw_source, index+1])
1971
+ header_part, body_part = raw_source.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
1882
1972
  self.header = header_part
1883
1973
  self.body = body_part
1884
1974
  end
1885
1975
 
1886
1976
  def raw_source=(value)
1977
+ value.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1887
1978
  @raw_source = value.to_crlf
1888
1979
  end
1889
1980
 
@@ -1906,17 +1997,18 @@ module Mail
1906
1997
 
1907
1998
 
1908
1999
  def process_body_raw
1909
- @body = Mail::Body.new(@body_raw)
1910
- @body_raw = nil
1911
- separate_parts if @separate_parts
2000
+ @body = Mail::Body.new(@body_raw)
2001
+ @body_raw = nil
2002
+ separate_parts if @separate_parts
1912
2003
 
1913
- add_encoding_to_body
2004
+ add_encoding_to_body
1914
2005
  end
1915
2006
 
1916
2007
  def set_envelope_header
1917
- if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}(.*)/m)
2008
+ raw_string = raw_source.to_s
2009
+ if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
1918
2010
  set_envelope(match_data[1])
1919
- self.raw_source = match_data[2]
2011
+ self.raw_source = raw_string.sub(match_data[0], "")
1920
2012
  end
1921
2013
  end
1922
2014
 
@@ -1939,16 +2031,19 @@ module Mail
1939
2031
  end
1940
2032
 
1941
2033
  def add_required_fields
1942
- add_multipart_mixed_header unless !body.multipart?
1943
- body = nil if body.nil?
1944
- add_message_id unless (has_message_id? || self.class == Mail::Part)
1945
- add_date unless has_date?
1946
- add_mime_version unless has_mime_version?
2034
+ add_required_message_fields
2035
+ add_multipart_mixed_header if body.multipart?
1947
2036
  add_content_type unless has_content_type?
1948
2037
  add_charset unless has_charset?
1949
2038
  add_content_transfer_encoding unless has_content_transfer_encoding?
1950
2039
  end
1951
2040
 
2041
+ def add_required_message_fields
2042
+ add_date unless has_date?
2043
+ add_mime_version unless has_mime_version?
2044
+ add_message_id unless has_message_id?
2045
+ end
2046
+
1952
2047
  def add_multipart_alternate_header
1953
2048
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
1954
2049
  header['content_type'].parameters[:charset] = @charset
@@ -2047,7 +2142,7 @@ module Mail
2047
2142
  else
2048
2143
  if encoding = Encoding.find(charset) rescue nil
2049
2144
  body_text.force_encoding(encoding)
2050
- return body_text.encode(Encoding::UTF_8)
2145
+ return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
2051
2146
  end
2052
2147
  end
2053
2148
  end
@@ -339,7 +339,7 @@ module Mail #:nodoc:
339
339
  # Example:
340
340
  # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
341
341
  def upcase
342
- chars(Unicode.apply_mapping(@wrapped_string), :uppercase_mapping)
342
+ chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping))
343
343
  end
344
344
 
345
345
  # Convert characters in the string to lowercase.
@@ -347,7 +347,7 @@ module Mail #:nodoc:
347
347
  # Example:
348
348
  # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
349
349
  def downcase
350
- chars(Unicode.apply_mapping(@wrapped_string), :lowercase_mapping)
350
+ chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping))
351
351
  end
352
352
 
353
353
  # Converts the first character to uppercase and the remainder to lowercase.
@@ -364,7 +364,7 @@ module Mail #:nodoc:
364
364
  # "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
365
365
  # "日本語".mb_chars.titleize # => "日本語"
366
366
  def titleize
367
- chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.apply_mapping $1, :uppercase_mapping })
367
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping })
368
368
  end
369
369
  alias_method :titlecase, :titleize
370
370
 
@@ -412,7 +412,7 @@ module Mail #:nodoc:
412
412
  chars(Unicode.tidy_bytes(@wrapped_string, force))
413
413
  end
414
414
 
415
- %w(capitalize downcase lstrip reverse rstrip slice strip tidy_bytes upcase).each do |method|
415
+ %w(capitalize downcase lstrip reverse rstrip slice strip tidy_bytes upcase).each do |method|
416
416
  # Only define a corresponding bang method for methods defined in the proxy; On 1.9 the proxy will
417
417
  # exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
418
418
  if public_method_defined?(method)
@@ -390,3 +390,11 @@ module Mail
390
390
  end
391
391
  end
392
392
  end
393
+
394
+ unless defined?(ActiveSupport)
395
+ module ActiveSupport
396
+ unless const_defined?(:Multibyte)
397
+ Multibyte = Mail::Multibyte
398
+ end
399
+ end
400
+ end
@@ -36,18 +36,13 @@ module Mail
36
36
  #
37
37
  # mail.deliver!
38
38
  class Exim < Sendmail
39
+ DEFAULTS = {
40
+ :location => '/usr/sbin/exim',
41
+ :arguments => '-i -t'
42
+ }
39
43
 
40
- def initialize(values)
41
- self.settings = { :location => '/usr/sbin/exim',
42
- :arguments => '-i -t' }.merge(values)
44
+ def self.call(path, arguments, destinations, encoded_message)
45
+ super path, arguments, nil, encoded_message
43
46
  end
44
-
45
- def self.call(path, arguments, mail)
46
- IO.popen("#{path} #{arguments}", "w+") do |io|
47
- io.puts mail.encoded.to_lf
48
- io.flush
49
- end
50
- end
51
-
52
47
  end
53
48
  end
@@ -1,5 +1,6 @@
1
+ require 'mail/check_delivery_params'
2
+
1
3
  module Mail
2
-
3
4
  # FileDelivery class delivers emails into multiple files based on the destination
4
5
  # address. Each file is appended to if it already exists.
5
6
  #
@@ -11,20 +12,21 @@ module Mail
11
12
  # Make sure the path you specify with :location is writable by the Ruby process
12
13
  # running Mail.
13
14
  class FileDelivery
14
-
15
15
  if RUBY_VERSION >= '1.9.1'
16
16
  require 'fileutils'
17
17
  else
18
18
  require 'ftools'
19
19
  end
20
20
 
21
+ attr_accessor :settings
22
+
21
23
  def initialize(values)
22
24
  self.settings = { :location => './mails' }.merge!(values)
23
25
  end
24
-
25
- attr_accessor :settings
26
-
26
+
27
27
  def deliver!(mail)
28
+ Mail::CheckDeliveryParams.check(mail)
29
+
28
30
  if ::File.respond_to?(:makedirs)
29
31
  ::File.makedirs settings[:location]
30
32
  else
@@ -35,6 +37,5 @@ module Mail
35
37
  ::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
36
38
  end
37
39
  end
38
-
39
40
  end
40
41
  end