from __future__ import with_statement from sleekxmpp.plugins import base import logging #from xml.etree import cElementTree as ET from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET from sleekxmpp.plugins.xep_0060 import stanza from sleekxmpp.plugins.xep_0004 import Form log = logging.getLogger(__name__) class xep_0060(base.base_plugin): """ XEP-0060 Publish Subscribe """ def plugin_init(self): self.xep = '0060' self.description = 'Publish-Subscribe' def create_node(self, jid, node, config=None, collection=False, ntype=None): pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') create = ET.Element('create') create.set('node', node) pubsub.append(create) configure = ET.Element('configure') if collection: ntype = 'collection' #if config is None: # submitform = self.xmpp.plugin['xep_0004'].makeForm('submit') #else: if config is not None: submitform = config if 'FORM_TYPE' in submitform.field: submitform.field['FORM_TYPE'].setValue('http://jabber.org/protocol/pubsub#node_config') else: submitform.addField('FORM_TYPE', 'hidden', value='http://jabber.org/protocol/pubsub#node_config') if ntype: if 'pubsub#node_type' in submitform.field: submitform.field['pubsub#node_type'].setValue(ntype) else: submitform.addField('pubsub#node_type', value=ntype) else: if 'pubsub#node_type' in submitform.field: submitform.field['pubsub#node_type'].setValue('leaf') else: submitform.addField('pubsub#node_type', value='leaf') submitform['type'] = 'submit' configure.append(submitform.xml) pubsub.append(configure) iq = self.xmpp.makeIqSet(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is False or result is None or result['type'] == 'error': return False return True def subscribe(self, jid, node, bare=True, subscribee=None): pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') subscribe = ET.Element('subscribe') subscribe.attrib['node'] = node if subscribee is None: if bare: subscribe.attrib['jid'] = self.xmpp.boundjid.bare else: subscribe.attrib['jid'] = self.xmpp.boundjid.full else: subscribe.attrib['jid'] = subscribee pubsub.append(subscribe) iq = self.xmpp.makeIqSet(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is False or result is None or result['type'] == 'error': return False return True def unsubscribe(self, jid, node, bare=True, subscribee=None): pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') unsubscribe = ET.Element('unsubscribe') unsubscribe.attrib['node'] = node if subscribee is None: if bare: unsubscribe.attrib['jid'] = self.xmpp.boundjid.bare else: unsubscribe.attrib['jid'] = self.xmpp.boundjid.full else: unsubscribe.attrib['jid'] = subscribee pubsub.append(unsubscribe) iq = self.xmpp.makeIqSet(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is False or result is None or result['type'] == 'error': return False return True def getNodeConfig(self, jid, node=None): # if no node, then grab default pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') if node is not None: configure = ET.Element('configure') configure.attrib['node'] = node else: configure = ET.Element('default') pubsub.append(configure) #TODO: Add configure support. iq = self.xmpp.makeIqGet() iq.append(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] #self.xmpp.add_handler("<iq id='%s'/>" % id, self.handlerCreateNodeResponse) result = iq.send() if result is None or result == False or result['type'] == 'error': log.warning("got error instead of config") return False if node is not None: form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}configure/{jabber:x:data}x') else: form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}default/{jabber:x:data}x') if not form or form is None: log.error("No form found.") return False return Form(xml=form) def getNodeSubscriptions(self, jid, node): pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') subscriptions = ET.Element('subscriptions') subscriptions.attrib['node'] = node pubsub.append(subscriptions) iq = self.xmpp.makeIqGet() iq.append(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is None or result == False or result['type'] == 'error': log.warning("got error instead of config") return False else: results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}subscriptions/{http://jabber.org/protocol/pubsub#owner}subscription') if results is None: return False subs = {} for sub in results: subs[sub.get('jid')] = sub.get('subscription') return subs def getNodeAffiliations(self, jid, node): pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') affiliations = ET.Element('affiliations') affiliations.attrib['node'] = node pubsub.append(affiliations) iq = self.xmpp.makeIqGet() iq.append(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is None or result == False or result['type'] == 'error': log.warning("got error instead of config") return False else: results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}affiliations/{http://jabber.org/protocol/pubsub#owner}affiliation') if results is None: return False subs = {} for sub in results: subs[sub.get('jid')] = sub.get('affiliation') return subs def deleteNode(self, jid, node): pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') iq = self.xmpp.makeIqSet() delete = ET.Element('delete') delete.attrib['node'] = node pubsub.append(delete) iq.append(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full result = iq.send() if result is not None and result is not False and result['type'] != 'error': return True else: return False def setNodeConfig(self, jid, node, config): pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') configure = ET.Element('configure') configure.attrib['node'] = node config = config.getXML('submit') configure.append(config) pubsub.append(configure) iq = self.xmpp.makeIqSet(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is None or result['type'] == 'error': return False return True def setItem(self, jid, node, items=[]): pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') publish = ET.Element('publish') publish.attrib['node'] = node for pub_item in items: id, payload = pub_item item = ET.Element('item') if id is not None: item.attrib['id'] = id item.append(payload) publish.append(item) pubsub.append(publish) iq = self.xmpp.makeIqSet(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is None or result is False or result['type'] == 'error': return False return True def addItem(self, jid, node, items=[]): return self.setItem(jid, node, items) def deleteItem(self, jid, node, item): pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') retract = ET.Element('retract') retract.attrib['node'] = node itemn = ET.Element('item') itemn.attrib['id'] = item retract.append(itemn) pubsub.append(retract) iq = self.xmpp.makeIqSet(pubsub) iq.attrib['to'] = jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is None or result is False or result['type'] == 'error': return False return True def getNodes(self, jid): response = self.xmpp.plugin['xep_0030'].getItems(jid) items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item') nodes = {} if items is not None and items is not False: for item in items: nodes[item.get('node')] = item.get('name') return nodes def getItems(self, jid, node): response = self.xmpp.plugin['xep_0030'].getItems(jid, node) items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item') nodeitems = [] if items is not None and items is not False: for item in items: nodeitems.append(item.get('node')) return nodeitems def addNodeToCollection(self, jid, child, parent=''): config = self.getNodeConfig(jid, child) if not config or config is None: self.lasterror = "Config Error" return False try: config.field['pubsub#collection'].setValue(parent) except KeyError: log.warning("pubsub#collection doesn't exist in config, trying to add it") config.addField('pubsub#collection', value=parent) if not self.setNodeConfig(jid, child, config): return False return True def modifyAffiliation(self, ps_jid, node, user_jid, affiliation): if affiliation not in ('owner', 'publisher', 'member', 'none', 'outcast'): raise TypeError pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') affs = ET.Element('affiliations') affs.attrib['node'] = node aff = ET.Element('affiliation') aff.attrib['jid'] = user_jid aff.attrib['affiliation'] = affiliation affs.append(aff) pubsub.append(affs) iq = self.xmpp.makeIqSet(pubsub) iq.attrib['to'] = ps_jid iq.attrib['from'] = self.xmpp.boundjid.full id = iq['id'] result = iq.send() if result is None or result is False or result['type'] == 'error': return False return True def addNodeToCollection(self, jid, child, parent=''): config = self.getNodeConfig(jid, child) if not config or config is None: self.lasterror = "Config Error" return False try: config.field['pubsub#collection'].setValue(parent) except KeyError: log.warning("pubsub#collection doesn't exist in config, trying to add it") config.addField('pubsub#collection', value=parent) if not self.setNodeConfig(jid, child, config): return False return True def removeNodeFromCollection(self, jid, child): self.addNodeToCollection(jid, child, '')