mail-portertech 2.6.2.edge

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +753 -0
  3. data/CONTRIBUTING.md +60 -0
  4. data/Dependencies.txt +2 -0
  5. data/Gemfile +15 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +683 -0
  8. data/Rakefile +29 -0
  9. data/TODO.rdoc +9 -0
  10. data/lib/mail.rb +91 -0
  11. data/lib/mail/attachments_list.rb +104 -0
  12. data/lib/mail/body.rb +291 -0
  13. data/lib/mail/check_delivery_params.rb +20 -0
  14. data/lib/mail/configuration.rb +75 -0
  15. data/lib/mail/core_extensions/nil.rb +19 -0
  16. data/lib/mail/core_extensions/object.rb +13 -0
  17. data/lib/mail/core_extensions/smtp.rb +24 -0
  18. data/lib/mail/core_extensions/string.rb +43 -0
  19. data/lib/mail/core_extensions/string/access.rb +145 -0
  20. data/lib/mail/core_extensions/string/multibyte.rb +78 -0
  21. data/lib/mail/elements.rb +14 -0
  22. data/lib/mail/elements/address.rb +270 -0
  23. data/lib/mail/elements/address_list.rb +51 -0
  24. data/lib/mail/elements/content_disposition_element.rb +26 -0
  25. data/lib/mail/elements/content_location_element.rb +21 -0
  26. data/lib/mail/elements/content_transfer_encoding_element.rb +17 -0
  27. data/lib/mail/elements/content_type_element.rb +31 -0
  28. data/lib/mail/elements/date_time_element.rb +22 -0
  29. data/lib/mail/elements/envelope_from_element.rb +39 -0
  30. data/lib/mail/elements/message_ids_element.rb +24 -0
  31. data/lib/mail/elements/mime_version_element.rb +22 -0
  32. data/lib/mail/elements/phrase_list.rb +16 -0
  33. data/lib/mail/elements/received_element.rb +26 -0
  34. data/lib/mail/encodings.rb +304 -0
  35. data/lib/mail/encodings/7bit.rb +31 -0
  36. data/lib/mail/encodings/8bit.rb +31 -0
  37. data/lib/mail/encodings/base64.rb +33 -0
  38. data/lib/mail/encodings/binary.rb +31 -0
  39. data/lib/mail/encodings/quoted_printable.rb +39 -0
  40. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  41. data/lib/mail/envelope.rb +30 -0
  42. data/lib/mail/field.rb +247 -0
  43. data/lib/mail/field_list.rb +33 -0
  44. data/lib/mail/fields.rb +35 -0
  45. data/lib/mail/fields/bcc_field.rb +56 -0
  46. data/lib/mail/fields/cc_field.rb +55 -0
  47. data/lib/mail/fields/comments_field.rb +41 -0
  48. data/lib/mail/fields/common/address_container.rb +16 -0
  49. data/lib/mail/fields/common/common_address.rb +135 -0
  50. data/lib/mail/fields/common/common_date.rb +35 -0
  51. data/lib/mail/fields/common/common_field.rb +57 -0
  52. data/lib/mail/fields/common/common_message_id.rb +48 -0
  53. data/lib/mail/fields/common/parameter_hash.rb +58 -0
  54. data/lib/mail/fields/content_description_field.rb +19 -0
  55. data/lib/mail/fields/content_disposition_field.rb +70 -0
  56. data/lib/mail/fields/content_id_field.rb +62 -0
  57. data/lib/mail/fields/content_location_field.rb +42 -0
  58. data/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
  59. data/lib/mail/fields/content_type_field.rb +201 -0
  60. data/lib/mail/fields/date_field.rb +57 -0
  61. data/lib/mail/fields/from_field.rb +55 -0
  62. data/lib/mail/fields/in_reply_to_field.rb +56 -0
  63. data/lib/mail/fields/keywords_field.rb +44 -0
  64. data/lib/mail/fields/message_id_field.rb +82 -0
  65. data/lib/mail/fields/mime_version_field.rb +53 -0
  66. data/lib/mail/fields/optional_field.rb +13 -0
  67. data/lib/mail/fields/received_field.rb +75 -0
  68. data/lib/mail/fields/references_field.rb +56 -0
  69. data/lib/mail/fields/reply_to_field.rb +55 -0
  70. data/lib/mail/fields/resent_bcc_field.rb +55 -0
  71. data/lib/mail/fields/resent_cc_field.rb +55 -0
  72. data/lib/mail/fields/resent_date_field.rb +35 -0
  73. data/lib/mail/fields/resent_from_field.rb +55 -0
  74. data/lib/mail/fields/resent_message_id_field.rb +34 -0
  75. data/lib/mail/fields/resent_sender_field.rb +62 -0
  76. data/lib/mail/fields/resent_to_field.rb +55 -0
  77. data/lib/mail/fields/return_path_field.rb +65 -0
  78. data/lib/mail/fields/sender_field.rb +67 -0
  79. data/lib/mail/fields/structured_field.rb +51 -0
  80. data/lib/mail/fields/subject_field.rb +16 -0
  81. data/lib/mail/fields/to_field.rb +55 -0
  82. data/lib/mail/fields/unstructured_field.rb +204 -0
  83. data/lib/mail/header.rb +274 -0
  84. data/lib/mail/indifferent_hash.rb +146 -0
  85. data/lib/mail/mail.rb +267 -0
  86. data/lib/mail/matchers/has_sent_mail.rb +157 -0
  87. data/lib/mail/message.rb +2160 -0
  88. data/lib/mail/multibyte.rb +42 -0
  89. data/lib/mail/multibyte/chars.rb +474 -0
  90. data/lib/mail/multibyte/exceptions.rb +8 -0
  91. data/lib/mail/multibyte/unicode.rb +400 -0
  92. data/lib/mail/multibyte/utils.rb +60 -0
  93. data/lib/mail/network.rb +14 -0
  94. data/lib/mail/network/delivery_methods/exim.rb +52 -0
  95. data/lib/mail/network/delivery_methods/file_delivery.rb +45 -0
  96. data/lib/mail/network/delivery_methods/sendmail.rb +89 -0
  97. data/lib/mail/network/delivery_methods/smtp.rb +142 -0
  98. data/lib/mail/network/delivery_methods/smtp_connection.rb +61 -0
  99. data/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
  100. data/lib/mail/network/retriever_methods/base.rb +63 -0
  101. data/lib/mail/network/retriever_methods/imap.rb +173 -0
  102. data/lib/mail/network/retriever_methods/pop3.rb +140 -0
  103. data/lib/mail/network/retriever_methods/test_retriever.rb +43 -0
  104. data/lib/mail/parsers.rb +26 -0
  105. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  106. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  107. data/lib/mail/parsers/content_location_parser.rb +35 -0
  108. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  109. data/lib/mail/parsers/content_type_parser.rb +64 -0
  110. data/lib/mail/parsers/date_time_parser.rb +36 -0
  111. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  112. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  114. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  115. data/lib/mail/parsers/ragel.rb +17 -0
  116. data/lib/mail/parsers/ragel/common.rl +184 -0
  117. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  118. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  119. data/lib/mail/parsers/ragel/ruby.rb +39 -0
  120. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  121. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  122. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  123. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  124. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  125. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  126. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  127. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  128. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  129. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  130. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  131. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  132. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  133. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  134. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  135. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  136. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  137. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  138. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  139. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  140. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  141. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  142. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  143. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  144. data/lib/mail/parsers/received_parser.rb +47 -0
  145. data/lib/mail/part.rb +120 -0
  146. data/lib/mail/parts_list.rb +57 -0
  147. data/lib/mail/patterns.rb +37 -0
  148. data/lib/mail/utilities.rb +225 -0
  149. data/lib/mail/values/unicode_tables.dat +0 -0
  150. data/lib/mail/version.rb +4 -0
  151. data/lib/mail/version_specific/ruby_1_8.rb +119 -0
  152. data/lib/mail/version_specific/ruby_1_9.rb +159 -0
  153. metadata +276 -0
