mail 2.5.3 → 2.6.0

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 (151) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +70 -0
  3. data/CONTRIBUTING.md +17 -4
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +50 -29
  8. data/Rakefile +11 -20
  9. data/VERSION +4 -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 +12 -22
  13. data/lib/mail/core_extensions/object.rb +8 -8
  14. data/lib/mail/core_extensions/smtp.rb +12 -13
  15. data/lib/mail/core_extensions/string.rb +16 -6
  16. data/lib/mail/elements/address.rb +43 -79
  17. data/lib/mail/elements/address_list.rb +19 -42
  18. data/lib/mail/elements/content_disposition_element.rb +3 -7
  19. data/lib/mail/elements/content_location_element.rb +2 -6
  20. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  21. data/lib/mail/elements/content_type_element.rb +4 -8
  22. data/lib/mail/elements/date_time_element.rb +3 -7
  23. data/lib/mail/elements/envelope_from_element.rb +18 -13
  24. data/lib/mail/elements/message_ids_element.rb +1 -6
  25. data/lib/mail/elements/mime_version_element.rb +3 -7
  26. data/lib/mail/elements/phrase_list.rb +2 -7
  27. data/lib/mail/elements/received_element.rb +3 -7
  28. data/lib/mail/encodings/quoted_printable.rb +4 -3
  29. data/lib/mail/encodings.rb +60 -28
  30. data/lib/mail/envelope.rb +0 -5
  31. data/lib/mail/field.rb +63 -28
  32. data/lib/mail/field_list.rb +18 -18
  33. data/lib/mail/fields/bcc_field.rb +2 -2
  34. data/lib/mail/fields/cc_field.rb +2 -2
  35. data/lib/mail/fields/comments_field.rb +1 -1
  36. data/lib/mail/fields/common/common_address.rb +26 -21
  37. data/lib/mail/fields/common/common_date.rb +0 -7
  38. data/lib/mail/fields/common/common_field.rb +5 -5
  39. data/lib/mail/fields/content_id_field.rb +1 -2
  40. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  41. data/lib/mail/fields/content_type_field.rb +1 -1
  42. data/lib/mail/fields/date_field.rb +1 -1
  43. data/lib/mail/fields/from_field.rb +2 -2
  44. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  45. data/lib/mail/fields/message_id_field.rb +2 -3
  46. data/lib/mail/fields/references_field.rb +2 -1
  47. data/lib/mail/fields/reply_to_field.rb +2 -2
  48. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  49. data/lib/mail/fields/resent_cc_field.rb +2 -2
  50. data/lib/mail/fields/resent_from_field.rb +2 -2
  51. data/lib/mail/fields/resent_sender_field.rb +3 -3
  52. data/lib/mail/fields/resent_to_field.rb +2 -2
  53. data/lib/mail/fields/sender_field.rb +8 -8
  54. data/lib/mail/fields/to_field.rb +2 -2
  55. data/lib/mail/fields/unstructured_field.rb +8 -2
  56. data/lib/mail/header.rb +13 -23
  57. data/lib/mail/mail.rb +12 -0
  58. data/lib/mail/matchers/has_sent_mail.rb +34 -1
  59. data/lib/mail/message.rb +151 -48
  60. data/lib/mail/multibyte/chars.rb +2 -2
  61. data/lib/mail/multibyte/unicode.rb +6 -4
  62. data/lib/mail/network/delivery_methods/exim.rb +1 -6
  63. data/lib/mail/network/delivery_methods/file_delivery.rb +1 -1
  64. data/lib/mail/network/delivery_methods/sendmail.rb +32 -10
  65. data/lib/mail/network/delivery_methods/smtp.rb +31 -34
  66. data/lib/mail/network/delivery_methods/smtp_connection.rb +6 -6
  67. data/lib/mail/network/delivery_methods/test_mailer.rb +2 -2
  68. data/lib/mail/network/retriever_methods/imap.rb +18 -13
  69. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  70. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  71. data/lib/mail/parsers/content_location_parser.rb +35 -0
  72. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  73. data/lib/mail/parsers/content_type_parser.rb +64 -0
  74. data/lib/mail/parsers/date_time_parser.rb +36 -0
  75. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  76. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  77. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  78. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  79. data/lib/mail/parsers/ragel/common.rl +184 -0
  80. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  81. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  82. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  83. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  84. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  85. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  86. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  87. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  88. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  89. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  90. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  91. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  92. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  93. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  105. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  106. data/lib/mail/parsers/ragel/ruby.rb +29 -0
  107. data/lib/mail/parsers/ragel.rb +17 -0
  108. data/lib/mail/parsers/received_parser.rb +47 -0
  109. data/lib/mail/parsers.rb +26 -0
  110. data/lib/mail/part.rb +6 -2
  111. data/lib/mail/parts_list.rb +5 -3
  112. data/lib/mail/patterns.rb +4 -1
  113. data/lib/mail/utilities.rb +28 -18
  114. data/lib/mail/version.rb +1 -1
  115. data/lib/mail/version_specific/ruby_1_8.rb +6 -2
  116. data/lib/mail/version_specific/ruby_1_9.rb +61 -21
  117. data/lib/mail.rb +1 -2
  118. metadata +112 -73
  119. data/lib/VERSION +0 -4
  120. data/lib/load_parsers.rb +0 -35
  121. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  122. data/lib/mail/parsers/address_lists.rb +0 -64
  123. data/lib/mail/parsers/address_lists.treetop +0 -19
  124. data/lib/mail/parsers/content_disposition.rb +0 -535
  125. data/lib/mail/parsers/content_disposition.treetop +0 -46
  126. data/lib/mail/parsers/content_location.rb +0 -139
  127. data/lib/mail/parsers/content_location.treetop +0 -20
  128. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  129. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  130. data/lib/mail/parsers/content_type.rb +0 -967
  131. data/lib/mail/parsers/content_type.treetop +0 -68
  132. data/lib/mail/parsers/date_time.rb +0 -114
  133. data/lib/mail/parsers/date_time.treetop +0 -11
  134. data/lib/mail/parsers/envelope_from.rb +0 -194
  135. data/lib/mail/parsers/envelope_from.treetop +0 -32
  136. data/lib/mail/parsers/message_ids.rb +0 -45
  137. data/lib/mail/parsers/message_ids.treetop +0 -15
  138. data/lib/mail/parsers/mime_version.rb +0 -144
  139. data/lib/mail/parsers/mime_version.treetop +0 -19
  140. data/lib/mail/parsers/phrase_lists.rb +0 -45
  141. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  142. data/lib/mail/parsers/received.rb +0 -71
  143. data/lib/mail/parsers/received.treetop +0 -11
  144. data/lib/mail/parsers/rfc2045.rb +0 -464
  145. data/lib/mail/parsers/rfc2045.treetop +0 -36
  146. data/lib/mail/parsers/rfc2822.rb +0 -5397
  147. data/lib/mail/parsers/rfc2822.treetop +0 -410
  148. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  149. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  150. data/lib/tasks/corpus.rake +0 -125
  151. data/lib/tasks/treetop.rake +0 -10
