blather 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +2 -0
 - data/README.rdoc +54 -29
 - data/Rakefile +94 -13
 - data/VERSION.yml +4 -0
 - data/examples/drb_client.rb +2 -4
 - data/examples/echo.rb +13 -8
 - data/examples/pubsub/cli.rb +64 -0
 - data/examples/pubsub/ping_pong.rb +18 -0
 - data/examples/pubsub/pubsub_dsl.rb +52 -0
 - data/examples/pubsub_client.rb +39 -0
 - data/examples/rosterprint.rb +14 -0
 - data/examples/xmpp4r/echo.rb +35 -0
 - data/ext/extconf.rb +65 -0
 - data/lib/blather.rb +18 -121
 - data/lib/blather/client.rb +13 -0
 - data/lib/blather/client/client.rb +165 -0
 - data/lib/blather/client/dsl.rb +99 -0
 - data/lib/blather/client/pubsub.rb +53 -0
 - data/lib/blather/client/pubsub/node.rb +27 -0
 - data/lib/blather/core_ext/active_support.rb +1 -0
 - data/lib/blather/core_ext/libxml.rb +7 -1
 - data/lib/blather/errors.rb +39 -18
 - data/lib/blather/errors/sasl_error.rb +87 -0
 - data/lib/blather/errors/stanza_error.rb +262 -0
 - data/lib/blather/errors/stream_error.rb +253 -0
 - data/lib/blather/jid.rb +9 -16
 - data/lib/blather/roster.rb +9 -0
 - data/lib/blather/roster_item.rb +7 -4
 - data/lib/blather/stanza.rb +19 -25
 - data/lib/blather/stanza/disco.rb +9 -0
 - data/lib/blather/stanza/disco/disco_info.rb +84 -0
 - data/lib/blather/stanza/disco/disco_items.rb +59 -0
 - data/lib/blather/stanza/iq.rb +16 -4
 - data/lib/blather/stanza/iq/query.rb +6 -4
 - data/lib/blather/stanza/iq/roster.rb +38 -38
 - data/lib/blather/stanza/pubsub.rb +33 -0
 - data/lib/blather/stanza/pubsub/affiliations.rb +52 -0
 - data/lib/blather/stanza/pubsub/errors.rb +9 -0
 - data/lib/blather/stanza/pubsub/event.rb +21 -0
 - data/lib/blather/stanza/pubsub/items.rb +59 -0
 - data/lib/blather/stanza/pubsub/owner.rb +9 -0
 - data/lib/blather/stanza/pubsub/subscriptions.rb +57 -0
 - data/lib/blather/stream.rb +125 -57
 - data/lib/blather/stream/client.rb +26 -0
 - data/lib/blather/stream/component.rb +34 -0
 - data/lib/blather/stream/parser.rb +17 -27
 - data/lib/blather/stream/resource.rb +21 -24
 - data/lib/blather/stream/sasl.rb +60 -37
 - data/lib/blather/stream/session.rb +12 -19
 - data/lib/blather/stream/stream_handler.rb +39 -0
 - data/lib/blather/stream/tls.rb +22 -18
 - data/lib/blather/xmpp_node.rb +91 -17
 - data/spec/blather/core_ext/libxml_spec.rb +58 -0
 - data/spec/blather/errors/sasl_error_spec.rb +56 -0
 - data/spec/blather/errors/stanza_error_spec.rb +148 -0
 - data/spec/blather/errors/stream_error_spec.rb +114 -0
 - data/spec/blather/errors_spec.rb +40 -0
 - data/spec/blather/jid_spec.rb +0 -7
 - data/spec/blather/roster_item_spec.rb +5 -0
 - data/spec/blather/roster_spec.rb +6 -6
 - data/spec/blather/stanza/discos/disco_info_spec.rb +207 -0
 - data/spec/blather/stanza/discos/disco_items_spec.rb +136 -0
 - data/spec/blather/stanza/iq/query_spec.rb +9 -2
 - data/spec/blather/stanza/iq/roster_spec.rb +117 -1
 - data/spec/blather/stanza/iq_spec.rb +29 -0
 - data/spec/blather/stanza/presence/subscription_spec.rb +12 -1
 - data/spec/blather/stanza/presence_spec.rb +29 -0
 - data/spec/blather/stanza/pubsub/affiliations_spec.rb +46 -0
 - data/spec/blather/stanza/pubsub/items_spec.rb +59 -0
 - data/spec/blather/stanza/pubsub/subscriptions_spec.rb +63 -0
 - data/spec/blather/stanza/pubsub_spec.rb +26 -0
 - data/spec/blather/stanza_spec.rb +13 -1
 - data/spec/blather/stream/client_spec.rb +787 -0
 - data/spec/blather/stream/component_spec.rb +86 -0
 - data/spec/blather/xmpp_node_spec.rb +75 -22
 - data/spec/fixtures/pubsub.rb +157 -0
 - data/spec/spec_helper.rb +6 -14
 - metadata +86 -74
 - data/CHANGELOG +0 -5
 - data/Manifest +0 -47
 - data/blather.gemspec +0 -41
 - data/lib/blather/stanza/error.rb +0 -31
 - data/spec/blather/stream_spec.rb +0 -462
 - data/spec/build_safe.rb +0 -20
 