@@ -0,0 +1,2160 @@
1
+ # encoding: utf-8
2
+ require "yaml"
3
+
4
+ module Mail
5
+ # The Message class provides a single point of access to all things to do with an
6
+ # email message.
7
+ #
8
+ # You create a new email message by calling the Mail::Message.new method, or just
9
+ # Mail.new
10
+ #
11
+ # A Message object by default has the following objects inside it:
12
+ #
13
+ # * A Header object which contains all information and settings of the header of the email
14
+ # * Body object which contains all parts of the email that are not part of the header, this
15
+ # includes any attachments, body text, MIME parts etc.
16
+ #
17
+ # ==Per RFC2822
18
+ #
19
+ # 2.1. General Description
20
+ #
21
+ # At the most basic level, a message is a series of characters. A
22
+ # message that is conformant with this standard is comprised of
23
+ # characters with values in the range 1 through 127 and interpreted as
24
+ # US-ASCII characters [ASCII]. For brevity, this document sometimes
25
+ # refers to this range of characters as simply "US-ASCII characters".
26
+ #
27
+ # Note: This standard specifies that messages are made up of characters
28
+ # in the US-ASCII range of 1 through 127. There are other documents,
29
+ # specifically the MIME document series [RFC2045, RFC2046, RFC2047,
30
+ # RFC2048, RFC2049], that extend this standard to allow for values
31
+ # outside of that range. Discussion of those mechanisms is not within
32
+ # the scope of this standard.
33
+ #
34
+ # Messages are divided into lines of characters. A line is a series of
35
+ # characters that is delimited with the two characters carriage-return
36
+ # and line-feed; that is, the carriage return (CR) character (ASCII
37
+ # value 13) followed immediately by the line feed (LF) character (ASCII
38
+ # value 10). (The carriage-return/line-feed pair is usually written in
39
+ # this document as "CRLF".)
40
+ #
41
+ # A message consists of header fields (collectively called "the header
42
+ # of the message") followed, optionally, by a body. The header is a
43
+ # sequence of lines of characters with special syntax as defined in
44
+ # this standard. The body is simply a sequence of characters that
45
+ # follows the header and is separated from the header by an empty line
46
+ # (i.e., a line with nothing preceding the CRLF).
47
+ class Message
48
+
49
+ include Patterns
50
+ include Utilities
51
+
52
+ # ==Making an email
53
+ #
54
+ # You can make an new mail object via a block, passing a string, file or direct assignment.
55
+ #
56
+ # ===Making an email via a block
57
+ #
58
+ # mail = Mail.new do
59
+ # from 'mikel@test.lindsaar.net'
60
+ # to 'you@test.lindsaar.net'
61
+ # subject 'This is a test email'
62
+ # body File.read('body.txt')
63
+ # end
64
+ #
65
+ # mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
66
+ #
67
+ # ===Making an email via passing a string
68
+ #
69
+ # mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
70
+ # mail.body.to_s #=> 'Hi there!'
71
+ # mail.subject #=> 'Hello'
72
+ # mail.to #=> 'mikel@test.lindsaar.net'
73
+ #
74
+ # ===Making an email from a file
75
+ #
76
+ # mail = Mail.read('path/to/file.eml')
77
+ # mail.body.to_s #=> 'Hi there!'
78
+ # mail.subject #=> 'Hello'
79
+ # mail.to #=> 'mikel@test.lindsaar.net'
80
+ #
81
+ # ===Making an email via assignment
82
+ #
83
+ # You can assign values to a mail object via four approaches:
84
+ #
85
+ # * Message#field_name=(value)
86
+ # * Message#field_name(value)
87
+ # * Message#['field_name']=(value)
88
+ # * Message#[:field_name]=(value)
89
+ #
90
+ # Examples:
91
+ #
92
+ # mail = Mail.new
93
+ # mail['from'] = 'mikel@test.lindsaar.net'
94
+ # mail[:to] = 'you@test.lindsaar.net'
95
+ # mail.subject 'This is a test email'
96
+ # mail.body = 'This is a body'
97
+ #
98
+ # mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
99
+ #
100
+ def initialize(*args, &block)
101
+ @body = nil
102
+ @body_raw = nil
103
+ @separate_parts = false
104
+ @text_part = nil
105
+ @html_part = nil
106
+ @errors = nil
107
+ @header = nil
108
+ @charset = 'UTF-8'
109
+ @defaulted_charset = true
110
+
111
+ @smtp_envelope_from = nil
112
+ @smtp_envelope_to = nil
113
+
114
+ @perform_deliveries = true
115
+ @raise_delivery_errors = true
116
+
117
+ @delivery_handler = nil
118
+
119
+ @delivery_method = Mail.delivery_method.dup
120
+
121
+ @transport_encoding = Mail::Encodings.get_encoding('7bit')
122
+
123
+ @mark_for_delete = false
124
+
125
+ if args.flatten.first.respond_to?(:each_pair)
126
+ init_with_hash(args.flatten.first)
127
+ else
128
+ init_with_string(args.flatten[0].to_s)
129
+ end
130
+
131
+ if block_given?
132
+ instance_eval(&block)
133
+ end
134
+
135
+ self
136
+ end
137
+
138
+ # If you assign a delivery handler, mail will call :deliver_mail on the
139
+ # object you assign to delivery_handler, it will pass itself as the
140
+ # single argument.
141
+ #
142
+ # If you define a delivery_handler, then you are responsible for the
143
+ # following actions in the delivery cycle:
144
+ #
145
+ # * Appending the mail object to Mail.deliveries as you see fit.
146
+ # * Checking the mail.perform_deliveries flag to decide if you should
147
+ # actually call :deliver! the mail object or not.
148
+ # * Checking the mail.raise_delivery_errors flag to decide if you
149
+ # should raise delivery errors if they occur.
150
+ # * Actually calling :deliver! (with the bang) on the mail object to
151
+ # get it to deliver itself.
152
+ #
153
+ # A simplest implementation of a delivery_handler would be
154
+ #
155
+ # class MyObject
156
+ #
157
+ # def initialize
158
+ # @mail = Mail.new('To: mikel@test.lindsaar.net')
159
+ # @mail.delivery_handler = self
160
+ # end
161
+ #
162
+ # attr_accessor :mail
163
+ #
164
+ # def deliver_mail(mail)
165
+ # yield
166
+ # end
167
+ # end
168
+ #
169
+ # Then doing:
170
+ #
171
+ # obj = MyObject.new
172
+ # obj.mail.deliver
173
+ #
174
+ # Would cause Mail to call obj.deliver_mail passing itself as a parameter,
175
+ # which then can just yield and let Mail do its own private do_delivery
176
+ # method.
177
+ attr_accessor :delivery_handler
178
+
179
+ # If set to false, mail will go through the motions of doing a delivery,
180
+ # but not actually call the delivery method or append the mail object to
181
+ # the Mail.deliveries collection. Useful for testing.
182
+ #
183
+ # Mail.deliveries.size #=> 0
184
+ # mail.delivery_method :smtp
185
+ # mail.perform_deliveries = false
186
+ # mail.deliver # Mail::SMTP not called here
187
+ # Mail.deliveries.size #=> 0
188
+ #
189
+ # If you want to test and query the Mail.deliveries collection to see what
190
+ # mail you sent, you should set perform_deliveries to true and use
191
+ # the :test mail delivery_method:
192
+ #
193
+ # Mail.deliveries.size #=> 0
194
+ # mail.delivery_method :test
195
+ # mail.perform_deliveries = true
196
+ # mail.deliver
197
+ # Mail.deliveries.size #=> 1
198
+ #
199
+ # This setting is ignored by mail (though still available as a flag) if you
200
+ # define a delivery_handler
201
+ attr_accessor :perform_deliveries
202
+
203
+ # If set to false, mail will silently catch and ignore any exceptions
204
+ # raised through attempting to deliver an email.
205
+ #
206
+ # This setting is ignored by mail (though still available as a flag) if you
207
+ # define a delivery_handler
208
+ attr_accessor :raise_delivery_errors
209
+
210
+ def register_for_delivery_notification(observer)
211
+ STDERR.puts("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
212
+ Mail.register_observer(observer)
213
+ end
214
+
215
+ def inform_observers
216
+ Mail.inform_observers(self)
217
+ end
218
+
219
+ def inform_interceptors
220
+ Mail.inform_interceptors(self)
221
+ end
222
+
223
+ # Delivers an mail object.
224
+ #
225
+ # Examples:
226
+ #
227
+ # mail = Mail.read('file.eml')
228
+ # mail.deliver
229
+ def deliver
230
+ inform_interceptors
231
+ if delivery_handler
232
+ delivery_handler.deliver_mail(self) { do_delivery }
233
+ else
234
+ do_delivery
235
+ end
236
+ inform_observers
237
+ self
238
+ end
239
+
240
+ # This method bypasses checking perform_deliveries and raise_delivery_errors,
241
+ # so use with caution.
242
+ #
243
+ # It still however fires off the interceptors and calls the observers callbacks if they are defined.
244
+ #
245
+ # Returns self
246
+ def deliver!
247
+ inform_interceptors
248
+ response = delivery_method.deliver!(self)
249
+ inform_observers
250
+ delivery_method.settings[:return_response] ? response : self
251
+ end
252
+
253
+ def delivery_method(method = nil, settings = {})
254
+ unless method
255
+ @delivery_method
256
+ else
257
+ @delivery_method = Configuration.instance.lookup_delivery_method(method).new(settings)
258
+ end
259
+ end
260
+
261
+ def reply(*args, &block)
262
+ self.class.new.tap do |reply|
263
+ if message_id
264
+ bracketed_message_id = "<#{message_id}>"
265
+ reply.in_reply_to = bracketed_message_id
266
+ if !references.nil?
267
+ refs = [references].flatten.map { |r| "<#{r}>" }
268
+ refs << bracketed_message_id
269
+ reply.references = refs.join(' ')
270
+ elsif !in_reply_to.nil? && !in_reply_to.kind_of?(Array)
271
+ reply.references = "<#{in_reply_to}> #{bracketed_message_id}"
272
+ end
273
+ reply.references ||= bracketed_message_id
274
+ end
275
+ if subject
276
+ reply.subject = subject =~ /^Re:/i ? subject : "RE: #{subject}"
277
+ end
278
+ if reply_to || from
279
+ reply.to = self[reply_to ? :reply_to : :from].to_s
280
+ end
281
+ if to
282
+ reply.from = self[:to].formatted.first.to_s
283
+ end
284
+
285
+ unless args.empty?
286
+ if args.flatten.first.respond_to?(:each_pair)
287
+ reply.send(:init_with_hash, args.flatten.first)
288
+ else
289
+ reply.send(:init_with_string, args.flatten[0].to_s.strip)
290
+ end
291
+ end
292
+
293
+ if block_given?
294
+ reply.instance_eval(&block)
295
+ end
296
+ end
297
+ end
298
+
299
+ # Provides the operator needed for sort et al.
300
+ #
301
+ # Compares this mail object with another mail object, this is done by date, so an
302
+ # email that is older than another will appear first.
303
+ #
304
+ # Example:
305
+ #
306
+ # mail1 = Mail.new do
307
+ # date(Time.now)
308
+ # end
309
+ # mail2 = Mail.new do
310
+ # date(Time.now - 86400) # 1 day older
311
+ # end
312
+ # [mail2, mail1].sort #=> [mail2, mail1]
313
+ def <=>(other)
314
+ if other.nil?
315
+ 1
316
+ else
317
+ self.date <=> other.date
318
+ end
319
+ end
320
+
321
+ # Two emails are the same if they have the same fields and body contents. One
322
+ # gotcha here is that Mail will insert Message-IDs when calling encoded, so doing
323
+ # mail1.encoded == mail2.encoded is most probably not going to return what you think
324
+ # as the assigned Message-IDs by Mail (if not already defined as the same) will ensure
325
+ # that the two objects are unique, and this comparison will ALWAYS return false.
326
+ #
327
+ # So the == operator has been defined like so: Two messages are the same if they have
328
+ # the same content, ignoring the Message-ID field, unless BOTH emails have a defined and
329
+ # different Message-ID value, then they are false.
330
+ #
331
+ # So, in practice the == operator works like this:
332
+ #
333
+ # m1 = Mail.new("Subject: Hello\r\n\r\nHello")
334
+ # m2 = Mail.new("Subject: Hello\r\n\r\nHello")
335
+ # m1 == m2 #=> true
336
+ #
337
+ # m1 = Mail.new("Subject: Hello\r\n\r\nHello")
338
+ # m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
339
+ # m1 == m2 #=> true
340
+ #
341
+ # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
342
+ # m2 = Mail.new("Subject: Hello\r\n\r\nHello")
343
+ # m1 == m2 #=> true
344
+ #
345
+ # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
346
+ # m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
347
+ # m1 == m2 #=> true
348
+ #
349
+ # m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
350
+ # m2 = Mail.new("Message-ID: <DIFFERENT@test>\r\nSubject: Hello\r\n\r\nHello")
351
+ # m1 == m2 #=> false
352
+ def ==(other)
353
+ return false unless other.respond_to?(:encoded)
354
+
355
+ if self.message_id && other.message_id
356
+ self.encoded == other.encoded
357
+ else
358
+ self_message_id, other_message_id = self.message_id, other.message_id
359
+ begin
360
+ self.message_id, other.message_id = '<temp@test>', '<temp@test>'
361
+ self.encoded == other.encoded
362
+ ensure
363
+ self.message_id, other.message_id = self_message_id, other_message_id
364
+ end
365
+ end
366
+ end
367
+
368
+ def initialize_copy(original)
369
+ super
370
+ @header = @header.dup
371
+ end
372
+
373
+ # Provides access to the raw source of the message as it was when it
374
+ # was instantiated. This is set at initialization and so is untouched
375
+ # by the parsers or decoder / encoders
376
+ #
377
+ # Example:
378
+ #
379
+ # mail = Mail.new('This is an invalid email message')
380
+ # mail.raw_source #=> "This is an invalid email message"
381
+ def raw_source
382
+ @raw_source
383
+ end
384
+
385
+ # Sets the envelope from for the email
386
+ def set_envelope( val )
387
+ @raw_envelope = val
388
+ @envelope = Mail::Envelope.new( val )
389
+ end
390
+
391
+ # The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
392
+ # type field that you can see at the top of any email that has come
393
+ # from a mailbox
394
+ def raw_envelope
395
+ @raw_envelope
396
+ end
397
+
398
+ def envelope_from
399
+ @envelope ? @envelope.from : nil
400
+ end
401
+
402
+ def envelope_date
403
+ @envelope ? @envelope.date : nil
404
+ end
405
+
406
+ # Sets the header of the message object.
407
+ #
408
+ # Example:
409
+ #
410
+ # mail.header = 'To: mikel@test.lindsaar.net\r\nFrom: Bob@bob.com'
411
+ # mail.header #=> <#Mail::Header
412
+ def header=(value)
413
+ @header = Mail::Header.new(value, charset)
414
+ end
415
+
416
+ # Returns the header object of the message object. Or, if passed
417
+ # a parameter sets the value.
418
+ #
419
+ # Example:
420
+ #
421
+ # mail = Mail::Message.new('To: mikel\r\nFrom: you')
422
+ # mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
423
+ #
424
+ # mail.header #=> nil
425
+ # mail.header 'To: mikel\r\nFrom: you'
426
+ # mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
427
+ def header(value = nil)
428
+ value ? self.header = value : @header
429
+ end
430
+
431
+ # Provides a way to set custom headers, by passing in a hash
432
+ def headers(hash = {})
433
+ hash.each_pair do |k,v|
434
+ header[k] = v
435
+ end
436
+ end
437
+
438
+ # Returns a list of parser errors on the header, each field that had an error
439
+ # will be reparsed as an unstructured field to preserve the data inside, but
440
+ # will not be used for further processing.
441
+ #
442
+ # It returns a nested array of [field_name, value, original_error_message]
443
+ # per error found.
444
+ #
445
+ # Example:
446
+ #
447
+ # message = Mail.new("Content-Transfer-Encoding: weirdo\r\n")
448
+ # message.errors.size #=> 1
449
+ # message.errors.first[0] #=> "Content-Transfer-Encoding"
450
+ # message.errors.first[1] #=> "weirdo"
451
+ # message.errors.first[3] #=> <The original error message exception>
452
+ #
453
+ # This is a good first defence on detecting spam by the way. Some spammers send
454
+ # invalid emails to try and get email parsers to give up parsing them.
455
+ def errors
456
+ header.errors
457
+ end
458
+
459
+ # Returns the Bcc value of the mail object as an array of strings of
460
+ # address specs.
461
+ #
462
+ # Example:
463
+ #
464
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
465
+ # mail.bcc #=> ['mikel@test.lindsaar.net']
466
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
467
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
468
+ #
469
+ # Also allows you to set the value by passing a value as a parameter
470
+ #
471
+ # Example:
472
+ #
473
+ # mail.bcc 'Mikel <mikel@test.lindsaar.net>'
474
+ # mail.bcc #=> ['mikel@test.lindsaar.net']
475
+ #
476
+ # Additionally, you can append new addresses to the returned Array like
477
+ # object.
478
+ #
479
+ # Example:
480
+ #
481
+ # mail.bcc 'Mikel <mikel@test.lindsaar.net>'
482
+ # mail.bcc << 'ada@test.lindsaar.net'
483
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
484
+ def bcc( val = nil )
485
+ default :bcc, val
486
+ end
487
+
488
+ # Sets the Bcc value of the mail object, pass in a string of the field
489
+ #
490
+ # Example:
491
+ #
492
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
493
+ # mail.bcc #=> ['mikel@test.lindsaar.net']
494
+ # mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
495
+ # mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
496
+ def bcc=( val )
497
+ header[:bcc] = val
498
+ end
499
+
500
+ # Returns the Cc value of the mail object as an array of strings of
501
+ # address specs.
502
+ #
503
+ # Example:
504
+ #
505
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>'
506
+ # mail.cc #=> ['mikel@test.lindsaar.net']
507
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
508
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
509
+ #
510
+ # Also allows you to set the value by passing a value as a parameter
511
+ #
512
+ # Example:
513
+ #
514
+ # mail.cc 'Mikel <mikel@test.lindsaar.net>'
515
+ # mail.cc #=> ['mikel@test.lindsaar.net']
516
+ #
517
+ # Additionally, you can append new addresses to the returned Array like
518
+ # object.
519
+ #
520
+ # Example:
521
+ #
522
+ # mail.cc 'Mikel <mikel@test.lindsaar.net>'
523
+ # mail.cc << 'ada@test.lindsaar.net'
524
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
525
+ def cc( val = nil )
526
+ default :cc, val
527
+ end
528
+
529
+ # Sets the Cc value of the mail object, pass in a string of the field
530
+ #
531
+ # Example:
532
+ #
533
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>'
534
+ # mail.cc #=> ['mikel@test.lindsaar.net']
535
+ # mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
536
+ # mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
537
+ def cc=( val )
538
+ header[:cc] = val
539
+ end
540
+
541
+ def comments( val = nil )
542
+ default :comments, val
543
+ end
544
+
545
+ def comments=( val )
546
+ header[:comments] = val
547
+ end
548
+
549
+ def content_description( val = nil )
550
+ default :content_description, val
551
+ end
552
+
553
+ def content_description=( val )
554
+ header[:content_description] = val
555
+ end
556
+
557
+ def content_disposition( val = nil )
558
+ default :content_disposition, val
559
+ end
560
+
561
+ def content_disposition=( val )
562
+ header[:content_disposition] = val
563
+ end
564
+
565
+ def content_id( val = nil )
566
+ default :content_id, val
567
+ end
568
+
569
+ def content_id=( val )
570
+ header[:content_id] = val
571
+ end
572
+
573
+ def content_location( val = nil )
574
+ default :content_location, val
575
+ end
576
+
577
+ def content_location=( val )
578
+ header[:content_location] = val
579
+ end
580
+
581
+ def content_transfer_encoding( val = nil )
582
+ default :content_transfer_encoding, val
583
+ end
584
+
585
+ def content_transfer_encoding=( val )
586
+ header[:content_transfer_encoding] = val
587
+ end
588
+
589
+ def content_type( val = nil )
590
+ default :content_type, val
591
+ end
592
+
593
+ def content_type=( val )
594
+ header[:content_type] = val
595
+ end
596
+
597
+ def date( val = nil )
598
+ default :date, val
599
+ end
600
+
601
+ def date=( val )
602
+ header[:date] = val
603
+ end
604
+
605
+ def transport_encoding( val = nil)
606
+ if val
607
+ self.transport_encoding = val
608
+ else
609
+ @transport_encoding
610
+ end
611
+ end
612
+
613
+ def transport_encoding=( val )
614
+ @transport_encoding = Mail::Encodings.get_encoding(val)
615
+ end
616
+
617
+ # Returns the From value of the mail object as an array of strings of
618
+ # address specs.
619
+ #
620
+ # Example:
621
+ #
622
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>'
623
+ # mail.from #=> ['mikel@test.lindsaar.net']
624
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
625
+ # mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
626
+ #
627
+ # Also allows you to set the value by passing a value as a parameter
628
+ #
629
+ # Example:
630
+ #
631
+ # mail.from 'Mikel <mikel@test.lindsaar.net>'
632
+ # mail.from #=> ['mikel@test.lindsaar.net']
633
+ #
634
+ # Additionally, you can append new addresses to the returned Array like
635
+ # object.
636
+ #
637
+ # Example:
638
+ #
639
+ # mail.from 'Mikel <mikel@test.lindsaar.net>'
640
+ # mail.from << 'ada@test.lindsaar.net'
641
+ # mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
642
+ def from( val = nil )
643
+ default :from, val
644
+ end
645
+
646
+ # Sets the From value of the mail object, pass in a string of the field
647
+ #
648
+ # Example:
649
+ #
650
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>'
651
+ # mail.from #=> ['mikel@test.lindsaar.net']
652
+ # mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
653
+ # mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
654
+ def from=( val )
655
+ header[:from] = val
656
+ end
657
+
658
+ def in_reply_to( val = nil )
659
+ default :in_reply_to, val
660
+ end
661
+
662
+ def in_reply_to=( val )
663
+ header[:in_reply_to] = val
664
+ end
665
+
666
+ def keywords( val = nil )
667
+ default :keywords, val
668
+ end
669
+
670
+ def keywords=( val )
671
+ header[:keywords] = val
672
+ end
673
+
674
+ # Returns the Message-ID of the mail object. Note, per RFC 2822 the Message ID
675
+ # consists of what is INSIDE the < > usually seen in the mail header, so this method
676
+ # will return only what is inside.
677
+ #
678
+ # Example:
679
+ #
680
+ # mail.message_id = '<1234@message.id>'
681
+ # mail.message_id #=> '1234@message.id'
682
+ #
683
+ # Also allows you to set the Message-ID by passing a string as a parameter
684
+ #
685
+ # mail.message_id '<1234@message.id>'
686
+ # mail.message_id #=> '1234@message.id'
687
+ def message_id( val = nil )
688
+ default :message_id, val
689
+ end
690
+
691
+ # Sets the Message-ID. Note, per RFC 2822 the Message ID consists of what is INSIDE
692
+ # the < > usually seen in the mail header, so this method will return only what is inside.
693
+ #
694
+ # mail.message_id = '<1234@message.id>'
695
+ # mail.message_id #=> '1234@message.id'
696
+ def message_id=( val )
697
+ header[:message_id] = val
698
+ end
699
+
700
+ # Returns the MIME version of the email as a string
701
+ #
702
+ # Example:
703
+ #
704
+ # mail.mime_version = '1.0'
705
+ # mail.mime_version #=> '1.0'
706
+ #
707
+ # Also allows you to set the MIME version by passing a string as a parameter.
708
+ #
709
+ # Example:
710
+ #
711
+ # mail.mime_version '1.0'
712
+ # mail.mime_version #=> '1.0'
713
+ def mime_version( val = nil )
714
+ default :mime_version, val
715
+ end
716
+
717
+ # Sets the MIME version of the email by accepting a string
718
+ #
719
+ # Example:
720
+ #
721
+ # mail.mime_version = '1.0'
722
+ # mail.mime_version #=> '1.0'
723
+ def mime_version=( val )
724
+ header[:mime_version] = val
725
+ end
726
+
727
+ def received( val = nil )
728
+ if val
729
+ header[:received] = val
730
+ else
731
+ header[:received]
732
+ end
733
+ end
734
+
735
+ def received=( val )
736
+ header[:received] = val
737
+ end
738
+
739
+ def references( val = nil )
740
+ default :references, val
741
+ end
742
+
743
+ def references=( val )
744
+ header[:references] = val
745
+ end
746
+
747
+ # Returns the Reply-To value of the mail object as an array of strings of
748
+ # address specs.
749
+ #
750
+ # Example:
751
+ #
752
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
753
+ # mail.reply_to #=> ['mikel@test.lindsaar.net']
754
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
755
+ # mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
756
+ #
757
+ # Also allows you to set the value by passing a value as a parameter
758
+ #
759
+ # Example:
760
+ #
761
+ # mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
762
+ # mail.reply_to #=> ['mikel@test.lindsaar.net']
763
+ #
764
+ # Additionally, you can append new addresses to the returned Array like
765
+ # object.
766
+ #
767
+ # Example:
768
+ #
769
+ # mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
770
+ # mail.reply_to << 'ada@test.lindsaar.net'
771
+ # mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
772
+ def reply_to( val = nil )
773
+ default :reply_to, val
774
+ end
775
+
776
+ # Sets the Reply-To value of the mail object, pass in a string of the field
777
+ #
778
+ # Example:
779
+ #
780
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
781
+ # mail.reply_to #=> ['mikel@test.lindsaar.net']
782
+ # mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
783
+ # mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
784
+ def reply_to=( val )
785
+ header[:reply_to] = val
786
+ end
787
+
788
+ # Returns the Resent-Bcc value of the mail object as an array of strings of
789
+ # address specs.
790
+ #
791
+ # Example:
792
+ #
793
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
794
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net']
795
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
796
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
797
+ #
798
+ # Also allows you to set the value by passing a value as a parameter
799
+ #
800
+ # Example:
801
+ #
802
+ # mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
803
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net']
804
+ #
805
+ # Additionally, you can append new addresses to the returned Array like
806
+ # object.
807
+ #
808
+ # Example:
809
+ #
810
+ # mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
811
+ # mail.resent_bcc << 'ada@test.lindsaar.net'
812
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
813
+ def resent_bcc( val = nil )
814
+ default :resent_bcc, val
815
+ end
816
+
817
+ # Sets the Resent-Bcc value of the mail object, pass in a string of the field
818
+ #
819
+ # Example:
820
+ #
821
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
822
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net']
823
+ # mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
824
+ # mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
825
+ def resent_bcc=( val )
826
+ header[:resent_bcc] = val
827
+ end
828
+
829
+ # Returns the Resent-Cc value of the mail object as an array of strings of
830
+ # address specs.
831
+ #
832
+ # Example:
833
+ #
834
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
835
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net']
836
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
837
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
838
+ #
839
+ # Also allows you to set the value by passing a value as a parameter
840
+ #
841
+ # Example:
842
+ #
843
+ # mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
844
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net']
845
+ #
846
+ # Additionally, you can append new addresses to the returned Array like
847
+ # object.
848
+ #
849
+ # Example:
850
+ #
851
+ # mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
852
+ # mail.resent_cc << 'ada@test.lindsaar.net'
853
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
854
+ def resent_cc( val = nil )
855
+ default :resent_cc, val
856
+ end
857
+
858
+ # Sets the Resent-Cc value of the mail object, pass in a string of the field
859
+ #
860
+ # Example:
861
+ #
862
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
863
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net']
864
+ # mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
865
+ # mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
866
+ def resent_cc=( val )
867
+ header[:resent_cc] = val
868
+ end
869
+
870
+ def resent_date( val = nil )
871
+ default :resent_date, val
872
+ end
873
+
874
+ def resent_date=( val )
875
+ header[:resent_date] = val
876
+ end
877
+
878
+ # Returns the Resent-From value of the mail object as an array of strings of
879
+ # address specs.
880
+ #
881
+ # Example:
882
+ #
883
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
884
+ # mail.resent_from #=> ['mikel@test.lindsaar.net']
885
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
886
+ # mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
887
+ #
888
+ # Also allows you to set the value by passing a value as a parameter
889
+ #
890
+ # Example:
891
+ #
892
+ # mail.resent_from ['Mikel <mikel@test.lindsaar.net>']
893
+ # mail.resent_from #=> 'mikel@test.lindsaar.net'
894
+ #
895
+ # Additionally, you can append new addresses to the returned Array like
896
+ # object.
897
+ #
898
+ # Example:
899
+ #
900
+ # mail.resent_from 'Mikel <mikel@test.lindsaar.net>'
901
+ # mail.resent_from << 'ada@test.lindsaar.net'
902
+ # mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
903
+ def resent_from( val = nil )
904
+ default :resent_from, val
905
+ end
906
+
907
+ # Sets the Resent-From value of the mail object, pass in a string of the field
908
+ #
909
+ # Example:
910
+ #
911
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
912
+ # mail.resent_from #=> ['mikel@test.lindsaar.net']
913
+ # mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
914
+ # mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
915
+ def resent_from=( val )
916
+ header[:resent_from] = val
917
+ end
918
+
919
+ def resent_message_id( val = nil )
920
+ default :resent_message_id, val
921
+ end
922
+
923
+ def resent_message_id=( val )
924
+ header[:resent_message_id] = val
925
+ end
926
+
927
+ # Returns the Resent-Sender value of the mail object, as a single string of an address
928
+ # spec. A sender per RFC 2822 must be a single address, so you can not append to
929
+ # this address.
930
+ #
931
+ # Example:
932
+ #
933
+ # mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
934
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
935
+ #
936
+ # Also allows you to set the value by passing a value as a parameter
937
+ #
938
+ # Example:
939
+ #
940
+ # mail.resent_sender 'Mikel <mikel@test.lindsaar.net>'
941
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
942
+ def resent_sender( val = nil )
943
+ default :resent_sender, val
944
+ end
945
+
946
+ # Sets the Resent-Sender value of the mail object, pass in a string of the field
947
+ #
948
+ # Example:
949
+ #
950
+ # mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
951
+ # mail.resent_sender #=> 'mikel@test.lindsaar.net'
952
+ def resent_sender=( val )
953
+ header[:resent_sender] = val
954
+ end
955
+
956
+ # Returns the Resent-To value of the mail object as an array of strings of
957
+ # address specs.
958
+ #
959
+ # Example:
960
+ #
961
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
962
+ # mail.resent_to #=> ['mikel@test.lindsaar.net']
963
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
964
+ # mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
965
+ #
966
+ # Also allows you to set the value by passing a value as a parameter
967
+ #
968
+ # Example:
969
+ #
970
+ # mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
971
+ # mail.resent_to #=> ['mikel@test.lindsaar.net']
972
+ #
973
+ # Additionally, you can append new addresses to the returned Array like
974
+ # object.
975
+ #
976
+ # Example:
977
+ #
978
+ # mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
979
+ # mail.resent_to << 'ada@test.lindsaar.net'
980
+ # mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
981
+ def resent_to( val = nil )
982
+ default :resent_to, val
983
+ end
984
+
985
+ # Sets the Resent-To value of the mail object, pass in a string of the field
986
+ #
987
+ # Example:
988
+ #
989
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
990
+ # mail.resent_to #=> ['mikel@test.lindsaar.net']
991
+ # mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
992
+ # mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
993
+ def resent_to=( val )
994
+ header[:resent_to] = val
995
+ end
996
+
997
+ # Returns the return path of the mail object, or sets it if you pass a string
998
+ def return_path( val = nil )
999
+ default :return_path, val
1000
+ end
1001
+
1002
+ # Sets the return path of the object
1003
+ def return_path=( val )
1004
+ header[:return_path] = val
1005
+ end
1006
+
1007
+ # Returns the Sender value of the mail object, as a single string of an address
1008
+ # spec. A sender per RFC 2822 must be a single address.
1009
+ #
1010
+ # Example:
1011
+ #
1012
+ # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
1013
+ # mail.sender #=> 'mikel@test.lindsaar.net'
1014
+ #
1015
+ # Also allows you to set the value by passing a value as a parameter
1016
+ #
1017
+ # Example:
1018
+ #
1019
+ # mail.sender 'Mikel <mikel@test.lindsaar.net>'
1020
+ # mail.sender #=> 'mikel@test.lindsaar.net'
1021
+ def sender( val = nil )
1022
+ default :sender, val
1023
+ end
1024
+
1025
+ # Sets the Sender value of the mail object, pass in a string of the field
1026
+ #
1027
+ # Example:
1028
+ #
1029
+ # mail.sender = 'Mikel <mikel@test.lindsaar.net>'
1030
+ # mail.sender #=> 'mikel@test.lindsaar.net'
1031
+ def sender=( val )
1032
+ header[:sender] = val
1033
+ end
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
+
1111
+ # Returns the decoded value of the subject field, as a single string.
1112
+ #
1113
+ # Example:
1114
+ #
1115
+ # mail.subject = "G'Day mate"
1116
+ # mail.subject #=> "G'Day mate"
1117
+ # mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
1118
+ # mail.subject #=> "This is あ string"
1119
+ #
1120
+ # Also allows you to set the value by passing a value as a parameter
1121
+ #
1122
+ # Example:
1123
+ #
1124
+ # mail.subject "G'Day mate"
1125
+ # mail.subject #=> "G'Day mate"
1126
+ def subject( val = nil )
1127
+ default :subject, val
1128
+ end
1129
+
1130
+ # Sets the Subject value of the mail object, pass in a string of the field
1131
+ #
1132
+ # Example:
1133
+ #
1134
+ # mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
1135
+ # mail.subject #=> "This is あ string"
1136
+ def subject=( val )
1137
+ header[:subject] = val
1138
+ end
1139
+
1140
+ # Returns the To value of the mail object as an array of strings of
1141
+ # address specs.
1142
+ #
1143
+ # Example:
1144
+ #
1145
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>'
1146
+ # mail.to #=> ['mikel@test.lindsaar.net']
1147
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
1148
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
1149
+ #
1150
+ # Also allows you to set the value by passing a value as a parameter
1151
+ #
1152
+ # Example:
1153
+ #
1154
+ # mail.to 'Mikel <mikel@test.lindsaar.net>'
1155
+ # mail.to #=> ['mikel@test.lindsaar.net']
1156
+ #
1157
+ # Additionally, you can append new addresses to the returned Array like
1158
+ # object.
1159
+ #
1160
+ # Example:
1161
+ #
1162
+ # mail.to 'Mikel <mikel@test.lindsaar.net>'
1163
+ # mail.to << 'ada@test.lindsaar.net'
1164
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
1165
+ def to( val = nil )
1166
+ default :to, val
1167
+ end
1168
+
1169
+ # Sets the To value of the mail object, pass in a string of the field
1170
+ #
1171
+ # Example:
1172
+ #
1173
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>'
1174
+ # mail.to #=> ['mikel@test.lindsaar.net']
1175
+ # mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
1176
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
1177
+ def to=( val )
1178
+ header[:to] = val
1179
+ end
1180
+
1181
+ # Returns the default value of the field requested as a symbol.
1182
+ #
1183
+ # Each header field has a :default method which returns the most common use case for
1184
+ # that field, for example, the date field types will return a DateTime object when
1185
+ # sent :default, the subject, or unstructured fields will return a decoded string of
1186
+ # their value, the address field types will return a single addr_spec or an array of
1187
+ # addr_specs if there is more than one.
1188
+ def default( sym, val = nil )
1189
+ if val
1190
+ header[sym] = val
1191
+ else
1192
+ header[sym].default if header[sym]
1193
+ end
1194
+ end
1195
+
1196
+ # Sets the body object of the message object.
1197
+ #
1198
+ # Example:
1199
+ #
1200
+ # mail.body = 'This is the body'
1201
+ # mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
1202
+ #
1203
+ # You can also reset the body of an Message object by setting body to nil
1204
+ #
1205
+ # Example:
1206
+ #
1207
+ # mail.body = 'this is the body'
1208
+ # mail.body.encoded #=> 'this is the body'
1209
+ # mail.body = nil
1210
+ # mail.body.encoded #=> ''
1211
+ #
1212
+ # If you try and set the body of an email that is a multipart email, then instead
1213
+ # of deleting all the parts of your email, mail will add a text/plain part to
1214
+ # your email:
1215
+ #
1216
+ # mail.add_file 'somefilename.png'
1217
+ # mail.parts.length #=> 1
1218
+ # mail.body = "This is a body"
1219
+ # mail.parts.length #=> 2
1220
+ # mail.parts.last.content_type.content_type #=> 'This is a body'
1221
+ def body=(value)
1222
+ body_lazy(value)
1223
+ end
1224
+
1225
+ # Returns the body of the message object. Or, if passed
1226
+ # a parameter sets the value.
1227
+ #
1228
+ # Example:
1229
+ #
1230
+ # mail = Mail::Message.new('To: mikel\r\n\r\nThis is the body')
1231
+ # mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
1232
+ #
1233
+ # mail.body 'This is another body'
1234
+ # mail.body #=> #<Mail::Body:0x13919c @raw_source="This is anothe...
1235
+ def body(value = nil)
1236
+ if value
1237
+ self.body = value
1238
+ # add_encoding_to_body
1239
+ else
1240
+ process_body_raw if @body_raw
1241
+ @body
1242
+ end
1243
+ end
1244
+
1245
+ def body_encoding(value)
1246
+ if value.nil?
1247
+ body.encoding
1248
+ else
1249
+ body.encoding = value
1250
+ end
1251
+ end
1252
+
1253
+ def body_encoding=(value)
1254
+ body.encoding = value
1255
+ end
1256
+
1257
+ # Returns the list of addresses this message should be sent to by
1258
+ # collecting the addresses off the to, cc and bcc fields.
1259
+ #
1260
+ # Example:
1261
+ #
1262
+ # mail.to = 'mikel@test.lindsaar.net'
1263
+ # mail.cc = 'sam@test.lindsaar.net'
1264
+ # mail.bcc = 'bob@test.lindsaar.net'
1265
+ # mail.destinations.length #=> 3
1266
+ # mail.destinations.first #=> 'mikel@test.lindsaar.net'
1267
+ def destinations
1268
+ [to_addrs, cc_addrs, bcc_addrs].compact.flatten
1269
+ end
1270
+
1271
+ # Returns an array of addresses (the encoded value) in the From field,
1272
+ # if no From field, returns an empty array
1273
+ def from_addrs
1274
+ from ? [from].flatten : []
1275
+ end
1276
+
1277
+ # Returns an array of addresses (the encoded value) in the To field,
1278
+ # if no To field, returns an empty array
1279
+ def to_addrs
1280
+ to ? [to].flatten : []
1281
+ end
1282
+
1283
+ # Returns an array of addresses (the encoded value) in the Cc field,
1284
+ # if no Cc field, returns an empty array
1285
+ def cc_addrs
1286
+ cc ? [cc].flatten : []
1287
+ end
1288
+
1289
+ # Returns an array of addresses (the encoded value) in the Bcc field,
1290
+ # if no Bcc field, returns an empty array
1291
+ def bcc_addrs
1292
+ bcc ? [bcc].flatten : []
1293
+ end
1294
+
1295
+ # Allows you to add an arbitrary header
1296
+ #
1297
+ # Example:
1298
+ #
1299
+ # mail['foo'] = '1234'
1300
+ # mail['foo'].to_s #=> '1234'
1301
+ def []=(name, value)
1302
+ if name.to_s == 'body'
1303
+ self.body = value
1304
+ elsif name.to_s =~ /content[-_]type/i
1305
+ header[name] = value
1306
+ elsif name.to_s == 'charset'
1307
+ self.charset = value
1308
+ else
1309
+ header[name] = value
1310
+ end
1311
+ end
1312
+
1313
+ # Allows you to read an arbitrary header
1314
+ #
1315
+ # Example:
1316
+ #
1317
+ # mail['foo'] = '1234'
1318
+ # mail['foo'].to_s #=> '1234'
1319
+ def [](name)
1320
+ header[underscoreize(name)]
1321
+ end
1322
+
1323
+ # Method Missing in this implementation allows you to set any of the
1324
+ # standard fields directly as you would the "to", "subject" etc.
1325
+ #
1326
+ # Those fields used most often (to, subject et al) are given their
1327
+ # own method for ease of documentation and also to avoid the hook
1328
+ # call to method missing.
1329
+ #
1330
+ # This will only catch the known fields listed in:
1331
+ #
1332
+ # Mail::Field::KNOWN_FIELDS
1333
+ #
1334
+ # as per RFC 2822, any ruby string or method name could pretty much
1335
+ # be a field name, so we don't want to just catch ANYTHING sent to
1336
+ # a message object and interpret it as a header.
1337
+ #
1338
+ # This method provides all three types of header call to set, read
1339
+ # and explicitly set with the = operator
1340
+ #
1341
+ # Examples:
1342
+ #
1343
+ # mail.comments = 'These are some comments'
1344
+ # mail.comments #=> 'These are some comments'
1345
+ #
1346
+ # mail.comments 'These are other comments'
1347
+ # mail.comments #=> 'These are other comments'
1348
+ #
1349
+ #
1350
+ # mail.date = 'Tue, 1 Jul 2003 10:52:37 +0200'
1351
+ # mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
1352
+ #
1353
+ # mail.date 'Tue, 1 Jul 2003 10:52:37 +0200'
1354
+ # mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
1355
+ #
1356
+ #
1357
+ # mail.resent_msg_id = '<1234@resent_msg_id.lindsaar.net>'
1358
+ # mail.resent_msg_id #=> '<1234@resent_msg_id.lindsaar.net>'
1359
+ #
1360
+ # mail.resent_msg_id '<4567@resent_msg_id.lindsaar.net>'
1361
+ # mail.resent_msg_id #=> '<4567@resent_msg_id.lindsaar.net>'
1362
+ def method_missing(name, *args, &block)
1363
+ #:nodoc:
1364
+ # Only take the structured fields, as we could take _anything_ really
1365
+ # as it could become an optional field... "but therin lies the dark side"
1366
+ field_name = underscoreize(name).chomp("=")
1367
+ if Mail::Field::KNOWN_FIELDS.include?(field_name)
1368
+ if args.empty?
1369
+ header[field_name]
1370
+ else
1371
+ header[field_name] = args.first
1372
+ end
1373
+ else
1374
+ super # otherwise pass it on
1375
+ end
1376
+ #:startdoc:
1377
+ end
1378
+
1379
+ # Returns an FieldList of all the fields in the header in the order that
1380
+ # they appear in the header
1381
+ def header_fields
1382
+ header.fields
1383
+ end
1384
+
1385
+ # Returns true if the message has a message ID field, the field may or may
1386
+ # not have a value, but the field exists or not.
1387
+ def has_message_id?
1388
+ header.has_message_id?
1389
+ end
1390
+
1391
+ # Returns true if the message has a Date field, the field may or may
1392
+ # not have a value, but the field exists or not.
1393
+ def has_date?
1394
+ header.has_date?
1395
+ end
1396
+
1397
+ # Returns true if the message has a Mime-Version field, the field may or may
1398
+ # not have a value, but the field exists or not.
1399
+ def has_mime_version?
1400
+ header.has_mime_version?
1401
+ end
1402
+
1403
+ def has_content_type?
1404
+ tmp = header[:content_type].main_type rescue nil
1405
+ !!tmp
1406
+ end
1407
+
1408
+ def has_charset?
1409
+ tmp = header[:content_type].parameters rescue nil
1410
+ !!(has_content_type? && tmp && tmp['charset'])
1411
+ end
1412
+
1413
+ def has_content_transfer_encoding?
1414
+ header[:content_transfer_encoding] && header[:content_transfer_encoding].errors.blank?
1415
+ end
1416
+
1417
+ def has_transfer_encoding? # :nodoc:
1418
+ STDERR.puts(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
1419
+ has_content_transfer_encoding?
1420
+ end
1421
+
1422
+ # Creates a new empty Message-ID field and inserts it in the correct order
1423
+ # into the Header. The MessageIdField object will automatically generate
1424
+ # a unique message ID if you try and encode it or output it to_s without
1425
+ # specifying a message id.
1426
+ #
1427
+ # It will preserve the message ID you specify if you do.
1428
+ def add_message_id(msg_id_val = '')
1429
+ header['message-id'] = msg_id_val
1430
+ end
1431
+
1432
+ # Creates a new empty Date field and inserts it in the correct order
1433
+ # into the Header. The DateField object will automatically generate
1434
+ # DateTime.now's date if you try and encode it or output it to_s without
1435
+ # specifying a date yourself.
1436
+ #
1437
+ # It will preserve any date you specify if you do.
1438
+ def add_date(date_val = '')
1439
+ header['date'] = date_val
1440
+ end
1441
+
1442
+ # Creates a new empty Mime Version field and inserts it in the correct order
1443
+ # into the Header. The MimeVersion object will automatically generate
1444
+ # set itself to '1.0' if you try and encode it or output it to_s without
1445
+ # specifying a version yourself.
1446
+ #
1447
+ # It will preserve any date you specify if you do.
1448
+ def add_mime_version(ver_val = '')
1449
+ header['mime-version'] = ver_val
1450
+ end
1451
+
1452
+ # Adds a content type and charset if the body is US-ASCII
1453
+ #
1454
+ # Otherwise raises a warning
1455
+ def add_content_type
1456
+ header[:content_type] = 'text/plain'
1457
+ end
1458
+
1459
+ # Adds a content type and charset if the body is US-ASCII
1460
+ #
1461
+ # Otherwise raises a warning
1462
+ def add_charset
1463
+ if !body.empty?
1464
+ # Only give a warning if this isn't an attachment, has non US-ASCII and the user
1465
+ # has not specified an encoding explicitly.
1466
+ if @defaulted_charset && body.raw_source.not_ascii_only? && !self.attachment?
1467
+ warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
1468
+ STDERR.puts(warning)
1469
+ end
1470
+ header[:content_type].parameters['charset'] = @charset
1471
+ end
1472
+ end
1473
+
1474
+ # Adds a content transfer encoding
1475
+ #
1476
+ # Otherwise raises a warning
1477
+ def add_content_transfer_encoding
1478
+ if body.only_us_ascii?
1479
+ header[:content_transfer_encoding] = '7bit'
1480
+ else
1481
+ warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
1482
+ STDERR.puts(warning)
1483
+ header[:content_transfer_encoding] = '8bit'
1484
+ end
1485
+ end
1486
+
1487
+ def add_transfer_encoding # :nodoc:
1488
+ STDERR.puts(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
1489
+ add_content_transfer_encoding
1490
+ end
1491
+
1492
+ def transfer_encoding # :nodoc:
1493
+ STDERR.puts(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
1494
+ content_transfer_encoding
1495
+ end
1496
+
1497
+ # Returns the MIME media type of part we are on, this is taken from the content-type header
1498
+ def mime_type
1499
+ has_content_type? ? header[:content_type].string : nil rescue nil
1500
+ end
1501
+
1502
+ def message_content_type
1503
+ STDERR.puts(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
1504
+ mime_type
1505
+ end
1506
+
1507
+ # Returns the character set defined in the content type field
1508
+ def charset
1509
+ if @header
1510
+ has_content_type? ? content_type_parameters['charset'] : @charset
1511
+ else
1512
+ @charset
1513
+ end
1514
+ end
1515
+
1516
+ # Sets the charset to the supplied value.
1517
+ def charset=(value)
1518
+ @defaulted_charset = false
1519
+ @charset = value
1520
+ @header.charset = value
1521
+ end
1522
+
1523
+ # Returns the main content type
1524
+ def main_type
1525
+ has_content_type? ? header[:content_type].main_type : nil rescue nil
1526
+ end
1527
+
1528
+ # Returns the sub content type
1529
+ def sub_type
1530
+ has_content_type? ? header[:content_type].sub_type : nil rescue nil
1531
+ end
1532
+
1533
+ # Returns the content type parameters
1534
+ def mime_parameters
1535
+ STDERR.puts(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
1536
+ content_type_parameters
1537
+ end
1538
+
1539
+ # Returns the content type parameters
1540
+ def content_type_parameters
1541
+ has_content_type? ? header[:content_type].parameters : nil rescue nil
1542
+ end
1543
+
1544
+ # Returns true if the message is multipart
1545
+ def multipart?
1546
+ has_content_type? ? !!(main_type =~ /^multipart$/i) : false
1547
+ end
1548
+
1549
+ # Returns true if the message is a multipart/report
1550
+ def multipart_report?
1551
+ multipart? && sub_type =~ /^report$/i
1552
+ end
1553
+
1554
+ # Returns true if the message is a multipart/report; report-type=delivery-status;
1555
+ def delivery_status_report?
1556
+ multipart_report? && content_type_parameters['report-type'] =~ /^delivery-status$/i
1557
+ end
1558
+
1559
+ # returns the part in a multipart/report email that has the content-type delivery-status
1560
+ def delivery_status_part
1561
+ @delivery_stats_part ||= parts.select { |p| p.delivery_status_report_part? }.first
1562
+ end
1563
+
1564
+ def bounced?
1565
+ delivery_status_part and delivery_status_part.bounced?
1566
+ end
1567
+
1568
+ def action
1569
+ delivery_status_part and delivery_status_part.action
1570
+ end
1571
+
1572
+ def final_recipient
1573
+ delivery_status_part and delivery_status_part.final_recipient
1574
+ end
1575
+
1576
+ def error_status
1577
+ delivery_status_part and delivery_status_part.error_status
1578
+ end
1579
+
1580
+ def diagnostic_code
1581
+ delivery_status_part and delivery_status_part.diagnostic_code
1582
+ end
1583
+
1584
+ def remote_mta
1585
+ delivery_status_part and delivery_status_part.remote_mta
1586
+ end
1587
+
1588
+ def retryable?
1589
+ delivery_status_part and delivery_status_part.retryable?
1590
+ end
1591
+
1592
+ # Returns the current boundary for this message part
1593
+ def boundary
1594
+ content_type_parameters ? content_type_parameters['boundary'] : nil
1595
+ end
1596
+
1597
+ # Returns a parts list object of all the parts in the message
1598
+ def parts
1599
+ body.parts
1600
+ end
1601
+
1602
+ # Returns an AttachmentsList object, which holds all of the attachments in
1603
+ # the receiver object (either the entire email or a part within) and all
1604
+ # of its descendants.
1605
+ #
1606
+ # It also allows you to add attachments to the mail object directly, like so:
1607
+ #
1608
+ # mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
1609
+ #
1610
+ # If you do this, then Mail will take the file name and work out the MIME media type
1611
+ # set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
1612
+ # base64 encode the contents of the attachment all for you.
1613
+ #
1614
+ # You can also specify overrides if you want by passing a hash instead of a string:
1615
+ #
1616
+ # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
1617
+ # :content => File.read('/path/to/filename.jpg')}
1618
+ #
1619
+ # If you want to use a different encoding than Base64, you can pass an encoding in,
1620
+ # but then it is up to you to pass in the content pre-encoded, and don't expect
1621
+ # Mail to know how to decode this data:
1622
+ #
1623
+ # file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
1624
+ # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
1625
+ # :encoding => 'SpecialEncoding',
1626
+ # :content => file_content }
1627
+ #
1628
+ # You can also search for specific attachments:
1629
+ #
1630
+ # # By Filename
1631
+ # mail.attachments['filename.jpg'] #=> Mail::Part object or nil
1632
+ #
1633
+ # # or by index
1634
+ # mail.attachments[0] #=> Mail::Part (first attachment)
1635
+ #
1636
+ def attachments
1637
+ parts.attachments
1638
+ end
1639
+
1640
+ def has_attachments?
1641
+ !attachments.empty?
1642
+ end
1643
+
1644
+ # Accessor for html_part
1645
+ def html_part(&block)
1646
+ if block_given?
1647
+ self.html_part = Mail::Part.new(:content_type => 'text/html', &block)
1648
+ else
1649
+ @html_part || find_first_mime_type('text/html')
1650
+ end
1651
+ end
1652
+
1653
+ # Accessor for text_part
1654
+ def text_part(&block)
1655
+ if block_given?
1656
+ self.text_part = Mail::Part.new(:content_type => 'text/plain', &block)
1657
+ else
1658
+ @text_part || find_first_mime_type('text/plain')
1659
+ end
1660
+ end
1661
+
1662
+ # Helper to add a html part to a multipart/alternative email. If this and
1663
+ # text_part are both defined in a message, then it will be a multipart/alternative
1664
+ # message and set itself that way.
1665
+ def html_part=(msg)
1666
+ # Assign the html part and set multipart/alternative if there's a text part.
1667
+ if msg
1668
+ @html_part = msg
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
1681
+ end
1682
+ end
1683
+
1684
+ # Helper to add a text part to a multipart/alternative email. If this and
1685
+ # html_part are both defined in a message, then it will be a multipart/alternative
1686
+ # message and set itself that way.
1687
+ def text_part=(msg)
1688
+ # Assign the text part and set multipart/alternative if there's an html part.
1689
+ if msg
1690
+ @text_part = msg
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
1703
+ end
1704
+ end
1705
+
1706
+ # Adds a part to the parts list or creates the part list
1707
+ def add_part(part)
1708
+ if !body.multipart? && !self.body.decoded.blank?
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
1713
+ end
1714
+ add_boundary
1715
+ self.body << part
1716
+ end
1717
+
1718
+ # Allows you to add a part in block form to an existing mail message object
1719
+ #
1720
+ # Example:
1721
+ #
1722
+ # mail = Mail.new do
1723
+ # part :content_type => "multipart/alternative", :content_disposition => "inline" do |p|
1724
+ # p.part :content_type => "text/plain", :body => "test text\nline #2"
1725
+ # p.part :content_type => "text/html", :body => "<b>test</b> HTML<br/>\nline #2"
1726
+ # end
1727
+ # end
1728
+ def part(params = {})
1729
+ new_part = Part.new(params)
1730
+ yield new_part if block_given?
1731
+ add_part(new_part)
1732
+ end
1733
+
1734
+ # Adds a file to the message. You have two options with this method, you can
1735
+ # just pass in the absolute path to the file you want and Mail will read the file,
1736
+ # get the filename from the path you pass in and guess the MIME media type, or you
1737
+ # can pass in the filename as a string, and pass in the file content as a blob.
1738
+ #
1739
+ # Example:
1740
+ #
1741
+ # m = Mail.new
1742
+ # m.add_file('/path/to/filename.png')
1743
+ #
1744
+ # m = Mail.new
1745
+ # m.add_file(:filename => 'filename.png', :content => File.read('/path/to/file.jpg'))
1746
+ #
1747
+ # Note also that if you add a file to an existing message, Mail will convert that message
1748
+ # to a MIME multipart email, moving whatever plain text body you had into its own text
1749
+ # plain part.
1750
+ #
1751
+ # Example:
1752
+ #
1753
+ # m = Mail.new do
1754
+ # body 'this is some text'
1755
+ # end
1756
+ # m.multipart? #=> false
1757
+ # m.add_file('/path/to/filename.png')
1758
+ # m.multipart? #=> true
1759
+ # m.parts.first.content_type.content_type #=> 'text/plain'
1760
+ # m.parts.last.content_type.content_type #=> 'image/png'
1761
+ #
1762
+ # See also #attachments
1763
+ def add_file(values)
1764
+ convert_to_multipart unless self.multipart? || self.body.decoded.blank?
1765
+ add_multipart_mixed_header
1766
+ if values.is_a?(String)
1767
+ basename = File.basename(values)
1768
+ filedata = File.open(values, 'rb') { |f| f.read }
1769
+ else
1770
+ basename = values[:filename]
1771
+ filedata = values[:content] || File.open(values[:filename], 'rb') { |f| f.read }
1772
+ end
1773
+ self.attachments[basename] = filedata
1774
+ end
1775
+
1776
+ def convert_to_multipart
1777
+ text = body.decoded
1778
+ self.body = ''
1779
+ text_part = Mail::Part.new({:content_type => 'text/plain;',
1780
+ :body => text})
1781
+ text_part.charset = charset unless @defaulted_charset
1782
+ self.body << text_part
1783
+ end
1784
+
1785
+ # Encodes the message, calls encode on all its parts, gets an email message
1786
+ # ready to send
1787
+ def ready_to_send!
1788
+ identify_and_set_transfer_encoding
1789
+ parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
1790
+ parts.each do |part|
1791
+ part.transport_encoding = transport_encoding
1792
+ part.ready_to_send!
1793
+ end
1794
+ add_required_fields
1795
+ end
1796
+
1797
+ def encode!
1798
+ STDERR.puts("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
1799
+ ready_to_send!
1800
+ end
1801
+
1802
+ # Outputs an encoded string representation of the mail message including
1803
+ # all headers, attachments, etc. This is an encoded email in US-ASCII,
1804
+ # so it is able to be directly sent to an email server.
1805
+ def encoded
1806
+ ready_to_send!
1807
+ buffer = header.encoded
1808
+ buffer << "\r\n"
1809
+ buffer << body.encoded(content_transfer_encoding)
1810
+ buffer
1811
+ end
1812
+
1813
+ def without_attachments!
1814
+ return self unless has_attachments?
1815
+
1816
+ parts.delete_if { |p| p.attachment? }
1817
+ body_raw = if parts.empty?
1818
+ ''
1819
+ else
1820
+ body.encoded
1821
+ end
1822
+
1823
+ @body = Mail::Body.new(body_raw)
1824
+
1825
+ self
1826
+ end
1827
+
1828
+ def to_yaml(opts = {})
1829
+ hash = {}
1830
+ hash['headers'] = {}
1831
+ header.fields.each do |field|
1832
+ hash['headers'][field.name] = field.value
1833
+ end
1834
+ hash['delivery_handler'] = delivery_handler.to_s if delivery_handler
1835
+ hash['transport_encoding'] = transport_encoding.to_s
1836
+ special_variables = [:@header, :@delivery_handler, :@transport_encoding]
1837
+ if multipart?
1838
+ hash['multipart_body'] = []
1839
+ body.parts.map { |part| hash['multipart_body'] << part.to_yaml }
1840
+ special_variables.push(:@body, :@text_part, :@html_part)
1841
+ end
1842
+ (instance_variables.map(&:to_sym) - special_variables).each do |var|
1843
+ hash[var.to_s] = instance_variable_get(var)
1844
+ end
1845
+ hash.to_yaml(opts)
1846
+ end
1847
+
1848
+ def self.from_yaml(str)
1849
+ hash = YAML.load(str)
1850
+ m = self.new(:headers => hash['headers'])
1851
+ hash.delete('headers')
1852
+ hash.each do |k,v|
1853
+ case
1854
+ when k == 'delivery_handler'
1855
+ begin
1856
+ m.delivery_handler = Object.const_get(v) unless v.blank?
1857
+ rescue NameError
1858
+ end
1859
+ when k == 'transport_encoding'
1860
+ m.transport_encoding(v)
1861
+ when k == 'multipart_body'
1862
+ v.map {|part| m.add_part Mail::Part.from_yaml(part) }
1863
+ when k =~ /^@/
1864
+ m.instance_variable_set(k.to_sym, v)
1865
+ end
1866
+ end
1867
+ m
1868
+ end
1869
+
1870
+ def self.from_hash(hash)
1871
+ Mail::Message.new(hash)
1872
+ end
1873
+
1874
+ def to_s
1875
+ encoded
1876
+ end
1877
+
1878
+ def inspect
1879
+ "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1880
+ end
1881
+
1882
+ def decoded
1883
+ case
1884
+ when self.text?
1885
+ decode_body_as_text
1886
+ when self.attachment?
1887
+ decode_body
1888
+ when !self.multipart?
1889
+ body.decoded
1890
+ else
1891
+ raise NoMethodError, 'Can not decode an entire message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
1892
+ end
1893
+ end
1894
+
1895
+ def read
1896
+ if self.attachment?
1897
+ decode_body
1898
+ else
1899
+ raise NoMethodError, 'Can not call read on a part unless it is an attachment.'
1900
+ end
1901
+ end
1902
+
1903
+ def decode_body
1904
+ body.decoded
1905
+ end
1906
+
1907
+ # Returns true if this part is an attachment,
1908
+ # false otherwise.
1909
+ def attachment?
1910
+ !!find_attachment
1911
+ end
1912
+
1913
+ # Returns the attachment data if there is any
1914
+ def attachment
1915
+ @attachment
1916
+ end
1917
+
1918
+ # Returns the filename of the attachment
1919
+ def filename
1920
+ find_attachment
1921
+ end
1922
+
1923
+ def all_parts
1924
+ parts.map { |p| [p, p.all_parts] }.flatten
1925
+ end
1926
+
1927
+ def find_first_mime_type(mt)
1928
+ all_parts.detect { |p| p.mime_type == mt && !p.attachment? }
1929
+ end
1930
+
1931
+ # Skips the deletion of this message. All other messages
1932
+ # flagged for delete still will be deleted at session close (i.e. when
1933
+ # #find exits). Only has an effect if you're using #find_and_delete
1934
+ # or #find with :delete_after_find set to true.
1935
+ def skip_deletion
1936
+ @mark_for_delete = false
1937
+ end
1938
+
1939
+ # Sets whether this message should be deleted at session close (i.e.
1940
+ # after #find). Message will only be deleted if messages are retrieved
1941
+ # using the #find_and_delete method, or by calling #find with
1942
+ # :delete_after_find set to true.
1943
+ def mark_for_delete=(value = true)
1944
+ @mark_for_delete = value
1945
+ end
1946
+
1947
+ # Returns whether message will be marked for deletion.
1948
+ # If so, the message will be deleted at session close (i.e. after #find
1949
+ # exits), but only if also using the #find_and_delete method, or by
1950
+ # calling #find with :delete_after_find set to true.
1951
+ #
1952
+ # Side-note: Just to be clear, this method will return true even if
1953
+ # the message hasn't yet been marked for delete on the mail server.
1954
+ # However, if this method returns true, it *will be* marked on the
1955
+ # server after each block yields back to #find or #find_and_delete.
1956
+ def is_marked_for_delete?
1957
+ return @mark_for_delete
1958
+ end
1959
+
1960
+ def text?
1961
+ has_content_type? ? !!(main_type =~ /^text$/i) : false
1962
+ end
1963
+
1964
+ private
1965
+
1966
+ # 2.1. General Description
1967
+ # A message consists of header fields (collectively called "the header
1968
+ # of the message") followed, optionally, by a body. The header is a
1969
+ # sequence of lines of characters with special syntax as defined in
1970
+ # this standard. The body is simply a sequence of characters that
1971
+ # follows the header and is separated from the header by an empty line
1972
+ # (i.e., a line with nothing preceding the CRLF).
1973
+ #
1974
+ # Additionally, I allow for the case where someone might have put whitespace
1975
+ # on the "gap line"
1976
+ def parse_message
1977
+ header_part, body_part = raw_source.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
1978
+ self.header = header_part
1979
+ self.body = body_part
1980
+ end
1981
+
1982
+ def raw_source=(value)
1983
+ @raw_source = value.to_crlf
1984
+ @raw_source.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
1985
+ @raw_source
1986
+ end
1987
+
1988
+ # see comments to body=. We take data and process it lazily
1989
+ def body_lazy(value)
1990
+ process_body_raw if @body_raw && value
1991
+ case
1992
+ when value == nil || value.length<=0
1993
+ @body = Mail::Body.new('')
1994
+ @body_raw = nil
1995
+ add_encoding_to_body
1996
+ when @body && @body.multipart?
1997
+ @body << Mail::Part.new(value)
1998
+ add_encoding_to_body
1999
+ else
2000
+ @body_raw = value
2001
+ # process_body_raw
2002
+ end
2003
+ end
2004
+
2005
+
2006
+ def process_body_raw
2007
+ @body = Mail::Body.new(@body_raw)
2008
+ @body_raw = nil
2009
+ separate_parts if @separate_parts
2010
+
2011
+ add_encoding_to_body
2012
+ end
2013
+
2014
+ def set_envelope_header
2015
+ raw_string = raw_source.to_s
2016
+ if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
2017
+ set_envelope(match_data[1])
2018
+ self.raw_source = raw_string.sub(match_data[0], "")
2019
+ end
2020
+ end
2021
+
2022
+ def separate_parts
2023
+ body.split!(boundary)
2024
+ end
2025
+
2026
+ def add_encoding_to_body
2027
+ if has_content_transfer_encoding?
2028
+ @body.encoding = content_transfer_encoding
2029
+ end
2030
+ end
2031
+
2032
+ def identify_and_set_transfer_encoding
2033
+ if body && body.multipart?
2034
+ self.content_transfer_encoding = @transport_encoding
2035
+ else
2036
+ self.content_transfer_encoding = body.get_best_encoding(@transport_encoding)
2037
+ end
2038
+ end
2039
+
2040
+ def add_required_fields
2041
+ add_required_message_fields
2042
+ add_multipart_mixed_header if body.multipart?
2043
+ add_content_type unless has_content_type?
2044
+ add_charset unless has_charset?
2045
+ add_content_transfer_encoding unless has_content_transfer_encoding?
2046
+ end
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
+
2054
+ def add_multipart_alternate_header
2055
+ header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
2056
+ header['content_type'].parameters[:charset] = @charset
2057
+ body.boundary = boundary
2058
+ end
2059
+
2060
+ def add_boundary
2061
+ unless body.boundary && boundary
2062
+ header['content-type'] = 'multipart/mixed' unless header['content-type']
2063
+ header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
2064
+ header['content_type'].parameters[:charset] = @charset
2065
+ body.boundary = boundary
2066
+ end
2067
+ end
2068
+
2069
+ def add_multipart_mixed_header
2070
+ unless header['content-type']
2071
+ header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
2072
+ header['content_type'].parameters[:charset] = @charset
2073
+ body.boundary = boundary
2074
+ end
2075
+ end
2076
+
2077
+ def init_with_hash(hash)
2078
+ passed_in_options = IndifferentHash.new(hash)
2079
+ self.raw_source = ''
2080
+
2081
+ @header = Mail::Header.new
2082
+ @body = Mail::Body.new
2083
+ @body_raw = nil
2084
+
2085
+ # We need to store the body until last, as we need all headers added first
2086
+ body_content = nil
2087
+
2088
+ passed_in_options.each_pair do |k,v|
2089
+ k = underscoreize(k).to_sym if k.class == String
2090
+ if k == :headers
2091
+ self.headers(v)
2092
+ elsif k == :body
2093
+ body_content = v
2094
+ else
2095
+ self[k] = v
2096
+ end
2097
+ end
2098
+
2099
+ if body_content
2100
+ self.body = body_content
2101
+ if has_content_transfer_encoding?
2102
+ body.encoding = content_transfer_encoding
2103
+ end
2104
+ end
2105
+ end
2106
+
2107
+ def init_with_string(string)
2108
+ self.raw_source = string
2109
+ set_envelope_header
2110
+ parse_message
2111
+ @separate_parts = multipart?
2112
+ end
2113
+
2114
+ # Returns the filename of the attachment (if it exists) or returns nil
2115
+ def find_attachment
2116
+ content_type_name = header[:content_type].filename rescue nil
2117
+ content_disp_name = header[:content_disposition].filename rescue nil
2118
+ content_loc_name = header[:content_location].location rescue nil
2119
+ case
2120
+ when content_type && content_type_name
2121
+ filename = content_type_name
2122
+ when content_disposition && content_disp_name
2123
+ filename = content_disp_name
2124
+ when content_location && content_loc_name
2125
+ filename = content_loc_name
2126
+ else
2127
+ filename = nil
2128
+ end
2129
+ filename = Mail::Encodings.decode_encode(filename, :decode) if filename rescue filename
2130
+ filename
2131
+ end
2132
+
2133
+ def do_delivery
2134
+ begin
2135
+ if perform_deliveries
2136
+ delivery_method.deliver!(self)
2137
+ end
2138
+ rescue => e # Net::SMTP errors or sendmail pipe errors
2139
+ raise e if raise_delivery_errors
2140
+ end
2141
+ end
2142
+
2143
+ def decode_body_as_text
2144
+ body_text = decode_body
2145
+ if charset
2146
+ if RUBY_VERSION < '1.9'
2147
+ require 'iconv'
2148
+ return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
2149
+ else
2150
+ if encoding = Encoding.find(charset) rescue nil
2151
+ body_text.force_encoding(encoding)
2152
+ return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
2153
+ end
2154
+ end
2155
+ end
2156
+ body_text
2157
+ end
2158
+
2159
+ end
2160
+ end