mail-trunk 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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