summaryrefslogtreecommitdiff
path: root/sleekxmpp/plugins/xep_0060
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/plugins/xep_0060')
-rw-r--r--sleekxmpp/plugins/xep_0060/__init__.py19
-rw-r--r--sleekxmpp/plugins/xep_0060/pubsub.py162
-rw-r--r--sleekxmpp/plugins/xep_0060/stanza/pubsub_event.py47
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)