@@ -6,7 +6,7 @@
6
6
  # field in the email.
7
7
  #
8
8
  # Sending resent_to to a mail message will instantiate a Mail::Field object that
9
- # has a ResentToField as it's field type. This includes all Mail::CommonAddress
9
+ # has a ResentToField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance metods.
11
11
  #
12
12
  # Only one Resent-To field can appear in a header, though it can have multiple
@@ -16,7 +16,7 @@
16
16
  #
17
17
  # mail = Mail.new
18
18
  # mail.resent_to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.resent_to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
19
+ # mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
20
  # mail[:resent_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
21
21
  # mail['resent-to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
22
22
  # mail['Resent-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
@@ -6,7 +6,7 @@
6
6
  # field in the email.
7
7
  #
8
8
  # Sending sender to a mail message will instantiate a Mail::Field object that
9
- # has a SenderField as it's field type. This includes all Mail::CommonAddress
9
+ # has a SenderField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance metods.
11
11
  #
12
12
  # Only one Sender field can appear in a header, though it can have multiple
@@ -15,16 +15,16 @@
15
15
  # == Examples:
16
16
  #
17
17
  # mail = Mail.new
18
- # mail.sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.sender #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
18
+ # mail.sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>'
19
+ # mail.sender #=> 'mikel@test.lindsaar.net'
20
20
  # mail[:sender] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
21
21
  # mail['sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
22
22
  # mail['Sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
23
23
  #
