class Knjappserver attr_reader :mails_waiting def initialize_mailing require "knj/autoload/ping" @mails_waiting = [] @mails_mutex = Mutex.new @mails_queue_mutex = Mutex.new @mails_timeout = self.timeout(:time => 10) do self.mail_flush end end #Queue a mail for sending. Possible keys are: :subject, :from, :to, :text and :html. def mail(mail_args) raise "'smtp_args' has not been given for the Knjappserver." if !@config[:smtp_args] @mails_queue_mutex.synchronize do count_wait = 0 while @mails_waiting.length > 100 if count_wait >= 30 raise "Could not send email - too many emails was pending and none of them were being sent?" end count_wait += 1 sleep 1 end mailobj = Knjappserver::Mail.new({:kas => self, :errors => {}, :status => :waiting}.merge(mail_args)) STDOUT.print "Added mail '#{mailobj.__id__}' to the mail-send-queue.\n" if debug @mails_waiting << mailobj return mailobj end end #Sends all queued mails to the respective servers, if we are online. def mail_flush @mails_mutex.synchronize do return false if @mails_waiting.length <= 0 status = Ping.pingecho("google.dk", 10, 80) if !status STDOUT.print "We are not online - skipping mail flush.\n" return false #Dont run if we dont have a connection to the internet and then properly dont have a connection to the SMTP as well. end begin #Use subprocessing to avoid the mail-framework (activesupport and so on, also possible memory leaks in those large frameworks). require "knj/process_meta" subproc = Knj::Process_meta.new("debug" => @debug, "debug_err" => true, "id" => "knjappserver_mailing") subproc.static("Object", "require", "rubygems") subproc.static("Object", "require", "mail") subproc.static("Object", "require", "#{@config[:knjrbfw_path]}knjrbfw") subproc.static("Object", "require", "knj/autoload") STDOUT.print "Flushing emails." if @debug @mails_waiting.each do |mail| begin STDOUT.print "Sending email: #{mail.__id__}\n" if @debug if mail.send("proc" => subproc) STDOUT.print "Email sent: #{mail.__id__}\n" if @debug @mails_waiting.delete(mail) end rescue Timeout::Error #ignore - rescue => e @mails_waiting.delete(mail) self.handle_error(e, {:email => false}) end sleep 1 #sleep so we dont take up too much bandwidth. end ensure subproc.destroy if subproc subproc = nil end end end #This class represents the queued mails. class Mail def initialize(args) @args = args raise "No knjappserver-object was given (as :kas)." if !@args[:kas].is_a?(Knjappserver) raise "No :to was given." if !@args[:to] raise "No content was given (:html or :text)." if !@args[:html] and !@args[:text] end #Returns a key from the arguments. def [](key) return @args[key] end #Sends the email to the receiver. def send(args = {}) STDOUT.print "Sending mail '#{__id__}'.\n" if @args[:kas].debug if @args[:from] from = @args[:from] elsif @args[:kas].config[:error_report_from] from = @args[:kas].config[:error_report_from] else raise "Dont know where to take the 'from'-paramter from - none given in appserver config or mail-method-arguments?" end if args["proc"] args["proc"].static("Object", "require", "knj/mailobj") mail = args["proc"].new("Knj::Mailobj", @args[:kas].config[:smtp_args]) mail._pm_send_noret("to=", @args[:to]) mail._pm_send_noret("subject=", @args[:subject]) if @args[:subject] mail._pm_send_noret("html=", Knj::Strings.email_str_safe(@args[:html])) if @args[:html] mail._pm_send_noret("text=", Knj::Strings.email_str_safe(@args[:text])) if @args[:text] mail._pm_send_noret("from=", from) mail._pm_send_noret("send") else require "knj/mailobj" mail = Knj::Mailobj.new(@args[:kas].config[:smtp_args]) mail.to = @args[:to] mail.subject = @args[:subject] if @args[:subject] mail.html = Knj::Strings.email_str_safe(@args[:html]) if @args[:html] mail.text = Knj::Strings.email_str_safe(@args[:text]) if @args[:text] mail.from = from mail.send end @args[:status] = :sent STDOUT.print "Sent email #{self.__id__}\n" if @args[:kas].debug return true rescue Exception => e if @args[:kas].debug STDOUT.print "Could not send email.\n" STDOUT.puts e.inspect STDOUT.puts e.backtrace end @args[:errors][e.class.name] = {:count => 0} if !@args[:errors].has_key?(e.class.name) @args[:errors][e.class.name][:count] += 1 raise e if @args[:errors][e.class.name][:count] >= 5 @args[:status] = :error @args[:error] = e return false end end end