| 
         @@ -0,0 +1,253 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Blather
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ##
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Stream Errors
         
     | 
| 
      
 5 
     | 
    
         
            +
            # RFC3920 Section 9.3 (http://xmpp.org/rfcs/rfc3920.html#streams-error-rules)
         
     | 
| 
      
 6 
     | 
    
         
            +
            class StreamError < BlatherError
         
     | 
| 
      
 7 
     | 
    
         
            +
              class_inheritable_accessor :err_name
         
     | 
| 
      
 8 
     | 
    
         
            +
              @@registrations = {}
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              register :stream_error
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              attr_reader :text, :extras
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              ##
         
     | 
| 
      
 15 
     | 
    
         
            +
              # Register the handler and type to simplify importing
         
     | 
| 
      
 16 
     | 
    
         
            +
              def self.register(handler, err_name)
         
     | 
| 
      
 17 
     | 
    
         
            +
                super handler
         
     | 
| 
      
 18 
     | 
    
         
            +
                self.err_name = err_name
         
     | 
| 
      
 19 
     | 
    
         
            +
                @@registrations[err_name] = self
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              ##
         
     | 
| 
      
 23 
     | 
    
         
            +
              # Retreive an error class from a given name
         
     | 
| 
      
 24 
     | 
    
         
            +
              def self.class_from_registration(err_name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                @@registrations[err_name.to_s] || self
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              ##
         
     | 
| 
      
 29 
     | 
    
         
            +
              # Factory method for instantiating the proper class
         
     | 
| 
      
 30 
     | 
    
         
            +
              # for the error
         
     | 
| 
      
 31 
     | 
    
         
            +
              def self.import(node)
         
     | 
| 
      
 32 
     | 
    
         
            +
                name = node.find_first('descendant::*[name()!="text"]', 'urn:ietf:params:xml:ns:xmpp-streams').element_name
         
     | 
| 
      
 33 
     | 
    
         
            +
                text = node.find_first 'descendant::text', 'urn:ietf:params:xml:ns:xmpp-streams'
         
     | 
| 
      
 34 
     | 
    
         
            +
                text = text.content if text
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                extras = node.find("descendant::*[name()!='text' and name()!='#{name}']").map { |n| n }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                class_from_registration(name).new text, extras
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              ##
         
     | 
| 
      
 42 
     | 
    
         
            +
              # <tt>text</tt> is the (optional) error message.
         
     | 
| 
      
 43 
     | 
    
         
            +
              # <tt>extras</tt> should be an array of nodes to attach to the error
         
     | 
| 
      
 44 
     | 
    
         
            +
              # each extra should be in an application specific namespace
         
     | 
| 
      
 45 
     | 
    
         
            +
              # see RFC3920 Section 4.7.2 (http://xmpp.org/rfcs/rfc3920.html#rfc.section.4.7.2)
         
     | 
| 
      
 46 
     | 
    
         
            +
              def initialize(text = nil, extras = [])
         
     | 
| 
      
 47 
     | 
    
         
            +
                @text = text
         
     | 
| 
      
 48 
     | 
    
         
            +
                @extras = extras
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              ##
         
     | 
| 
      
 52 
     | 
    
         
            +
              # XMPP defined error name
         
     | 
| 
      
 53 
     | 
    
         
            +
              def err_name
         
     | 
| 
      
 54 
     | 
    
         
            +
                self.class.err_name
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              ##
         
     | 
| 
      
 58 
     | 
    
         
            +
              # Creates an XML node from the error
         
     | 
| 
      
 59 
     | 
    
         
            +
              def to_node
         
     | 
| 
      
 60 
     | 
    
         
            +
                node = XMPPNode.new('stream:error')
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                err = XMPPNode.new(self.err_name)
         
     | 
| 
      
 63 
     | 
    
         
            +
                err.namespace = 'urn:ietf:params:xml:ns:xmpp-streams'
         
     | 
| 
      
 64 
     | 
    
         
            +
                node << err
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                if self.text
         
     | 
| 
      
 67 
     | 
    
         
            +
                  text = XMPPNode.new('text')
         
     | 
| 
      
 68 
     | 
    
         
            +
                  text.namespace = 'urn:ietf:params:xml:ns:xmpp-streams'
         
     | 
| 
      
 69 
     | 
    
         
            +
                  text << self.text
         
     | 
| 
      
 70 
     | 
    
         
            +
                  node << text
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                self.extras.each do |extra|
         
     | 
| 
      
 74 
     | 
    
         
            +
                  extra_copy = extra.copy
         
     | 
| 
      
 75 
     | 
    
         
            +
                  extra_copy.namespace = extra.namespace
         
     | 
| 
      
 76 
     | 
    
         
            +
                  node << extra_copy
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
                node
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              ##
         
     | 
| 
      
 82 
     | 
    
         
            +
              # Turns the object into XML fit to be sent over the stream
         
     | 
| 
      
 83 
     | 
    
         
            +
              def to_xml
         
     | 
| 
      
 84 
     | 
    
         
            +
                to_node.to_s
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
              def inspect # :nodoc:
         
     | 
| 
      
 88 
     | 
    
         
            +
                "Stream Error (#{self.err_name}): #{self.text}"
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
              alias_method :to_s, :inspect # :nodoc:
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
              ##
         
     | 
| 
      
 93 
     | 
    
         
            +
              # The entity has sent XML that cannot be processed; this error MAY be used instead of the more specific XML-related errors,
         
     | 
| 
      
 94 
     | 
    
         
            +
              # such as <bad-namespace-prefix/>, <invalid-xml/>, <restricted-xml/>, <unsupported-encoding/>, and <xml-not-well-formed/>,
         
     | 
| 
      
 95 
     | 
    
         
            +
              # although the more specific errors are preferred.
         
     | 
| 
      
 96 
     | 
    
         
            +
              class BadFormat < StreamError
         
     | 
| 
      
 97 
     | 
    
         
            +
                register :stream_bad_format_error, 'bad-format'
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              ##
         
     | 
| 
      
 101 
     | 
    
         
            +
              # The entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires
         
     | 
| 
      
 102 
     | 
    
         
            +
              # such a prefix (see XML Namespace Names and Prefixes).
         
     | 
| 
      
 103 
     | 
    
         
            +
              class BadNamespacePrefix < StreamError
         
     | 
| 
      
 104 
     | 
    
         
            +
                register :stream_bad_namespace_prefix_error, 'bad-namespace-prefix'
         
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
              
         
     | 
| 
      
 107 
     | 
    
         
            +
              ##
         
     | 
| 
      
 108 
     | 
    
         
            +
              # The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the
         
     | 
| 
      
 109 
     | 
    
         
            +
              # existing stream.
         
     | 
| 
      
 110 
     | 
    
         
            +
              class Conflict < StreamError
         
     | 
| 
      
 111 
     | 
    
         
            +
                register :stream_conflict_error, 'conflict'
         
     | 
| 
      
 112 
     | 
    
         
            +
              end
         
     | 
| 
      
 113 
     | 
    
         
            +
              
         
     | 
| 
      
 114 
     | 
    
         
            +
              ##
         
     | 
| 
      
 115 
     | 
    
         
            +
              # The entity has not generated any traffic over the stream for some period of time (configurable according to a local service policy).
         
     | 
| 
      
 116 
     | 
    
         
            +
              class ConnectionTimeout < StreamError
         
     | 
| 
      
 117 
     | 
    
         
            +
                register :stream_connection_timeout_error, 'connection-timeout'
         
     | 
| 
      
 118 
     | 
    
         
            +
              end
         
     | 
| 
      
 119 
     | 
    
         
            +
              
         
     | 
| 
      
 120 
     | 
    
         
            +
              ##
         
     | 
| 
      
 121 
     | 
    
         
            +
              # The value of the 'to' attribute provided by the initiating entity in the stream header corresponds to a hostname that is no
         
     | 
| 
      
 122 
     | 
    
         
            +
              # longer hosted by the server.
         
     | 
| 
      
 123 
     | 
    
         
            +
              class HostGone < StreamError
         
     | 
| 
      
 124 
     | 
    
         
            +
                register :stream_host_gone_error, 'host-gone'
         
     | 
| 
      
 125 
     | 
    
         
            +
              end
         
     | 
| 
      
 126 
     | 
    
         
            +
              
         
     | 
| 
      
 127 
     | 
    
         
            +
              ##
         
     | 
| 
      
 128 
     | 
    
         
            +
              # The value of the 'to' attribute provided by the initiating entity in the stream header does not correspond to a hostname that
         
     | 
| 
      
 129 
     | 
    
         
            +
              # is hosted by the server.
         
     | 
| 
      
 130 
     | 
    
         
            +
              class HostUnknown < StreamError
         
     | 
| 
      
 131 
     | 
    
         
            +
                register :stream_host_unknown_error, 'host-unknown'
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
              
         
     | 
| 
      
 134 
     | 
    
         
            +
              ##
         
     | 
| 
      
 135 
     | 
    
         
            +
              # a stanza sent between two servers lacks a 'to' or 'from' attribute (or the attribute has no value).
         
     | 
| 
      
 136 
     | 
    
         
            +
              class ImproperAddressing < StreamError
         
     | 
| 
      
 137 
     | 
    
         
            +
                register :stream_improper_addressing_error, 'improper-addressing'
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
              
         
     | 
| 
      
 140 
     | 
    
         
            +
              ##
         
     | 
| 
      
 141 
     | 
    
         
            +
              # The server has experienced a misconfiguration or an otherwise-undefined internal error that prevents it from servicing the stream.
         
     | 
| 
      
 142 
     | 
    
         
            +
              class InternalServerError < StreamError
         
     | 
| 
      
 143 
     | 
    
         
            +
                register :stream_internal_server_error, 'internal-server-error'
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
      
 145 
     | 
    
         
            +
              
         
     | 
| 
      
 146 
     | 
    
         
            +
              ##
         
     | 
| 
      
 147 
     | 
    
         
            +
              # The JID or hostname provided in a 'from' address does not match an authorized JID or validated domain negotiated between
         
     | 
| 
      
 148 
     | 
    
         
            +
              # servers via SASL or dialback, or between a client and a server via authentication and resource binding.
         
     | 
| 
      
 149 
     | 
    
         
            +
              class InvalidFrom < StreamError
         
     | 
| 
      
 150 
     | 
    
         
            +
                register :stream_invalid_from_error, 'invalid-from'
         
     | 
| 
      
 151 
     | 
    
         
            +
              end
         
     | 
| 
      
 152 
     | 
    
         
            +
              
         
     | 
| 
      
 153 
     | 
    
         
            +
              ##
         
     | 
| 
      
 154 
     | 
    
         
            +
              # The stream ID or dialback ID is invalid or does not match an ID previously provided.
         
     | 
| 
      
 155 
     | 
    
         
            +
              class InvalidId < StreamError
         
     | 
| 
      
 156 
     | 
    
         
            +
                register :stream_invalid_id_error, 'invalid-id'
         
     | 
| 
      
 157 
     | 
    
         
            +
              end
         
     | 
| 
      
 158 
     | 
    
         
            +
              
         
     | 
| 
      
 159 
     | 
    
         
            +
              ##
         
     | 
| 
      
 160 
     | 
    
         
            +
              # The streams namespace name is something other than "http://etherx.jabber.org/streams" or the dialback namespace name is something
         
     | 
| 
      
 161 
     | 
    
         
            +
              # other than "jabber:server:dialback" (see XML Namespace Names and Prefixes).
         
     | 
| 
      
 162 
     | 
    
         
            +
              class InvalidNamespace < StreamError
         
     | 
| 
      
 163 
     | 
    
         
            +
                register :stream_invalid_namespace_error, 'invalid-namespace'
         
     | 
| 
      
 164 
     | 
    
         
            +
              end
         
     | 
| 
      
 165 
     | 
    
         
            +
              
         
     | 
| 
      
 166 
     | 
    
         
            +
              ##
         
     | 
| 
      
 167 
     | 
    
         
            +
              # The entity has sent invalid XML over the stream to a server that performs validation (see Validation).
         
     | 
| 
      
 168 
     | 
    
         
            +
              class InvalidXml < StreamError
         
     | 
| 
      
 169 
     | 
    
         
            +
                register :stream_invalid_xml_error, 'invalid-xml'
         
     | 
| 
      
 170 
     | 
    
         
            +
              end
         
     | 
| 
      
 171 
     | 
    
         
            +
              
         
     | 
| 
      
 172 
     | 
    
         
            +
              ##
         
     | 
| 
      
 173 
     | 
    
         
            +
              # The entity has attempted to send data before the stream has been authenticated, or otherwise is not authorized to perform an action
         
     | 
| 
      
 174 
     | 
    
         
            +
              # related to stream negotiation; the receiving entity MUST NOT process the offending stanza before sending the stream error.
         
     | 
| 
      
 175 
     | 
    
         
            +
              class NotAuthorized < StreamError
         
     | 
| 
      
 176 
     | 
    
         
            +
                register :stream_not_authorized_error, 'not-authorized'
         
     | 
| 
      
 177 
     | 
    
         
            +
              end
         
     | 
| 
      
 178 
     | 
    
         
            +
              
         
     | 
| 
      
 179 
     | 
    
         
            +
              ##
         
     | 
| 
      
 180 
     | 
    
         
            +
              # The entity has violated some local service policy; the server MAY choose to specify the policy in the <text/> element or an
         
     | 
| 
      
 181 
     | 
    
         
            +
              # application-specific condition element.
         
     | 
| 
      
 182 
     | 
    
         
            +
              class PolicyViolation < StreamError
         
     | 
| 
      
 183 
     | 
    
         
            +
                register :stream_policy_violation_error, 'policy-violation'
         
     | 
| 
      
 184 
     | 
    
         
            +
              end
         
     | 
| 
      
 185 
     | 
    
         
            +
              
         
     | 
| 
      
 186 
     | 
    
         
            +
              ##
         
     | 
| 
      
 187 
     | 
    
         
            +
              # The server is unable to properly connect to a remote entity that is required for authentication or authorization.
         
     | 
| 
      
 188 
     | 
    
         
            +
              class RemoteConnectionFailed < StreamError
         
     | 
| 
      
 189 
     | 
    
         
            +
                register :stream_remote_connection_failed_error, 'remote-connection-failed'
         
     | 
| 
      
 190 
     | 
    
         
            +
              end
         
     | 
| 
      
 191 
     | 
    
         
            +
              
         
     | 
| 
      
 192 
     | 
    
         
            +
              ##
         
     | 
| 
      
 193 
     | 
    
         
            +
              # The server lacks the system resources necessary to service the stream.
         
     | 
| 
      
 194 
     | 
    
         
            +
              class ResourceConstraint < StreamError
         
     | 
| 
      
 195 
     | 
    
         
            +
                register :stream_resource_constraint_error, 'resource-constraint'
         
     | 
| 
      
 196 
     | 
    
         
            +
              end
         
     | 
| 
      
 197 
     | 
    
         
            +
              
         
     | 
| 
      
 198 
     | 
    
         
            +
              ##
         
     | 
| 
      
 199 
     | 
    
         
            +
              # The entity has attempted to send restricted XML features such as a comment, processing instruction, DTD, entity reference,
         
     | 
| 
      
 200 
     | 
    
         
            +
              # or unescaped character (see Restrictions).
         
     | 
| 
      
 201 
     | 
    
         
            +
              class RestrictedXml < StreamError
         
     | 
| 
      
 202 
     | 
    
         
            +
                register :stream_restricted_xml_error, 'restricted-xml'
         
     | 
| 
      
 203 
     | 
    
         
            +
              end
         
     | 
| 
      
 204 
     | 
    
         
            +
              
         
     | 
| 
      
 205 
     | 
    
         
            +
              ##
         
     | 
| 
      
 206 
     | 
    
         
            +
              # The server will not provide service to the initiating entity but is redirecting traffic to another host; the server SHOULD
         
     | 
| 
      
 207 
     | 
    
         
            +
              # specify the alternate hostname or IP address (which MUST be a valid domain identifier) as the XML character data of the
         
     | 
| 
      
 208 
     | 
    
         
            +
              # <see-other-host/> element.
         
     | 
| 
      
 209 
     | 
    
         
            +
              class SeeOtherHost < StreamError
         
     | 
| 
      
 210 
     | 
    
         
            +
                register :stream_see_other_host_error, 'see-other-host'
         
     | 
| 
      
 211 
     | 
    
         
            +
              end
         
     | 
| 
      
 212 
     | 
    
         
            +
              
         
     | 
| 
      
 213 
     | 
    
         
            +
              ##
         
     | 
| 
      
 214 
     | 
    
         
            +
              # The server is being shut down and all active streams are being closed.
         
     | 
| 
      
 215 
     | 
    
         
            +
              class SystemShutdown < StreamError
         
     | 
| 
      
 216 
     | 
    
         
            +
                register :stream_system_shutdown_error, 'system-shutdown'
         
     | 
| 
      
 217 
     | 
    
         
            +
              end
         
     | 
| 
      
 218 
     | 
    
         
            +
              
         
     | 
| 
      
 219 
     | 
    
         
            +
              ##
         
     | 
| 
      
 220 
     | 
    
         
            +
              # The error condition is not one of those defined by the other conditions in this list; this error condition SHOULD be used
         
     | 
| 
      
 221 
     | 
    
         
            +
              # only in conjunction with an application-specific condition.
         
     | 
| 
      
 222 
     | 
    
         
            +
              class UndefinedCondition < StreamError
         
     | 
| 
      
 223 
     | 
    
         
            +
                register :stream_undefined_condition_error, 'undefined-condition'
         
     | 
| 
      
 224 
     | 
    
         
            +
              end
         
     | 
| 
      
 225 
     | 
    
         
            +
              
         
     | 
| 
      
 226 
     | 
    
         
            +
              ##
         
     | 
| 
      
 227 
     | 
    
         
            +
              # The initiating entity has encoded the stream in an encoding that is not supported by the server (see Character Encoding).
         
     | 
| 
      
 228 
     | 
    
         
            +
              class UnsupportedEncoding < StreamError
         
     | 
| 
      
 229 
     | 
    
         
            +
                register :stream_unsupported_encoding_error, 'unsupported-encoding'
         
     | 
| 
      
 230 
     | 
    
         
            +
              end
         
     | 
| 
      
 231 
     | 
    
         
            +
              
         
     | 
| 
      
 232 
     | 
    
         
            +
              ##
         
     | 
| 
      
 233 
     | 
    
         
            +
              # The initiating entity has sent a first-level child of the stream that is not supported by the server.
         
     | 
| 
      
 234 
     | 
    
         
            +
              class UnsupportedStanzaType < StreamError
         
     | 
| 
      
 235 
     | 
    
         
            +
                register :stream_unsupported_stanza_type_error, 'unsupported-stanza-type'
         
     | 
| 
      
 236 
     | 
    
         
            +
              end
         
     | 
| 
      
 237 
     | 
    
         
            +
              
         
     | 
| 
      
 238 
     | 
    
         
            +
              ##
         
     | 
| 
      
 239 
     | 
    
         
            +
              # The value of the 'version' attribute provided by the initiating entity in the stream header specifies a version of XMPP
         
     | 
| 
      
 240 
     | 
    
         
            +
              # That is not supported by the server; the server MAY specify the version(s) it supports in the <text/> element.
         
     | 
| 
      
 241 
     | 
    
         
            +
              class UnsupportedVersion < StreamError
         
     | 
| 
      
 242 
     | 
    
         
            +
                register :stream_unsupported_version_error, 'unsupported-version'
         
     | 
| 
      
 243 
     | 
    
         
            +
              end
         
     | 
| 
      
 244 
     | 
    
         
            +
              
         
     | 
| 
      
 245 
     | 
    
         
            +
              ##
         
     | 
| 
      
 246 
     | 
    
         
            +
              # The initiating entity has sent XML that is not well-formed as defined by [XML].
         
     | 
| 
      
 247 
     | 
    
         
            +
              class XmlNotWellFormed < StreamError
         
     | 
| 
      
 248 
     | 
    
         
            +
                register :stream_xml_not_well_formed_error, 'xml-not-well-formed'
         
     | 
| 
      
 249 
     | 
    
         
            +
              end
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
            end #StreamError
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
            end #Blather
         
     | 
    
        data/lib/blather/jid.rb
    CHANGED
    
    | 
         @@ -5,24 +5,23 @@ module Blather 
     | 
|
| 
       5 
5 
     | 
    
         
             
              class JID
         
     | 
| 
       6 
6 
     | 
    
         
             
                include Comparable
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?)) 
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
                begin
         
     | 