24
- # mail[:sender].encoded #=> 'Sender: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
25
- # mail[:sender].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
- # mail[:sender].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
- # mail[:sender].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
24
+ # mail[:sender].encoded #=> "Sender: Mikel Lindsaar <mikel@test.lindsaar.net>\r\n"
25
+ # mail[:sender].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
26
+ # mail[:sender].addresses #=> ['mikel@test.lindsaar.net']
27
+ # mail[:sender].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>']
28
28
  #
29
29
  require 'mail/fields/common/common_address'
30
30
 
@@ -48,7 +48,7 @@ module Mail
48
48
  end
49
49
 
50
50
  def address
51
- tree.addresses.first
51
+ address_list.addresses.first
52
52
  end
53
53
 
54
54
  def encoded
@@ -6,7 +6,7 @@
6
6
  # field in the email.
7
7
  #
8
8
  # Sending to to a mail message will instantiate a Mail::Field object that
9
- # has a ToField as it's field type. This includes all Mail::CommonAddress
9
+ # has a ToField as its field type. This includes all Mail::CommonAddress
10
10
  # module instance metods.
11
11
  #
12
12
  # Only one To field can appear in a header, though it can have multiple
@@ -16,7 +16,7 @@
16
16
  #
17
17
  # mail = Mail.new
18
18
  # mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
19
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
20
  # mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
21
21
  # mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
22
22
  # mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
@@ -144,9 +144,10 @@ module Mail
144
144
  limit = 78 - prepend
145
145
  limit = limit - 7 - encoding.length if should_encode
146
146
  line = ""
147
+ first_word = true
147
148
  while !words.empty?
148
149
  break unless word = words.first.dup
149
- word.encode!(charset) if defined?(Encoding) && charset
150
+ word.encode!(charset) if charset && word.respond_to?(:encode!)
150
151
  word = encode(word) if should_encode
151
152
  word = encode_crlf(word)
152
153
  # Skip to next line if we're going to go past the limit
@@ -158,7 +159,12 @@ module Mail
158
159
  # Remove the word from the queue ...
159
160
  words.shift
160
161
  # Add word separator
161
- line << " " unless (line.empty? || should_encode)
162
+ if first_word
163
+ first_word = false
164
+ else
165
+ line << " " if !should_encode
166
+ end
167
+
162
168
  # ... add it in encoded form to the current line
163
169
  line << word
164
170
  end
data/lib/mail/header.rb CHANGED
@@ -48,11 +48,15 @@ module Mail
48
48
  # these cases, please make a patch and send it in, or at the least, send
49
49
  # me the example so we can fix it.
50
50
  def initialize(header_text = nil, charset = nil)
51
- @errors = []
52
51
  @charset = charset
53
- self.raw_source = header_text.to_crlf
52
+ self.raw_source = header_text.to_crlf.lstrip
54
53
  split_header if header_text
55
54
  end
55
+
56
+ def initialize_copy(original)
57
+ super
58
+ @fields = @fields.dup
59
+ end
56
60
 
57
61
  # The preserved raw source of the header as you passed it in, untouched
58
62
  # for your Regexing glory.
@@ -91,7 +95,6 @@ module Mail
91
95
  unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
92
96
 
93
97
  field = Field.new(field, nil, charset)
94
- field.errors.each { |error| self.errors << error }
95
98
  if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
96
99
  selected.first.update(field.name, field.value)
97
100
  else
@@ -102,7 +105,7 @@ module Mail
102
105
  end
103
106
 
104
107
  def errors
105
- @errors
108
+ @fields.map(&:errors).flatten(1)
106
109
  end
107
110
 
108
111
  # 3.6. Field definitions
@@ -154,6 +157,9 @@ module Mail
154
157
  # h['X-Mail-SPAM'] # => nil
155
158
  def []=(name, value)
156
159
  name = dasherize(name)
160
+ if name.include?(':')
161
+ raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
162
+ end
157
163
  fn = name.downcase
158
164
  selected = select_field_for(fn)
159
165
 
@@ -174,7 +180,7 @@ module Mail
174
180
  if dasherize(fn) == "content-type"
175
181
  # Update charset if specified in Content-Type
176
182
  params = self[:content_type].parameters rescue nil
177
- @charset = params && params[:charset]
183
+ @charset = params[:charset] if params && params[:charset]
178
184
  end
179
185
  end
180
186
 
@@ -198,6 +204,7 @@ module Mail
198
204
 
199
205
  def encoded
200
206
  buffer = ''
207
+ buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
201
208
  fields.each do |field|
202
209
  buffer << field.encoded
203
210
  end
