mail-trunk 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG.rdoc +555 -0
  2. data/Dependencies.txt +3 -0
  3. data/Gemfile +29 -0
  4. data/README.mkd +583 -0
  5. data/Rakefile +66 -0
  6. data/TODO.rdoc +9 -0
  7. data/lib/VERSION +4 -0
  8. data/lib/mail.rb +89 -0
  9. data/lib/mail/attachments_list.rb +105 -0
  10. data/lib/mail/body.rb +292 -0
  11. data/lib/mail/configuration.rb +73 -0
  12. data/lib/mail/core_extensions/nil.rb +17 -0
  13. data/lib/mail/core_extensions/object.rb +13 -0
  14. data/lib/mail/core_extensions/shellwords.rb +57 -0
  15. data/lib/mail/core_extensions/smtp.rb +25 -0
  16. data/lib/mail/core_extensions/string.rb +31 -0
  17. data/lib/mail/core_extensions/string/access.rb +104 -0
  18. data/lib/mail/core_extensions/string/multibyte.rb +78 -0
  19. data/lib/mail/elements.rb +14 -0
  20. data/lib/mail/elements/address.rb +306 -0
  21. data/lib/mail/elements/address_list.rb +74 -0
  22. data/lib/mail/elements/content_disposition_element.rb +30 -0
  23. data/lib/mail/elements/content_location_element.rb +25 -0
  24. data/lib/mail/elements/content_transfer_encoding_element.rb +24 -0
  25. data/lib/mail/elements/content_type_element.rb +35 -0
  26. data/lib/mail/elements/date_time_element.rb +26 -0
  27. data/lib/mail/elements/envelope_from_element.rb +34 -0
  28. data/lib/mail/elements/message_ids_element.rb +29 -0
  29. data/lib/mail/elements/mime_version_element.rb +26 -0
  30. data/lib/mail/elements/phrase_list.rb +21 -0
  31. data/lib/mail/elements/received_element.rb +30 -0
  32. data/lib/mail/encodings.rb +266 -0
  33. data/lib/mail/encodings/7bit.rb +31 -0
  34. data/lib/mail/encodings/8bit.rb +31 -0
  35. data/lib/mail/encodings/base64.rb +33 -0
  36. data/lib/mail/encodings/binary.rb +31 -0
  37. data/lib/mail/encodings/quoted_printable.rb +38 -0
  38. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  39. data/lib/mail/envelope.rb +35 -0
  40. data/lib/mail/field.rb +224 -0
  41. data/lib/mail/field_list.rb +33 -0
  42. data/lib/mail/fields.rb +35 -0
  43. data/lib/mail/fields/bcc_field.rb +56 -0
  44. data/lib/mail/fields/cc_field.rb +55 -0
  45. data/lib/mail/fields/comments_field.rb +41 -0
  46. data/lib/mail/fields/common/address_container.rb +16 -0
  47. data/lib/mail/fields/common/common_address.rb +125 -0
  48. data/lib/mail/fields/common/common_date.rb +42 -0
  49. data/lib/mail/fields/common/common_field.rb +51 -0
  50. data/lib/mail/fields/common/common_message_id.rb +44 -0
  51. data/lib/mail/fields/common/parameter_hash.rb +58 -0
  52. data/lib/mail/fields/content_description_field.rb +19 -0
  53. data/lib/mail/fields/content_disposition_field.rb +69 -0
  54. data/lib/mail/fields/content_id_field.rb +63 -0
  55. data/lib/mail/fields/content_location_field.rb +42 -0
  56. data/lib/mail/fields/content_transfer_encoding_field.rb +50 -0
  57. data/lib/mail/fields/content_type_field.rb +198 -0
  58. data/lib/mail/fields/date_field.rb +57 -0
  59. data/lib/mail/fields/from_field.rb +55 -0
  60. data/lib/mail/fields/in_reply_to_field.rb +55 -0
  61. data/lib/mail/fields/keywords_field.rb +44 -0
  62. data/lib/mail/fields/message_id_field.rb +83 -0
  63. data/lib/mail/fields/mime_version_field.rb +53 -0
  64. data/lib/mail/fields/optional_field.rb +13 -0
  65. data/lib/mail/fields/received_field.rb +75 -0
  66. data/lib/mail/fields/references_field.rb +55 -0
  67. data/lib/mail/fields/reply_to_field.rb +55 -0
  68. data/lib/mail/fields/resent_bcc_field.rb +55 -0
  69. data/lib/mail/fields/resent_cc_field.rb +55 -0
  70. data/lib/mail/fields/resent_date_field.rb +35 -0
  71. data/lib/mail/fields/resent_from_field.rb +55 -0
  72. data/lib/mail/fields/resent_message_id_field.rb +34 -0
  73. data/lib/mail/fields/resent_sender_field.rb +62 -0
  74. data/lib/mail/fields/resent_to_field.rb +55 -0
  75. data/lib/mail/fields/return_path_field.rb +65 -0
  76. data/lib/mail/fields/sender_field.rb +67 -0
  77. data/lib/mail/fields/structured_field.rb +51 -0
  78. data/lib/mail/fields/subject_field.rb +16 -0
  79. data/lib/mail/fields/to_field.rb +55 -0
  80. data/lib/mail/fields/unstructured_field.rb +182 -0
  81. data/lib/mail/header.rb +265 -0
  82. data/lib/mail/indifferent_hash.rb +146 -0
  83. data/lib/mail/mail.rb +255 -0
  84. data/lib/mail/message.rb +2017 -0
  85. data/lib/mail/multibyte.rb +42 -0
  86. data/lib/mail/multibyte/chars.rb +474 -0
  87. data/lib/mail/multibyte/exceptions.rb +8 -0
  88. data/lib/mail/multibyte/unicode.rb +392 -0
  89. data/lib/mail/multibyte/utils.rb +60 -0
  90. data/lib/mail/network.rb +13 -0
  91. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  92. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  93. data/lib/mail/network/delivery_methods/smtp.rb +137 -0
  94. data/lib/mail/network/delivery_methods/smtp_connection.rb +74 -0
  95. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  96. data/lib/mail/network/retriever_methods/base.rb +63 -0
  97. data/lib/mail/network/retriever_methods/imap.rb +158 -0
  98. data/lib/mail/network/retriever_methods/pop3.rb +140 -0
  99. data/lib/mail/network/retriever_methods/test_retriever.rb +47 -0
  100. data/lib/mail/parsers/address_lists.rb +64 -0
  101. data/lib/mail/parsers/address_lists.treetop +19 -0
  102. data/lib/mail/parsers/content_disposition.rb +535 -0
  103. data/lib/mail/parsers/content_disposition.treetop +46 -0
  104. data/lib/mail/parsers/content_location.rb +139 -0
  105. data/lib/mail/parsers/content_location.treetop +20 -0
  106. data/lib/mail/parsers/content_transfer_encoding.rb +162 -0
  107. data/lib/mail/parsers/content_transfer_encoding.treetop +20 -0
  108. data/lib/mail/parsers/content_type.rb +967 -0
  109. data/lib/mail/parsers/content_type.treetop +68 -0
  110. data/lib/mail/parsers/date_time.rb +114 -0
  111. data/lib/mail/parsers/date_time.treetop +11 -0
  112. data/lib/mail/parsers/envelope_from.rb +194 -0
  113. data/lib/mail/parsers/envelope_from.treetop +32 -0
  114. data/lib/mail/parsers/message_ids.rb +45 -0
  115. data/lib/mail/parsers/message_ids.treetop +15 -0
  116. data/lib/mail/parsers/mime_version.rb +144 -0
  117. data/lib/mail/parsers/mime_version.treetop +19 -0
  118. data/lib/mail/parsers/phrase_lists.rb +45 -0
  119. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  120. data/lib/mail/parsers/received.rb +71 -0
  121. data/lib/mail/parsers/received.treetop +11 -0
  122. data/lib/mail/parsers/rfc2045.rb +464 -0
  123. data/lib/mail/parsers/rfc2045.treetop +36 -0
  124. data/lib/mail/parsers/rfc2822.rb +5341 -0
  125. data/lib/mail/parsers/rfc2822.treetop +410 -0
  126. data/lib/mail/parsers/rfc2822_obsolete.rb +3757 -0
  127. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  128. data/lib/mail/part.rb +116 -0
  129. data/lib/mail/parts_list.rb +51 -0
  130. data/lib/mail/patterns.rb +35 -0
  131. data/lib/mail/utilities.rb +215 -0
  132. data/lib/mail/version.rb +24 -0
  133. data/lib/mail/version_specific/ruby_1_8.rb +98 -0
  134. data/lib/mail/version_specific/ruby_1_9.rb +113 -0
  135. data/lib/tasks/corpus.rake +125 -0
  136. data/lib/tasks/treetop.rake +10 -0
  137. metadata +221 -0
