PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/howlr/message.rb

http://howlr.googlecode.com/
Ruby | 201 lines | 127 code | 37 blank | 37 comment | 13 complexity | af869b8bf67ec47cc004f329e72c4608 MD5 | raw file
  1. # This file is part of Howlr.
  2. #
  3. # Howlr is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # Howlr is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with Howlr. If not, see <http://www.gnu.org/licenses/>.
  15. require 'howlr/deliverers'
  16. require 'restr'
  17. class Howlr::Message
  18. attr_accessor :from, :subject, :body, :author, :content_type
  19. attr_accessor :callback_url, :callback_method,
  20. :callback_username, :callback_password
  21. attr_reader :recipients, :id, :sent, :delivery_errors
  22. # Initializes a new message with the given data.
  23. # <tt>params</tt> should be a hash with values for the various Message attributes.
  24. def initialize(data)
  25. self.from = data[:from]
  26. self.subject = data[:subject]
  27. self.body = data[:body]
  28. self.author = data[:author]
  29. self.content_type = data[:content_type] || 'text/plain'
  30. self.callback_url = data[:callback_url]
  31. self.callback_method = data[:callback_method]
  32. self.callback_username = data[:callback_username]
  33. self.callback_password = data[:callback_password]
  34. @recipients = parse_recipients(data[:recipients])
  35. @id = "#{Time.now.to_i}#{rand(9999999)}".to_i
  36. @delivery_errors = []
  37. @sent = nil
  38. end
  39. def parse_recipients(raw_recipients)
  40. $LOG.debug "Parsing recipients: #{raw_recipients.inspect}"
  41. raw_recipients = raw_recipients.split(/[,;]/) if raw_recipients.kind_of?(String)
  42. recipients = []
  43. raw_recipients.each do |raw|
  44. recipients << Recipient.parse(raw)
  45. end
  46. return recipients
  47. end
  48. # Delivers the message to each recipient, stores the result of the overall delivery
  49. # in @sent, and triggers the callback if a callback_url is present.
  50. #
  51. # After running this, @sent will contain one of the following values:
  52. # nil :: Nothing was not sent (the recipients list is probably empty)
  53. # Time :: The message was sent successfully to all recipients at this time
  54. # false :: There was a problem sending the message to at least one recipient.
  55. # Check delivery_errors for more information about what caused the failure.
  56. def send
  57. recipients.each do |r|
  58. next if r.nil?
  59. msg = self.dup
  60. msg.instance_variable_set(:@recipients, [r])
  61. $LOG.debug "Sending: #{msg.inspect}"
  62. result = r.deliverer.deliver(msg)
  63. @sent = Time.now.to_s if (result && (@sent || @sent.nil?))
  64. begin
  65. callback(r, result) unless callback_url.nil? || callback_url.blank?
  66. rescue => e
  67. $LOG.error("Error during callback: #{e}")
  68. e2 = CallbackError.new("Error during callback: #{e}")
  69. e2.underlying_error = e
  70. raise e2
  71. end
  72. end
  73. end
  74. # If the message has a callback_url, this callback method is triggered
  75. # for each delivery attempt (i.e. for every recipient, when #send is
  76. # called).
  77. #
  78. # The callback sends a REST request to the given callback_url with
  79. # the given callback_method, submitting information about the result
  80. # of the delivery attempt. Have a look at this method's body for
  81. # information about what data is submitted with the request.
  82. def callback(recipient, result)
  83. auth = {}
  84. auth[:username] = callback_username if callback_username
  85. auth[:password] = callback_password if callback_password
  86. data = {}
  87. data[:message_id] = id
  88. data[:recipient_address] = recipient.address
  89. data[:recipient_protocol] = recipient.deliverer.protocol
  90. data[:send_success] = result
  91. data[:send_datetime] = sent
  92. data[:send_timestamp] = sent.to_i if sent.kind_of? Time
  93. data[:delivery_errors] = delivery_errors.join("; ")
  94. Restr.do(callback_method, callback_url, data, auth)
  95. end
  96. # Serializes this Message into XML and returns a Builder::XmlMarkup
  97. # object.
  98. #
  99. # You should be able to call #to_s on the returned Builder::XmlMarkup
  100. # object to get the XML as a String.
  101. def to_xml(options = {})
  102. xml = options[:builder] ||= Builder::XmlMarkup.new(options)
  103. xml.instruct! unless options[:skip_instruct]
  104. xml.message(id ? {:id => id} : {}) do
  105. xml.recipients do
  106. recipients.each{|r| r.to_xml(options)}
  107. end
  108. h = {:from => from, :subject => subject, :author => author}
  109. h.each do |k,v|
  110. xml.tag!(k, v)
  111. end
  112. xml.sent sent
  113. unless sent.nil?
  114. xml.delivery_errors do
  115. delivery_errors.each do |err|
  116. xml.delivery_error err
  117. end
  118. end
  119. end
  120. xml.body {xml.cdata! body}
  121. end
  122. end
  123. class Recipient
  124. attr_accessor :deliverer, :address
  125. def initialize(deliverer, address)
  126. @deliverer = deliverer
  127. @address = address
  128. end
  129. def self.parse(raw)
  130. return nil if raw.nil?
  131. raw =~ /^(?:(\w*):)?(.*)/
  132. if $~[1]
  133. protocol = $~[1].intern
  134. address = $~[2]
  135. elsif $~
  136. protocol = :mailto
  137. address = raw
  138. else
  139. raise ArgumentError, "#{raw.inspect} is not a valid address."
  140. end
  141. $LOG.debug "Parsing raw recipient: #{raw.inspect} --> protocol: #{protocol.inspect}, address: #{address.inspect}"
  142. case protocol
  143. when :mailto
  144. deliverer = Howlr::Deliverers::Email
  145. else
  146. raise ArgumentError, "#{protocol.inspect} is not a valid delivery protocol!"
  147. end
  148. return Recipient.new(deliverer, address)
  149. end
  150. def to_s
  151. "#{deliverer.protocol}:#{address}"
  152. end
  153. def to_xml(options = {})
  154. if b = options[:builder]
  155. b.recipient to_s
  156. else
  157. "<recipient>#{to_s}</recipient>"
  158. end
  159. end
  160. end
  161. class CallbackError < Exception
  162. attr_accessor :underlying_error
  163. end
  164. end