@@ -242,27 +249,10 @@ module Mail
242
249
  @raw_source = val
243
250
  end
244
251
 
245
- # 2.2.3. Long Header Fields
246
- #
247
- # The process of moving from this folded multiple-line representation
248
- # of a header field to its single line representation is called
249
- # "unfolding". Unfolding is accomplished by simply removing any CRLF
250
- # that is immediately followed by WSP. Each header field should be
251
- # treated in its unfolded form for further syntactic and semantic
252
- # evaluation.
253
- def unfold(string)
254
- string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
255
- end
256
-
257
- # Returns the header with all the folds removed
258
- def unfolded_header
259
- @unfolded_header ||= unfold(raw_source)
260
- end
261
-
262
252
  # Splits an unfolded and line break cleaned header into individual field
263
253
  # strings.
264
254
  def split_header
265
- self.fields = unfolded_header.split(CRLF)
255
+ self.fields = raw_source.split(HEADER_SPLIT)
266
256
  end
267
257
 
268
258
  def select_field_for(name)
data/lib/mail/mail.rb CHANGED
@@ -206,6 +206,12 @@ module Mail
206
206
  end
207
207
  end
208
208
 
209
+ # Unregister the given observer, allowing mail to resume operations
210
+ # without it.
211
+ def self.unregister_observer(observer)
212
+ @@delivery_notification_observers.delete(observer)
213
+ end
214
+
209
215
  # You can register an object to be given every mail object that will be sent,
210
216
  # before it is sent. So if you want to add special headers or modify any
211
217
  # email that gets sent through the Mail library, you can do so.
@@ -219,6 +225,12 @@ module Mail
219
225
  end
220
226
  end
221
227
 
228
+ # Unregister the given interceptor, allowing mail to resume operations
229
+ # without it.
230
+ def self.unregister_interceptor(interceptor)
231
+ @@delivery_interceptors.delete(interceptor)
232
+ end
233
+
222
234
  def self.inform_observers(mail)
223
235
  @@delivery_notification_observers.each do |observer|
224
236
  observer.delivered_email(mail)
@@ -29,6 +29,29 @@ module Mail
29
29
  self
30
30
  end
31
31
 
32
+ def cc(recipient_or_list)
33
+ @copy_recipients ||= []
34
+
35
+ if recipient_or_list.kind_of?(Array)
36
+ @copy_recipients += recipient_or_list
37
+ else
38
+ @copy_recipients << recipient_or_list
39
+ end
40
+ self
41
+ end
42
+
43
+ def bcc(recipient_or_list)
44
+ @blind_copy_recipients ||= []
45
+
46
+ if recipient_or_list.kind_of?(Array)
47
+ @blind_copy_recipients += recipient_or_list
48
+ else
49
+ @blind_copy_recipients << recipient_or_list
50
+ end
51
+ self
52
+ end
53
+
54
+
32
55
  def with_subject(subject)
33
56
  @subject = subject
34
57
  self
@@ -73,7 +96,7 @@ module Mail
73
96
  def filter_matched_deliveries(deliveries)
74
97
  candidate_deliveries = deliveries
75
98
 
76
- %w(sender recipients subject subject_matcher body body_matcher).each do |modifier_name|
99
+ %w(sender recipients copy_recipients blind_copy_recipients subject subject_matcher body body_matcher).each do |modifier_name|
77
100
  next unless instance_variable_defined?("@#{modifier_name}")
78
101
  candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
79
102
  end
@@ -89,6 +112,14 @@ module Mail
89
112
  @recipients.all? {|recipient| delivery.to.include?(recipient) }
90
113
  end
91
114
 
115
+ def matches_on_copy_recipients?(delivery)
116
+ @copy_recipients.all? {|recipient| delivery.cc.include?(recipient) }
117
+ end
118
+
119
+ def matches_on_blind_copy_recipients?(delivery)
120
+ @blind_copy_recipients.all? {|recipient| delivery.bcc.include?(recipient) }
121
+ end
122
+
92
123
  def matches_on_subject?(delivery)
93
124
  delivery.subject == @subject
94
125
  end
@@ -109,6 +140,8 @@ module Mail
109
140
  result = ''
110
141
  result += "from #{@sender} " if instance_variable_defined?('@sender')
111
142
  result += "to #{@recipients.inspect} " if instance_variable_defined?('@recipients')
