module Blather
##
# Stanza errors
# RFC3920 Section 9.3 (http://xmpp.org/rfcs/rfc3920.html#stanzas-error)
class StanzaError < BlatherError
VALID_TYPES = [:cancel, :continue, :modify, :auth, :wait]
class_inheritable_accessor :err_name
@@registrations = {}
register :stanza_error
attr_reader :original, :type, :text, :extras
##
# Register the handler and type to simplify importing
def self.register(handler, err_name)
super handler
self.err_name = err_name
@@registrations[err_name] = self
end
##
# Retreive an error class from a given name
def self.class_from_registration(err_name)
@@registrations[err_name.to_s] || self
end
##
# Factory method for instantiating the proper class
# for the error
def self.import(node)
original = node.copy
original.remove_child 'error'
error_node = node.find_first 'child::error'
name = error_node.find_first('child::*[name()!="text"]', 'urn:ietf:params:xml:ns:xmpp-stanzas').element_name
type = error_node['type']
text = node.find_first 'descendant::text', 'urn:ietf:params:xml:ns:xmpp-stanzas'
text = text.content if text
extras = error_node.find("descendant::*[name()!='text' and name()!='#{name}']").map { |n| n }
class_from_registration(name).new original, type, text, extras
end
##
# original An original node must be provided for stanza errors. You can't declare
# a stanza error on without a stanza.
# type is the error type specified in RFC3920 (http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.3.2)
# text is an option error description
# extras an array of application specific nodes to add to the error. These should be properly namespaced.
def initialize(original, type, text = nil, extras = [])
@original = original
self.type = type
@text = text
@extras = extras
end
##
# XMPP defined error name
def err_name
self.class.err_name
end
##
# Set the error type (see RFC3920 Section 9.3.2 (http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.3.2))
def type=(type)
type = type.to_sym
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if !VALID_TYPES.include?(type)
@type = type
end
##
# Creates an XML node from the error
def to_node
node = self.original.reply
error_node = XMPPNode.new 'error'
err = XMPPNode.new(self.err_name)
err.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
error_node << err
if self.text
text = XMPPNode.new('text')
text.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
text << self.text
error_node << text
end
self.extras.each do |extra|
extra_copy = extra.copy
extra_copy.namespace = extra.namespace
error_node << extra_copy
end
node << error_node
node.type = 'error'
node
end
##
# Turns the object into XML fit to be sent over the stream
def to_xml
to_node.to_s
end
def inspect # :nodoc:
"Stanza Error (#{self.err_name}): #{self.text}"
end
alias_method :to_s, :inspect # :nodoc:
##
# The sender has sent XML that is malformed or that cannot be processed (e.g., an IQ stanza that includes
# an unrecognized value of the 'type' attribute); the associated error type SHOULD be "modify"
class BadRequest < StanzaError
register :stanza_bad_request_error, 'bad-request'
end
##
# Access cannot be granted because an existing resource or session exists with the same name or address;
# the associated error type SHOULD be "cancel"
class Conflict < StanzaError
register :stanza_conflict_error, 'conflict'
end
##
# the feature requested is not implemented by the recipient or server and therefore cannot be processed;
# the associated error type SHOULD be "cancel".
class FeatureNotImplemented < StanzaError
register :stanza_feature_not_implemented_error, 'feature-not-implemented'
end
##
# the requesting entity does not possess the required permissions to perform the action;
# the associated error type SHOULD be "auth".
class Forbidden < StanzaError
register :stanza_forbidden_error, 'forbidden'
end
##
# the recipient or server can no longer be contacted at this address (the error stanza MAY contain a new address
# in the XML character data of the element); the associated error type SHOULD be "modify".
class Gone < StanzaError
register :stanza_gone_error, 'gone'
end
##
# the server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error;
# the associated error type SHOULD be "wait".
class InternalServerError < StanzaError
register :stanza_internal_server_error, 'internal-server-error'
end
##
# the addressed JID or item requested cannot be found; the associated error type SHOULD be "cancel".
class ItemNotFound < StanzaError
register :stanza_item_not_found_error, 'item-not-found'
end
##
# the addressed JID or item requested cannot be found; the associated error type SHOULD be "cancel".
class JidMalformed < StanzaError
register :stanza_jid_malformed_error, 'jid-malformed'
end
##
# the recipient or server understands the request but is refusing to process it because it does not meet criteria defined
# by the recipient or server (e.g., a local policy regarding acceptable words in messages); the associated error type SHOULD be "modify".
class NotAcceptable < StanzaError
register :stanza_not_acceptable_error, 'not-acceptable'
end
##
# The recipient or server does not allow any entity to perform the action; the associated error type SHOULD be "cancel".
class NotAllowed < StanzaError
register :stanza_not_allowed_error, 'not-allowed'
end
##
# the sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials;
# the associated error type SHOULD be "auth".
class NotAuthorized < StanzaError
register :stanza_not_authorized_error, 'not-authorized'
end
##
# the requesting entity is not authorized to access the requested service because payment is required; the associated error type SHOULD be "auth".
class PaymentRequired < StanzaError
register :stanza_payment_required_error, 'payment-required'
end
##
# the intended recipient is temporarily unavailable; the associated error type SHOULD be "wait" (note: an application MUST NOT
# return this error if doing so would provide information about the intended recipient's network availability to an entity that
# is not authorized to know such information).
class RecipientUnavailable < StanzaError
register :stanza_recipient_unavailable_error, 'recipient-unavailable'
end
##
# the recipient or server is redirecting requests for this information to another entity, usually temporarily (the error stanza SHOULD contain
# the alternate address, which MUST be a valid JID, in the XML character data of the element); the associated error type SHOULD be "modify".
class Redirect < StanzaError
register :stanza_redirect_error, 'redirect'
end
##
# the requesting entity is not authorized to access the requested service because registration is required; the associated error type SHOULD be "auth".
class RegistrationRequired < StanzaError
register :stanza_registration_required_error, 'registration-required'
end
##
# a remote server or service specified as part or all of the JID of the intended recipient does not exist; the associated error type SHOULD be "cancel".
class RemoteServerNotFound < StanzaError
register :stanza_remote_server_not_found_error, 'remote-server-not-found'
end
##
# a remote server or service specified as part or all of the JID of the intended recipient (or required to fulfill a request) could not be
# contacted within a reasonable amount of time; the associated error type SHOULD be "wait".
class RemoteServerTimeout < StanzaError
register :stanza_remote_server_timeout_error, 'remote-server-timeout'
end
##
# the server or recipient lacks the system resources necessary to service the request; the associated error type SHOULD be "wait".
class ResourceConstraint < StanzaError
register :stanza_resource_constraint_error, 'resource-constraint'
end
##
# the server or recipient does not currently provide the requested service; the associated error type SHOULD be "cancel".
class ServiceUnavailable < StanzaError
register :stanza_service_unavailable_error, 'service-unavailable'
end
##
# the requesting entity is not authorized to access the requested service because a subscription is required; the associated error type SHOULD be "auth".
class SubscriptionRequired < StanzaError
register :stanza_subscription_required_error, 'subscription-required'
end
##
# the error condition is not one of those defined by the other conditions in this list; any error type may be associated with this condition,
# and it SHOULD be used only in conjunction with an application-specific condition.
class UndefinedCondition < StanzaError
register :stanza_undefined_condition_error, 'undefined-condition'
end
##
# the recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order);
# the associated error type SHOULD be "wait".
class UnexpectedRequest < StanzaError
register :stanza_unexpected_request_error, 'unexpected-request'
end
end #StanzaError
end #Blather