humanzz-ar_mailer 2.1.4

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,130 @@
1
+ = 2.1.4
2
+
3
+ * Bugs fixed
4
+ * Explicitly require ar_mailer in ar_sendmail because its not getting loaded by the Rails environment for some reason
5
+
6
+ = 2.1.3
7
+
8
+ * Tests now pass on gem install
9
+ * Removed deprecated ActionMailer::ARMailer class
10
+ * Bugs fixed
11
+ * Fixed issue with pre-loading ActionMailer. No use ActionMailer::Base.email_class directly rather than store in ARSendmail instance var so no need to pre-load ActionMailer.
12
+
13
+ = 2.1.2
14
+
15
+ * Bugs fixed
16
+ * Require ar_mailer in ar_sendmail since the change to remove TableName and use email_class
17
+
18
+ = 2.1.1
19
+
20
+ * Force gem rebuild
21
+
22
+ = 2.1.0
23
+
24
+ * Switched to using a Rails generator for migration and model files. The ar_sendmail options have been removed.
25
+
26
+ = 2.0.2
27
+
28
+ * Removed TableName option from ar_sendmail options as its redundant. The Rails environment gets loaded so the settings for email class also get loaded
29
+ * Bugs fixed
30
+ * Email class reloading issue in development mode causing AR email class defaults to be lost when cached
31
+
32
+ = 2.0.1
33
+
34
+ * Added option to use smtp setting of :tls => false to disable TLS auto start in Ruby 1.8.7+
35
+ * Removed some cruft which can be handled by ActiveSupport
36
+
37
+ = 2.0.0
38
+
39
+ * Removed need to use ARMailer subclass. Just set the delivery method and you are ready to go. Backwards compatible with a deprecation notice if you subclass old ARMailer class.
40
+ * Only include SMTP TLS patch if Ruby version < 1.8.7 as it has an alternative. Changes based on Calvin Yu's [cyu] fork.
41
+ * Renamed default migration name to the modern Rails default
42
+ * Only authenticate if emails waiting to be sent
43
+ * Added --version switch to ar_sendmail binary
44
+ * Created a lighthouse account for this project (adzap fork only). See README.
45
+
46
+ = 1.4.4
47
+
48
+ * Exit init.d script with message if no mailers defined.
49
+
50
+ = 1.4.3
51
+
52
+ * Bugs fixed
53
+ * Replaced mistaken call to log when removing pid file artifact for
54
+ non-running daemon
55
+
56
+ = 1.4.2
57
+
58
+ * New Features
59
+ * Added Ruby based linux init.d script for handling daemon startup using yaml
60
+ config file. See files share/linux/ar_sendmail and ar_sendmail.conf
61
+ * Bugs fixed
62
+ * Proper handling for relative and absolute paths for the pid file
63
+ * Removed hoe dependency since we need the explicit gemspec file for github and
64
+ not deploying to RubyForge its not as useful.
65
+ * Moved old BSD rc.d script to share/bsd folder
66
+ * Updated README with github gem install, docs and init script info
67
+
68
+ = 1.4.1
69
+
70
+ * Bugs fixed
71
+ * Daemon failed on startup fixed with expanding full path of pid file
72
+
73
+ = 1.4.0
74
+
75
+ * Forked gem and published on GitHub (gem sources -a http://gems.github.com)
76
+ * New Features
77
+ * Added pid file creation on daemonize with command line option to specify pid filename [Dylan Egan]
78
+
79
+ = 1.3.1
80
+
81
+ * Fix bug #12530, gmail causes SSL errors. Submitted by Kyle Maxwell
82
+ and Alex Ostleitner.
83
+ * Try ActionMailer::Base::server_settings then ::smtp_settings. Fixes
84
+ bug #12516. Submitted by Alex Ostleitner.
85
+
86
+ = 1.3.0
87
+
88
+ * New Features
89
+ * Added automatic mail queue cleanup.
90
+ * MAY CAUSE LOSS OF DATA. If you haven't run ar_sendmail within
91
+ the expiry time, set it to 0.
92
+ * Bugs fixed
93
+ * Authentication errors are now handled by retrying once.
94
+
95
+ = 1.2.0
96
+
97
+ * Bugs fixed
98
+ * Handle SMTPServerBusy by backing off @delay seconds then re-queueing
99
+ * Allow email delivery class to be set in ARMailer.
100
+ * ar_sendmail --mailq works with --table-name now.
101
+ * Miscellaneous Updates
102
+ * Added documentation to require 'action_mailer/ar_mailer' in
103
+ instructions.
104
+ * Moved to ZSS p4 repository
105
+ * Supports TLS now. Requested by Dave Thomas. smtp_tls.rb from Kyle
106
+ Maxwell & etc.
107
+
108
+ = 1.1.0
109
+
110
+ * Features
111
+ * Added --chdir to set rails directory
112
+ * Added --environment to set RAILS_ENV
113
+ * Exits cleanly on TERM or INT signals
114
+ * Added FreeBSD rc.d script
115
+ * Exceptions during SMTP sending are now logged
116
+ * No longer waits if sending email took too long
117
+ * Bugs fixed
118
+ * Fixed last send attempt in --mailq
119
+ * Better SMTP error handling
120
+ * Messages are removed from the queue on 5xx errors
121
+ * Added Net::SMTP.reset to avoid needing to recreate the connection
122
+
123
+ = 1.0.1
124
+
125
+ * Bugs fixed
126
+ * From and to of email destination were swapped
127
+
128
+ = 1.0.0
129
+
130
+ * 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,139 @@
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
+ and for forked additions
14
+
15
+ http://github.com/adzap/ar_mailer/wikis
16
+
17
+ Bugs:
18
+
19
+ http://adzap.lighthouseapp.com/projects/26997-ar_mailer
20
+
21
+ == About
22
+
23
+ Even delivering email to the local machine may take too long when you have to
24
+ send hundreds of messages. ar_mailer allows you to store messages into the
25
+ database for later delivery by a separate process, ar_sendmail.
26
+
27
+ == Installing ar_mailer (forked)
28
+
29
+ Before installing you will need to make sure the original gem is uninstalled as they can't coexist:
30
+
31
+ $ sudo gem uninstall ar_mailer
32
+
33
+ Install the gem from GitHub gems server:
34
+
35
+ First, if you haven't already:
36
+
37
+ $ sudo gem sources -a http://gems.github.com
38
+
39
+ Then
40
+
41
+ $ sudo gem install adzap-ar_mailer
42
+
43
+ For Rails >= 2.1, in your environment.rb:
44
+
45
+ config.gem "adzap-ar_mailer", :lib => 'action_mailer/ar_mailer', :source => 'http://gems.github.com'
46
+
47
+ For Rails 2.0, in an initializer file:
48
+
49
+ require 'action_mailer/ar_mailer'
50
+
51
+ == Usage
52
+
53
+ Go to your Rails project:
54
+
55
+ $ cd your_rails_project
56
+
57
+ Create the migration and model:
58
+
59
+ This shows the options which are only the model name, which defaults to Email
60
+
61
+ ./script/generate ar_mailer -h
62
+
63
+ Then run with defaults
64
+
65
+ ./script/generate ar_mailer
66
+
67
+ Or specify a custom model name
68
+
69
+ ./script/generate ar_mailer Newsletter
70
+
71
+ See Alternate Mail Storage if you use a custom model name
72
+
73
+ In your mailer class methods you must be sure to set the From address for your emails.
74
+ Something like:
75
+
76
+ def list_send(recipient)
77
+ from 'no_reply@example.com'
78
+ # ...
79
+
80
+ Edit config/environments/production.rb and set the delivery method:
81
+
82
+ config.action_mailer.delivery_method = :activerecord
83
+
84
+ Or if you need to, you can set each mailer class delivery method individually:
85
+
86
+ class MyMailer < ActionMailer::Base
87
+ self.delivery_method = :activerecord
88
+ end
89
+
90
+ This can be useful when using plugins like ExceptionNotification. Where it
91
+ might be foolish to tie the sending of the email alert to the database when the
92
+ database might be causing the exception being raised. In this instance you could
93
+ override ExceptionNofitier delivery method to be smtp or set the other
94
+ mailer classes to use ARMailer explicitly.
95
+
96
+ Then to run it:
97
+
98
+ $ ar_sendmail
99
+
100
+ You can also run it from cron with -o, or as a daemon with -d.
101
+
102
+ See <tt>ar_sendmail -h</tt> for full details.
103
+
104
+ === Alternate Mail Storage
105
+
106
+ By default ar_mailer assumes you are using an ActiveRecord model called
107
+ Email to store the emails created before sending. If you want to change
108
+ this you alter it in an intializer like so:
109
+
110
+ ActionMailer::Base.email_class = Newsletter
111
+
112
+ === A Word on TLS
113
+
114
+ If you are using Ruby >= 1.8.7, TLS will be enabled automatically if your
115
+ SMTP server supports it. If you do not want it to automatically enabled then
116
+ set the :tls option to false in your smtp_settings.
117
+
118
+ If you are on Ruby <= 1.8.6, then the TLS patch included in this plugin will
119
+ be loaded, so you don't need another TLS plugin to add the capability. This
120
+ patch allows you to explicit set if the server supports TLS by setting the
121
+ :tls option to true in your smtp_settings.
122
+
123
+ === Help
124
+
125
+ See ar_sendmail -h for options to ar_sendmail.
126
+
127
+ NOTE: You may need to delete an smtp_tls.rb file if you have one lying
128
+ around. ar_mailer supplies it own.
129
+
130
+ == Run as a service (init.d/rc.d scripts)
131
+
132
+ For Linux both script and demo config files are in share/linux.
133
+ See ar_sendmail.conf for setting up your config. Copy the ar_sendmail file
134
+ to /etc/init.d/ and make it executable. Then for Debian based distros run
135
+ 'sudo update-rc.d ar_sendmail defaults' and it should work. Make sure you have
136
+ the config file /etc/ar_sendmail.conf in place before starting.
137
+
138
+ For FreeBSD or NetBSD script is share/bsd/ar_sendmail. This is old and does not
139
+ support the config file unless someone wants to submit a patch.
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/lib'))
7
+
8
+ require './lib/action_mailer/ar_sendmail'
9
+
10
+ ar_mailer_gemspec = Gem::Specification.new do |s|
11
+ s.name = %q{ar_mailer}
12
+ s.version = ActionMailer::ARSendmail::VERSION
13
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
14
+ s.authors = ["Eric Hodel", "Adam Meehan"]
15
+ s.default_executable = %q{ar_sendmail}
16
+ s.description = %q{Even delivering email to the local machine may take too long when you have to send hundreds of messages. ar_mailer allows you to store messages into the database for later delivery by a separate process, ar_sendmail.}
17
+ s.email = %q{adam.meehan@gmail.com}
18
+ s.executables = ["ar_sendmail"]
19
+ s.extra_rdoc_files = ["History.txt", "LICENSE.txt", "README.rdoc"]
20
+ s.files = ["History.txt", "LICENSE.txt", "README.rdoc", "Rakefile", "bin/ar_sendmail", "generators/ar_mailer/ar_mailer_generator.rb", "generators/ar_mailer/templates/migration.rb", "generators/ar_mailer/templates/model.rb", "lib/action_mailer/ar_mailer.rb", "lib/action_mailer/ar_sendmail.rb", "lib/smtp_tls.rb", "share/bsd/ar_sendmail", "share/linux/ar_sendmail", "share/linux/ar_sendmail.conf", "test/resources/action_mailer.rb", "test/test_armailer.rb", "test/test_arsendmail.rb", "test/test_helper.rb"]
21
+ s.has_rdoc = true
22
+ s.homepage = %q{http://github.com/adzap/ar_mailer}
23
+ s.rdoc_options = ["--main", "README.rdoc"]
24
+ s.require_paths = ["lib"]
25
+ s.rubyforge_project = %q{seattlerb}
26
+ s.summary = %q{A two-phase delivery agent for ActionMailer}
27
+ s.test_files = ["test/test_armailer.rb", "test/test_arsendmail.rb"]
28
+ end
29
+
30
+ Rake::GemPackageTask.new(ar_mailer_gemspec) do |pkg|
31
+ pkg.gem_spec = ar_mailer_gemspec
32
+ end
33
+
34
+ desc "Update ar_mailer.gemspec"
35
+ task :make_spec do
36
+ File.open("ar_mailer.gemspec", "w") do |f|
37
+ f.puts(ar_mailer_gemspec.to_ruby)
38
+ end
39
+ end
40
+
41
+ desc "Build packages and install"
42
+ task :install => :package do
43
+ sh %{sudo gem install --local --test pkg/ar_mailer-#{ActionMailer::ARSendmail::VERSION}}
44
+ end
45
+
46
+ desc 'Default: run unit tests.'
47
+ task :default => :test
48
+
49
+ desc 'Test the ar_mailer gem.'
50
+ Rake::TestTask.new(:test) do |t|
51
+ t.libs << 'lib' << 'test'
52
+ t.test_files = FileList['test/**/test_*.rb'].exclude("test/test_helper.rb")
53
+ t.verbose = true
54
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'action_mailer/ar_sendmail'
4
+
5
+ ActionMailer::ARSendmail.run
6
+
@@ -0,0 +1,25 @@
1
+ class ArMailerGenerator < Rails::Generator::NamedBase
2
+
3
+ def initialize(runtime_args, runtime_options = {})
4
+ runtime_args.unshift('Email') if runtime_args.empty?
5
+ super
6
+ end
7
+
8
+ def manifest
9
+ record do |m|
10
+ m.class_collisions class_name
11
+
12
+ m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
13
+
14
+ m.migration_template 'migration.rb', 'db/migrate', :assigns => {
15
+ :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
16
+ }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
17
+ end
18
+ end
19
+
20
+ protected
21
+ def banner
22
+ "Usage: #{$0} #{spec.name} EmailModelName (default: Email)"
23
+ end
24
+
25
+ end
@@ -0,0 +1,15 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :<%= table_name %> do |t|
4
+ t.column :from, :string
5
+ t.column :to, :string
6
+ t.column :last_send_attempt, :integer, :default => 0
7
+ t.column :mail, :text
8
+ t.column :created_on, :datetime
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :<%= table_name %>
14
+ end
15
+ end
@@ -0,0 +1,2 @@
1
+ class <%= class_name %> < ActiveRecord::Base
2
+ end
@@ -0,0 +1,32 @@
1
+ require 'action_mailer'
2
+ ##
3
+ # Adds sending email through an ActiveRecord table as a delivery method for
4
+ # ActionMailer.
5
+ #
6
+
7
+ class ActionMailer::Base
8
+
9
+ ##
10
+ # Set the email class for deliveries. Handle class reloading issues which prevents caching the email class.
11
+ #
12
+ @@email_class_name = 'Email'
13
+
14
+ def self.email_class=(klass)
15
+ @@email_class_name = klass.to_s
16
+ end
17
+
18
+ def self.email_class
19
+ @@email_class_name.constantize
20
+ end
21
+
22
+ ##
23
+ # Adds +mail+ to the Email table. Only the first From address for +mail+ is
24
+ # used.
25
+
26
+ def perform_delivery_activerecord(mail)
27
+ mail.destinations.each do |destination|
28
+ self.class.email_class.create :mail => mail.encoded, :to => destination, :from => mail.from.first
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,487 @@
1
+ require 'optparse'
2
+ require 'net/smtp'
3
+ require 'smtp_tls' unless Net::SMTP.instance_methods.include?("enable_starttls_auto")
4
+ require 'rubygems'
5
+
6
+ module ActionMailer; end
7
+ # This should get loaded by the environment later but for some reason fails with
8
+ # the github namespaced gem and succeeds with a local gem build install.
9
+ require 'action_mailer/ar_mailer'
10
+
11
+ ##
12
+ # Hack in RSET
13
+
14
+ module Net # :nodoc:
15
+ class SMTP # :nodoc:
16
+
17
+ unless instance_methods.include? 'reset' then
18
+ ##
19
+ # Resets the SMTP connection.
20
+
21
+ def reset
22
+ getok 'RSET'
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ ##
30
+ # ActionMailer::ARSendmail delivers email from the email table to the
31
+ # SMTP server configured in your application's config/environment.rb.
32
+ # ar_sendmail does not work with sendmail delivery.
33
+ #
34
+ # ar_mailer can deliver to SMTP with TLS using smtp_tls.rb borrowed from Kyle
35
+ # Maxwell's action_mailer_optional_tls plugin. Simply set the :tls option in
36
+ # ActionMailer::Base's smtp_settings to true to enable TLS.
37
+ #
38
+ # See ar_sendmail -h for the full list of supported options.
39
+ #
40
+ # The interesting options are:
41
+ # * --daemon
42
+ # * --mailq
43
+
44
+ class ActionMailer::ARSendmail
45
+
46
+ ##
47
+ # The version of ActionMailer::ARSendmail you are running.
48
+
49
+ VERSION = '2.1.4'
50
+
51
+ ##
52
+ # Maximum number of times authentication will be consecutively retried
53
+
54
+ MAX_AUTH_FAILURES = 2
55
+
56
+ ##
57
+ # Email delivery attempts per run
58
+
59
+ attr_accessor :batch_size
60
+
61
+ ##
62
+ # Seconds to delay between runs
63
+
64
+ attr_accessor :delay
65
+
66
+ ##
67
+ # Maximum age of emails in seconds before they are removed from the queue.
68
+
69
+ attr_accessor :max_age
70
+
71
+ ##
72
+ # Be verbose
73
+
74
+ attr_accessor :verbose
75
+
76
+
77
+ ##
78
+ # True if only one delivery attempt will be made per call to run
79
+
80
+ attr_reader :once
81
+
82
+ ##
83
+ # Times authentication has failed
84
+
85
+ attr_accessor :failed_auth_count
86
+
87
+ @@pid_file = nil
88
+
89
+ def self.remove_pid_file
90
+ if @@pid_file
91
+ require 'shell'
92
+ sh = Shell.new
93
+ sh.rm @@pid_file
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Prints a list of unsent emails and the last delivery attempt, if any.
99
+ #
100
+ # If ActiveRecord::Timestamp is not being used the arrival time will not be
101
+ # known. See http://api.rubyonrails.org/classes/ActiveRecord/Timestamp.html
102
+ # to learn how to enable ActiveRecord::Timestamp.
103
+
104
+ def self.mailq
105
+ emails = ActionMailer::Base.email_class.find :all
106
+
107
+ if emails.empty? then
108
+ puts "Mail queue is empty"
109
+ return
110
+ end
111
+
112
+ total_size = 0
113
+
114
+ puts "-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------"
115
+ emails.each do |email|
116
+ size = email.mail.length
117
+ total_size += size
118
+
119
+ create_timestamp = email.created_on rescue
120
+ email.created_at rescue
121
+ Time.at(email.created_date) rescue # for Robot Co-op
122
+ nil
123
+
124
+ created = if create_timestamp.nil? then
125
+ ' Unknown'
126
+ else
127
+ create_timestamp.strftime '%a %b %d %H:%M:%S'
128
+ end
129
+
130
+ puts "%10d %8d %s %s" % [email.id, size, created, email.from]
131
+ if email.last_send_attempt > 0 then
132
+ puts "Last send attempt: #{Time.at email.last_send_attempt}"
133
+ end
134
+ puts " #{email.to}"
135
+ puts
136
+ end
137
+
138
+ puts "-- #{total_size/1024} Kbytes in #{emails.length} Requests."
139
+ end
140
+
141
+ ##
142
+ # Processes command line options in +args+
143
+
144
+ def self.process_args(args)
145
+ name = File.basename $0
146
+
147
+ options = {}
148
+ options[:Chdir] = '.'
149
+ options[:Daemon] = false
150
+ options[:Delay] = 60
151
+ options[:MaxAge] = 86400 * 7
152
+ options[:Once] = false
153
+ options[:RailsEnv] = ENV['RAILS_ENV']
154
+ options[:Pidfile] = options[:Chdir] + '/log/ar_sendmail.pid'
155
+
156
+ opts = OptionParser.new do |opts|
157
+ opts.banner = "Usage: #{name} [options]"
158
+ opts.separator ''
159
+
160
+ opts.separator "#{name} scans the email table for new messages and sends them to the"
161
+ opts.separator "website's configured SMTP host."
162
+ opts.separator ''
163
+ opts.separator "#{name} must be run from a Rails application's root."
164
+
165
+ opts.separator ''
166
+ opts.separator 'Sendmail options:'
167
+
168
+ opts.on("-b", "--batch-size BATCH_SIZE",
169
+ "Maximum number of emails to send per delay",
170
+ "Default: Deliver all available emails", Integer) do |batch_size|
171
+ options[:BatchSize] = batch_size
172
+ end
173
+
174
+ opts.on( "--delay DELAY",
175
+ "Delay between checks for new mail",
176
+ "in the database",
177
+ "Default: #{options[:Delay]}", Integer) do |delay|
178
+ options[:Delay] = delay
179
+ end
180
+
181
+ opts.on( "--max-age MAX_AGE",
182
+ "Maxmimum age for an email. After this",
183
+ "it will be removed from the queue.",
184
+ "Set to 0 to disable queue cleanup.",
185
+ "Default: #{options[:MaxAge]} seconds", Integer) do |max_age|
186
+ options[:MaxAge] = max_age
187
+ end
188
+
189
+ opts.on("-o", "--once",
190
+ "Only check for new mail and deliver once",
191
+ "Default: #{options[:Once]}") do |once|
192
+ options[:Once] = once
193
+ end
194
+
195
+ opts.on("-d", "--daemonize",
196
+ "Run as a daemon process",
197
+ "Default: #{options[:Daemon]}") do |daemon|
198
+ options[:Daemon] = true
199
+ end
200
+
201
+ opts.on("-p", "--pidfile PIDFILE",
202
+ "Set the pidfile location",
203
+ "Default: #{options[:Chdir]}#{options[:Pidfile]}", String) do |pidfile|
204
+ options[:Pidfile] = pidfile
205
+ end
206
+
207
+ opts.on( "--mailq",
208
+ "Display a list of emails waiting to be sent") do |mailq|
209
+ options[:MailQ] = true
210
+ end
211
+
212
+ opts.separator ''
213
+ opts.separator 'Setup Options:'
214
+
215
+ opts.separator ''
216
+ opts.separator 'Generic Options:'
217
+
218
+ opts.on("-c", "--chdir PATH",
219
+ "Use PATH for the application path",
220
+ "Default: #{options[:Chdir]}") do |path|
221
+ usage opts, "#{path} is not a directory" unless File.directory? path
222
+ usage opts, "#{path} is not readable" unless File.readable? path
223
+ options[:Chdir] = path
224
+ end
225
+
226
+ opts.on("-e", "--environment RAILS_ENV",
227
+ "Set the RAILS_ENV constant",
228
+ "Default: #{options[:RailsEnv]}") do |env|
229
+ options[:RailsEnv] = env
230
+ end
231
+
232
+ opts.on("-v", "--[no-]verbose",
233
+ "Be verbose",
234
+ "Default: #{options[:Verbose]}") do |verbose|
235
+ options[:Verbose] = verbose
236
+ end
237
+
238
+ opts.on("-h", "--help",
239
+ "You're looking at it") do
240
+ usage opts
241
+ end
242
+
243
+ opts.on("--version", "Version of ARMailer") do
244
+ usage "ar_mailer #{VERSION} (adzap fork)"
245
+ end
246
+
247
+ opts.separator ''
248
+ end
249
+
250
+ opts.parse! args
251
+
252
+ ENV['RAILS_ENV'] = options[:RailsEnv]
253
+
254
+ Dir.chdir options[:Chdir] do
255
+ begin
256
+ require 'config/environment'
257
+ rescue LoadError
258
+ usage opts, <<-EOF
259
+ #{name} must be run from a Rails application's root to deliver email.
260
+ #{Dir.pwd} does not appear to be a Rails application root.
261
+ EOF
262
+ end
263
+ end
264
+
265
+ return options
266
+ end
267
+
268
+ ##
269
+ # Processes +args+ and runs as appropriate
270
+
271
+ def self.run(args = ARGV)
272
+ options = process_args args
273
+
274
+ if options.include? :MailQ then
275
+ mailq
276
+ exit
277
+ end
278
+
279
+ if options[:Daemon] then
280
+ require 'webrick/server'
281
+ @@pid_file = File.expand_path(options[:Pidfile], options[:Chdir])
282
+ if File.exists? @@pid_file
283
+ # check to see if process is actually running
284
+ pid = ''
285
+ File.open(@@pid_file, 'r') {|f| pid = f.read.chomp }
286
+ if system("ps -p #{pid} | grep #{pid}") # returns true if process is running, o.w. false
287
+ $stderr.puts "Warning: The pid file #{@@pid_file} exists and ar_sendmail is running. Shutting down."
288
+ exit
289
+ else
290
+ # not running, so remove existing pid file and continue
291
+ self.remove_pid_file
292
+ $stderr.puts "ar_sendmail is not running. Removing existing pid file and starting up..."
293
+ end
294
+ end
295
+ WEBrick::Daemon.start
296
+ File.open(@@pid_file, 'w') {|f| f.write("#{Process.pid}\n")}
297
+ end
298
+
299
+ new(options).run
300
+
301
+ rescue SystemExit
302
+ raise
303
+ rescue SignalException
304
+ exit
305
+ rescue Exception => e
306
+ $stderr.puts "Unhandled exception #{e.message}(#{e.class}):"
307
+ $stderr.puts "\t#{e.backtrace.join "\n\t"}"
308
+ exit 1
309
+ end
310
+
311
+ ##
312
+ # Prints a usage message to $stderr using +opts+ and exits
313
+
314
+ def self.usage(opts, message = nil)
315
+ if message then
316
+ $stderr.puts message
317
+ $stderr.puts
318
+ end
319
+
320
+ $stderr.puts opts
321
+ exit 1
322
+ end
323
+
324
+ ##
325
+ # Creates a new ARSendmail.
326
+ #
327
+ # Valid options are:
328
+ # <tt>:BatchSize</tt>:: Maximum number of emails to send per delay
329
+ # <tt>:Delay</tt>:: Delay between deliver attempts
330
+ # <tt>:Once</tt>:: Only attempt to deliver emails once when run is called
331
+ # <tt>:Verbose</tt>:: Be verbose.
332
+
333
+ def initialize(options = {})
334
+ options[:Delay] ||= 60
335
+ options[:MaxAge] ||= 86400 * 7
336
+
337
+ @batch_size = options[:BatchSize]
338
+ @delay = options[:Delay]
339
+ @once = options[:Once]
340
+ @verbose = options[:Verbose]
341
+ @max_age = options[:MaxAge]
342
+
343
+ @failed_auth_count = 0
344
+ end
345
+
346
+ ##
347
+ # Removes emails that have lived in the queue for too long. If max_age is
348
+ # set to 0, no emails will be removed.
349
+
350
+ def cleanup
351
+ return if @max_age == 0
352
+ timeout = Time.now - @max_age
353
+ conditions = ['last_send_attempt > 0 and created_on < ?', timeout]
354
+ mail = ActionMailer::Base.email_class.destroy_all conditions
355
+
356
+ log "expired #{mail.length} emails from the queue"
357
+ end
358
+
359
+ ##
360
+ # Delivers +emails+ to ActionMailer's SMTP server and destroys them.
361
+
362
+ def deliver(emails)
363
+ settings = [
364
+ smtp_settings[:domain],
365
+ (smtp_settings[:user] || smtp_settings[:user_name]),
366
+ smtp_settings[:password],
367
+ smtp_settings[:authentication]
368
+ ]
369
+
370
+ smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
371
+ if smtp.respond_to?(:enable_starttls_auto)
372
+ smtp.enable_starttls_auto unless smtp_settings[:tls] == false
373
+ else
374
+ settings << smtp_settings[:tls]
375
+ end
376
+
377
+ smtp.start(*settings) do |session|
378
+ @failed_auth_count = 0
379
+ until emails.empty? do
380
+ email = emails.shift
381
+ begin
382
+ res = session.send_message email.mail, email.from, email.to
383
+ email.destroy
384
+ log "sent email %011d from %s to %s: %p" %
385
+ [email.id, email.from, email.to, res]
386
+ rescue Net::SMTPFatalError => e
387
+ log "5xx error sending email %d, removing from queue: %p(%s):\n\t%s" %
388
+ [email.id, e.message, e.class, e.backtrace.join("\n\t")]
389
+ email.destroy
390
+ session.reset
391
+ rescue Net::SMTPServerBusy => e
392
+ log "server too busy, sleeping #{@delay} seconds"
393
+ sleep delay
394
+ return
395
+ rescue Net::SMTPUnknownError, Net::SMTPSyntaxError, TimeoutError => e
396
+ email.last_send_attempt = Time.now.to_i
397
+ email.save rescue nil
398
+ log "error sending email %d: %p(%s):\n\t%s" %
399
+ [email.id, e.message, e.class, e.backtrace.join("\n\t")]
400
+ session.reset
401
+ end
402
+ end
403
+ end
404
+ rescue Net::SMTPAuthenticationError => e
405
+ @failed_auth_count += 1
406
+ if @failed_auth_count >= MAX_AUTH_FAILURES then
407
+ log "authentication error, giving up: #{e.message}"
408
+ raise e
409
+ else
410
+ log "authentication error, retrying: #{e.message}"
411
+ end
412
+ sleep delay
413
+ rescue Net::SMTPServerBusy, SystemCallError, OpenSSL::SSL::SSLError
414
+ # ignore SMTPServerBusy/EPIPE/ECONNRESET from Net::SMTP.start's ensure
415
+ end
416
+
417
+ ##
418
+ # Prepares ar_sendmail for exiting
419
+
420
+ def do_exit
421
+ log "caught signal, shutting down"
422
+ self.class.remove_pid_file
423
+ exit
424
+ end
425
+
426
+ ##
427
+ # Returns emails in email_class that haven't had a delivery attempt in the
428
+ # last 300 seconds.
429
+
430
+ def find_emails
431
+ options = { :conditions => ['last_send_attempt < ?', Time.now.to_i - 300] }
432
+ options[:limit] = batch_size unless batch_size.nil?
433
+ mail = ActionMailer::Base.email_class.find :all, options
434
+
435
+ log "found #{mail.length} emails to send"
436
+ mail
437
+ end
438
+
439
+ ##
440
+ # Installs signal handlers to gracefully exit.
441
+
442
+ def install_signal_handlers
443
+ trap 'TERM' do do_exit end
444
+ trap 'INT' do do_exit end
445
+ end
446
+
447
+ ##
448
+ # Logs +message+ if verbose
449
+
450
+ def log(message)
451
+ $stderr.puts message if @verbose
452
+ ActionMailer::Base.logger.info "ar_sendmail: #{message}"
453
+ end
454
+
455
+ ##
456
+ # Scans for emails and delivers them every delay seconds. Only returns if
457
+ # once is true.
458
+
459
+ def run
460
+ install_signal_handlers
461
+
462
+ loop do
463
+ now = Time.now
464
+ begin
465
+ cleanup
466
+ emails = find_emails
467
+ deliver(emails) unless emails.empty?
468
+ rescue ActiveRecord::Transactions::TransactionError
469
+ end
470
+ break if @once
471
+ sleep @delay if now + @delay > Time.now
472
+ end
473
+ end
474
+
475
+ ##
476
+ # Proxy to ActionMailer::Base::smtp_settings. See
477
+ # http://api.rubyonrails.org/classes/ActionMailer/Base.html
478
+ # for instructions on how to configure ActionMailer's SMTP server.
479
+ #
480
+ # Falls back to ::server_settings if ::smtp_settings doesn't exist for
481
+ # backwards compatibility.
482
+
483
+ def smtp_settings
484
+ ActionMailer::Base.smtp_settings rescue ActionMailer::Base.server_settings
485
+ end
486
+
487
+ end