143
+ result += "cc #{@copy_recipients.inspect} " if instance_variable_defined?('@copy_recipients')
144
+ result += "bcc #{@blind_copy_recipients.inspect} " if instance_variable_defined?('@blind_copy_recipients')
112
145
  result += "with subject \"#{@subject}\" " if instance_variable_defined?('@subject')
113
146
  result += "with subject matching \"#{@subject_matcher}\" " if instance_variable_defined?('@subject_matcher')
114
147
  result += "with body \"#{@body}\" " if instance_variable_defined?('@body')
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
 
@@ -237,7 +240,7 @@ module Mail
237
240
  # This method bypasses checking perform_deliveries and raise_delivery_errors,
238
241
  # so use with caution.
239
242
  #
240
- # It still however fires off the intercepters and calls the observers callbacks if they are defined.
243
+ # It still however fires off the interceptors and calls the observers callbacks if they are defined.
241
244
  #
242
245
  # Returns self
243
246
  def deliver!
@@ -350,17 +353,23 @@ module Mail
350
353
  return false unless other.respond_to?(:encoded)
351
354
 
352
355
  if self.message_id && other.message_id
353
- result = (self.encoded == other.encoded)
356
+ self.encoded == other.encoded
354
357
  else
355
358
  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
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
361
365
  end
362
366
  end
363
367
 
368
+ def initialize_copy(original)
369
+ super
370
+ @header = @header.dup
371
+ end
372
+
364
373
  # Provides access to the raw source of the message as it was when it
365
374
  # was instantiated. This is set at initialization and so is untouched
366
375
  # by the parsers or decoder / encoders
@@ -938,8 +947,8 @@ module Mail
938
947
  #
939
948
  # Example:
940
949
  #
941
- # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
942
- # mail.sender #=> 'mikel@test.lindsaar.net'
950
+ # mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
951
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
943
952
  def resent_sender=( val )
944
953
  header[:resent_sender] = val
945
954
  end
@@ -1023,6 +1032,82 @@ module Mail
1023
1032
  header[:sender] = val
1024
1033
  end
1025
1034
 
1035
+ # Returns the SMTP Envelope From value of the mail object, as a single
1036
+ # string of an address spec.
1037
+ #
1038
+ # Defaults to Return-Path, Sender, or the first From address.
1039
+ #
1040
+ # Example:
1041
+ #
1042
+ # mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
1043
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1044
+ #
1045
+ # Also allows you to set the value by passing a value as a parameter
1046
+ #
1047
+ # Example:
1048
+ #
1049
+ # mail.smtp_envelope_from 'Mikel <mikel@test.lindsaar.net>'
1050
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1051
+ def smtp_envelope_from( val = nil )
1052
+ if val
1053
+ self.smtp_envelope_from = val
1054
+ else
1055
+ @smtp_envelope_from || return_path || sender || from_addrs.first
1056
+ end
1057
+ end
1058
+
1059
+ # Sets the From address on the SMTP Envelope.
1060
+ #
1061
+ # Example:
1062
+ #
1063
+ # mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
1064
+ # mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
1065
+ def smtp_envelope_from=( val )
1066
+ @smtp_envelope_from = val
1067
+ end
1068
+
1069
+ # Returns the SMTP Envelope To value of the mail object.
1070
+ #
1071
+ # Defaults to #destinations: To, Cc, and Bcc addresses.
1072
+ #
1073
+ # Example:
1074
+ #
1075
+ # mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
1076
+ # mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
1077
+ #
1078
+ # Also allows you to set the value by passing a value as a parameter
1079
+ #
1080
+ # Example:
1081
+ #
1082
+ # mail.smtp_envelope_to ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
1083
+ # mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
1084
+ def smtp_envelope_to( val = nil )
1085
+ if val
1086
+ self.smtp_envelope_to = val
1087
+ else
1088
+ @smtp_envelope_to || destinations
1089
+ end
1090
+ end
1091
+
1092
+ # Sets the To addresses on the SMTP Envelope.
1093
+ #
1094
+ # Example:
1095
+ #
1096
+ # mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
1097
+ # mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
1098
+ #
1099
+ # mail.smtp_envelope_to = ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
1100
+ # mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
1101
+ def smtp_envelope_to=( val )
1102
+ @smtp_envelope_to =
1103
+ case val
1104
+ when Array, NilClass
1105
+ val
1106
+ else
1107
+ [val]
1108
+ end
1109
+ end
1110
+
1026
1111
  # Returns the decoded value of the subject field, as a single string.
