diff options
-rw-r--r-- | sleekxmpp/basexmpp.py | 2 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0030.py | 51 | ||||
-rw-r--r-- | sleekxmpp/test/sleektest.py | 10 | ||||
-rw-r--r-- | tests/test_stream_xep_0030.py | 83 |
4 files changed, 127 insertions, 19 deletions
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index cd7d2510..0ff573ff 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -110,6 +110,8 @@ class BaseXMPP(XMLStream): self.boundjid = JID("") self.plugin = {} + self.plugin_config = {} + self.plugin_whitelist = [] self.roster = {} self.is_component = False self.auto_authorize = True diff --git a/sleekxmpp/plugins/xep_0030.py b/sleekxmpp/plugins/xep_0030.py index c8050809..59c60e66 100644 --- a/sleekxmpp/plugins/xep_0030.py +++ b/sleekxmpp/plugins/xep_0030.py @@ -71,11 +71,12 @@ class DiscoInfo(ElementBase): for idXML in idsXML: self.xml.remove(idXML) - def addIdentity(self, category, id_type, name=''): - idXML = ET.Element('{%s}identity' % self.namespace, - {'category': category, - 'type': id_type, - 'name': name}) + def addIdentity(self, category, itype, name=''): + idXML = ET.Element('{%s}identity' % self.namespace) + idXML.attrib['category'] = category + idXML.attrib['type'] = itype + if name: + idXML.attrib['name'] = name self.xml.append(idXML) def delIdentity(self, category, id_type, name=''): @@ -213,8 +214,11 @@ class xep_0030(base.base_plugin): self.xmpp.add_event_handler('disco_items_request', self.handle_disco_items) self.xmpp.add_event_handler('disco_info_request', self.handle_disco_info) + + self.nodes = {} - self.nodes = {'main': DiscoNode('main')} + self.add_node('') + self.add_feature('http://jabber.org/protocol/disco#info', node='') def add_node(self, node): if node not in self.nodes: @@ -258,14 +262,30 @@ class xep_0030(base.base_plugin): return node_name = iq['disco_info']['node'] - if not node_name: - node_name = 'main' - log.debug("Using default handler for disco#info on node '%s'." % node_name) if node_name in self.nodes: node = self.nodes[node_name] - iq.reply().setPayload(node.info.xml).send() + iq.reply() + iq['disco_info']['node'] = node_name + + identities = node.info['identities'] + if identities: + iq['disco_info']['identities'] = identities + else: + if self.xmpp.is_component: + iq['disco_info'].addIdentity( + category='component', + itype='generic') + else: + iq['disco_info'].addIdentity( + category='client', + itype='bot') + log.info("No identity found for node '%'," + \ + "using default, generic identity") + + iq['disco_info']['features'] = node.info['features'] + iq.send() else: log.debug("Node %s requested, but does not exist." % node_name) iq.reply().error().setPayload(iq['disco_info'].xml) @@ -287,10 +307,7 @@ class xep_0030(base.base_plugin): return node_name = iq['disco_items']['node'] - if not node_name: - node_name = 'main' - - log.debug("Using default handler for disco#items on node '%s'." % node_name) + log.debug("Using default handler for disco#items on node: '%s'." % node_name) if node_name in self.nodes: node = self.nodes[node_name] @@ -321,17 +338,17 @@ class xep_0030(base.base_plugin): iq['disco_items']['node'] = node return iq.send() - def add_feature(self, feature, node='main'): + def add_feature(self, feature, node=''): self.add_node(node) self.nodes[node].addFeature(feature) - def add_identity(self, category='', itype='', name='', node='main'): + def add_identity(self, category='', itype='', name='', node=''): self.add_node(node) self.nodes[node].addIdentity(category=category, id_type=itype, name=name) - def add_item(self, jid=None, name='', node='main', subnode=''): + def add_item(self, jid=None, name='', node='', subnode=''): self.add_node(node) self.add_node(subnode) if jid is None: diff --git a/sleekxmpp/test/sleektest.py b/sleekxmpp/test/sleektest.py index 5e61cec2..f1b5ef93 100644 --- a/sleekxmpp/test/sleektest.py +++ b/sleekxmpp/test/sleektest.py @@ -257,7 +257,7 @@ class SleekTest(unittest.TestCase): def stream_start(self, mode='client', skip=True, header=None, socket='mock', jid='tester@localhost', password='test', server='localhost', - port=5222): + port=5222, plugins=None): """ Initialize an XMPP client or component using a dummy XML stream. @@ -277,6 +277,8 @@ class SleekTest(unittest.TestCase): server -- The name of the XMPP server. Defaults to 'localhost'. port -- The port to use when connecting to the server. Defaults to 5222. + plugins -- List of plugins to register. By default, all plugins + are loaded. """ if mode == 'client': self.xmpp = ClientXMPP(jid, password) @@ -312,7 +314,11 @@ class SleekTest(unittest.TestCase): else: raise ValueError("Unknown socket type.") - self.xmpp.register_plugins() + if plugins is None: + self.xmpp.register_plugins() + else: + for plugin in plugins: + self.xmpp.register_plugin(plugin) self.xmpp.process(threaded=True) if skip: if socket != 'live': diff --git a/tests/test_stream_xep_0030.py b/tests/test_stream_xep_0030.py new file mode 100644 index 00000000..5efce788 --- /dev/null +++ b/tests/test_stream_xep_0030.py @@ -0,0 +1,83 @@ +import time +from sleekxmpp.test import * + + +class TestStreamDisco(SleekTest): + """ + Test using the XEP-0030 plugin. + """ + + def tearDown(self): + self.stream_close() + + def testInfoEmptyNode(self): + """ + Info queries to a node MUST have at least one identity + and feature, namely http://jabber.org/protocol/disco#info. + + Since the XEP-0030 plugin is loaded, a disco response should + be generated and not an error result. + """ + self.stream_start(plugins=['xep_0030']) + + self.recv(""" + <iq type="get" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="client" type="bot" /> + <feature var="http://jabber.org/protocol/disco#info" /> + </query> + </iq>""") + + def testInfoEmptyNodeComponent(self): + """ + Test requesting an empty node using a Component. + """ + self.stream_start(mode='component', + plugins=['xep_0030']) + + self.recv(""" + <iq type="get" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="component" type="generic" /> + <feature var="http://jabber.org/protocol/disco#info" /> + </query> + </iq>""") + + def testInfoIncludeNode(self): + """ + Results for info queries directed to a particular node MUST + include the node in the query response. + """ + self.stream_start(plugins=['xep_0030']) + + self.xmpp['xep_0030'].add_node('testing') + + self.recv(""" + <iq type="get" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing"> + </query> + </iq>""", + method='mask') + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamDisco) |