| 
       11 
     | 
    
         
            -
                  require 'idn'
         
     | 
| 
       12 
     | 
    
         
            -
                  USE_STRINGPREP = true
         
     | 
| 
       13 
     | 
    
         
            -
                rescue LoadError
         
     | 
| 
       14 
     | 
    
         
            -
                  USE_STRINGPREP = false
         
     | 
| 
       15 
     | 
    
         
            -
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
                PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/.freeze
         
     | 
| 
       16 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
                ##
         
     | 
| 
       17 
11 
     | 
    
         
             
                # Get the JID's node
         
     | 
| 
       18 
12 
     | 
    
         
             
                attr_reader :node
         
     | 
| 
       19 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                ##
         
     | 
| 
       20 
15 
     | 
    
         
             
                # Get the JID's domain
         
     | 
| 
       21 
16 
     | 
    
         
             
                attr_reader :domain
         
     | 
| 
       22 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
                ##
         
     | 
| 
       23 
19 
     | 
    
         
             
                # Get the JID's resource
         
     | 
| 
       24 
20 
     | 
    
         
             
                attr_reader :resource
         
     | 
| 
       25 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
                ##
         
     | 
| 
      
 23 
     | 
    
         
            +
                # If a JID is passed in just return it. 
         
     | 
| 
      
 24 
     | 
    
         
            +
                # No need to copy out all the values
         
     | 
