humanzz-ar_mailer 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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