dball-mail 2.2.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/CHANGELOG.rdoc +459 -0
  2. data/README.rdoc +582 -0
  3. data/Rakefile +66 -0
  4. data/TODO.rdoc +9 -0
  5. data/lib/VERSION +4 -0
  6. data/lib/mail/attachments_list.rb +105 -0
  7. data/lib/mail/body.rb +286 -0
  8. data/lib/mail/configuration.rb +71 -0
  9. data/lib/mail/core_extensions/nil.rb +11 -0
  10. data/lib/mail/core_extensions/string.rb +27 -0
  11. data/lib/mail/elements/address.rb +306 -0
  12. data/lib/mail/elements/address_list.rb +74 -0
  13. data/lib/mail/elements/content_disposition_element.rb +30 -0
  14. data/lib/mail/elements/content_location_element.rb +25 -0
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +24 -0
  16. data/lib/mail/elements/content_type_element.rb +35 -0
  17. data/lib/mail/elements/date_time_element.rb +26 -0
  18. data/lib/mail/elements/envelope_from_element.rb +34 -0
  19. data/lib/mail/elements/message_ids_element.rb +29 -0
  20. data/lib/mail/elements/mime_version_element.rb +26 -0
  21. data/lib/mail/elements/phrase_list.rb +21 -0
  22. data/lib/mail/elements/received_element.rb +30 -0
  23. data/lib/mail/elements.rb +14 -0
  24. data/lib/mail/encodings/7bit.rb +31 -0
  25. data/lib/mail/encodings/8bit.rb +31 -0
  26. data/lib/mail/encodings/base64.rb +33 -0
  27. data/lib/mail/encodings/binary.rb +31 -0
  28. data/lib/mail/encodings/quoted_printable.rb +38 -0
  29. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  30. data/lib/mail/encodings.rb +268 -0
  31. data/lib/mail/envelope.rb +35 -0
  32. data/lib/mail/field.rb +223 -0
  33. data/lib/mail/field_list.rb +33 -0
  34. data/lib/mail/fields/bcc_field.rb +56 -0
  35. data/lib/mail/fields/cc_field.rb +55 -0
  36. data/lib/mail/fields/comments_field.rb +41 -0
  37. data/lib/mail/fields/common/address_container.rb +16 -0
  38. data/lib/mail/fields/common/common_address.rb +125 -0
  39. data/lib/mail/fields/common/common_date.rb +42 -0
  40. data/lib/mail/fields/common/common_field.rb +50 -0
  41. data/lib/mail/fields/common/common_message_id.rb +44 -0
  42. data/lib/mail/fields/common/parameter_hash.rb +58 -0
  43. data/lib/mail/fields/content_description_field.rb +19 -0
  44. data/lib/mail/fields/content_disposition_field.rb +69 -0
  45. data/lib/mail/fields/content_id_field.rb +63 -0
  46. data/lib/mail/fields/content_location_field.rb +42 -0
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +50 -0
  48. data/lib/mail/fields/content_type_field.rb +198 -0
  49. data/lib/mail/fields/date_field.rb +55 -0
  50. data/lib/mail/fields/from_field.rb +55 -0
  51. data/lib/mail/fields/in_reply_to_field.rb +55 -0
  52. data/lib/mail/fields/keywords_field.rb +44 -0
  53. data/lib/mail/fields/message_id_field.rb +83 -0
  54. data/lib/mail/fields/mime_version_field.rb +53 -0
  55. data/lib/mail/fields/optional_field.rb +13 -0
  56. data/lib/mail/fields/received_field.rb +67 -0
  57. data/lib/mail/fields/references_field.rb +55 -0
  58. data/lib/mail/fields/reply_to_field.rb +55 -0
  59. data/lib/mail/fields/resent_bcc_field.rb +55 -0
  60. data/lib/mail/fields/resent_cc_field.rb +55 -0
  61. data/lib/mail/fields/resent_date_field.rb +35 -0
  62. data/lib/mail/fields/resent_from_field.rb +55 -0
  63. data/lib/mail/fields/resent_message_id_field.rb +34 -0
  64. data/lib/mail/fields/resent_sender_field.rb +62 -0
  65. data/lib/mail/fields/resent_to_field.rb +55 -0
  66. data/lib/mail/fields/return_path_field.rb +64 -0
  67. data/lib/mail/fields/sender_field.rb +67 -0
  68. data/lib/mail/fields/structured_field.rb +51 -0
  69. data/lib/mail/fields/subject_field.rb +16 -0
  70. data/lib/mail/fields/to_field.rb +55 -0
  71. data/lib/mail/fields/unstructured_field.rb +179 -0
  72. data/lib/mail/fields.rb +35 -0
  73. data/lib/mail/header.rb +264 -0
  74. data/lib/mail/mail.rb +255 -0
  75. data/lib/mail/message.rb +1972 -0
  76. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  77. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  78. data/lib/mail/network/delivery_methods/smtp.rb +136 -0
  79. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  80. data/lib/mail/network/retriever_methods/imap.rb +213 -0
  81. data/lib/mail/network/retriever_methods/pop3.rb +194 -0
  82. data/lib/mail/network/retriever_methods/test_retriever.rb +31 -0
  83. data/lib/mail/network.rb +10 -0
  84. data/lib/mail/parsers/address_lists.rb +64 -0
  85. data/lib/mail/parsers/address_lists.treetop +19 -0
  86. data/lib/mail/parsers/content_disposition.rb +535 -0
  87. data/lib/mail/parsers/content_disposition.treetop +46 -0
  88. data/lib/mail/parsers/content_location.rb +139 -0
  89. data/lib/mail/parsers/content_location.treetop +20 -0
  90. data/lib/mail/parsers/content_transfer_encoding.rb +162 -0
  91. data/lib/mail/parsers/content_transfer_encoding.treetop +20 -0
  92. data/lib/mail/parsers/content_type.rb +967 -0
  93. data/lib/mail/parsers/content_type.treetop +68 -0
  94. data/lib/mail/parsers/date_time.rb +114 -0
  95. data/lib/mail/parsers/date_time.treetop +11 -0
  96. data/lib/mail/parsers/envelope_from.rb +194 -0
  97. data/lib/mail/parsers/envelope_from.treetop +32 -0
  98. data/lib/mail/parsers/message_ids.rb +45 -0
  99. data/lib/mail/parsers/message_ids.treetop +15 -0
  100. data/lib/mail/parsers/mime_version.rb +144 -0
  101. data/lib/mail/parsers/mime_version.treetop +19 -0
  102. data/lib/mail/parsers/phrase_lists.rb +45 -0
  103. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  104. data/lib/mail/parsers/received.rb +71 -0
  105. data/lib/mail/parsers/received.treetop +11 -0
  106. data/lib/mail/parsers/rfc2045.rb +464 -0
  107. data/lib/mail/parsers/rfc2045.treetop +36 -0
  108. data/lib/mail/parsers/rfc2822.rb +5318 -0
  109. data/lib/mail/parsers/rfc2822.treetop +410 -0
  110. data/lib/mail/parsers/rfc2822_obsolete.rb +3757 -0
  111. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  112. data/lib/mail/part.rb +116 -0
  113. data/lib/mail/parts_list.rb +43 -0
  114. data/lib/mail/patterns.rb +34 -0
  115. data/lib/mail/utilities.rb +211 -0
  116. data/lib/mail/version.rb +24 -0
  117. data/lib/mail/version_specific/ruby_1_8.rb +97 -0
  118. data/lib/mail/version_specific/ruby_1_9.rb +87 -0
  119. data/lib/mail.rb +80 -0
  120. data/lib/tasks/corpus.rake +125 -0
  121. data/lib/tasks/treetop.rake +10 -0
  122. metadata +255 -0
