dball-mail 2.2.9.1

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