1027
1112
  #
1028
1113
  # Example:
@@ -1309,7 +1394,7 @@ module Mail
1309
1394
  header.has_date?
1310
1395
  end
1311
1396
 
1312
- # Returns true if the message has a Date field, the field may or may
1397
+ # Returns true if the message has a Mime-Version field, the field may or may
1313
1398
  # not have a value, but the field exists or not.
1314
1399
  def has_mime_version?
1315
1400
  header.has_mime_version?
@@ -1515,8 +1600,8 @@ module Mail
1515
1600
  end
1516
1601
 
1517
1602
  # 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.
1603
+ # the receiver object (either the entire email or a part within) and all
1604
+ # of its descendants.
1520
1605
  #
1521
1606
  # It also allows you to add attachments to the mail object directly, like so:
1522
1607
  #
@@ -1559,9 +1644,7 @@ module Mail
1559
1644
  # Accessor for html_part
1560
1645
  def html_part(&block)
1561
1646
  if block_given?
1562
- @html_part = Mail::Part.new(&block)
1563
- add_multipart_alternate_header unless html_part.blank?
1564
- add_part(@html_part)
1647
+ self.html_part = Mail::Part.new(:content_type => 'text/html', &block)
1565
1648
  else
1566
1649
  @html_part || find_first_mime_type('text/html')
1567
1650
  end
@@ -1570,9 +1653,7 @@ module Mail
1570
1653
  # Accessor for text_part
1571
1654
  def text_part(&block)
1572
1655
  if block_given?
1573
- @text_part = Mail::Part.new(&block)
1574
- add_multipart_alternate_header unless html_part.blank?
1575
- add_part(@text_part)
1656
+ self.text_part = Mail::Part.new(:content_type => 'text/plain', &block)
1576
1657
  else
1577
1658
  @text_part || find_first_mime_type('text/plain')
1578
1659
  end
@@ -1581,36 +1662,54 @@ module Mail
1581
1662
  # Helper to add a html part to a multipart/alternative email. If this and
1582
1663
  # text_part are both defined in a message, then it will be a multipart/alternative
1583
1664
  # message and set itself that way.
1584
- def html_part=(msg = nil)
1665
+ def html_part=(msg)
1666
+ # Assign the html part and set multipart/alternative if there's a text part.
1585
1667
  if msg
1586
1668
  @html_part = msg
1587
- else
1588
- @html_part = Mail::Part.new('Content-Type: text/html;')
1669
+ @html_part.content_type = 'text/html' unless @html_part.has_content_type?
1670
+ add_multipart_alternate_header if text_part
1671
+ add_part @html_part
1672
+
1673
+ # If nil, delete the html part and back out of multipart/alternative.
1674
+ elsif @html_part
1675
+ parts.delete_if { |p| p.object_id == @html_part.object_id }
1676
+ @html_part = nil
1677
+ if text_part
1678
+ self.content_type = nil
1679
+ body.boundary = nil
1680
+ end
1589
1681
  end
1590
- add_multipart_alternate_header unless text_part.blank?
1591
- add_part(@html_part)
1592
1682
  end
1593
1683
 
1594
1684
  # Helper to add a text part to a multipart/alternative email. If this and
1595
1685
  # html_part are both defined in a message, then it will be a multipart/alternative
1596
1686
  # message and set itself that way.
1597
- def text_part=(msg = nil)
1687
+ def text_part=(msg)
1688
+ # Assign the text part and set multipart/alternative if there's an html part.
1598
1689
  if msg
1599
1690
  @text_part = msg
1600
- else
1601
- @text_part = Mail::Part.new('Content-Type: text/plain;')
1691
+ @text_part.content_type = 'text/plain' unless @text_part.has_content_type?
1692
+ add_multipart_alternate_header if html_part
1693
+ add_part @text_part
1694
+
1695
+ # If nil, delete the text part and back out of multipart/alternative.
1696
+ elsif @text_part
1697
+ parts.delete_if { |p| p.object_id == @text_part.object_id }
1698
+ @text_part = nil
1699
+ if html_part
1700
+ self.content_type = nil
1701
+ body.boundary = nil
1702
+ end
1602
1703
  end
1603
- add_multipart_alternate_header unless html_part.blank?
1604
- add_part(@text_part)
1605
1704
  end
1606
1705
 
1607
1706
  # Adds a part to the parts list or creates the part list