@@ -0,0 +1,146 @@
1
+ # encoding: utf-8
2
+
3
+ # This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
4
+ # itself does not depend on ActiveSupport to avoid versioning conflicts
5
+
6
+ module Mail
7
+ class IndifferentHash < Hash
8
+
9
+ def initialize(constructor = {})
10
+ if constructor.is_a?(Hash)
11
+ super()
12
+ update(constructor)
13
+ else
14
+ super(constructor)
15
+ end
16
+ end
17
+
18
+ def default(key = nil)
19
+ if key.is_a?(Symbol) && include?(key = key.to_s)
20
+ self[key]
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def self.new_from_hash_copying_default(hash)
27
+ IndifferentHash.new(hash).tap do |new_hash|
28
+ new_hash.default = hash.default
29
+ end
30
+ end
31
+
32
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
33
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
34
+
35
+ # Assigns a new value to the hash:
36
+ #
37
+ # hash = HashWithIndifferentAccess.new
38
+ # hash[:key] = "value"
39
+ #
40
+ def []=(key, value)
41
+ regular_writer(convert_key(key), convert_value(value))
42
+ end
43
+
44
+ alias_method :store, :[]=
45
+
46
+ # Updates the instantized hash with values from the second:
47
+ #
48
+ # hash_1 = HashWithIndifferentAccess.new
49
+ # hash_1[:key] = "value"
50
+ #
51
+ # hash_2 = HashWithIndifferentAccess.new
52
+ # hash_2[:key] = "New Value!"
53
+ #
54
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
55
+ #
56
+ def update(other_hash)
57
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
58
+ self
59
+ end
60
+
61
+ alias_method :merge!, :update
62
+
63
+ # Checks the hash for a key matching the argument passed in:
64
+ #
65
+ # hash = HashWithIndifferentAccess.new
66
+ # hash["key"] = "value"
67
+ # hash.key? :key # => true
68
+ # hash.key? "key" # => true
69
+ #
70
+ def key?(key)
71
+ super(convert_key(key))
72
+ end
73
+
74
+ alias_method :include?, :key?
75
+ alias_method :has_key?, :key?
76
+ alias_method :member?, :key?
77
+
78
+ # Fetches the value for the specified key, same as doing hash[key]
79
+ def fetch(key, *extras)
80
+ super(convert_key(key), *extras)
81
+ end
82
+
83
+ # Returns an array of the values at the specified indices:
84
+ #
85
+ # hash = HashWithIndifferentAccess.new
86
+ # hash[:a] = "x"
87
+ # hash[:b] = "y"
88
+ # hash.values_at("a", "b") # => ["x", "y"]
89
+ #
90
+ def values_at(*indices)
91
+ indices.collect {|key| self[convert_key(key)]}
92
+ end
93
+
94
+ # Returns an exact copy of the hash.
95
+ def dup
96
+ IndifferentHash.new(self)
97
+ end
98
+
99
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
100
+ # Does not overwrite the existing hash.
101
+ def merge(hash)
102
+ self.dup.update(hash)
103
+ end
104
+
105
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
106
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
107
+ def reverse_merge(other_hash)
108
+ super self.class.new_from_hash_copying_default(other_hash)
109
+ end
110
+
111
+ def reverse_merge!(other_hash)
112
+ replace(reverse_merge( other_hash ))
113
+ end
114
+
115
+ # Removes a specified key from the hash.
116
+ def delete(key)
117
+ super(convert_key(key))
118
+ end
119
+
120
+ def stringify_keys!; self end
121
+ def stringify_keys; dup end
122
+ def symbolize_keys; to_hash.symbolize_keys end
123
+ def to_options!; self end
124
+
125
+ def to_hash
126
+ Hash.new(default).merge!(self)
127
+ end
128
+
129
+ protected
130
+
131
+ def convert_key(key)
132
+ key.kind_of?(Symbol) ? key.to_s : key
133
+ end
134
+
135
+ def convert_value(value)
136
+ if value.class == Hash
137
+ self.class.new_from_hash_copying_default(value)
138
+ elsif value.is_a?(Array)
139
+ value.dup.replace(value.map { |e| convert_value(e) })
140
+ else
141
+ value
142
+ end
143
+ end
144
+
145
+ end
146
+ end
@@ -0,0 +1,255 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+
4
+ # Allows you to create a new Mail::Message object.
5
+ #
6
+ # You can make an email via passing a string or passing a block.
7
+ #
8
+ # For example, the following two examples will create the same email
9
+ # message:
10
+ #
11
+ # Creating via a string:
12
+ #
13
+ # string = "To: mikel@test.lindsaar.net\r\n"
14
+ # string << "From: bob@test.lindsaar.net\r\n"
15
+ # string << "Subject: This is an email\r\n"
16
+ # string << "\r\n"
17
+ # string << "This is the body"
18
+ # Mail.new(string)
19
+ #
20
+ # Or creating via a block:
21
+ #
22
+ # message = Mail.new do
23
+ # to 'mikel@test.lindsaar.net'
24
+ # from 'bob@test.lindsaar.net'
25
+ # subject 'This is an email'
26
+ # body 'This is the body'
27
+ # end
28
+ #
29
+ # Or creating via a hash (or hash like object):
30
+ #
31
+ # message = Mail.new({:to => 'mikel@test.lindsaar.net',
32
+ # 'from' => 'bob@test.lindsaar.net',
33
+ # :subject => 'This is an email',
34
+ # :body => 'This is the body' })
35
+ #
36
+ # Note, the hash keys can be strings or symbols, the passed in object
37
+ # does not need to be a hash, it just needs to respond to :each_pair
38
+ # and yield each key value pair.
39
+ #
40
+ # As a side note, you can also create a new email through creating
41
+ # a Mail::Message object directly and then passing in values via string,
42
+ # symbol or direct method calls. See Mail::Message for more information.
43
+ #
44
+ # mail = Mail.new
45
+ # mail.to = 'mikel@test.lindsaar.net'
46
+ # mail[:from] = 'bob@test.lindsaar.net'
47
+ # mail['subject'] = 'This is an email'
48
+ # mail.body = 'This is the body'
49
+ def self.new(*args, &block)
50
+ Message.new(args, &block)
51
+ end
52
+
53
+ # Sets the default delivery method and retriever method for all new Mail objects.
54
+ # The delivery_method and retriever_method default to :smtp and :pop3, with defaults
55
+ # set.
56
+ #
57
+ # So sending a new email, if you have an SMTP server running on localhost is
58
+ # as easy as:
59
+ #
60
+ # Mail.deliver do
61
+ # to 'mikel@test.lindsaar.net'
62
+ # from 'bob@test.lindsaar.net'
63
+ # subject 'hi there!'
64
+ # body 'this is a body'
65
+ # end
66
+ #
67
+ # If you do not specify anything, you will get the following equivalent code set in
68
+ # every new mail object:
69
+ #
70
+ # Mail.defaults do
71
+ # delivery_method :smtp, { :address => "localhost",
72
+ # :port => 25,
73
+ # :domain => 'localhost.localdomain',
74
+ # :user_name => nil,
75
+ # :password => nil,
76
+ # :authentication => nil,
77
+ # :enable_starttls_auto => true }
78
+ #
79
+ # retriever_method :pop3, { :address => "localhost",
80
+ # :port => 995,
81
+ # :user_name => nil,
82
+ # :password => nil,
83
+ # :enable_ssl => true }
84
+ # end
85
+ #
86
+ # Mail.delivery_method.new #=> Mail::SMTP instance
87
+ # Mail.retriever_method.new #=> Mail::POP3 instance
88
+ #
89
+ # Each mail object inherits the default set in Mail.delivery_method, however, on
90
+ # a per email basis, you can override the method:
91
+ #
92
+ # mail.delivery_method :sendmail
93
+ #
94
+ # Or you can override the method and pass in settings:
95
+ #
96
+ # mail.delivery_method :sendmail, { :address => 'some.host' }
97
+ #
98
+ # You can also just modify the settings:
99
+ #
100
+ # mail.delivery_settings = { :address => 'some.host' }
101
+ #
102
+ # The passed in hash is just merged against the defaults with +merge!+ and the result
103
+ # assigned the mail object. So the above example will change only the :address value
104
+ # of the global smtp_settings to be 'some.host', keeping all other values
105
+ def self.defaults(&block)
106
+ Configuration.instance.instance_eval(&block)
107
+ end
108
+
109
+ # Returns the delivery method selected, defaults to an instance of Mail::SMTP
110
+ def self.delivery_method
111
+ Configuration.instance.delivery_method
112
+ end
113
+
114
+ # Returns the retriever method selected, defaults to an instance of Mail::POP3
115
+ def self.retriever_method
116
+ Configuration.instance.retriever_method
117
+ end
118
+
119
+ # Send an email using the default configuration. You do need to set a default
120
+ # configuration first before you use self.deliver, if you don't, an appropriate
121
+ # error will be raised telling you to.
122
+ #
123
+ # If you do not specify a delivery type, SMTP will be used.
124
+ #
125
+ # Mail.deliver do
126
+ # to 'mikel@test.lindsaar.net'
127
+ # from 'ada@test.lindsaar.net'
128
+ # subject 'This is a test email'
129
+ # body 'Not much to say here'
130
+ # end
131
+ #
132
+ # You can also do:
133
+ #
134
+ # mail = Mail.read('email.eml')
135
+ # mail.deliver!
136
+ #
137
+ # And your email object will be created and sent.
138
+ def self.deliver(*args, &block)
139
+ mail = self.new(args, &block)
140
+ mail.deliver
141
+ mail
142
+ end
143
+
144
+ # Find emails from the default retriever
145
+ # See Mail::Retriever for a complete documentation.
146
+ def self.find(*args, &block)
147
+ retriever_method.find(*args, &block)
148
+ end
149
+
150
+ # Finds and then deletes retrieved emails from the default retriever
151
+ # See Mail::Retriever for a complete documentation.
152
+ def self.find_and_delete(*args, &block)
153
+ retriever_method.find_and_delete(*args, &block)
154
+ end
155
+
156
+ # Receive the first email(s) from the default retriever
157
+ # See Mail::Retriever for a complete documentation.
158
+ def self.first(*args, &block)
159
+ retriever_method.first(*args, &block)
160
+ end
161
+
162
+ # Receive the first email(s) from the default retriever
163
+ # See Mail::Retriever for a complete documentation.
164
+ def self.last(*args, &block)
165
+ retriever_method.last(*args, &block)
166
+ end
167
+
168
+ # Receive all emails from the default retriever
169
+ # See Mail::Retriever for a complete documentation.
170
+ def self.all(*args, &block)
171
+ retriever_method.all(*args, &block)
172
+ end
173
+
174
+ # Reads in an email message from a path and instantiates it as a new Mail::Message
175
+ def self.read(filename)
176
+ self.new(File.open(filename, 'rb') { |f| f.read })
177
+ end
178
+
179
+ # Delete all emails from the default retriever
180
+ # See Mail::Retriever for a complete documentation.
181
+ def self.delete_all(*args, &block)
182
+ retriever_method.delete_all(*args, &block)
183
+ end
184
+
185
+ # Instantiates a new Mail::Message using a string
186
+ def Mail.read_from_string(mail_as_string)
187
+ Mail.new(mail_as_string)
188
+ end
189
+
190
+ def Mail.connection(&block)
191
+ retriever_method.connection(&block)
192
+ end
193
+
194
+ # Initialize the observers and interceptors arrays
195
+ @@delivery_notification_observers = []
196
+ @@delivery_interceptors = []
197
+
198
+ # You can register an object to be informed of every email that is sent through
199
+ # this method.
200
+ #
201
+ # Your object needs to respond to a single method #delivered_email(mail)
202
+ # which receives the email that is sent.
203
+ def self.register_observer(observer)
204
+ unless @@delivery_notification_observers.include?(observer)
205
+ @@delivery_notification_observers << observer
206
+ end
207
+ end
208
+
209
+ # You can register an object to be given every mail object that will be sent,
210
+ # before it is sent. So if you want to add special headers or modify any
211
+ # email that gets sent through the Mail library, you can do so.
212
+ #
213
+ # Your object needs to respond to a single method #delivering_email(mail)
214
+ # which receives the email that is about to be sent. Make your modifications
215
+ # directly to this object.
216
+ def self.register_interceptor(interceptor)
217
+ unless @@delivery_interceptors.include?(interceptor)
218
+ @@delivery_interceptors << interceptor
219
+ end
220
+ end
221
+
222
+ def self.inform_observers(mail)
223
+ @@delivery_notification_observers.each do |observer|
224
+ observer.delivered_email(mail)
225
+ end
226
+ end
227
+
228
+ def self.inform_interceptors(mail)
229
+ @@delivery_interceptors.each do |interceptor|
230
+ interceptor.delivering_email(mail)
231
+ end
232
+ end
233
+
234
+ protected
235
+
236
+ def self.random_tag
237
+ t = Time.now
238
+ sprintf('%x%x_%x%x%d%x',
239
+ t.to_i, t.tv_usec,
240
+ $$, Thread.current.object_id.abs, self.uniq, rand(255))
241
+ end
242
+
243
+ private
244
+
245
+ def self.something_random
246
+ (Thread.current.object_id * rand(255) / Time.now.to_f).to_s.slice(-3..-1).to_i
247
+ end
248
+
249
+ def self.uniq
250
+ @@uniq += 1
251
+ end
252
+
253
+ @@uniq = self.something_random
254
+
255
+ end
@@ -0,0 +1,2017 @@
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 contians 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
+ @body_raw_index = nil
104
+ @separate_parts = false
105
+ @text_part = nil
106
+ @html_part = nil
107
+ @errors = nil
108
+ @header = nil
109
+ @charset = 'UTF-8'
110
+ @defaulted_charset = true
111
+
112
+ @perform_deliveries = true
113
+ @raise_delivery_errors = true
114
+
115
+ @delivery_handler = nil
116
+
117
+ @delivery_method = Mail.delivery_method.dup
118
+
119
+ @transport_encoding = Mail::Encodings.get_encoding('7bit')
120
+
121
+ @mark_for_delete = false
122
+
123
+ if args.flatten.first.respond_to?(:each_pair)
124
+ init_with_hash(args.flatten.first)
125
+ else
126
+ init_with_string(args.flatten[0].to_s.strip)
127
+ end
128
+
129
+ if block_given?
130
+ instance_eval(&block)
131
+ end
132
+
133
+ self
134
+ end
135
+
136
+ # If you assign a delivery handler, mail will call :deliver_mail on the
137
+ # object you assign to delivery_handler, it will pass itself as the
138
+ # single argument.
139
+ #
140
+ # If you define a delivery_handler, then you are responsible for the
141
+ # following actions in the delivery cycle:
142
+ #
143
+ # * Appending the mail object to Mail.deliveries as you see fit.
144
+ # * Checking the mail.perform_deliveries flag to decide if you should
145
+ # actually call :deliver! the mail object or not.
146
+ # * Checking the mail.raise_delivery_errors flag to decide if you
147
+ # should raise delivery errors if they occur.
148
+ # * Actually calling :deliver! (with the bang) on the mail object to
149
+ # get it to deliver itself.
150
+ #
151
+ # A simplest implementation of a delivery_handler would be
152
+ #
153
+ # class MyObject
154
+ #
155
+ # def initialize
156
+ # @mail = Mail.new('To: mikel@test.lindsaar.net')
157
+ # @mail.delivery_handler = self
158
+ # end
159
+ #
160
+ # attr_accessor :mail
161
+ #
162
+ # def deliver_mail(mail)
163
+ # yield
164
+ # end
165
+ # end
166
+ #
167
+ # Then doing:
168
+ #
169
+ # obj = MyObject.new
170
+ # obj.mail.deliver
171
+ #
172
+ # Would cause Mail to call obj.deliver_mail passing itself as a parameter,
173
+ # which then can just yield and let Mail do it's own private do_delivery
174
+ # method.
175
+ attr_accessor :delivery_handler
176
+
177
+ # If set to false, mail will go through the motions of doing a delivery,
178
+ # but not actually call the delivery method or append the mail object to
179
+ # the Mail.deliveries collection. Useful for testing.
180
+ #
181
+ # Mail.deliveries.size #=> 0
182
+ # mail.delivery_method :smtp
183
+ # mail.perform_deliveries = false
184
+ # mail.deliver # Mail::SMTP not called here
185
+ # Mail.deliveries.size #=> 0
186
+ #
187
+ # If you want to test and query the Mail.deliveries collection to see what
188
+ # mail you sent, you should set perform_deliveries to true and use
189
+ # the :test mail delivery_method:
190
+ #
191
+ # Mail.deliveries.size #=> 0
192
+ # mail.delivery_method :test
193
+ # mail.perform_deliveries = true
194
+ # mail.deliver
195
+ # Mail.deliveries.size #=> 1
196
+ #
197
+ # This setting is ignored by mail (though still available as a flag) if you
198
+ # define a delivery_handler
199
+ attr_accessor :perform_deliveries
200
+
201
+ # If set to false, mail will silently catch and ignore any exceptions
202
+ # raised through attempting to deliver an email.
203
+ #
204
+ # This setting is ignored by mail (though still available as a flag) if you
205
+ # define a delivery_handler
206
+ attr_accessor :raise_delivery_errors
207
+
208
+ def register_for_delivery_notification(observer)
209
+ STDERR.puts("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
210
+ Mail.register_observer(observer)
211
+ end
212
+
213
+ def inform_observers
214
+ Mail.inform_observers(self)
215
+ end
216
+
217
+ def inform_interceptors
218
+ Mail.inform_interceptors(self)
219
+ end
220
+
221
+ # Delivers an mail object.
222
+ #
223
+ # Examples:
224
+ #
225
+ # mail = Mail.read('file.eml')
226
+ # mail.deliver
227
+ def deliver
228
+ inform_interceptors
229
+ if delivery_handler
230
+ delivery_handler.deliver_mail(self) { do_delivery }
231
+ else
232
+ do_delivery
233
+ end
234
+ inform_observers
235
+ self
236
+ end
237
+
238
+ # This method bypasses checking perform_deliveries and raise_delivery_errors,
239
+ # so use with caution.
240
+ #
241
+ # It still however fires callbacks to the observers if they are defined.
242
+ #
243
+ # Returns self
244
+ def deliver!
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, 0)
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 to_yaml(opts = {})
1715
+ hash = {}
1716
+ hash['headers'] = {}
1717
+ header.fields.each do |field|
1718
+ hash['headers'][field.name] = field.value
1719
+ end
1720
+ hash['delivery_handler'] = delivery_handler.to_s if delivery_handler
1721
+ hash['transport_encoding'] = transport_encoding.to_s
1722
+ special_variables = [:@header, :@delivery_handler, :@transport_encoding]
1723
+ (instance_variables.map(&:to_sym) - special_variables).each do |var|
1724
+ hash[var.to_s] = instance_variable_get(var)
1725
+ end
1726
+ hash.to_yaml(opts)
1727
+ end
1728
+
1729
+ def self.from_yaml(str)
1730
+ hash = YAML.load(str)
1731
+ m = Mail::Message.new(:headers => hash['headers'])
1732
+ hash.delete('headers')
1733
+ hash.each do |k,v|
1734
+ case
1735
+ when k == 'delivery_handler'
1736
+ begin
1737
+ m.delivery_handler = Object.const_get(v) unless v.blank?
1738
+ rescue NameError
1739
+ end
1740
+ when k == 'transport_encoding'
1741
+ m.transport_encoding(v)
1742
+ when k =~ /^@/
1743
+ m.instance_variable_set(k.to_sym, v)
1744
+ end
1745
+ end
1746
+ m
1747
+ end
1748
+
1749
+ def self.from_hash(hash)
1750
+ Mail::Message.new(hash)
1751
+ end
1752
+
1753
+ def to_s
1754
+ encoded
1755
+ end
1756
+
1757
+ def inspect
1758
+ "#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
1759
+ end
1760
+
1761
+ def decoded
1762
+ case
1763
+ when self.attachment?
1764
+ decode_body
1765
+ when !self.multipart?
1766
+ body.decoded
1767
+ else
1768
+ 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.'
1769
+ end
1770
+ end
1771
+
1772
+ def read
1773
+ if self.attachment?
1774
+ decode_body
1775
+ else
1776
+ raise NoMethodError, 'Can not call read on a part unless it is an attachment.'
1777
+ end
1778
+ end
1779
+
1780
+ def decode_body
1781
+ body.decoded
1782
+ end
1783
+
1784
+ # Returns true if this part is an attachment,
1785
+ # false otherwise.
1786
+ def attachment?
1787
+ !!find_attachment
1788
+ end
1789
+
1790
+ # Returns the attachment data if there is any
1791
+ def attachment
1792
+ @attachment
1793
+ end
1794
+
1795
+ # Returns the filename of the attachment
1796
+ def filename
1797
+ find_attachment
1798
+ end
1799
+
1800
+ def all_parts
1801
+ parts.map { |p| [p, p.all_parts] }.flatten
1802
+ end
1803
+
1804
+ def find_first_mime_type(mt)
1805
+ all_parts.detect { |p| p.mime_type == mt && !p.attachment? }
1806
+ end
1807
+
1808
+ # Skips the deletion of this message. All other messages
1809
+ # flagged for delete still will be deleted at session close (i.e. when
1810
+ # #find exits). Only has an effect if you're using #find_and_delete
1811
+ # or #find with :delete_after_find set to true.
1812
+ def skip_deletion
1813
+ @mark_for_delete = false
1814
+ end
1815
+
1816
+ # Sets whether this message should be deleted at session close (i.e.
1817
+ # after #find). Message will only be deleted if messages are retrieved
1818
+ # using the #find_and_delete method, or by calling #find with
1819
+ # :delete_after_find set to true.
1820
+ def mark_for_delete=(value = true)
1821
+ @mark_for_delete = value
1822
+ end
1823
+
1824
+ # Returns whether message will be marked for deletion.
1825
+ # If so, the message will be deleted at session close (i.e. after #find
1826
+ # exits), but only if also using the #find_and_delete method, or by
1827
+ # calling #find with :delete_after_find set to true.
1828
+ #
1829
+ # Side-note: Just to be clear, this method will return true even if
1830
+ # the message hasn't yet been marked for delete on the mail server.
1831
+ # However, if this method returns true, it *will be* marked on the
1832
+ # server after each block yields back to #find or #find_and_delete.
1833
+ def is_marked_for_delete?
1834
+ return @mark_for_delete
1835
+ end
1836
+
1837
+ private
1838
+
1839
+ # 2.1. General Description
1840
+ # A message consists of header fields (collectively called "the header
1841
+ # of the message") followed, optionally, by a body. The header is a
1842
+ # sequence of lines of characters with special syntax as defined in
1843
+ # this standard. The body is simply a sequence of characters that
1844
+ # follows the header and is separated from the header by an empty line
1845
+ # (i.e., a line with nothing preceding the CRLF).
1846
+ #
1847
+ # Additionally, I allow for the case where someone might have put whitespace
1848
+ # on the "gap line"
1849
+ def parse_message
1850
+ header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
1851
+ # index = raw_source.index(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
1852
+ # self.header = (index) ? header_part[0,index] : nil
1853
+ # lazy_body ( [raw_source, index+1])
1854
+ self.header = header_part
1855
+ self.body = body_part
1856
+ end
1857
+
1858
+ def raw_source=(value)
1859
+ @raw_source = value.to_crlf
1860
+ end
1861
+
1862
+ # see comments to body=. We take data starting from index and process it lazily
1863
+ def body_lazy(value, index)
1864
+ process_body_raw if @body_raw && value
1865
+ case
1866
+ when value == nil || value.length<=index
1867
+ @body = Mail::Body.new('')
1868
+ @body_raw = nil
1869
+ @body_raw_index = nil
1870
+ add_encoding_to_body
1871
+ when @body && @body.multipart?
1872
+ @body << Mail::Part.new(value[index, value.length-index])
1873
+ add_encoding_to_body
1874
+ else
1875
+ @body_raw = value
1876
+ @body_raw_index = index
1877
+ # process_body_raw
1878
+ end
1879
+ end
1880
+
1881
+
1882
+ def process_body_raw
1883
+ @body = Mail::Body.new(@body_raw[@body_raw_index, @body_raw.length-@body_raw_index])
1884
+ @body_raw = nil
1885
+ @body_raw_index = nil
1886
+ separate_parts if @separate_parts
1887
+
1888
+ add_encoding_to_body
1889
+ end
1890
+
1891
+ def set_envelope_header
1892
+ if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}(.*)/m)
1893
+ set_envelope(match_data[1])
1894
+ self.raw_source = match_data[2]
1895
+ end
1896
+ end
1897
+
1898
+ def separate_parts
1899
+ body.split!(boundary)
1900
+ end
1901
+
1902
+ def add_encoding_to_body
1903
+ if has_content_transfer_encoding?
1904
+ @body.encoding = content_transfer_encoding
1905
+ end
1906
+ end
1907
+
1908
+ def identify_and_set_transfer_encoding
1909
+ if body && body.multipart?
1910
+ self.content_transfer_encoding = @transport_encoding
1911
+ else
1912
+ self.content_transfer_encoding = body.get_best_encoding(@transport_encoding)
1913
+ end
1914
+ end
1915
+
1916
+ def add_required_fields
1917
+ add_multipart_mixed_header unless !body.multipart?
1918
+ body = nil if body.nil?
1919
+ add_message_id unless (has_message_id? || self.class == Mail::Part)
1920
+ add_date unless has_date?
1921
+ add_mime_version unless has_mime_version?
1922
+ add_content_type unless has_content_type?
1923
+ add_charset unless has_charset?
1924
+ add_content_transfer_encoding unless has_content_transfer_encoding?
1925
+ end
1926
+
1927
+ def add_multipart_alternate_header
1928
+ header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
1929
+ header['content_type'].parameters[:charset] = @charset
1930
+ body.boundary = boundary
1931
+ end
1932
+
1933
+ def add_boundary
1934
+ unless body.boundary && boundary
1935
+ header['content-type'] = 'multipart/mixed' unless header['content-type']
1936
+ header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
1937
+ header['content_type'].parameters[:charset] = @charset
1938
+ body.boundary = boundary
1939
+ end
1940
+ end
1941
+
1942
+ def add_multipart_mixed_header
1943
+ unless header['content-type']
1944
+ header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
1945
+ header['content_type'].parameters[:charset] = @charset
1946
+ body.boundary = boundary
1947
+ end
1948
+ end
1949
+
1950
+ def init_with_hash(hash)
1951
+ passed_in_options = IndifferentHash.new(hash)
1952
+ self.raw_source = ''
1953
+
1954
+ @header = Mail::Header.new
1955
+ @body = Mail::Body.new
1956
+ @body_raw = nil
1957
+
1958
+ # We need to store the body until last, as we need all headers added first
1959
+ body_content = nil
1960
+
1961
+ passed_in_options.each_pair do |k,v|
1962
+ k = underscoreize(k).to_sym if k.class == String
1963
+ if k == :headers
1964
+ self.headers(v)
1965
+ elsif k == :body
1966
+ body_content = v
1967
+ else
1968
+ self[k] = v
1969
+ end
1970
+ end
1971
+
1972
+ if body_content
1973
+ self.body = body_content
1974
+ if has_content_transfer_encoding?
1975
+ body.encoding = content_transfer_encoding
1976
+ end
1977
+ end
1978
+ end
1979
+
1980
+ def init_with_string(string)
1981
+ self.raw_source = string
1982
+ set_envelope_header
1983
+ parse_message
1984
+ @separate_parts = multipart?
1985
+ end
1986
+
1987
+ # Returns the filename of the attachment (if it exists) or returns nil
1988
+ def find_attachment
1989
+ content_type_name = header[:content_type].filename rescue nil
1990
+ content_disp_name = header[:content_disposition].filename rescue nil
1991
+ content_loc_name = header[:content_location].location rescue nil
1992
+ case
1993
+ when content_type && content_type_name
1994
+ filename = content_type_name
1995
+ when content_disposition && content_disp_name
1996
+ filename = content_disp_name
1997
+ when content_location && content_loc_name
1998
+ filename = content_loc_name
1999
+ else
2000
+ filename = nil
2001
+ end
2002
+ filename = Mail::Encodings.decode_encode(filename, :decode) if filename rescue filename
2003
+ filename
2004
+ end
2005
+
2006
+ def do_delivery
2007
+ begin
2008
+ if perform_deliveries
2009
+ delivery_method.deliver!(self)
2010
+ end
2011
+ rescue Exception => e # Net::SMTP errors or sendmail pipe errors
2012
+ raise e if raise_delivery_errors
2013
+ end
2014
+ end
2015
+
2016
+ end
2017
+ end