diff options
Diffstat (limited to 'sleekxmpp/plugins/xep_0060')
-rw-r--r-- | sleekxmpp/plugins/xep_0060/__init__.py | 19 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/pubsub.py | 162 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py | 47 |
3 files changed, 201 insertions, 27 deletions
diff --git a/sleekxmpp/plugins/xep_0060/__init__.py b/sleekxmpp/plugins/xep_0060/__init__.py index 026f7c2b..86e2f472 100644 --- a/sleekxmpp/plugins/xep_0060/__init__.py +++ b/sleekxmpp/plugins/xep_0060/__init__.py @@ -1,2 +1,19 @@ -from sleekxmpp.plugins.xep_0060.pubsub import xep_0060 +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.base import register_plugin + +from sleekxmpp.plugins.xep_0060.pubsub import XEP_0060 from sleekxmpp.plugins.xep_0060 import stanza + + +register_plugin(XEP_0060) + + +# Retain some backwards compatibility +xep_0060 = XEP_0060 diff --git a/sleekxmpp/plugins/xep_0060/pubsub.py b/sleekxmpp/plugins/xep_0060/pubsub.py index 9e394ef2..31e59be9 100644 --- a/sleekxmpp/plugins/xep_0060/pubsub.py +++ b/sleekxmpp/plugins/xep_0060/pubsub.py @@ -9,23 +9,138 @@ import logging from sleekxmpp.xmlstream import JID -from sleekxmpp.plugins.base import base_plugin +from sleekxmpp.xmlstream.handler import Callback +from sleekxmpp.xmlstream.matcher import StanzaPath +from sleekxmpp.plugins.base import BasePlugin from sleekxmpp.plugins.xep_0060 import stanza log = logging.getLogger(__name__) -class xep_0060(base_plugin): +class XEP_0060(BasePlugin): """ XEP-0060 Publish Subscribe """ + name = 'xep_0060' + description = 'XEP-0060: Publish-Subscribe' + dependencies = set(['xep_0030', 'xep_0004']) + stanza = stanza + def plugin_init(self): - self.xep = '0060' - self.description = 'Publish-Subscribe' - self.stanza = stanza + self.node_event_map = {} + + self.xmpp.register_handler( + Callback('Pubsub Event: Items', + StanzaPath('message/pubsub_event/items'), + self._handle_event_items)) + self.xmpp.register_handler( + Callback('Pubsub Event: Purge', + StanzaPath('message/pubsub_event/purge'), + self._handle_event_purge)) + self.xmpp.register_handler( + Callback('Pubsub Event: Delete', + StanzaPath('message/pubsub_event/delete'), + self._handle_event_delete)) + self.xmpp.register_handler( + Callback('Pubsub Event: Configuration', + StanzaPath('message/pubsub_event/configuration'), + self._handle_event_configuration)) + self.xmpp.register_handler( + Callback('Pubsub Event: Subscription', + StanzaPath('message/pubsub_event/subscription'), + self._handle_event_subscription)) + + def _handle_event_items(self, msg): + """Raise events for publish and retraction notifications.""" + node = msg['pubsub_event']['items']['node'] + + multi = len(msg['pubsub_event']['items']) > 1 + values = {} + if multi: + values = msg.values + del values['pubsub_event'] + + for item in msg['pubsub_event']['items']: + event_name = self.node_event_map.get(node, None) + event_type = 'publish' + if item.name == 'retract': + event_type = 'retract' + + if multi: + condensed = self.xmpp.Message() + condensed.values = values + condensed['pubsub_event']['items']['node'] = node + condensed['pubsub_event']['items'].append(item) + self.xmpp.event('pubsub_%s' % event_type, msg) + if event_name: + self.xmpp.event('%s_%s' % (event_name, event_type), + condensed) + else: + self.xmpp.event('pubsub_%s' % event_type, msg) + if event_name: + self.xmpp.event('%s_%s' % (event_name, event_type), msg) + + def _handle_event_purge(self, msg): + """Raise events for node purge notifications.""" + node = msg['pubsub_event']['purge']['node'] + event_name = self.node_event_map.get(node, None) + + self.xmpp.event('pubsub_purge', msg) + if event_name: + self.xmpp.event('%s_purge' % event_name, msg) + + def _handle_event_delete(self, msg): + """Raise events for node deletion notifications.""" + node = msg['pubsub_event']['delete']['node'] + event_name = self.node_event_map.get(node, None) + + self.xmpp.event('pubsub_delete', msg) + if event_name: + self.xmpp.event('%s_delete' % event_name, msg) + + def _handle_event_configuration(self, msg): + """Raise events for node configuration notifications.""" + node = msg['pubsub_event']['configuration']['node'] + event_name = self.node_event_map.get(node, None) + + self.xmpp.event('pubsub_config', msg) + if event_name: + self.xmpp.event('%s_config' % event_name, msg) + + def _handle_event_subscription(self, msg): + """Raise events for node subscription notifications.""" + node = msg['pubsub_event']['subscription']['node'] + event_name = self.node_event_map.get(node, None) + + self.xmpp.event('pubsub_subscription', msg) + if event_name: + self.xmpp.event('%s_subscription' % event_name, msg) + + def map_node_event(self, node, event_name): + """ + Map node names to events. + + When a pubsub event is received for the given node, + raise the provided event. + + For example:: + + map_node_event('http://jabber.org/protocol/tune', + 'user_tune') + + will produce the events 'user_tune_publish' and 'user_tune_retract' + when the respective notifications are received from the node + 'http://jabber.org/protocol/tune', among other events. + + Arguments: + node -- The node name to map to an event. + event_name -- The name of the event to raise when a + notification from the given node is received. + """ + self.node_event_map[node] = event_name def create_node(self, jid, node, config=None, ntype=None, ifrom=None, block=True, callback=None, timeout=None): @@ -98,8 +213,9 @@ class xep_0060(base_plugin): ifrom -- Specify the sender's JID. block -- Specify if the send call will block until a response is received, or a timeout occurs. Defaults to True. - timeout -- The length of time (in seconds) to wait for a response - before exiting the send call if blocking is used. + timeout -- The length of time (in seconds) to wait for a + response before exiting the send call if blocking + is used. Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT callback -- Optional reference to a stream handler function. Will be executed when a reply stanza is received. @@ -146,8 +262,9 @@ class xep_0060(base_plugin): ifrom -- Specify the sender's JID. block -- Specify if the send call will block until a response is received, or a timeout occurs. Defaults to True. - timeout -- The length of time (in seconds) to wait for a response - before exiting the send call if blocking is used. + timeout -- The length of time (in seconds) to wait for a + response before exiting the send call if blocking + is used. Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT callback -- Optional reference to a stream handler function. Will be executed when a reply stanza is received. @@ -183,8 +300,9 @@ class xep_0060(base_plugin): iq['pubsub']['affiliations']['node'] = node return iq.send(block=block, callback=callback, timeout=timeout) - def get_subscription_options(self, jid, node=None, user_jid=None, ifrom=None, - block=True, callback=None, timeout=None): + def get_subscription_options(self, jid, node=None, user_jid=None, + ifrom=None, block=True, callback=None, + timeout=None): iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get') if user_jid is None: iq['pubsub']['default']['node'] = node @@ -364,7 +482,7 @@ class xep_0060(base_plugin): """ Discover the nodes provided by a Pubsub service, using disco. """ - return self.xmpp.plugin['xep_0030'].get_items(*args, **kwargs) + return self.xmpp['xep_0030'].get_items(*args, **kwargs) def get_item(self, jid, node, item_id, ifrom=None, block=True, callback=None, timeout=None): @@ -372,7 +490,7 @@ class xep_0060(base_plugin): Retrieve the content of an individual item. """ iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get') - item = self.stanza.Item() + item = stanza.Item() item['id'] = item_id iq['pubsub']['items']['node'] = node iq['pubsub']['items'].append(item) @@ -396,7 +514,7 @@ class xep_0060(base_plugin): if item_ids is not None: for item_id in item_ids: - item = self.stanza.Item() + item = stanza.Item() item['id'] = item_id iq['pubsub']['items'].append(item) @@ -410,12 +528,12 @@ class xep_0060(base_plugin): """ Retrieve the ItemIDs hosted by a given node, using disco. """ - return self.xmpp.plugin['xep_0030'].get_items(jid, node, - ifrom=ifrom, - block=block, - callback=callback, - timeout=timeout, - iterator=iterator) + return self.xmpp['xep_0030'].get_items(jid, node, + ifrom=ifrom, + block=block, + callback=callback, + timeout=timeout, + iterator=iterator) def modify_affiliations(self, jid, node, affiliations=None, ifrom=None, block=True, callback=None, timeout=None): @@ -426,7 +544,7 @@ class xep_0060(base_plugin): affiliations = [] for jid, affiliation in affiliations: - aff = self.stanza.OwnerAffiliation() + aff = stanza.OwnerAffiliation() aff['jid'] = jid aff['affiliation'] = affiliation iq['pubsub_owner']['affiliations'].append(aff) @@ -442,7 +560,7 @@ class xep_0060(base_plugin): subscriptions = [] for jid, subscription in subscriptions: - sub = self.stanza.OwnerSubscription() + sub = stanza.OwnerSubscription() sub['jid'] = jid sub['subscription'] = subscription iq['pubsub_owner']['subscriptions'].append(sub) diff --git a/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py b/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py index c7263577..c0d4929e 100644 --- a/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py +++ b/sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py @@ -6,23 +6,26 @@ See the file LICENSE for copying permission. """ +import datetime as dt + from sleekxmpp import Message from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase, ET, JID from sleekxmpp.plugins.xep_0004 import Form +from sleekxmpp.plugins import xep_0082 class Event(ElementBase): namespace = 'http://jabber.org/protocol/pubsub#event' name = 'event' plugin_attrib = 'pubsub_event' - interfaces = set(('node',)) + interfaces = set() class EventItem(ElementBase): namespace = 'http://jabber.org/protocol/pubsub#event' name = 'item' plugin_attrib = name - interfaces = set(('id', 'payload')) + interfaces = set(('id', 'payload', 'node', 'publisher')) def set_payload(self, value): self.xml.append(value) @@ -76,7 +79,7 @@ class EventConfiguration(ElementBase): namespace = 'http://jabber.org/protocol/pubsub#event' name = 'configuration' plugin_attrib = name - interfaces = set(('node', 'config')) + interfaces = set(('node',)) class EventPurge(ElementBase): @@ -86,12 +89,47 @@ class EventPurge(ElementBase): interfaces = set(('node',)) +class EventDelete(ElementBase): + namespace = 'http://jabber.org/protocol/pubsub#event' + name = 'delete' + plugin_attrib = name + interfaces = set(('node', 'redirect')) + + def set_redirect(self, uri): + del self['redirect'] + redirect = ET.Element('{%s}redirect' % self.namespace) + redirect.attrib['uri'] = uri + self.xml.append(redirect) + + def get_redirect(self): + redirect = self.xml.find('{%s}redirect' % self.namespace) + if redirect is not None: + return redirect.attrib.get('uri', '') + return '' + + def del_redirect(self): + redirect = self.xml.find('{%s}redirect' % self.namespace) + if redirect is not None: + self.xml.remove(redirect) + + class EventSubscription(ElementBase): namespace = 'http://jabber.org/protocol/pubsub#event' name = 'subscription' plugin_attrib = name interfaces = set(('node', 'expiry', 'jid', 'subid', 'subscription')) + def get_expiry(self): + expiry = self._get_attr('expiry') + if expiry.lower() == 'presence': + return expiry + return xep_0082.parse(expiry) + + def set_expiry(self, value): + if isinstance(value, dt.datetime): + value = xep_0082.format_datetime(value) + self._set_attr('expiry', value) + def set_jid(self, value): self._set_attr('jid', str(value)) @@ -102,8 +140,9 @@ class EventSubscription(ElementBase): register_stanza_plugin(Message, Event) register_stanza_plugin(Event, EventCollection) register_stanza_plugin(Event, EventConfiguration) -register_stanza_plugin(Event, EventItems) register_stanza_plugin(Event, EventPurge) +register_stanza_plugin(Event, EventDelete) +register_stanza_plugin(Event, EventItems) register_stanza_plugin(Event, EventSubscription) register_stanza_plugin(EventCollection, EventAssociate) register_stanza_plugin(EventCollection, EventDisassociate) |