otherinbox-mail 2.4.4

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