mail-portertech 2.6.2.edge

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 (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