/lib/howlr/message.rb
Ruby | 201 lines | 127 code | 37 blank | 37 comment | 13 complexity | af869b8bf67ec47cc004f329e72c4608 MD5 | raw file
- # This file is part of Howlr.
- #
- # Howlr is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # Howlr is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with Howlr. If not, see <http://www.gnu.org/licenses/>.
- require 'howlr/deliverers'
- require 'restr'
- class Howlr::Message
-
- attr_accessor :from, :subject, :body, :author, :content_type
- attr_accessor :callback_url, :callback_method,
- :callback_username, :callback_password
- attr_reader :recipients, :id, :sent, :delivery_errors
-
- # Initializes a new message with the given data.
- # <tt>params</tt> should be a hash with values for the various Message attributes.
- def initialize(data)
- self.from = data[:from]
- self.subject = data[:subject]
- self.body = data[:body]
- self.author = data[:author]
- self.content_type = data[:content_type] || 'text/plain'
-
- self.callback_url = data[:callback_url]
- self.callback_method = data[:callback_method]
- self.callback_username = data[:callback_username]
- self.callback_password = data[:callback_password]
-
- @recipients = parse_recipients(data[:recipients])
- @id = "#{Time.now.to_i}#{rand(9999999)}".to_i
- @delivery_errors = []
- @sent = nil
- end
-
- def parse_recipients(raw_recipients)
- $LOG.debug "Parsing recipients: #{raw_recipients.inspect}"
-
- raw_recipients = raw_recipients.split(/[,;]/) if raw_recipients.kind_of?(String)
- recipients = []
- raw_recipients.each do |raw|
- recipients << Recipient.parse(raw)
- end
-
- return recipients
- end
-
- # Delivers the message to each recipient, stores the result of the overall delivery
- # in @sent, and triggers the callback if a callback_url is present.
- #
- # After running this, @sent will contain one of the following values:
- # nil :: Nothing was not sent (the recipients list is probably empty)
- # Time :: The message was sent successfully to all recipients at this time
- # false :: There was a problem sending the message to at least one recipient.
- # Check delivery_errors for more information about what caused the failure.
- def send
- recipients.each do |r|
- next if r.nil?
-
- msg = self.dup
- msg.instance_variable_set(:@recipients, [r])
-
- $LOG.debug "Sending: #{msg.inspect}"
-
- result = r.deliverer.deliver(msg)
- @sent = Time.now.to_s if (result && (@sent || @sent.nil?))
-
- begin
- callback(r, result) unless callback_url.nil? || callback_url.blank?
- rescue => e
- $LOG.error("Error during callback: #{e}")
- e2 = CallbackError.new("Error during callback: #{e}")
- e2.underlying_error = e
- raise e2
- end
- end
- end
-
- # If the message has a callback_url, this callback method is triggered
- # for each delivery attempt (i.e. for every recipient, when #send is
- # called).
- #
- # The callback sends a REST request to the given callback_url with
- # the given callback_method, submitting information about the result
- # of the delivery attempt. Have a look at this method's body for
- # information about what data is submitted with the request.
- def callback(recipient, result)
- auth = {}
- auth[:username] = callback_username if callback_username
- auth[:password] = callback_password if callback_password
-
- data = {}
- data[:message_id] = id
- data[:recipient_address] = recipient.address
- data[:recipient_protocol] = recipient.deliverer.protocol
- data[:send_success] = result
- data[:send_datetime] = sent
- data[:send_timestamp] = sent.to_i if sent.kind_of? Time
- data[:delivery_errors] = delivery_errors.join("; ")
-
- Restr.do(callback_method, callback_url, data, auth)
- end
-
- # Serializes this Message into XML and returns a Builder::XmlMarkup
- # object.
- #
- # You should be able to call #to_s on the returned Builder::XmlMarkup
- # object to get the XML as a String.
- def to_xml(options = {})
- xml = options[:builder] ||= Builder::XmlMarkup.new(options)
- xml.instruct! unless options[:skip_instruct]
-
- xml.message(id ? {:id => id} : {}) do
- xml.recipients do
- recipients.each{|r| r.to_xml(options)}
- end
-
- h = {:from => from, :subject => subject, :author => author}
- h.each do |k,v|
- xml.tag!(k, v)
- end
-
- xml.sent sent
-
- unless sent.nil?
- xml.delivery_errors do
- delivery_errors.each do |err|
- xml.delivery_error err
- end
- end
- end
-
- xml.body {xml.cdata! body}
- end
- end
-
-
- class Recipient
-
- attr_accessor :deliverer, :address
-
- def initialize(deliverer, address)
- @deliverer = deliverer
- @address = address
- end
-
-
- def self.parse(raw)
- return nil if raw.nil?
-
- raw =~ /^(?:(\w*):)?(.*)/
-
- if $~[1]
- protocol = $~[1].intern
- address = $~[2]
- elsif $~
- protocol = :mailto
- address = raw
- else
- raise ArgumentError, "#{raw.inspect} is not a valid address."
- end
-
- $LOG.debug "Parsing raw recipient: #{raw.inspect} --> protocol: #{protocol.inspect}, address: #{address.inspect}"
-
- case protocol
- when :mailto
- deliverer = Howlr::Deliverers::Email
- else
- raise ArgumentError, "#{protocol.inspect} is not a valid delivery protocol!"
- end
-
- return Recipient.new(deliverer, address)
- end
-
- def to_s
- "#{deliverer.protocol}:#{address}"
- end
-
- def to_xml(options = {})
- if b = options[:builder]
- b.recipient to_s
- else
- "<recipient>#{to_s}</recipient>"
- end
- end
- end
-
- class CallbackError < Exception
- attr_accessor :underlying_error
- end
- end