| 
       26 
25 
     | 
    
         
             
                def self.new(node, domain = nil, resource = nil)
         
     | 
| 
       27 
26 
     | 
    
         
             
                  node.is_a?(JID) ? node : super
         
     | 
| 
       28 
27 
     | 
    
         
             
                end
         
     | 
| 
         @@ -39,14 +38,8 @@ module Blather 
     | 
|
| 
       39 
38 
     | 
    
         
             
                    @node, @domain, @resource = @node.to_s.scan(PATTERN).first
         
     | 
| 
       40 
39 
     | 
    
         
             
                  end
         
     | 
| 
       41 
40 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                  if  
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                    @domain = IDN::Stringprep.nameprep(@domain)         if @domain
         
     | 
| 
       45 
     | 
    
         
            -
                    @resource = IDN::Stringprep.resourceprep(@resource) if @resource
         
     | 
| 
       46 
     | 
    
         
            -
                  else
         
     | 
| 
       47 
     | 
    
         
            -
                    @node.downcase!   if @node
         
     | 
| 
       48 
     | 
    
         
            -
                    @domain.downcase! if @domain
         
     | 
| 
       49 
     | 
    
         
            -
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @node.downcase!   if @node
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @domain.downcase! if @domain
         
     | 
| 
       50 
43 
     | 
    
         | 
