# Based on original code from the RubyOnRails project. # http://www.rubyonrails.com # Copyright (c) 2004 David Heinemeier Hansson require 'net/smtp' require 'glue/attribute' require 'nitro/template' module Nitro # Encapsulates an email message. class Mail # Sender, can be an array. attr_accessor :from # The list of the recipients, can be arrays. attr_accessor :to, :cc, :bcc # The subject attr_accessor :subject # The body of the message. attr_accessor :body # Reply to. attr_accessor :reply_to # Sent on attr_accessor :sent_on # Encode the subject? attr_accessor :encode_subject # The charset used to encode the message. attr_accessor :charset # Additional headers attr_accessor :headers def initialize(from = nil, to = nil, subject = nil, body = nil) @from, @to, @subject, @body = from, to, subject, body @headers = {} end def [](key) @headers[key] end def []=(key, value) @headers[key] = value end # Returns the Mail message in encoded format. def encoded raise 'No body defined' unless @body raise 'No sender defined' unless @from raise 'No recipients defined' unless @to # gmosx: From is typically NOT an array. from = @from.is_a?(Array) ? @from.join(', ') : @from buf = "From: #{from}\n" to = @to.is_a?(Array) ? @to.join(', ') : @to buf << "To: #{to}\n" if @cc cc = @cc.is_a?(Array) ? @cc.join(', ') : @cc buf << "Cc: #{cc}\n" end if @bcc bcc = @bcc.is_a?(Array) ? @bcc.join(', ') : @bcc buf << "Bcc: #{bcc}\n" end buf << "Subject: #@subject\n" if @subject buf << "\n" buf << @body return buf end end module MailerMixin def self.append_features(base) # :nodoc: super base.extend(SingletonMethods) end module SingletonMethods def mailer(klass) end end end # A flexible mailing service. #-- # TODO: add callback/observers support. #++ class Mailer < Mail # The mail server configuration. cattr_accessor :server; @@server = { :address => 'localhost', :port => 25, :domain => 'localhost.localdomain', :username => nil, :password => nil, :authentication => nil } # The delivery method. The following options are # supported: # # * :smtp # * :sendmail # * :test cattr_accessor :delivery_method; @@delivery_method = :smtp # The encode subject. cattr_accessor :encode_subject; @@encode_subject = false # true # The default charset. cattr_accessor :default_charset, 'utf-8' # An array to store the delivered mails, useful # for testing. cattr_accessor :deliveries; @@deliveries = [] # Disable deliveries, useful for testing. cattr_accessor :disable_deliveries, false # The root directory where the templates reside attr_accessor :template_root def initialize(from = nil, to = nil, subject = nil, body = FileTemplate.new) super @charset = @@default_charset.dup @encode_subject = @@encode_subject @template_root = 'public' end class << self def method_missing(method_symbol, *params) #:nodoc: case method_symbol.id2name when /^create_([_a-z]*)/ create_from_method($1, *params) when /^deliver_([_a-z]*)/ begin deliver(send("create_" + $1, *params)) rescue Object => e raise e # FIXME end end end def mail(from, to, subject, body, timestamp = nil, headers = {}, encode = @@encode_subject, charset = @@default_charset) #:nodoc: deliver(create(from, to, subject, body, timestamp, headers, charset)) end def create(from, to, subject, body, timestamp = nil, headers = {}, encode = @@encode_subject, charset = @@default_charset) #:nodoc: m = Mail.new m.to, m.subject, m.body, m.from = to, ( encode ? quoted_printable(subject, charset) : subject ), body, from # m.date = timestamp.respond_to?("to_time") ? timestamp.to_time : (timestamp || Time.now) # m.set_content_type "text", "plain", { "charset" => charset } headers.each do |k, v| m[k] = v end return m end def deliver(mail) #:nodoc: send("perform_delivery_#{delivery_method}", mail) unless disable_deliveries end def quoted_printable(text, charset) #:nodoc: text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" ) "=?#{charset}?Q?#{text}?=" end private def create_from_method(method_name, *params) mailer = new mailer.send(method_name, *params) unless mailer.body.is_a?(String) mailer.body = render_body(method_name, mailer) end mail = create( mailer.from, mailer.to, mailer.subject, mailer.body, mailer.sent_on, mailer.headers, mailer.charset ) mail.cc = mailer.cc if mailer.cc mail.bcc = mailer.bcc if mailer.bcc return mail end # Render the body by expanfing the template def render_body(method_name, mailer) mailer.body.template_filename = "#{mailer.template_root}/#{method_name.to_s}.xhtml" return mailer.body.process end # Deliver emails using SMTP. def perform_delivery_smtp(mail) # :nodoc: c = @@server Net::SMTP.start(c[:address], c[:port], c[:domain], c[:username], c[:password], c[:authentication]) do |smtp| smtp.send_message(mail.encoded, mail.from, mail.to) end end # Deliver emails using sendmail. def perform_delivery_sendmail(mail) # :nodoc: IO.popen('/usr/sbin/sendmail -i -t', 'w+') do |sm| sm.print(mail.encoded) sm.flush end end # Used for testing, does not actually send the # mail. def perform_delivery_test(mail) # :nodoc: deliveries << mail end end end end # * George Moschovitis