0
+ unless respond_to? :path2class then
0
+ def self.path2class(path)
0
+ path.split(/::/).inject self do |k,n| k.const_get n end
0
+ unless instance_methods.include? 'reset' then
0
+ # Resets the SMTP connection.
0
+# Sequel::Sendmail delivers email from the email table to the
0
+# SMTP server configured in your application's config/init.rb.
0
+# sequel_sendmail does not work with sendmail delivery.
0
+# sequel_mailer can deliver to SMTP with TLS using smtp_tls.rb borrowed from Kyle
0
+# Maxwell's action_mailer_optional_tls plugin. Simply set the :tls option in
0
+# Merb::Mailer.config to true to enable TLS.
0
+# See sequel_sendmail -h for the full list of supported options.
0
+# The interesting options are:
0
+ # The version of Sequel::Sendmail you are running.
0
+ # Maximum number of times authentication will be consecutively retried
0
+ # Email delivery attempts per run
0
+ attr_accessor :batch_size
0
+ # Seconds to delay between runs
0
+ # Maximum age of emails in seconds before they are removed from the queue.
0
+ attr_accessor :max_age
0
+ attr_accessor :verbose
0
+ # Sequel class that holds emails
0
+ attr_reader :email_class
0
+ # True if only one delivery attempt will be made per call to run
0
+ # Times authentication has failed
0
+ attr_accessor :failed_auth_count
0
+ # Creates a new migration using +table_name+ and prints it on stdout.
0
+ def self.create_migration(table_name)
0
+class #{table_name.classify}Migration < Sequel::Migration
0
+ create_table "#{table_name.tableize}" do
0
+ integer :last_send_attempt, :default => 0
0
+ execute "DROP TABLE #{table_name.tableize}"
0
+ # Creates a new model using +table_name+ and prints it on stdout.
0
+ def self.create_model(table_name)
0
+class #{table_name.classify} < Sequel::Model
0
+ # Prints a list of unsent emails and the last delivery attempt, if any.
0
+ def self.mailq(table_name)
0
+ klass = table_name.split('::').inject(Object) { |k,n| k.const_get n }
0
+ puts "Mail queue is empty"
0
+ puts "-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------"
0
+ emails.each do |email|
0
+ size = email.mail.length
0
+ create_timestamp = email.created_on rescue
0
+ email.created_at rescue
0
+ Time.at(email.created_date) rescue # for Robot Co-op
0
+ created = if create_timestamp.nil? then
0
+ create_timestamp.strftime '%a %b %d %H:%M:%S'
0
+ puts "%10d %8d %s %s" % [email.id, size, created, email.from_address]
0
+ if email.last_send_attempt > 0 then
0
+ puts "Last send attempt: #{Time.at email.last_send_attempt}"
0
+ puts " #{email.to_address}"
0
+ puts "-- #{total_size/1024} Kbytes in #{emails.length} Requests."
0
+ # Processes command line options in +args+
0
+ def self.process_args(args)
0
+ name = File.basename $0
0
+ options[:Daemon] = false
0
+ options[:MaxAge] = 86400 * 7
0
+ options[:Once] = false
0
+ options[:MerbEnv] = ENV['MERB_ENV'] || "development"
0
+ options[:TableName] = 'Email'
0
+ opts = OptionParser.new do |opts|
0
+ opts.banner = "Usage: #{name} [options]"
0
+ opts.separator "#{name} scans the email table for new messages and sends them to the"
0
+ opts.separator "website's configured SMTP host."
0
+ opts.separator "#{name} must be run from a Merb application's root."
0
+ opts.separator 'Sendmail options:'
0
+ opts.on("-b", "--batch-size BATCH_SIZE",
0
+ "Maximum number of emails to send per delay",
0
+ "Default: Deliver all available emails", Integer) do |batch_size|
0
+ options[:BatchSize] = batch_size
0
+ opts.on( "--delay DELAY",
0
+ "Delay between checks for new mail",
0
+ "Default: #{options[:Delay]}", Integer) do |delay|
0
+ options[:Delay] = delay
0
+ opts.on( "--max-age MAX_AGE",
0
+ "Maxmimum age for an email. After this",
0
+ "it will be removed from the queue.",
0
+ "Set to 0 to disable queue cleanup.",
0
+ "Default: #{options[:MaxAge]} seconds", Integer) do |max_age|
0
+ options[:MaxAge] = max_age
0
+ opts.on("-o", "--once",
0
+ "Only check for new mail and deliver once",
0
+ "Default: #{options[:Once]}") do |once|
0
+ opts.on("-d", "--daemonize",
0
+ "Run as a daemon process",
0
+ "Default: #{options[:Daemon]}") do |daemon|
0
+ options[:Daemon] = true
0
+ "Display a list of emails waiting to be sent") do |mailq|
0
+ options[:MailQ] = true
0
+ opts.separator 'Setup Options:'
0
+ opts.on( "--create-migration",
0
+ "Prints a migration to add an Email table",
0
+ "to stdout") do |create|
0
+ options[:Migrate] = true
0
+ opts.on( "--create-model",
0
+ "Prints a model for an Email Sequel",
0
+ "object to stdout") do |create|
0
+ options[:Model] = true
0
+ opts.separator 'Generic Options:'
0
+ opts.on("-c", "--chdir PATH",
0
+ "Use PATH for the application path",
0
+ "Default: #{options[:Chdir]}") do |path|
0
+ usage opts, "#{path} is not a directory" unless File.directory? path
0
+ usage opts, "#{path} is not readable" unless File.readable? path
0
+ options[:Chdir] = path
0
+ opts.on("-e", "--environment MERB_ENV",
0
+ "Set the MERB_ENV constant",
0
+ "Default: #{options[:MerbEnv]}") do |env|
0
+ options[:MerbEnv] = env
0
+ opts.on("-t", "--table-name TABLE_NAME",
0
+ "Name of table holding emails",
0
+ "Used for both sendmail and",
0
+ "Default: #{options[:TableName]}") do |name|
0
+ options[:TableName] = name
0
+ opts.on("-v", "--[no-]verbose",
0
+ "Default: #{options[:Verbose]}") do |verbose|
0
+ options[:Verbose] = verbose
0
+ opts.on("-h", "--help",
0
+ "You're looking at it") do
0
+ return options if options.include? :Migrate or options.include? :Model
0
+ ENV['MERB_ENV'] = options[:MerbEnv]
0
+ Dir.chdir options[:Chdir] do
0
+ require 'config/init.rb'
0
+#{name} must be run from a Merb application's root to deliver email.
0
+#{Dir.pwd} does not appear to be a Merb application root.
0
+ # Processes +args+ and runs as appropriate
0
+ def self.run(args = ARGV)
0
+ options = process_args args
0
+ require "app" / "models" / "#{options[:TableName].underscore}.rb"
0
+ puts "DOING THIS THANG"
0
+ Merb.environment = options[:MerbEnv]
0
+ Merb.root = Merb::Config[:merb_root]
0
+ puts "IN ENV = #{Merb.environment}"
0
+ if options.include? :Migrate then
0
+ create_migration options[:TableName]
0
+ elsif options.include? :Model then
0
+ create_model options[:TableName]
0
+ elsif options.include? :MailQ then
0
+ mailq options[:TableName]
0
+ if options[:Daemon] then
0
+ require 'webrick/server'
0
+ rescue SignalException
0
+ $stderr.puts "Unhandled exception #{e.message}(#{e.class}):"
0
+ $stderr.puts "\t#{e.backtrace.join "\n\t"}"
0
+ # Prints a usage message to $stderr using +opts+ and exits
0
+ def self.usage(opts, message = nil)
0
+ # Creates a new Sequel::Sendmail.
0
+ # <tt>:BatchSize</tt>:: Maximum number of emails to send per delay
0
+ # <tt>:Delay</tt>:: Delay between deliver attempts
0
+ # <tt>:TableName</tt>:: Table name that stores the emails
0
+ # <tt>:Once</tt>:: Only attempt to deliver emails once when run is called
0
+ # <tt>:Verbose</tt>:: Be verbose.
0
+ def initialize(options = {})
0
+ options[:Delay] ||= 60
0
+ options[:TableName] ||= 'Email'
0
+ options[:MaxAge] ||= 86400 * 7
0
+ @batch_size = options[:BatchSize]
0
+ @delay = options[:Delay]
0
+ @email_class = Object.path2class options[:TableName]
0
+ @once = options[:Once]
0
+ @verbose = options[:Verbose]
0
+ @max_age = options[:MaxAge]
0
+ @failed_auth_count = 0
0
+ # Removes emails that have lived in the queue for too long. If max_age is
0
+ # set to 0, no emails will be removed.
0
+ return if @max_age == 0
0
+ timeout = Time.now - @max_age
0
+ conditions = ['last_send_attempt > 0 and created_on < ?', timeout]
0
+ mail = @email_class.destroy_all conditions
0
+ log "expired #{mail.length} emails from the queue"
0
+ # Delivers +emails+ to Merb::Mailer's SMTP server and destroys them.
0
+ user = Merb::Mailer.config[:user] || Merb::Mailer.config[:user_name]
0
+ Net::SMTP.start Merb::Mailer.config[:host],
0
+ Merb::Mailer.config[:port],
0
+ Merb::Mailer.config[:domain],
0
+ Merb::Mailer.config[:password],
0
+ Merb::Mailer.config[:auth],
0
+ Merb::Mailer.config[:tls] do |smtp|
0
+ @failed_auth_count = 0
0
+ until emails.empty? do
0
+ res = smtp.send_message email.mail, email.from_address, email.to_address
0
+ log "sent email %011d from %s to %s: %p" %
0
+ [email.id, email.from_address, email.to_address, res]
0
+ rescue Net::SMTPFatalError => e
0
+ log "5xx error sending email %d, removing from queue: %p(%s):\n\t%s" %
0
+ [email.id, e.message, e.class, e.backtrace.join("\n\t")]
0
+ rescue Net::SMTPServerBusy => e
0
+ log "server too busy, sleeping #{@delay} seconds"
0
+ rescue Net::SMTPUnknownError, Net::SMTPSyntaxError, TimeoutError => e
0
+ email.last_send_attempt = Time.now.to_i
0
+ log "error sending email %d: %p(%s):\n\t%s" %
0
+ [email.id, e.message, e.class, e.backtrace.join("\n\t")]
0
+ rescue Net::SMTPAuthenticationError => e
0
+ @failed_auth_count += 1
0
+ if @failed_auth_count >= MAX_AUTH_FAILURES then
0
+ log "authentication error, giving up: #{e.message}"
0
+ log "authentication error, retrying: #{e.message}"
0
+ rescue Net::SMTPServerBusy, SystemCallError, OpenSSL::SSL::SSLError
0
+ # ignore SMTPServerBusy/EPIPE/ECONNRESET from Net::SMTP.start's ensure
0
+ # Prepares ar_sendmail for exiting
0
+ log "caught signal, shutting down"
0
+ # Returns emails in email_class that haven't had a delivery attempt in the
0
+ options = { :conditions => ['last_send_attempt < ?', Time.now.to_i - 300] }
0
+ options[:limit] = batch_size unless batch_size.nil?
0
+ mail = @email_class.find :all, options
0
+ log "found #{mail.length} emails to send"
0
+ # Installs signal handlers to gracefully exit.
0
+ def install_signal_handlers
0
+ trap 'TERM' do do_exit end
0
+ trap 'INT' do do_exit end
0
+ # Logs +message+ if verbose
0
+ $stderr.puts message if @verbose
0
+ ActionMailer::Base.logger.info "ar_sendmail: #{message}"
0
+ # Scans for emails and delivers them every delay seconds. Only returns if
0
+ install_signal_handlers
0
+ sleep @delay if now + @delay > Time.now
Comments
No one has commented yet.