| 
       51 
44 
     | 
    
         
             
                  raise ArgumentError, 'Node too long'      if (@node || '').length > 1023
         
     | 
| 
       52 
45 
     | 
    
         
             
                  raise ArgumentError, 'Domain too long'    if (@domain || '').length > 1023
         
     | 
    
        data/lib/blather/roster.rb
    CHANGED
    
    | 
         @@ -71,6 +71,15 @@ module Blather 
     | 
|
| 
       71 
71 
     | 
    
         
             
                  @items.dup
         
     | 
| 
       72 
72 
     | 
    
         
             
                end
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
      
 74 
     | 
    
         
            +
                ##
         
     | 
| 
      
 75 
     | 
    
         
            +
                # A hash of items keyed by group
         
     | 
| 
      
 76 
     | 
    
         
            +
                def grouped
         
     | 
| 
      
 77 
     | 
    
         
            +
                  self.inject(Hash.new{|h,k|h[k]=[]}) do |hash, item|
         
     | 
| 
      
 78 
     | 
    
         
            +
                    item[1].groups.each { |group| hash[group] << item[1] }
         
     | 
| 
      
 79 
     | 
    
         
            +
                    hash
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
       74 
83 
     | 
    
         
             
              private
         
     | 
| 
       75 
84 
     | 
    
         
             
                def self.key(jid)
         
     | 