@@ -0,0 +1,264 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+
4
+ # Provides access to a header object.
5
+ #
6
+ # ===Per RFC2822
7
+ #
8
+ # 2.2. Header Fields
9
+ #
10
+ # Header fields are lines composed of a field name, followed by a colon
11
+ # (":"), followed by a field body, and terminated by CRLF. A field
12
+ # name MUST be composed of printable US-ASCII characters (i.e.,
13
+ # characters that have values between 33 and 126, inclusive), except
14
+ # colon. A field body may be composed of any US-ASCII characters,
15
+ # except for CR and LF. However, a field body may contain CRLF when
16
+ # used in header "folding" and "unfolding" as described in section
17
+ # 2.2.3. All field bodies MUST conform to the syntax described in
18
+ # sections 3 and 4 of this standard.
19
+ class Header
20
+ include Patterns
21
+ include Utilities
22
+ include Enumerable
23
+
24
+ # Creates a new header object.
25
+ #
26
+ # Accepts raw text or nothing. If given raw text will attempt to parse
27
+ # it and split it into the various fields, instantiating each field as
28
+ # it goes.
29
+ #
30
+ # If it finds a field that should be a structured field (such as content
31
+ # type), but it fails to parse it, it will simply make it an unstructured
32
+ # field and leave it alone. This will mean that the data is preserved but
33
+ # no automatic processing of that field will happen. If you find one of
34
+ # these cases, please make a patch and send it in, or at the least, send
35
+ # me the example so we can fix it.
36
+ def initialize(header_text = nil, charset = nil)
37
+ @errors = []
38
+ @charset = charset
39
+ self.raw_source = header_text.to_crlf
40
+ split_header if header_text
41
+ end
42
+
43
+ # The preserved raw source of the header as you passed it in, untouched
44
+ # for your Regexing glory.
45
+ def raw_source
46
+ @raw_source
47
+ end
48
+
49
+ # Returns an array of all the fields in the header in order that they
50
+ # were read in.
51
+ def fields
52
+ @fields ||= FieldList.new
53
+ end
54
+
55
+ # 3.6. Field definitions
56
+ #
57
+ # It is important to note that the header fields are not guaranteed to
58
+ # be in a particular order. They may appear in any order, and they
59
+ # have been known to be reordered occasionally when transported over
60
+ # the Internet. However, for the purposes of this standard, header
61
+ # fields SHOULD NOT be reordered when a message is transported or
62
+ # transformed. More importantly, the trace header fields and resent
63
+ # header fields MUST NOT be reordered, and SHOULD be kept in blocks
64
+ # prepended to the message. See sections 3.6.6 and 3.6.7 for more
65
+ # information.
66
+ #
67
+ # Populates the fields container with Field objects in the order it
68
+ # receives them in.
69
+ #
70
+ # Acceps an array of field string values, for example:
71
+ #
72
+ # h = Header.new
73
+ # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
74
+ def fields=(unfolded_fields)
75
+ @fields = Mail::FieldList.new
76
+ unfolded_fields.each do |field|
77
+
78
+ field = Field.new(field, nil, charset)
79
+ field.errors.each { |error| self.errors << error }
80
+ selected = select_field_for(field.name)
81
+
82
+ if selected.any? && limited_field?(field.name)
83
+ selected.first.update(field.name, field.value)
84
+ else
85
+ @fields << field
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ def errors
92
+ @errors
93
+ end
94
+
95
+ # 3.6. Field definitions
96
+ #
97
+ # The following table indicates limits on the number of times each
98
+ # field may occur in a message header as well as any special
99
+ # limitations on the use of those fields. An asterisk next to a value
100
+ # in the minimum or maximum column indicates that a special restriction
101
+ # appears in the Notes column.
102
+ #
103
+ # <snip table from 3.6>
104
+ #
105
+ # As per RFC, many fields can appear more than once, we will return a string
106
+ # of the value if there is only one header, or if there is more than one
107
+ # matching header, will return an array of values in order that they appear
108
+ # in the header ordered from top to bottom.
109
+ #
110
+ # Example:
111
+ #
112
+ # h = Header.new
113
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
114
+ # h['To'] #=> 'mikel@me.com'
115
+ # h['X-Mail-SPAM'] #=> ['15', '20']
116
+ def [](name)
117
+ name = dasherize(name).downcase
118
+ selected = select_field_for(name)
119
+ case
120
+ when selected.length > 1
121
+ selected.map { |f| f }
122
+ when !selected.blank?
123
+ selected.first
124
+ else
125
+ nil
126
+ end
127
+ end
128
+
129
+ # Sets the FIRST matching field in the header to passed value, or deletes
130
+ # the FIRST field matched from the header if passed nil
131
+ #
132
+ # Example:
133
+ #
134
+ # h = Header.new
135
+ # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
136
+ # h['To'] = 'bob@you.com'
137
+ # h['To'] #=> 'bob@you.com'
138
+ # h['X-Mail-SPAM'] = '10000'
139
+ # h['X-Mail-SPAM'] # => ['15', '20', '10000']
140
+ # h['X-Mail-SPAM'] = nil
141
+ # h['X-Mail-SPAM'] # => nil
142
+ def []=(name, value)
143
+ name = dasherize(name)
144
+ fn = name.downcase
145
+ selected = select_field_for(fn)
146
+
147
+ case
148
+ # User wants to delete the field
149
+ when !selected.blank? && value == nil
150
+ fields.delete_if { |f| selected.include?(f) }
151
+
152
+ # User wants to change the field
153
+ when !selected.blank? && limited_field?(fn)
154
+ selected.first.update(fn, value)
155
+
156
+ # User wants to create the field
157
+ else
158
+ # Need to insert in correct order for trace fields
159
+ self.fields << Field.new(name.to_s, value, charset)
160
+ end
161
+ end
162
+
163
+ def charset
164
+ params = self[:content_type].parameters rescue nil
165
+ if params
166
+ params[:charset]
167
+ else
168
+ @charset
169
+ end
170
+ end
171
+
172
+ def charset=(val)
173
+ params = self[:content_type].parameters rescue nil
174
+ if params
175
+ params[:charset] = val
176
+ end
177
+ @charset = val
178
+ end
179
+
180
+ LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
181
+ message-id in-reply-to references subject
182
+ return-path content-type mime-version
183
+ content-transfer-encoding content-description
184
+ content-id content-disposition content-location]
185
+
186
+ def encoded
187
+ buffer = ''
188
+ fields.each do |field|
189
+ buffer << field.encoded
190
+ end
191
+ buffer
192
+ end
193
+
194
+ def to_s
195
+ encoded
196
+ end
197
+
198
+ def decoded
199
+ raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts, try calling #decoded on the various fields.'
200
+ end
201
+
202
+ def field_summary
203
+ fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
204
+ end
205
+
206
+ # Returns true if the header has a Message-ID defined (empty or not)
207
+ def has_message_id?
208
+ !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
209
+ end
210
+
211
+ # Returns true if the header has a Content-ID defined (empty or not)
212
+ def has_content_id?
213
+ !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
214
+ end
215
+
216
+ # Returns true if the header has a Date defined (empty or not)
217
+ def has_date?
218
+ !fields.select { |f| f.responsible_for?('Date') }.empty?
219
+ end
220
+
221
+ # Returns true if the header has a MIME version defined (empty or not)
222
+ def has_mime_version?
223
+ !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
224
+ end
225
+
226
+ private
227
+
228
+ def raw_source=(val)
229
+ @raw_source = val
230
+ end
231
+
232
+ # 2.2.3. Long Header Fields
233
+ #
234
+ # The process of moving from this folded multiple-line representation
235
+ # of a header field to its single line representation is called
236
+ # "unfolding". Unfolding is accomplished by simply removing any CRLF
237
+ # that is immediately followed by WSP. Each header field should be
238
+ # treated in its unfolded form for further syntactic and semantic
239
+ # evaluation.
240
+ def unfold(string)
241
+ string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
242
+ end
243
+
244
+ # Returns the header with all the folds removed
245
+ def unfolded_header
246
+ @unfolded_header ||= unfold(raw_source)
247
+ end
248
+
249
+ # Splits an unfolded and line break cleaned header into individual field
250
+ # strings.
251
+ def split_header
252
+ self.fields = unfolded_header.split(CRLF)
253
+ end
254
+
255
+ def select_field_for(name)
256
+ fields.select { |f| f.responsible_for?(name.to_s) }
257
+ end
258
+
259
+ def limited_field?(name)
260
+ LIMITED_FIELDS.include?(name.to_s.downcase)
261
+ end
262
+
263
+ end
264
+ end
data/lib/mail/mail.rb ADDED
@@ -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\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 in a POP3 server.
145
+ # See Mail::POP3 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 a POP3 server.
151
+ # See Mail::POP3 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 a Pop3 server.
157
+ # See Mail::POP3 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 a Pop3 server.
163
+ # See Mail::POP3 for a complete documentation.
164
+ def self.last(*args, &block)
165
+ retriever_method.last(*args, &block)
166
+ end
167
+
168
+ # Receive all emails from a POP3 server.
169
+ # See Mail::POP3 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 a POP3 server.
180
+ # See Mail::POP3 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