aussiegeek-ar_sendmail_delayed 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ = 1.3.1
2
+
3
+ * Fix bug #12530, gmail causes SSL errors. Submitted by Kyle Maxwell
4
+ and Alex Ostleitner.
5
+ * Try ActionMailer::Base::server_settings then ::smtp_settings. Fixes
6
+ bug #12516. Submitted by Alex Ostleitner.
7
+
8
+ = 1.3.0
9
+
10
+ * New Features
11
+ * Added automatic mail queue cleanup.
12
+ * MAY CAUSE LOSS OF DATA. If you haven't run ar_sendmail within
13
+ the expiry time, set it to 0.
14
+ * Bugs fixed
15
+ * Authentication errors are now handled by retrying once.
16
+
17
+ = 1.2.0
18
+
19
+ * Bugs fixed
20
+ * Handle SMTPServerBusy by backing off @delay seconds then re-queueing
21
+ * Allow email delivery class to be set in ARMailer.
22
+ * ar_sendmail --mailq works with --table-name now.
23
+ * Miscellaneous Updates
24
+ * Added documentation to require 'action_mailer/ar_mailer' in
25
+ instructions.
26
+ * Moved to ZSS p4 repository
27
+ * Supports TLS now. Requested by Dave Thomas. smtp_tls.rb from Kyle
28
+ Maxwell & etc.
29
+
30
+ = 1.1.0
31
+
32
+ * Features
33
+ * Added --chdir to set rails directory
34
+ * Added --environment to set RAILS_ENV
35
+ * Exits cleanly on TERM or INT signals
36
+ * Added FreeBSD rc.d script
37
+ * Exceptions during SMTP sending are now logged
38
+ * No longer waits if sending email took too long
39
+ * Bugs fixed
40
+ * Fixed last send attempt in --mailq
41
+ * Better SMTP error handling
42
+ * Messages are removed from the queue on 5xx errors
43
+ * Added Net::SMTP.reset to avoid needing to recreate the connection
44
+
45
+ = 1.0.1
46
+
47
+ * Bugs fixed
48
+ * From and to of email destination were swapped
49
+
50
+ = 1.0.0
51
+
52
+ * Birthday
@@ -0,0 +1,28 @@
1
+ Original code copyright 2006, 2007, Eric Hodel, The Robot Co-op. All
2
+ rights reserved. Some code under other license, see individual files
3
+ for details.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions
7
+ are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+ 3. Neither the names of the authors nor the names of their contributors
15
+ may be used to endorse or promote products derived from this software
16
+ without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
19
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
22
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ LICENSE.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/ar_sendmail
7
+ lib/action_mailer/ar_mailer.rb
8
+ lib/action_mailer/ar_sendmail.rb
9
+ lib/smtp_tls.rb
10
+ share/ar_sendmail
11
+ test/action_mailer.rb
12
+ test/test_armailer.rb
13
+ test/test_arsendmail.rb
@@ -0,0 +1,39 @@
1
+ = ar_mailer
2
+
3
+ A two-phase delivery agent for ActionMailer
4
+
5
+ Rubyforge Project:
6
+
7
+ http://rubyforge.org/projects/seattlerb
8
+
9
+ Documentation:
10
+
11
+ http://seattlerb.org/ar_mailer
12
+
13
+ Bugs:
14
+
15
+ http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921
16
+
17
+ == About
18
+
19
+ Even delivering email to the local machine may take too long when you have to
20
+ send hundreds of messages. ar_mailer allows you to store messages into the
21
+ database for later delivery by a separate process, ar_sendmail.
22
+
23
+ == Installing ar_mailer
24
+
25
+ Just install the gem:
26
+
27
+ $ sudo gem install ar_mailer
28
+
29
+ See ActionMailer::ARMailer for instructions on converting to ARMailer.
30
+
31
+ See ar_sendmail -h for options to ar_sendmail.
32
+
33
+ NOTE: You may need to delete an smtp_tls.rb file if you have one lying
34
+ around. ar_mailer supplies it own.
35
+
36
+ === ar_sendmail on FreeBSD or NetBSD
37
+
38
+ An rc.d script is included in share/ar_sendmail.
39
+
@@ -0,0 +1,14 @@
1
+ require 'hoe'
2
+
3
+ require './lib/action_mailer/ar_sendmail'
4
+
5
+ Hoe.new 'ar_mailer_delayed', ActionMailer::ARSendmail::VERSION do |s|
6
+ s.rubyforge_name = 'alangems'
7
+ s.summary = s.paragraphs_of('README.txt', 1).join(' ')
8
+ s.description = s.paragraphs_of('README.txt', 9).join(' ')
9
+ s.url = s.paragraphs_of('README.txt', 5).join(' ')
10
+ s.author = 'Alan Harper'
11
+ s.email = 'alan@aussiegeek.net'
12
+ s.changes = s.paragraphs_of('History.txt', 0..1).join("\n\n")
13
+ end
14
+
@@ -0,0 +1,6 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'action_mailer/ar_sendmail'
4
+
5
+ ActionMailer::ARSendmail.run
6
+
@@ -0,0 +1,98 @@
1
+ require 'action_mailer'
2
+
3
+ ##
4
+ # Adds sending email through an ActiveRecord table as a delivery method for
5
+ # ActionMailer.
6
+ #
7
+ # == Converting to ActionMailer::ARMailer
8
+ #
9
+ # Go to your Rails project:
10
+ #
11
+ # $ cd your_rails_project
12
+ #
13
+ # Create a new migration:
14
+ #
15
+ # $ ar_sendmail --create-migration
16
+ #
17
+ # You'll need to redirect this into a file. If you want a different name
18
+ # provide the --table-name option.
19
+ #
20
+ # Create a new model:
21
+ #
22
+ # $ ar_sendmail --create-model
23
+ #
24
+ # You'll need to redirect this into a file. If you want a different name
25
+ # provide the --table-name option.
26
+ #
27
+ # Change your email classes to inherit from ActionMailer::ARMailer instead of
28
+ # ActionMailer::Base:
29
+ #
30
+ # --- app/model/emailer.rb.orig 2006-08-10 13:16:33.000000000 -0700
31
+ # +++ app/model/emailer.rb 2006-08-10 13:16:43.000000000 -0700
32
+ # @@ -1,4 +1,4 @@
33
+ # -class Emailer < ActionMailer::Base
34
+ # +class Emailer < ActionMailer::ARMailer
35
+ #
36
+ # def comment_notification(comment)
37
+ # from comment.author.email
38
+ #
39
+ # You'll need to be sure to set the From address for your emails. Something
40
+ # like:
41
+ #
42
+ # def list_send(recipient)
43
+ # from 'no_reply@example.com'
44
+ # # ...
45
+ #
46
+ # Edit config/environment.rb and require ar_mailer.rb:
47
+ #
48
+ # require 'action_mailer/ar_mailer'
49
+ #
50
+ # Edit config/environments/production.rb and set the delivery agent:
51
+ #
52
+ # $ grep delivery_method config/environments/production.rb
53
+ # ActionMailer::Base.delivery_method = :activerecord
54
+ #
55
+ # Run ar_sendmail:
56
+ #
57
+ # $ ar_sendmail
58
+ #
59
+ # You can also run it from cron with -o, or as a daemon with -d.
60
+ #
61
+ # See <tt>ar_sendmail -h</tt> for full details.
62
+ #
63
+ # == Alternate Mail Storage
64
+ #
65
+ # If you want to set the ActiveRecord model that emails will be stored in,
66
+ # see ActionMailer::ARMailer::email_class=
67
+
68
+ class ActionMailer::ARMailer < ActionMailer::Base
69
+
70
+ @@email_class = Email
71
+
72
+ ##
73
+ # Current email class for deliveries.
74
+
75
+ def self.email_class
76
+ @@email_class
77
+ end
78
+
79
+ ##
80
+ # Sets the email class for deliveries.
81
+
82
+ def self.email_class=(klass)
83
+ @@email_class = klass
84
+ end
85
+
86
+ ##
87
+ # Adds +mail+ to the Email table. Only the first From address for +mail+ is
88
+ # used.
89
+
90
+ def perform_delivery_activerecord(mail)
91
+ mail.destinations.each do |destination|
92
+ @@email_class.create :mail => mail.encoded, :to => destination,
93
+ :from => mail.from.first
94
+ end
95
+ end
96
+
97
+ end
98
+
@@ -0,0 +1,522 @@
1
+ require 'optparse'
2
+ require 'net/smtp'
3
+ require 'smtp_tls'
4
+ require 'rubygems'
5
+
6
+ class Object # :nodoc:
7
+ unless respond_to? :path2class then
8
+ def self.path2class(path)
9
+ path.split(/::/).inject self do |k,n| k.const_get n end
10
+ end
11
+ end
12
+ end
13
+
14
+ ##
15
+ # Hack in RSET
16
+
17
+ module Net # :nodoc:
18
+ class SMTP # :nodoc:
19
+
20
+ unless instance_methods.include? 'reset' then
21
+ ##
22
+ # Resets the SMTP connection.
23
+
24
+ def reset
25
+ getok 'RSET'
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ module ActionMailer; end # :nodoc:
33
+
34
+ ##
35
+ # ActionMailer::ARSendmail delivers email from the email table to the
36
+ # SMTP server configured in your application's config/environment.rb.
37
+ # ar_sendmail does not work with sendmail delivery.
38
+ #
39
+ # ar_mailer can deliver to SMTP with TLS using smtp_tls.rb borrowed from Kyle
40
+ # Maxwell's action_mailer_optional_tls plugin. Simply set the :tls option in
41
+ # ActionMailer::Base's smtp_settings to true to enable TLS.
42
+ #
43
+ # See ar_sendmail -h for the full list of supported options.
44
+ #
45
+ # The interesting options are:
46
+ # * --daemon
47
+ # * --mailq
48
+ # * --create-migration
49
+ # * --create-model
50
+ # * --table-name
51
+
52
+ class ActionMailer::ARSendmail
53
+
54
+ ##
55
+ # The version of ActionMailer::ARSendmail you are running.
56
+
57
+ VERSION = '1.3.2'
58
+
59
+ ##
60
+ # Maximum number of times authentication will be consecutively retried
61
+
62
+ MAX_AUTH_FAILURES = 2
63
+
64
+ ##
65
+ # Email delivery attempts per run
66
+
67
+ attr_accessor :batch_size
68
+
69
+ ##
70
+ # Seconds to delay between runs
71
+
72
+ attr_accessor :delay
73
+
74
+ ##
75
+ # Maximum age of emails in seconds before they are removed from the queue.
76
+
77
+ attr_accessor :max_age
78
+
79
+ ##
80
+ # Be verbose
81
+
82
+ attr_accessor :verbose
83
+
84
+ ##
85
+ # ActiveRecord class that holds emails
86
+
87
+ attr_reader :email_class
88
+
89
+ ##
90
+ # True if only one delivery attempt will be made per call to run
91
+
92
+ attr_reader :once
93
+
94
+ ##
95
+ # Times authentication has failed
96
+
97
+ attr_accessor :failed_auth_count
98
+
99
+ ##
100
+ # Creates a new migration using +table_name+ and prints it on stdout.
101
+
102
+ def self.create_migration(table_name)
103
+ require 'active_support'
104
+ puts <<-EOF
105
+ class Add#{table_name.classify} < ActiveRecord::Migration
106
+ def self.up
107
+ create_table :#{table_name.tableize} do |t|
108
+ t.column :from, :string
109
+ t.column :to, :string
110
+ t.column :last_send_attempt, :integer, :default => 0
111
+ t.column :deliver_after, :datetime
112
+ t.column :mail, :text
113
+ t.column :created_on, :datetime
114
+ end
115
+ end
116
+
117
+ def self.down
118
+ drop_table :#{table_name.tableize}
119
+ end
120
+ end
121
+ EOF
122
+ end
123
+
124
+ ##
125
+ # Creates a new model using +table_name+ and prints it on stdout.
126
+
127
+ def self.create_model(table_name)
128
+ require 'active_support'
129
+ puts <<-EOF
130
+ class #{table_name.classify} < ActiveRecord::Base
131
+ end
132
+ EOF
133
+ end
134
+
135
+ ##
136
+ # Prints a list of unsent emails and the last delivery attempt, if any.
137
+ #
138
+ # If ActiveRecord::Timestamp is not being used the arrival time will not be
139
+ # known. See http://api.rubyonrails.org/classes/ActiveRecord/Timestamp.html
140
+ # to learn how to enable ActiveRecord::Timestamp.
141
+
142
+ def self.mailq(table_name)
143
+ klass = table_name.split('::').inject(Object) { |k,n| k.const_get n }
144
+ emails = klass.find :all, :conditions => ["deliver_after > ?", Time.now]
145
+
146
+ if emails.empty? then
147
+ puts "Mail queue is empty"
148
+ return
149
+ end
150
+
151
+ total_size = 0
152
+
153
+ puts "-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------"
154
+ emails.each do |email|
155
+ size = email.mail.length
156
+ total_size += size
157
+
158
+ create_timestamp = email.created_on rescue
159
+ email.created_at rescue
160
+ Time.at(email.created_date) rescue # for Robot Co-op
161
+ nil
162
+
163
+ created = if create_timestamp.nil? then
164
+ ' Unknown'
165
+ else
166
+ create_timestamp.strftime '%a %b %d %H:%M:%S'
167
+ end
168
+
169
+ puts "%10d %8d %s %s" % [email.id, size, created, email.from]
170
+ if email.last_send_attempt > 0 then
171
+ puts "Last send attempt: #{Time.at email.last_send_attempt}"
172
+ end
173
+ puts " #{email.to}"
174
+ puts
175
+ end
176
+
177
+ puts "-- #{total_size/1024} Kbytes in #{emails.length} Requests."
178
+ end
179
+
180
+ ##
181
+ # Processes command line options in +args+
182
+
183
+ def self.process_args(args)
184
+ name = File.basename $0
185
+
186
+ options = {}
187
+ options[:Chdir] = '.'
188
+ options[:Daemon] = false
189
+ options[:Delay] = 60
190
+ options[:MaxAge] = 86400 * 7
191
+ options[:Once] = false
192
+ options[:RailsEnv] = ENV['RAILS_ENV']
193
+ options[:TableName] = 'Email'
194
+
195
+ opts = OptionParser.new do |opts|
196
+ opts.banner = "Usage: #{name} [options]"
197
+ opts.separator ''
198
+
199
+ opts.separator "#{name} scans the email table for new messages and sends them to the"
200
+ opts.separator "website's configured SMTP host."
201
+ opts.separator ''
202
+ opts.separator "#{name} must be run from a Rails application's root."
203
+
204
+ opts.separator ''
205
+ opts.separator 'Sendmail options:'
206
+
207
+ opts.on("-b", "--batch-size BATCH_SIZE",
208
+ "Maximum number of emails to send per delay",
209
+ "Default: Deliver all available emails", Integer) do |batch_size|
210
+ options[:BatchSize] = batch_size
211
+ end
212
+
213
+ opts.on( "--delay DELAY",
214
+ "Delay between checks for new mail",
215
+ "in the database",
216
+ "Default: #{options[:Delay]}", Integer) do |delay|
217
+ options[:Delay] = delay
218
+ end
219
+
220
+ opts.on( "--max-age MAX_AGE",
221
+ "Maxmimum age for an email. After this",
222
+ "it will be removed from the queue.",
223
+ "Set to 0 to disable queue cleanup.",
224
+ "Default: #{options[:MaxAge]} seconds", Integer) do |max_age|
225
+ options[:MaxAge] = max_age
226
+ end
227
+
228
+ opts.on("-o", "--once",
229
+ "Only check for new mail and deliver once",
230
+ "Default: #{options[:Once]}") do |once|
231
+ options[:Once] = once
232
+ end
233
+
234
+ opts.on("-d", "--daemonize",
235
+ "Run as a daemon process",
236
+ "Default: #{options[:Daemon]}") do |daemon|
237
+ options[:Daemon] = true
238
+ end
239
+
240
+ opts.on( "--mailq",
241
+ "Display a list of emails waiting to be sent") do |mailq|
242
+ options[:MailQ] = true
243
+ end
244
+
245
+ opts.separator ''
246
+ opts.separator 'Setup Options:'
247
+
248
+ opts.on( "--create-migration",
249
+ "Prints a migration to add an Email table",
250
+ "to stdout") do |create|
251
+ options[:Migrate] = true
252
+ end
253
+
254
+ opts.on( "--create-model",
255
+ "Prints a model for an Email ActiveRecord",
256
+ "object to stdout") do |create|
257
+ options[:Model] = true
258
+ end
259
+
260
+ opts.separator ''
261
+ opts.separator 'Generic Options:'
262
+
263
+ opts.on("-c", "--chdir PATH",
264
+ "Use PATH for the application path",
265
+ "Default: #{options[:Chdir]}") do |path|
266
+ usage opts, "#{path} is not a directory" unless File.directory? path
267
+ usage opts, "#{path} is not readable" unless File.readable? path
268
+ options[:Chdir] = path
269
+ end
270
+
271
+ opts.on("-e", "--environment RAILS_ENV",
272
+ "Set the RAILS_ENV constant",
273
+ "Default: #{options[:RailsEnv]}") do |env|
274
+ options[:RailsEnv] = env
275
+ end
276
+
277
+ opts.on("-t", "--table-name TABLE_NAME",
278
+ "Name of table holding emails",
279
+ "Used for both sendmail and",
280
+ "migration creation",
281
+ "Default: #{options[:TableName]}") do |name|
282
+ options[:TableName] = name
283
+ end
284
+
285
+ opts.on("-v", "--[no-]verbose",
286
+ "Be verbose",
287
+ "Default: #{options[:Verbose]}") do |verbose|
288
+ options[:Verbose] = verbose
289
+ end
290
+
291
+ opts.on("-h", "--help",
292
+ "You're looking at it") do
293
+ usage opts
294
+ end
295
+
296
+ opts.separator ''
297
+ end
298
+
299
+ opts.parse! args
300
+
301
+ return options if options.include? :Migrate or options.include? :Model
302
+
303
+ ENV['RAILS_ENV'] = options[:RailsEnv]
304
+
305
+ Dir.chdir options[:Chdir] do
306
+ begin
307
+ require 'config/environment'
308
+ rescue LoadError
309
+ usage opts, <<-EOF
310
+ #{name} must be run from a Rails application's root to deliver email.
311
+ #{Dir.pwd} does not appear to be a Rails application root.
312
+ EOF
313
+ end
314
+ end
315
+
316
+ return options
317
+ end
318
+
319
+ ##
320
+ # Processes +args+ and runs as appropriate
321
+
322
+ def self.run(args = ARGV)
323
+ options = process_args args
324
+
325
+ if options.include? :Migrate then
326
+ create_migration options[:TableName]
327
+ exit
328
+ elsif options.include? :Model then
329
+ create_model options[:TableName]
330
+ exit
331
+ elsif options.include? :MailQ then
332
+ mailq options[:TableName]
333
+ exit
334
+ end
335
+
336
+ if options[:Daemon] then
337
+ require 'webrick/server'
338
+ WEBrick::Daemon.start
339
+ end
340
+
341
+ new(options).run
342
+
343
+ rescue SystemExit
344
+ raise
345
+ rescue SignalException
346
+ exit
347
+ rescue Exception => e
348
+ $stderr.puts "Unhandled exception #{e.message}(#{e.class}):"
349
+ $stderr.puts "\t#{e.backtrace.join "\n\t"}"
350
+ exit 1
351
+ end
352
+
353
+ ##
354
+ # Prints a usage message to $stderr using +opts+ and exits
355
+
356
+ def self.usage(opts, message = nil)
357
+ if message then
358
+ $stderr.puts message
359
+ $stderr.puts
360
+ end
361
+
362
+ $stderr.puts opts
363
+ exit 1
364
+ end
365
+
366
+ ##
367
+ # Creates a new ARSendmail.
368
+ #
369
+ # Valid options are:
370
+ # <tt>:BatchSize</tt>:: Maximum number of emails to send per delay
371
+ # <tt>:Delay</tt>:: Delay between deliver attempts
372
+ # <tt>:TableName</tt>:: Table name that stores the emails
373
+ # <tt>:Once</tt>:: Only attempt to deliver emails once when run is called
374
+ # <tt>:Verbose</tt>:: Be verbose.
375
+
376
+ def initialize(options = {})
377
+ options[:Delay] ||= 60
378
+ options[:TableName] ||= 'Email'
379
+ options[:MaxAge] ||= 86400 * 7
380
+
381
+ @batch_size = options[:BatchSize]
382
+ @delay = options[:Delay]
383
+ @email_class = Object.path2class options[:TableName]
384
+ @once = options[:Once]
385
+ @verbose = options[:Verbose]
386
+ @max_age = options[:MaxAge]
387
+
388
+ @failed_auth_count = 0
389
+ end
390
+
391
+ ##
392
+ # Removes emails that have lived in the queue for too long. If max_age is
393
+ # set to 0, no emails will be removed.
394
+
395
+ def cleanup
396
+ return if @max_age == 0
397
+ timeout = Time.now - @max_age
398
+ conditions = ['last_send_attempt > 0 and created_on < ?', timeout]
399
+ mail = @email_class.destroy_all conditions
400
+
401
+ log "expired #{mail.length} emails from the queue"
402
+ end
403
+
404
+ ##
405
+ # Delivers +emails+ to ActionMailer's SMTP server and destroys them.
406
+
407
+ def deliver(emails)
408
+ user = smtp_settings[:user] || smtp_settings[:user_name]
409
+ Net::SMTP.start smtp_settings[:address], smtp_settings[:port],
410
+ smtp_settings[:domain], user,
411
+ smtp_settings[:password],
412
+ smtp_settings[:authentication],
413
+ smtp_settings[:tls] do |smtp|
414
+ @failed_auth_count = 0
415
+ until emails.empty? do
416
+ email = emails.shift
417
+ begin
418
+ res = smtp.send_message email.mail, email.from, email.to
419
+ email.destroy
420
+ log "sent email %011d from %s to %s: %p" %
421
+ [email.id, email.from, email.to, res]
422
+ rescue Net::SMTPFatalError => e
423
+ log "5xx error sending email %d, removing from queue: %p(%s):\n\t%s" %
424
+ [email.id, e.message, e.class, e.backtrace.join("\n\t")]
425
+ email.destroy
426
+ smtp.reset
427
+ rescue Net::SMTPServerBusy => e
428
+ log "server too busy, sleeping #{@delay} seconds"
429
+ sleep delay
430
+ return
431
+ rescue Net::SMTPUnknownError, Net::SMTPSyntaxError, TimeoutError => e
432
+ email.last_send_attempt = Time.now.to_i
433
+ email.save rescue nil
434
+ log "error sending email %d: %p(%s):\n\t%s" %
435
+ [email.id, e.message, e.class, e.backtrace.join("\n\t")]
436
+ smtp.reset
437
+ end
438
+ end
439
+ end
440
+ rescue Net::SMTPAuthenticationError => e
441
+ @failed_auth_count += 1
442
+ if @failed_auth_count >= MAX_AUTH_FAILURES then
443
+ log "authentication error, giving up: #{e.message}"
444
+ raise e
445
+ else
446
+ log "authentication error, retrying: #{e.message}"
447
+ end
448
+ sleep delay
449
+ rescue Net::SMTPServerBusy, SystemCallError, OpenSSL::SSL::SSLError
450
+ # ignore SMTPServerBusy/EPIPE/ECONNRESET from Net::SMTP.start's ensure
451
+ end
452
+
453
+ ##
454
+ # Prepares ar_sendmail for exiting
455
+
456
+ def do_exit
457
+ log "caught signal, shutting down"
458
+ exit
459
+ end
460
+
461
+ ##
462
+ # Returns emails in email_class that haven't had a delivery attempt in the
463
+ # last 300 seconds.
464
+
465
+ def find_emails
466
+ options = { :conditions => ['last_send_attempt < ? and deliver_after > ?', Time.now.to_i - 300, Time.now] }
467
+ options[:limit] = batch_size unless batch_size.nil?
468
+ mail = @email_class.find :all, options
469
+
470
+ log "found #{mail.length} emails to send"
471
+ mail
472
+ end
473
+
474
+ ##
475
+ # Installs signal handlers to gracefully exit.
476
+
477
+ def install_signal_handlers
478
+ trap 'TERM' do do_exit end
479
+ trap 'INT' do do_exit end
480
+ end
481
+
482
+ ##
483
+ # Logs +message+ if verbose
484
+
485
+ def log(message)
486
+ $stderr.puts message if @verbose
487
+ ActionMailer::Base.logger.info "ar_sendmail: #{message}"
488
+ end
489
+
490
+ ##
491
+ # Scans for emails and delivers them every delay seconds. Only returns if
492
+ # once is true.
493
+
494
+ def run
495
+ install_signal_handlers
496
+
497
+ loop do
498
+ now = Time.now
499
+ begin
500
+ cleanup
501
+ deliver find_emails
502
+ rescue ActiveRecord::Transactions::TransactionError
503
+ end
504
+ break if @once
505
+ sleep @delay if now + @delay > Time.now
506
+ end
507
+ end
508
+
509
+ ##
510
+ # Proxy to ActionMailer::Base::smtp_settings. See
511
+ # http://api.rubyonrails.org/classes/ActionMailer/Base.html
512
+ # for instructions on how to configure ActionMailer's SMTP server.
513
+ #
514
+ # Falls back to ::server_settings if ::smtp_settings doesn't exist for
515
+ # backwards compatibility.
516
+
517
+ def smtp_settings
518
+ ActionMailer::Base.smtp_settings rescue ActionMailer::Base.server_settings
519
+ end
520
+
521
+ end
522
+