1608
1707
  def add_part(part)
1609
1708
  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
1709
+ @text_part = Mail::Part.new('Content-Type: text/plain;')
1710
+ @text_part.body = body.decoded
1711
+ self.body << @text_part
1712
+ add_multipart_alternate_header
1614
1713
  end
1615
1714
  add_boundary
1616
1715
  self.body << part
@@ -1646,7 +1745,7 @@ module Mail
1646
1745
  # m.add_file(:filename => 'filename.png', :content => File.read('/path/to/file.jpg'))
1647
1746
  #
1648
1747
  # 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
1748
+ # to a MIME multipart email, moving whatever plain text body you had into its own text
1650
1749
  # plain part.
1651
1750
  #
1652
1751
  # Example:
@@ -1683,7 +1782,7 @@ module Mail
1683
1782
  self.body << text_part
1684
1783
  end
1685
1784
 
1686
- # Encodes the message, calls encode on all it's parts, gets an email message
1785
+ # Encodes the message, calls encode on all its parts, gets an email message
1687
1786
  # ready to send
1688
1787
  def ready_to_send!
1689
1788
  identify_and_set_transfer_encoding
@@ -1875,14 +1974,15 @@ module Mail
1875
1974
  # Additionally, I allow for the case where someone might have put whitespace
1876
1975
  # on the "gap line"
1877
1976
  def parse_message
1878
- header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
1977
+ header_part, body_part = raw_source.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
1879
1978
  self.header = header_part
1880
1979
  self.body = body_part
1881
1980
  end
1882
1981
 
1883
1982
  def raw_source=(value)
1884
- value.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1885
1983
  @raw_source = value.to_crlf
1984
+ @raw_source.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1985
+ @raw_source
1886
1986
  end
1887
1987
 
1888
1988
  # see comments to body=. We take data and process it lazily
@@ -1904,11 +2004,11 @@ module Mail
1904
2004
 
1905
2005
 
1906
2006
  def process_body_raw
1907
- @body = Mail::Body.new(@body_raw)
1908
- @body_raw = nil
1909
- separate_parts if @separate_parts
2007
+ @body = Mail::Body.new(@body_raw)
2008
+ @body_raw = nil
2009
+ separate_parts if @separate_parts
1910
2010
 
1911
- add_encoding_to_body
2011
+ add_encoding_to_body
1912
2012
  end
1913
2013
 
1914
2014
  def set_envelope_header
@@ -1938,16 +2038,19 @@ module Mail
1938
2038
  end
1939
2039
 
1940
2040
  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?
2041
+ add_required_message_fields
2042
+ add_multipart_mixed_header if body.multipart?
1946
2043
  add_content_type unless has_content_type?
1947
2044
  add_charset unless has_charset?
1948
2045
  add_content_transfer_encoding unless has_content_transfer_encoding?
1949
2046
  end
1950
2047
 
2048
+ def add_required_message_fields
2049
+ add_date unless has_date?
2050
+ add_mime_version unless has_mime_version?
2051
+ add_message_id unless has_message_id?
2052
+ end
2053
+
1951
2054
  def add_multipart_alternate_header
1952
2055
  header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
1953
2056
  header['content_type'].parameters[:charset] = @charset
@@ -2032,7 +2135,7 @@ module Mail
2032
2135
  if perform_deliveries
2033
2136
  delivery_method.deliver!(self)
2034
2137
  end
2035
- rescue Exception => e # Net::SMTP errors or sendmail pipe errors
2138
+ rescue => e # Net::SMTP errors or sendmail pipe errors
2036
2139
  raise e if raise_delivery_errors
2037
2140
  end
2038
2141
  end
@@ -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)
@@ -341,7 +341,7 @@ module Mail
341
341
  def load
342
342
  begin
343
343
  @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
344
- rescue Exception => e
344
+ rescue => e
345
345
  raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), Mail::Multibyte is unusable")
346
346
  end
347
347
 
@@ -391,8 +391,10 @@ module Mail
391
391
  end
392
392
  end
393
393
 
394
- module ActiveSupport
395
- unless const_defined?(:Multibyte)
396
- Multibyte = Mail::Multibyte
394
+ unless defined?(ActiveSupport)
395
+ module ActiveSupport
396
+ unless const_defined?(:Multibyte)
397
+ Multibyte = Mail::Multibyte
398
+ end
397
399
  end
398
400
  end