aussiegeek-ar_sendmail_delayed 1.3.2

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.
@@ -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
+