| 
       76 
85 
     | 
    
         
             
                  JID.new(jid).stripped.to_s
         
     | 
    
        data/lib/blather/roster_item.rb
    CHANGED
    
    | 
         @@ -17,6 +17,7 @@ module Blather 
     | 
|
| 
       17 
17 
     | 
    
         
             
                # item:: can be a JID, String (a@b) or a Stanza
         
     | 
| 
       18 
18 
     | 
    
         
             
                def initialize(item)
         
     | 
| 
       19 
19 
     | 
    
         
             
                  @statuses = []
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @groups = []
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
       21 
22 
     | 
    
         
             
                  case item
         
     | 
| 
       22 
23 
     | 
    
         
             
                  when JID
         
     | 
| 
         @@ -24,12 +25,14 @@ module Blather 
     | 
|
| 
       24 
25 
     | 
    
         
             
                  when String
         
     | 
| 
       25 
26 
     | 
    
         
             
                    self.jid = JID.new(item).stripped
         
     | 
| 
       26 
27 
     | 
    
         
             
                  when XMPPNode
         
     | 
| 
       27 
     | 
    
         
            -
                    self.jid          = JID.new(item[ 
     | 
| 
       28 
     | 
    
         
            -
                    self.name         = item[ 
     | 
| 
       29 
     | 
    
         
            -
                    self.subscription = item[ 
     | 
| 
       30 
     | 
    
         
            -
                    self.ask          = item[ 
     | 
| 
      
 28 
     | 
    
         
            +
                    self.jid          = JID.new(item[:jid]).stripped
         
     | 
| 
      
 29 
     | 
    
         
            +
                    self.name         = item[:name]
         
     | 
| 
      
 30 
     | 
    
         
            +
                    self.subscription = item[:subscription]
         
     | 
| 
      
 31 
     | 
    
         
            +
                    self.ask          = item[:ask]
         
     | 
| 
       31 
32 
     | 
    
         
             
                    item.groups.each { |g| self.groups << g }
         
     | 
| 
       32 
33 
     | 
    
         
             
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  @groups = [nil] if @groups.empty?
         
     | 
| 
       33 
36 
     | 
    
         
             
                end
         
     | 
| 
       34 
37 
     | 
    
         | 
| 
       35 
38 
     | 
    
         
             
                ##
         
     | 
    
        data/lib/blather/stanza.rb
    CHANGED
    
    | 
         @@ -3,6 +3,7 @@ module Blather 
     | 
|
| 
       3 
3 
     | 
    
         
             
              # Base XMPP Stanza
         
     | 
| 
       4 
4 
     | 
    
         
             
              class Stanza < XMPPNode
         
     | 
| 
       5 
5 
     | 
    
         
             
                @@last_id = 0
         
     | 
| 
      
 6 
     | 
    
         
            +
                @@handler_list = []
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
8 
     | 
    
         
             
                class_inheritable_array :handler_heirarchy
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
         @@ -13,12 +14,17 @@ module Blather 
     | 
|
| 
       13 
14 
     | 
    
         
             
                # that inherits Stanza can register a callback for itself
         
     | 
| 
       14 
15 
     | 
    
         
             
                # which is added to a list and iterated over when looking for
         
     | 
| 
       15 
16 
     | 
    
         
             
                # a callback to use
         
     | 
| 
       16 
     | 
    
         
            -
                def self.register(type, name = nil,  
     | 
| 
      
 17 
     | 
    
         
            +
                def self.register(type, name = nil, ns = nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @@handler_list << type
         
     | 
| 
       17 
19 
     | 
    
         
             
                  self.handler_heirarchy ||= []
         
     | 
| 
       18 
20 
     | 
    
         
             
                  self.handler_heirarchy.unshift type
         
     | 
| 
       19 
21 
     | 
    
         | 
| 
       20 
22 
     | 
    
         
             
                  name = name || self.name || type
         
     | 
| 
       21 
     | 
    
         
            -
                  super name,  
     | 
| 
      
 23 
     | 
    
         
            +
                  super name, ns
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def self.handler_list
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @@handler_list
         
     | 
| 
       22 
28 
     | 
    
         
             
                end
         
     | 
| 
       23 
29 
     | 
    
         | 
| 
       24 
30 
     | 
    
         
             
                ##
         
     | 
| 
         @@ -65,43 +71,31 @@ module Blather 
     | 
|
| 
       65 
71 
     | 
    
         
             
                  self
         
     | 
| 
       66 
72 
     | 
    
         
             
                end
         
     | 
| 
       67 
73 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
                 
     | 
| 
       69 
     | 
    
         
            -
                  attributes['id'] = id
         
     | 
| 
       70 
     | 
    
         
            -
                end
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                def id
         
     | 
| 
       73 
     | 
    
         
            -
                  attributes['id']
         
     | 
| 
       74 
     | 
    
         
            -
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
                attribute_accessor :id, :to_sym => false
         
     | 
| 
       75 
75 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
                 
     | 
| 
       77 
     | 
    
         
            -
                  attributes['to'] = to
         
     | 
| 
       78 
     | 
    
         
            -
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
                attribute_writer :to, :from
         
     | 
| 
       79 
77 
     | 
    
         | 
| 
       80 
78 
     | 
    
         
             
                ##
         
     | 
| 
       81 
79 
     | 
    
         
             
                # returns:: JID created from the "to" value of the stanza
         
     | 
| 
       82 
80 
     | 
    
         
             
                def to
         
     | 
| 
       83 
     | 
    
         
            -
                  JID.new(attributes[ 
     | 
| 
       84 
     | 
    
         
            -
                end
         
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                def from=(from)
         
     | 
| 
       87 
     | 
    
         
            -
                  attributes['from'] = from
         
     | 
| 
      
 81 
     | 
    
         
            +
                  JID.new(attributes[:to]) if attributes[:to]
         
     | 
| 
       88 
82 
     | 
    
         
             
                end
         
     | 
| 
       89 
83 
     | 
    
         | 
| 
       90 
84 
     | 
    
         
             
                ##
         
     | 
| 
       91 
85 
     | 
    
         
             
                # returns:: JID created from the "from" value of the stanza
         
     | 
| 
       92 
86 
     | 
    
         
             
                def from
         
     | 
| 
       93 
     | 
    
         
            -
                  JID.new(attributes[ 
     | 
| 
      
 87 
     | 
    
         
            +
                  JID.new(attributes[:from]) if attributes[:from]
         
     | 
| 
       94 
88 
     | 
    
         
             
                end
         
     | 
| 
       95 
89 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
                 
     | 
| 
       97 
     | 
    
         
            -
                  attributes['type'] = type
         
     | 
| 
       98 
     | 
    
         
            -
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
                attribute_accessor :type
         
     | 
| 
       99 
91 
     | 
    
         | 
| 
       100 
92 
     | 
    
         
             
                ##
         
     | 
| 
       101 
     | 
    
         
            -
                #  
     | 
| 
       102 
     | 
    
         
            -
                 
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
      
 93 
     | 
    
         
            +
                # Transform the stanza into a stanza error
         
     | 
| 
      
 94 
     | 
    
         
            +
                # <tt>err_name_or_class</tt> can be the name of the error or the error class to use
         
     | 
| 
      
 95 
     | 
    
         
            +
                # <tt>type</tt>, <tt>text</tt>, <tt>extras</tt> are the same as for StanzaError#new
         
     | 
| 
      
 96 
     | 
    
         
            +
                def as_error(err_name_or_class, type, text = nil, extras = [])
         
     | 
| 
      
 97 
     | 
    
         
            +
                  klass = (err_name_or_class.is_a?(Class) ? err_name_or_class : StanzaError.class_from_registration(err_name_or_class))
         
     | 
| 
      
 98 
     | 
    
         
            +
                  klass.new self, type, text, extras
         
     | 
| 
       104 
99 
     | 
    
         
             
                end
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
100 
     | 
    
         
             
              end
         
     | 
| 
       107 
101 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,84 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Blather
         
     | 
| 
      
 2 
     | 
    
         
            +
            class Stanza
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              ##
         
     | 
| 
      
 5 
     | 
    
         
            +
              # DiscoInfo object ()
         
     | 
| 
      
 6 
     | 
    
         
            +
              # 
         
     | 
| 
      
 7 
     | 
    
         
            +
              class DiscoInfo < Disco
         
     | 
| 
      
 8 
     | 
    
         
            +
                register :disco_info, nil, 'http://jabber.org/protocol/disco#info'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(type = nil, node = nil, identities = [], features = [])
         
     | 
| 
      
 11 
     | 
    
         
            +
                  super type
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  self.node = node
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  [identities].flatten.each do |id|
         
     | 
| 
      
 16 
     | 
    
         
            +
                    query << (id.is_a?(Identity) ? id : Identity.new(id[:name], id[:type], id[:category]))
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  [features].flatten.each do |feature|
         
     | 
| 
      
 20 
     | 
    
         
            +
                    query << (feature.is_a?(Feature) ? feature : Feature.new(feature))
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                ##
         
     | 
| 
      
 25 
     | 
    
         
            +
                # List of identity objects
         
     | 
| 
      
 26 
     | 
    
         
            +
                def identities
         
     | 
| 
      
 27 
     | 
    
         
            +
                  identities = query.find('identity')
         
     | 
| 
      
 28 
     | 
    
         
            +
                  identities = query.find('query_ns:identity', :query_ns => self.class.ns) if identities.empty?
         
     | 
| 
      
 29 
     | 
    
         
            +
                  identities.map { |i| Identity.new i }
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                ##
         
     | 
| 
      
 33 
     | 
    
         
            +
                # List of feature objects
         
     | 
| 
      
 34 
     | 
    
         
            +
                def features
         
     | 
| 
      
 35 
     | 
    
         
            +
                 features = query.find('feature')
         
     | 
| 
      
 36 
     | 
    
         
            +
                 features = query.find('query_ns:feature', :query_ns => self.class.ns) if features.empty?
         
     | 
| 
      
 37 
     | 
    
         
            +
                 features.map { |i| Feature.new i }
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                class Identity < XMPPNode
         
     | 
| 
      
 41 
     | 
    
         
            +
                  attribute_accessor :category, :type
         
     | 
| 
      
 42 
     | 
    
         
            +
                  attribute_accessor :name, :to_sym => false
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def initialize(name, type = nil, category = nil)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    super :identity
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    if name.is_a?(XML::Node)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      self.inherit name
         
     | 
| 
      
 49 
     | 
    
         
            +
                    else
         
     | 
| 
      
 50 
     | 
    
         
            +
                      self.name = name
         
     | 
| 
      
 51 
     | 
    
         
            +
                      self.type = type
         
     | 
| 
      
 52 
     | 
    
         
            +
                      self.category = category
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def eql?(other)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    other.kind_of?(self.class) &&
         
     | 
| 
      
 58 
     | 
    
         
            +
                    other.name == self.name &&
         
     | 
| 
      
 59 
     | 
    
         
            +
                    other.type == self.type &&
         
     | 
| 
      
 60 
     | 
    
         
            +
                    other.category == self.category
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                class Feature < XMPPNode
         
     | 
| 
      
 65 
     | 
    
         
            +
                  attribute_accessor :var, :to_sym => false
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def initialize(var)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    super :feature
         
     | 
| 
      
 69 
     | 
    
         
            +
                    if var.is_a?(XML::Node)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      self.inherit var
         
     | 
| 
      
 71 
     | 
    
         
            +
                    else
         
     | 
| 
      
 72 
     | 
    
         
            +
                      self.var = var
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  def eql?(other)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    other.kind_of?(self.class) &&
         
     | 
| 
      
 78 
     | 
    
         
            +
                    other.var == self.var
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
            end #Stanza
         
     | 
| 
      
 84 
     | 
    
         
            +
            end #Blather
         
     |