diff options
Diffstat (limited to 'sleekxmpp/plugins')
-rw-r--r-- | sleekxmpp/plugins/base.py | 3 | ||||
-rw-r--r-- | sleekxmpp/plugins/old_0060.py (renamed from sleekxmpp/plugins/xep_0060.py) | 0 | ||||
-rw-r--r-- | sleekxmpp/plugins/stanza_pubsub.py | 557 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/__init__.py | 2 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/pubsub.py | 313 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/stanza/__init__.py | 3 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/stanza/base.py | 24 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/stanza/pubsub.py | 277 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py | 124 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/stanza/pubsub_owner.py | 152 |
10 files changed, 897 insertions, 558 deletions
diff --git a/sleekxmpp/plugins/base.py b/sleekxmpp/plugins/base.py index 2dd68c8d..561421d8 100644 --- a/sleekxmpp/plugins/base.py +++ b/sleekxmpp/plugins/base.py @@ -66,7 +66,8 @@ class base_plugin(object): """ if config is None: config = {} - self.xep = 'base' + self.xep = None + self.rfc = None self.description = 'Base Plugin' self.xmpp = xmpp self.config = config diff --git a/sleekxmpp/plugins/xep_0060.py b/sleekxmpp/plugins/old_0060.py index 93124fca..93124fca 100644 --- a/sleekxmpp/plugins/xep_0060.py +++ b/sleekxmpp/plugins/old_0060.py diff --git a/sleekxmpp/plugins/stanza_pubsub.py b/sleekxmpp/plugins/stanza_pubsub.py deleted file mode 100644 index b5964537..00000000 --- a/sleekxmpp/plugins/stanza_pubsub.py +++ /dev/null @@ -1,557 +0,0 @@ -from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID -from .. stanza.iq import Iq -from .. stanza.message import Message -from .. basexmpp import basexmpp -from .. xmlstream.xmlstream import XMLStream -import logging -from . import xep_0004 - - -class PubsubState(ElementBase): - namespace = 'http://jabber.org/protocol/psstate' - name = 'state' - plugin_attrib = 'psstate' - interfaces = set(('node', 'item', 'payload')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setPayload(self, value): - self.xml.append(value) - - def getPayload(self): - childs = self.xml.getchildren() - if len(childs) > 0: - return childs[0] - - def delPayload(self): - for child in self.xml.getchildren(): - self.xml.remove(child) - -registerStanzaPlugin(Iq, PubsubState) - -class PubsubStateEvent(ElementBase): - namespace = 'http://jabber.org/protocol/psstate#event' - name = 'event' - plugin_attrib = 'psstate_event' - intefaces = set(tuple()) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Message, PubsubStateEvent) -registerStanzaPlugin(PubsubStateEvent, PubsubState) - -class Pubsub(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'pubsub' - plugin_attrib = 'pubsub' - interfaces = set(tuple()) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Iq, Pubsub) - -class PubsubOwner(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#owner' - name = 'pubsub' - plugin_attrib = 'pubsub_owner' - interfaces = set(tuple()) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Iq, PubsubOwner) - -class Affiliation(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'affiliation' - plugin_attrib = name - interfaces = set(('node', 'affiliation')) - plugin_attrib_map = {} - plugin_tag_map = {} - -class Affiliations(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'affiliations' - plugin_attrib = 'affiliations' - interfaces = set(tuple()) - plugin_attrib_map = {} - plugin_tag_map = {} - subitem = (Affiliation,) - - def append(self, affiliation): - if not isinstance(affiliation, Affiliation): - raise TypeError - self.xml.append(affiliation.xml) - return self.iterables.append(affiliation) - -registerStanzaPlugin(Pubsub, Affiliations) - - -class Subscription(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'subscription' - plugin_attrib = name - interfaces = set(('jid', 'node', 'subscription', 'subid')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setjid(self, value): - self._setattr('jid', str(value)) - - def getjid(self): - return jid(self._getattr('jid')) - -registerStanzaPlugin(Pubsub, Subscription) - -class Subscriptions(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'subscriptions' - plugin_attrib = 'subscriptions' - interfaces = set(tuple()) - plugin_attrib_map = {} - plugin_tag_map = {} - subitem = (Subscription,) - -registerStanzaPlugin(Pubsub, Subscriptions) - -class OptionalSetting(object): - interfaces = set(('required',)) - - def setRequired(self, value): - value = bool(value) - if value and not self['required']: - self.xml.append(ET.Element("{%s}required" % self.namespace)) - elif not value and self['required']: - self.delRequired() - - def getRequired(self): - required = self.xml.find("{%s}required" % self.namespace) - if required is not None: - return True - else: - return False - - def delRequired(self): - required = self.xml.find("{%s}required" % self.namespace) - if required is not None: - self.xml.remove(required) - - -class SubscribeOptions(ElementBase, OptionalSetting): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'subscribe-options' - plugin_attrib = 'suboptions' - plugin_attrib_map = {} - plugin_tag_map = {} - interfaces = set(('required',)) - -registerStanzaPlugin(Subscription, SubscribeOptions) - -class Item(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'item' - plugin_attrib = name - interfaces = set(('id', 'payload')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setPayload(self, value): - self.xml.append(value) - - def getPayload(self): - childs = self.xml.getchildren() - if len(childs) > 0: - return childs[0] - - def delPayload(self): - for child in self.xml.getchildren(): - self.xml.remove(child) - -class Items(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'items' - plugin_attrib = 'items' - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - subitem = (Item,) - -registerStanzaPlugin(Pubsub, Items) - -class Create(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'create' - plugin_attrib = name - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Pubsub, Create) - -#class Default(ElementBase): -# namespace = 'http://jabber.org/protocol/pubsub' -# name = 'default' -# plugin_attrib = name -# interfaces = set(('node', 'type')) -# plugin_attrib_map = {} -# plugin_tag_map = {} -# -# def getType(self): -# t = self._getAttr('type') -# if not t: t == 'leaf' -# return t -# -#registerStanzaPlugin(Pubsub, Default) - -class Publish(Items): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'publish' - plugin_attrib = name - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - subitem = (Item,) - -registerStanzaPlugin(Pubsub, Publish) - -class Retract(Items): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'retract' - plugin_attrib = name - interfaces = set(('node', 'notify')) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Pubsub, Retract) - -class Unsubscribe(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'unsubscribe' - plugin_attrib = name - interfaces = set(('node', 'jid')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setJid(self, value): - self._setAttr('jid', str(value)) - - def getJid(self): - return JID(self._getAttr('jid')) - -registerStanzaPlugin(Pubsub, Unsubscribe) - -class Subscribe(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'subscribe' - plugin_attrib = name - interfaces = set(('node', 'jid')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setJid(self, value): - self._setAttr('jid', str(value)) - - def getJid(self): - return JID(self._getAttr('jid')) - -registerStanzaPlugin(Pubsub, Subscribe) - -class Configure(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'configure' - plugin_attrib = name - interfaces = set(('node', 'type')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def getType(self): - t = self._getAttr('type') - if not t: t == 'leaf' - return t - -registerStanzaPlugin(Pubsub, Configure) -registerStanzaPlugin(Configure, xep_0004.Form) - -class DefaultConfig(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#owner' - name = 'default' - plugin_attrib = 'default' - interfaces = set(('node', 'type', 'config')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def __init__(self, *args, **kwargs): - ElementBase.__init__(self, *args, **kwargs) - - def getType(self): - t = self._getAttr('type') - if not t: t = 'leaf' - return t - - def getConfig(self): - return self['form'] - - def setConfig(self, value): - self['form'].setStanzaValues(value.getStanzaValues()) - return self - -registerStanzaPlugin(PubsubOwner, DefaultConfig) -registerStanzaPlugin(DefaultConfig, xep_0004.Form) - -class Options(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub' - name = 'options' - plugin_attrib = 'options' - interfaces = set(('jid', 'node', 'options')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def __init__(self, *args, **kwargs): - ElementBase.__init__(self, *args, **kwargs) - - def getOptions(self): - config = self.xml.find('{jabber:x:data}x') - form = xep_0004.Form() - if config is not None: - form.fromXML(config) - return form - - def setOptions(self, value): - self.xml.append(value.getXML()) - return self - - def delOptions(self): - config = self.xml.find('{jabber:x:data}x') - self.xml.remove(config) - - def setJid(self, value): - self._setAttr('jid', str(value)) - - def getJid(self): - return JID(self._getAttr('jid')) - -registerStanzaPlugin(Pubsub, Options) -registerStanzaPlugin(Subscribe, Options) - -class OwnerAffiliations(Affiliations): - namespace = 'http://jabber.org/protocol/pubsub#owner' - interfaces = set(('node')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def append(self, affiliation): - if not isinstance(affiliation, OwnerAffiliation): - raise TypeError - self.xml.append(affiliation.xml) - return self.affiliations.append(affiliation) - -registerStanzaPlugin(PubsubOwner, OwnerAffiliations) - -class OwnerAffiliation(Affiliation): - namespace = 'http://jabber.org/protocol/pubsub#owner' - interfaces = set(('affiliation', 'jid')) - plugin_attrib_map = {} - plugin_tag_map = {} - -class OwnerConfigure(Configure): - namespace = 'http://jabber.org/protocol/pubsub#owner' - interfaces = set(('node', 'config')) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(PubsubOwner, OwnerConfigure) - -class OwnerDefault(OwnerConfigure): - namespace = 'http://jabber.org/protocol/pubsub#owner' - interfaces = set(('node', 'config')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def getConfig(self): - return self['form'] - - def setConfig(self, value): - self['form'].setStanzaValues(value.getStanzaValues()) - return self - -registerStanzaPlugin(PubsubOwner, OwnerDefault) -registerStanzaPlugin(OwnerDefault, xep_0004.Form) - -class OwnerDelete(ElementBase, OptionalSetting): - namespace = 'http://jabber.org/protocol/pubsub#owner' - name = 'delete' - plugin_attrib = 'delete' - plugin_attrib_map = {} - plugin_tag_map = {} - interfaces = set(('node',)) - -registerStanzaPlugin(PubsubOwner, OwnerDelete) - -class OwnerPurge(ElementBase, OptionalSetting): - namespace = 'http://jabber.org/protocol/pubsub#owner' - name = 'purge' - plugin_attrib = name - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(PubsubOwner, OwnerPurge) - -class OwnerRedirect(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#owner' - name = 'redirect' - plugin_attrib = name - interfaces = set(('node', 'jid')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setJid(self, value): - self._setAttr('jid', str(value)) - - def getJid(self): - return JID(self._getAttr('jid')) - -registerStanzaPlugin(OwnerDelete, OwnerRedirect) - -class OwnerSubscriptions(Subscriptions): - namespace = 'http://jabber.org/protocol/pubsub#owner' - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - - def append(self, subscription): - if not isinstance(subscription, OwnerSubscription): - raise TypeError - self.xml.append(subscription.xml) - return self.subscriptions.append(subscription) - -registerStanzaPlugin(PubsubOwner, OwnerSubscriptions) - -class OwnerSubscription(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#owner' - name = 'subscription' - plugin_attrib = name - interfaces = set(('jid', 'subscription')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setJid(self, value): - self._setAttr('jid', str(value)) - - def getJid(self): - return JID(self._getAttr('from')) - -class Event(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'event' - plugin_attrib = 'pubsub_event' - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Message, Event) - -class EventItem(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'item' - plugin_attrib = 'item' - interfaces = set(('id', 'payload')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setPayload(self, value): - self.xml.append(value) - - def getPayload(self): - childs = self.xml.getchildren() - if len(childs) > 0: - return childs[0] - - def delPayload(self): - for child in self.xml.getchildren(): - self.xml.remove(child) - - -class EventRetract(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'retract' - plugin_attrib = 'retract' - interfaces = set(('id',)) - plugin_attrib_map = {} - plugin_tag_map = {} - -class EventItems(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'items' - plugin_attrib = 'items' - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - subitem = (EventItem, EventRetract) - -registerStanzaPlugin(Event, EventItems) - -class EventCollection(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'collection' - plugin_attrib = name - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Event, EventCollection) - -class EventAssociate(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'associate' - plugin_attrib = name - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(EventCollection, EventAssociate) - -class EventDisassociate(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'disassociate' - plugin_attrib = name - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(EventCollection, EventDisassociate) - -class EventConfiguration(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'configuration' - plugin_attrib = name - interfaces = set(('node', 'config')) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Event, EventConfiguration) -registerStanzaPlugin(EventConfiguration, xep_0004.Form) - -class EventPurge(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'purge' - plugin_attrib = name - interfaces = set(('node',)) - plugin_attrib_map = {} - plugin_tag_map = {} - -registerStanzaPlugin(Event, EventPurge) - -class EventSubscription(ElementBase): - namespace = 'http://jabber.org/protocol/pubsub#event' - name = 'subscription' - plugin_attrib = name - interfaces = set(('node','expiry', 'jid', 'subid', 'subscription')) - plugin_attrib_map = {} - plugin_tag_map = {} - - def setJid(self, value): - self._setAttr('jid', str(value)) - - def getJid(self): - return JID(self._getAttr('jid')) - -registerStanzaPlugin(Event, EventSubscription) diff --git a/sleekxmpp/plugins/xep_0060/__init__.py b/sleekxmpp/plugins/xep_0060/__init__.py new file mode 100644 index 00000000..026f7c2b --- /dev/null +++ b/sleekxmpp/plugins/xep_0060/__init__.py @@ -0,0 +1,2 @@ +from sleekxmpp.plugins.xep_0060.pubsub import xep_0060 +from sleekxmpp.plugins.xep_0060 import stanza diff --git a/sleekxmpp/plugins/xep_0060/pubsub.py b/sleekxmpp/plugins/xep_0060/pubsub.py new file mode 100644 index 00000000..e199be07 --- /dev/null +++ b/sleekxmpp/plugins/xep_0060/pubsub.py @@ -0,0 +1,313 @@ +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, '') + diff --git a/sleekxmpp/plugins/xep_0060/stanza/__init__.py b/sleekxmpp/plugins/xep_0060/stanza/__init__.py new file mode 100644 index 00000000..d7cd91a8 --- /dev/null +++ b/sleekxmpp/plugins/xep_0060/stanza/__init__.py @@ -0,0 +1,3 @@ +from sleekxmpp.plugins.xep_0060.stanza.pubsub import Pubsub, Affiliation, Affiliations, Subscription, Subscriptions, SubscribeOptions, Item, Items, Create, Publish, Retract, Unsubscribe, Subscribe, Configure, Options, PubsubState, PubsubStateEvent +from sleekxmpp.plugins.xep_0060.stanza.pubsub_owner import PubsubOwner, DefaultConfig, OwnerAffiliations, OwnerAffiliation, OwnerConfigure, OwnerDefault, OwnerDelete, OwnerPurge, OwnerRedirect, OwnerSubscriptions, OwnerSubscription +from sleekxmpp.plugins.xep_0060.stanza.pubsub_event import Event, EventItem, EventRetract, EventItems, EventCollection, EventAssociate, EventDisassociate, EventConfiguration, EventPurge, EventSubscription diff --git a/sleekxmpp/plugins/xep_0060/stanza/base.py b/sleekxmpp/plugins/xep_0060/stanza/base.py new file mode 100644 index 00000000..9b1efe1b --- /dev/null +++ b/sleekxmpp/plugins/xep_0060/stanza/base.py @@ -0,0 +1,24 @@ +from xml.etree import cElementTree as ET + +class OptionalSetting(object): + interfaces = set(('required',)) + + def setRequired(self, value): + value = bool(value) + if value and not self['required']: + self.xml.append(ET.Element("{%s}required" % self.namespace)) + elif not value and self['required']: + self.delRequired() + + def getRequired(self): + required = self.xml.find("{%s}required" % self.namespace) + if required is not None: + return True + else: + return False + + def delRequired(self): + required = self.xml.find("{%s}required" % self.namespace) + if required is not None: + self.xml.remove(required) + diff --git a/sleekxmpp/plugins/xep_0060/stanza/pubsub.py b/sleekxmpp/plugins/xep_0060/stanza/pubsub.py new file mode 100644 index 00000000..96655942 --- /dev/null +++ b/sleekxmpp/plugins/xep_0060/stanza/pubsub.py @@ -0,0 +1,277 @@ +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID +from sleekxmpp.stanza.iq import Iq +from sleekxmpp.stanza.message import Message +from sleekxmpp.basexmpp import basexmpp +from sleekxmpp.xmlstream.xmlstream import XMLStream +import logging +from sleekxmpp.plugins import xep_0004 +from sleekxmpp.plugins.xep_0060.stanza.base import OptionalSetting + + +class Pubsub(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'pubsub' + plugin_attrib = 'pubsub' + interfaces = set(tuple()) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Iq, Pubsub) + + +class Affiliation(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'affiliation' + plugin_attrib = name + interfaces = set(('node', 'affiliation')) + plugin_attrib_map = {} + plugin_tag_map = {} + +class Affiliations(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'affiliations' + plugin_attrib = 'affiliations' + interfaces = set(tuple()) + plugin_attrib_map = {} + plugin_tag_map = {} + subitem = (Affiliation,) + + def append(self, affiliation): + if not isinstance(affiliation, Affiliation): + raise TypeError + self.xml.append(affiliation.xml) + return self.iterables.append(affiliation) + +registerStanzaPlugin(Pubsub, Affiliations) + + +class Subscription(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'subscription' + plugin_attrib = name + interfaces = set(('jid', 'node', 'subscription', 'subid')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setjid(self, value): + self._setattr('jid', str(value)) + + def getjid(self): + return jid(self._getattr('jid')) + +registerStanzaPlugin(Pubsub, Subscription) + +class Subscriptions(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'subscriptions' + plugin_attrib = 'subscriptions' + interfaces = set(tuple()) + plugin_attrib_map = {} + plugin_tag_map = {} + subitem = (Subscription,) + +registerStanzaPlugin(Pubsub, Subscriptions) + + +class SubscribeOptions(ElementBase, OptionalSetting): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'subscribe-options' + plugin_attrib = 'suboptions' + plugin_attrib_map = {} + plugin_tag_map = {} + interfaces = set(('required',)) + +registerStanzaPlugin(Subscription, SubscribeOptions) + +class Item(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'item' + plugin_attrib = name + interfaces = set(('id', 'payload')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setPayload(self, value): + self.xml.append(value) + + def getPayload(self): + childs = self.xml.getchildren() + if len(childs) > 0: + return childs[0] + + def delPayload(self): + for child in self.xml.getchildren(): + self.xml.remove(child) + +class Items(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'items' + plugin_attrib = 'items' + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + subitem = (Item,) + +registerStanzaPlugin(Pubsub, Items) + +class Create(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'create' + plugin_attrib = name + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Pubsub, Create) + +#class Default(ElementBase): +# namespace = 'http://jabber.org/protocol/pubsub' +# name = 'default' +# plugin_attrib = name +# interfaces = set(('node', 'type')) +# plugin_attrib_map = {} +# plugin_tag_map = {} +# +# def getType(self): +# t = self._getAttr('type') +# if not t: t == 'leaf' +# return t +# +#registerStanzaPlugin(Pubsub, Default) + +class Publish(Items): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'publish' + plugin_attrib = name + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + subitem = (Item,) + +registerStanzaPlugin(Pubsub, Publish) + +class Retract(Items): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'retract' + plugin_attrib = name + interfaces = set(('node', 'notify')) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Pubsub, Retract) + +class Unsubscribe(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'unsubscribe' + plugin_attrib = name + interfaces = set(('node', 'jid')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setJid(self, value): + self._setAttr('jid', str(value)) + + def getJid(self): + return JID(self._getAttr('jid')) + +registerStanzaPlugin(Pubsub, Unsubscribe) + +class Subscribe(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'subscribe' + plugin_attrib = name + interfaces = set(('node', 'jid')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setJid(self, value): + self._setAttr('jid', str(value)) + + def getJid(self): + return JID(self._getAttr('jid')) + +registerStanzaPlugin(Pubsub, Subscribe) + +class Configure(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'configure' + plugin_attrib = name + interfaces = set(('node', 'type')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def getType(self): + t = self._getAttr('type') + if not t: t == 'leaf' + return t + +registerStanzaPlugin(Pubsub, Configure) +registerStanzaPlugin(Configure, xep_0004.Form) + +class Options(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub' + name = 'options' + plugin_attrib = 'options' + interfaces = set(('jid', 'node', 'options')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def __init__(self, *args, **kwargs): + ElementBase.__init__(self, *args, **kwargs) + + def getOptions(self): + config = self.xml.find('{jabber:x:data}x') + form = xep_0004.Form() + if config is not None: + form.fromXML(config) + return form + + def setOptions(self, value): + self.xml.append(value.getXML()) + return self + + def delOptions(self): + config = self.xml.find('{jabber:x:data}x') + self.xml.remove(config) + + def setJid(self, value): + self._setAttr('jid', str(value)) + + def getJid(self): + return JID(self._getAttr('jid')) + +registerStanzaPlugin(Pubsub, Options) +registerStanzaPlugin(Subscribe, Options) + +class PubsubState(ElementBase): + namespace = 'http://jabber.org/protocol/psstate' + name = 'state' + plugin_attrib = 'psstate' + interfaces = set(('node', 'item', 'payload')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setPayload(self, value): + self.xml.append(value) + + def getPayload(self): + childs = self.xml.getchildren() + if len(childs) > 0: + return childs[0] + + def delPayload(self): + for child in self.xml.getchildren(): + self.xml.remove(child) + +registerStanzaPlugin(Iq, PubsubState) + +class PubsubStateEvent(ElementBase): + namespace = 'http://jabber.org/protocol/psstate#event' + name = 'event' + plugin_attrib = 'psstate_event' + intefaces = set(tuple()) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Message, PubsubStateEvent) +registerStanzaPlugin(PubsubStateEvent, PubsubState) diff --git a/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py b/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py new file mode 100644 index 00000000..2dfe6c4a --- /dev/null +++ b/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py @@ -0,0 +1,124 @@ +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID +from sleekxmpp.stanza.iq import Iq +from sleekxmpp.stanza.message import Message +from sleekxmpp.basexmpp import basexmpp +from sleekxmpp.xmlstream.xmlstream import XMLStream +import logging +from sleekxmpp.plugins import xep_0004 + +class Event(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'event' + plugin_attrib = 'pubsub_event' + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Message, Event) + +class EventItem(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'item' + plugin_attrib = 'item' + interfaces = set(('id', 'payload')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setPayload(self, value): + self.xml.append(value) + + def getPayload(self): + childs = self.xml.getchildren() + if len(childs) > 0: + return childs[0] + + def delPayload(self): + for child in self.xml.getchildren(): + self.xml.remove(child) + + +class EventRetract(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'retract' + plugin_attrib = 'retract' + interfaces = set(('id',)) + plugin_attrib_map = {} + plugin_tag_map = {} + +class EventItems(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'items' + plugin_attrib = 'items' + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + subitem = (EventItem, EventRetract) + +registerStanzaPlugin(Event, EventItems) + +class EventCollection(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'collection' + plugin_attrib = name + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Event, EventCollection) + +class EventAssociate(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'associate' + plugin_attrib = name + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(EventCollection, EventAssociate) + +class EventDisassociate(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'disassociate' + plugin_attrib = name + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(EventCollection, EventDisassociate) + +class EventConfiguration(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'configuration' + plugin_attrib = name + interfaces = set(('node', 'config')) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Event, EventConfiguration) +registerStanzaPlugin(EventConfiguration, xep_0004.Form) + +class EventPurge(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'purge' + plugin_attrib = name + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Event, EventPurge) + +class EventSubscription(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'subscription' + plugin_attrib = name + interfaces = set(('node','expiry', 'jid', 'subid', 'subscription')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setJid(self, value): + self._setAttr('jid', str(value)) + + def getJid(self): + return JID(self._getAttr('jid')) + +registerStanzaPlugin(Event, EventSubscription) diff --git a/sleekxmpp/plugins/xep_0060/stanza/pubsub_owner.py b/sleekxmpp/plugins/xep_0060/stanza/pubsub_owner.py new file mode 100644 index 00000000..a90780cc --- /dev/null +++ b/sleekxmpp/plugins/xep_0060/stanza/pubsub_owner.py @@ -0,0 +1,152 @@ +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID +from sleekxmpp.stanza.iq import Iq +from sleekxmpp.stanza.message import Message +from sleekxmpp.basexmpp import basexmpp +from sleekxmpp.xmlstream.xmlstream import XMLStream +import logging +from sleekxmpp.plugins import xep_0004 +from sleekxmpp.plugins.xep_0060.stanza.base import OptionalSetting +from sleekxmpp.plugins.xep_0060.stanza.pubsub import Affiliations, Affiliation, Configure, Subscriptions + +class PubsubOwner(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#owner' + name = 'pubsub' + plugin_attrib = 'pubsub_owner' + interfaces = set(tuple()) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(Iq, PubsubOwner) + +class DefaultConfig(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#owner' + name = 'default' + plugin_attrib = 'default' + interfaces = set(('node', 'type', 'config')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def __init__(self, *args, **kwargs): + ElementBase.__init__(self, *args, **kwargs) + + def getType(self): + t = self._getAttr('type') + if not t: t = 'leaf' + return t + + def getConfig(self): + return self['form'] + + def setConfig(self, value): + self['form'].setStanzaValues(value.getStanzaValues()) + return self + +registerStanzaPlugin(PubsubOwner, DefaultConfig) +registerStanzaPlugin(DefaultConfig, xep_0004.Form) + +class OwnerAffiliations(Affiliations): + namespace = 'http://jabber.org/protocol/pubsub#owner' + interfaces = set(('node')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def append(self, affiliation): + if not isinstance(affiliation, OwnerAffiliation): + raise TypeError + self.xml.append(affiliation.xml) + return self.affiliations.append(affiliation) + +registerStanzaPlugin(PubsubOwner, OwnerAffiliations) + +class OwnerAffiliation(Affiliation): + namespace = 'http://jabber.org/protocol/pubsub#owner' + interfaces = set(('affiliation', 'jid')) + plugin_attrib_map = {} + plugin_tag_map = {} + +class OwnerConfigure(Configure): + namespace = 'http://jabber.org/protocol/pubsub#owner' + interfaces = set(('node', 'config')) + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(PubsubOwner, OwnerConfigure) + +class OwnerDefault(OwnerConfigure): + namespace = 'http://jabber.org/protocol/pubsub#owner' + interfaces = set(('node', 'config')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def getConfig(self): + return self['form'] + + def setConfig(self, value): + self['form'].setStanzaValues(value.getStanzaValues()) + return self + +registerStanzaPlugin(PubsubOwner, OwnerDefault) +registerStanzaPlugin(OwnerDefault, xep_0004.Form) + +class OwnerDelete(ElementBase, OptionalSetting): + namespace = 'http://jabber.org/protocol/pubsub#owner' + name = 'delete' + plugin_attrib = 'delete' + plugin_attrib_map = {} + plugin_tag_map = {} + interfaces = set(('node',)) + +registerStanzaPlugin(PubsubOwner, OwnerDelete) + +class OwnerPurge(ElementBase, OptionalSetting): + namespace = 'http://jabber.org/protocol/pubsub#owner' + name = 'purge' + plugin_attrib = name + plugin_attrib_map = {} + plugin_tag_map = {} + +registerStanzaPlugin(PubsubOwner, OwnerPurge) + +class OwnerRedirect(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#owner' + name = 'redirect' + plugin_attrib = name + interfaces = set(('node', 'jid')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setJid(self, value): + self._setAttr('jid', str(value)) + + def getJid(self): + return JID(self._getAttr('jid')) + +registerStanzaPlugin(OwnerDelete, OwnerRedirect) + +class OwnerSubscriptions(Subscriptions): + namespace = 'http://jabber.org/protocol/pubsub#owner' + interfaces = set(('node',)) + plugin_attrib_map = {} + plugin_tag_map = {} + + def append(self, subscription): + if not isinstance(subscription, OwnerSubscription): + raise TypeError + self.xml.append(subscription.xml) + return self.subscriptions.append(subscription) + +registerStanzaPlugin(PubsubOwner, OwnerSubscriptions) + +class OwnerSubscription(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#owner' + name = 'subscription' + plugin_attrib = name + interfaces = set(('jid', 'subscription')) + plugin_attrib_map = {} + plugin_tag_map = {} + + def setJid(self, value): + self._setAttr('jid', str(value)) + + def getJid(self): + return JID(self._getAttr('from')) |