Bump to 1.3.1
[slixmpp.git] / sleekxmpp / plugins / xep_0060 / pubsub.py
blobbec5f565738433f5f1fc5c97eaa067901fd1af31
1 """
2 SleekXMPP: The Sleek XMPP Library
3 Copyright (C) 2011 Nathanael C. Fritz
4 This file is part of SleekXMPP.
6 See the file LICENSE for copying permission.
7 """
9 import logging
11 from sleekxmpp.xmlstream import JID
12 from sleekxmpp.xmlstream.handler import Callback
13 from sleekxmpp.xmlstream.matcher import StanzaPath
14 from sleekxmpp.plugins.base import BasePlugin
15 from sleekxmpp.plugins.xep_0060 import stanza
18 log = logging.getLogger(__name__)
21 class XEP_0060(BasePlugin):
23 """
24 XEP-0060 Publish Subscribe
25 """
27 name = 'xep_0060'
28 description = 'XEP-0060: Publish-Subscribe'
29 dependencies = set(['xep_0030', 'xep_0004', 'xep_0082', 'xep_0131'])
30 stanza = stanza
32 def plugin_init(self):
33 self.node_event_map = {}
35 self.xmpp.register_handler(
36 Callback('Pubsub Event: Items',
37 StanzaPath('message/pubsub_event/items'),
38 self._handle_event_items))
39 self.xmpp.register_handler(
40 Callback('Pubsub Event: Purge',
41 StanzaPath('message/pubsub_event/purge'),
42 self._handle_event_purge))
43 self.xmpp.register_handler(
44 Callback('Pubsub Event: Delete',
45 StanzaPath('message/pubsub_event/delete'),
46 self._handle_event_delete))
47 self.xmpp.register_handler(
48 Callback('Pubsub Event: Configuration',
49 StanzaPath('message/pubsub_event/configuration'),
50 self._handle_event_configuration))
51 self.xmpp.register_handler(
52 Callback('Pubsub Event: Subscription',
53 StanzaPath('message/pubsub_event/subscription'),
54 self._handle_event_subscription))
56 self.xmpp['xep_0131'].supported_headers.add('SubID')
58 def plugin_end(self):
59 self.xmpp.remove_handler('Pubsub Event: Items')
60 self.xmpp.remove_handler('Pubsub Event: Purge')
61 self.xmpp.remove_handler('Pubsub Event: Delete')
62 self.xmpp.remove_handler('Pubsub Event: Configuration')
63 self.xmpp.remove_handler('Pubsub Event: Subscription')
65 def _handle_event_items(self, msg):
66 """Raise events for publish and retraction notifications."""
67 node = msg['pubsub_event']['items']['node']
69 multi = len(msg['pubsub_event']['items']) > 1
70 values = {}
71 if multi:
72 values = msg.values
73 del values['pubsub_event']
75 for item in msg['pubsub_event']['items']:
76 event_name = self.node_event_map.get(node, None)
77 event_type = 'publish'
78 if item.name == 'retract':
79 event_type = 'retract'
81 if multi:
82 condensed = self.xmpp.Message()
83 condensed.values = values
84 condensed['pubsub_event']['items']['node'] = node
85 condensed['pubsub_event']['items'].append(item)
86 self.xmpp.event('pubsub_%s' % event_type, msg)
87 if event_name:
88 self.xmpp.event('%s_%s' % (event_name, event_type),
89 condensed)
90 else:
91 self.xmpp.event('pubsub_%s' % event_type, msg)
92 if event_name:
93 self.xmpp.event('%s_%s' % (event_name, event_type), msg)
95 def _handle_event_purge(self, msg):
96 """Raise events for node purge notifications."""
97 node = msg['pubsub_event']['purge']['node']
98 event_name = self.node_event_map.get(node, None)
100 self.xmpp.event('pubsub_purge', msg)
101 if event_name:
102 self.xmpp.event('%s_purge' % event_name, msg)
104 def _handle_event_delete(self, msg):
105 """Raise events for node deletion notifications."""
106 node = msg['pubsub_event']['delete']['node']
107 event_name = self.node_event_map.get(node, None)
109 self.xmpp.event('pubsub_delete', msg)
110 if event_name:
111 self.xmpp.event('%s_delete' % event_name, msg)
113 def _handle_event_configuration(self, msg):
114 """Raise events for node configuration notifications."""
115 node = msg['pubsub_event']['configuration']['node']
116 event_name = self.node_event_map.get(node, None)
118 self.xmpp.event('pubsub_config', msg)
119 if event_name:
120 self.xmpp.event('%s_config' % event_name, msg)
122 def _handle_event_subscription(self, msg):
123 """Raise events for node subscription notifications."""
124 node = msg['pubsub_event']['subscription']['node']
125 event_name = self.node_event_map.get(node, None)
127 self.xmpp.event('pubsub_subscription', msg)
128 if event_name:
129 self.xmpp.event('%s_subscription' % event_name, msg)
131 def map_node_event(self, node, event_name):
133 Map node names to events.
135 When a pubsub event is received for the given node,
136 raise the provided event.
138 For example::
140 map_node_event('http://jabber.org/protocol/tune',
141 'user_tune')
143 will produce the events 'user_tune_publish' and 'user_tune_retract'
144 when the respective notifications are received from the node
145 'http://jabber.org/protocol/tune', among other events.
147 Arguments:
148 node -- The node name to map to an event.
149 event_name -- The name of the event to raise when a
150 notification from the given node is received.
152 self.node_event_map[node] = event_name
154 def create_node(self, jid, node, config=None, ntype=None, ifrom=None,
155 block=True, callback=None, timeout=None):
157 Create and configure a new pubsub node.
159 A server MAY use a different name for the node than the one provided,
160 so be sure to check the result stanza for a server assigned name.
162 If no configuration form is provided, the node will be created using
163 the server's default configuration. To get the default configuration
164 use get_node_config().
166 Arguments:
167 jid -- The JID of the pubsub service.
168 node -- Optional name of the node to create. If no name is
169 provided, the server MAY generate a node ID for you.
170 The server can also assign a different name than the
171 one you provide; check the result stanza to see if
172 the server assigned a name.
173 config -- Optional XEP-0004 data form of configuration settings.
174 ntype -- The type of node to create. Servers typically default
175 to using 'leaf' if no type is provided.
176 ifrom -- Specify the sender's JID.
177 block -- Specify if the send call will block until a response
178 is received, or a timeout occurs. Defaults to True.
179 timeout -- The length of time (in seconds) to wait for a response
180 before exiting the send call if blocking is used.
181 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
182 callback -- Optional reference to a stream handler function. Will
183 be executed when a reply stanza is received.
185 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
186 iq['pubsub']['create']['node'] = node
188 if config is not None:
189 form_type = 'http://jabber.org/protocol/pubsub#node_config'
190 if 'FORM_TYPE' in config['fields']:
191 config.field['FORM_TYPE']['value'] = form_type
192 else:
193 config.add_field(var='FORM_TYPE',
194 ftype='hidden',
195 value=form_type)
196 if ntype:
197 if 'pubsub#node_type' in config['fields']:
198 config.field['pubsub#node_type']['value'] = ntype
199 else:
200 config.add_field(var='pubsub#node_type', value=ntype)
201 iq['pubsub']['configure'].append(config)
203 return iq.send(block=block, callback=callback, timeout=timeout)
205 def subscribe(self, jid, node, bare=True, subscribee=None, options=None,
206 ifrom=None, block=True, callback=None, timeout=None):
208 Subscribe to updates from a pubsub node.
210 The rules for determining the JID that is subscribing to the node are:
211 1. If subscribee is given, use that as provided.
212 2. If ifrom was given, use the bare or full version based on bare.
213 3. Otherwise, use self.xmpp.boundjid based on bare.
215 Arguments:
216 jid -- The pubsub service JID.
217 node -- The node to subscribe to.
218 bare -- Indicates if the subscribee is a bare or full JID.
219 Defaults to True for a bare JID.
220 subscribee -- The JID that is subscribing to the node.
221 options --
222 ifrom -- Specify the sender's JID.
223 block -- Specify if the send call will block until a response
224 is received, or a timeout occurs. Defaults to True.
225 timeout -- The length of time (in seconds) to wait for a
226 response before exiting the send call if blocking
227 is used.
228 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
229 callback -- Optional reference to a stream handler function. Will
230 be executed when a reply stanza is received.
232 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
233 iq['pubsub']['subscribe']['node'] = node
235 if subscribee is None:
236 if ifrom:
237 if bare:
238 subscribee = JID(ifrom).bare
239 else:
240 subscribee = ifrom
241 else:
242 if bare:
243 subscribee = self.xmpp.boundjid.bare
244 else:
245 subscribee = self.xmpp.boundjid
247 iq['pubsub']['subscribe']['jid'] = subscribee
248 if options is not None:
249 iq['pubsub']['options'].append(options)
250 return iq.send(block=block, callback=callback, timeout=timeout)
252 def unsubscribe(self, jid, node, subid=None, bare=True, subscribee=None,
253 ifrom=None, block=True, callback=None, timeout=None):
255 Unubscribe from updates from a pubsub node.
257 The rules for determining the JID that is unsubscribing
258 from the node are:
259 1. If subscribee is given, use that as provided.
260 2. If ifrom was given, use the bare or full version based on bare.
261 3. Otherwise, use self.xmpp.boundjid based on bare.
263 Arguments:
264 jid -- The pubsub service JID.
265 node -- The node to subscribe to.
266 subid -- The specific subscription, if multiple subscriptions
267 exist for this JID/node combination.
268 bare -- Indicates if the subscribee is a bare or full JID.
269 Defaults to True for a bare JID.
270 subscribee -- The JID that is subscribing to the node.
271 ifrom -- Specify the sender's JID.
272 block -- Specify if the send call will block until a response
273 is received, or a timeout occurs. Defaults to True.
274 timeout -- The length of time (in seconds) to wait for a
275 response before exiting the send call if blocking
276 is used.
277 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
278 callback -- Optional reference to a stream handler function. Will
279 be executed when a reply stanza is received.
281 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
282 iq['pubsub']['unsubscribe']['node'] = node
284 if subscribee is None:
285 if ifrom:
286 if bare:
287 subscribee = JID(ifrom).bare
288 else:
289 subscribee = ifrom
290 else:
291 if bare:
292 subscribee = self.xmpp.boundjid.bare
293 else:
294 subscribee = self.xmpp.boundjid
296 iq['pubsub']['unsubscribe']['jid'] = subscribee
297 iq['pubsub']['unsubscribe']['subid'] = subid
298 return iq.send(block=block, callback=callback, timeout=timeout)
300 def get_subscriptions(self, jid, node=None, ifrom=None, block=True,
301 callback=None, timeout=None):
302 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
303 iq['pubsub']['subscriptions']['node'] = node
304 return iq.send(block=block, callback=callback, timeout=timeout)
306 def get_affiliations(self, jid, node=None, ifrom=None, block=True,
307 callback=None, timeout=None):
308 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
309 iq['pubsub']['affiliations']['node'] = node
310 return iq.send(block=block, callback=callback, timeout=timeout)
312 def get_subscription_options(self, jid, node=None, user_jid=None,
313 ifrom=None, block=True, callback=None,
314 timeout=None):
315 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
316 if user_jid is None:
317 iq['pubsub']['default']['node'] = node
318 else:
319 iq['pubsub']['options']['node'] = node
320 iq['pubsub']['options']['jid'] = user_jid
321 return iq.send(block=block, callback=callback, timeout=timeout)
323 def set_subscription_options(self, jid, node, user_jid, options,
324 ifrom=None, block=True, callback=None,
325 timeout=None):
326 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
327 iq['pubsub']['options']['node'] = node
328 iq['pubsub']['options']['jid'] = user_jid
329 iq['pubsub']['options'].append(options)
330 return iq.send(block=block, callback=callback, timeout=timeout)
332 def get_node_config(self, jid, node=None, ifrom=None, block=True,
333 callback=None, timeout=None):
335 Retrieve the configuration for a node, or the pubsub service's
336 default configuration for new nodes.
338 Arguments:
339 jid -- The JID of the pubsub service.
340 node -- The node to retrieve the configuration for. If None,
341 the default configuration for new nodes will be
342 requested. Defaults to None.
343 ifrom -- Specify the sender's JID.
344 block -- Specify if the send call will block until a response
345 is received, or a timeout occurs. Defaults to True.
346 timeout -- The length of time (in seconds) to wait for a response
347 before exiting the send call if blocking is used.
348 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
349 callback -- Optional reference to a stream handler function. Will
350 be executed when a reply stanza is received.
352 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
353 if node is None:
354 iq['pubsub_owner']['default']
355 else:
356 iq['pubsub_owner']['configure']['node'] = node
357 return iq.send(block=block, callback=callback, timeout=timeout)
359 def get_node_subscriptions(self, jid, node, ifrom=None, block=True,
360 callback=None, timeout=None):
362 Retrieve the subscriptions associated with a given node.
364 Arguments:
365 jid -- The JID of the pubsub service.
366 node -- The node to retrieve subscriptions from.
367 ifrom -- Specify the sender's JID.
368 block -- Specify if the send call will block until a response
369 is received, or a timeout occurs. Defaults to True.
370 timeout -- The length of time (in seconds) to wait for a response
371 before exiting the send call if blocking is used.
372 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
373 callback -- Optional reference to a stream handler function. Will
374 be executed when a reply stanza is received.
376 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
377 iq['pubsub_owner']['subscriptions']['node'] = node
378 return iq.send(block=block, callback=callback, timeout=timeout)
380 def get_node_affiliations(self, jid, node, ifrom=None, block=True,
381 callback=None, timeout=None):
383 Retrieve the affiliations associated with a given node.
385 Arguments:
386 jid -- The JID of the pubsub service.
387 node -- The node to retrieve affiliations from.
388 ifrom -- Specify the sender's JID.
389 block -- Specify if the send call will block until a response
390 is received, or a timeout occurs. Defaults to True.
391 timeout -- The length of time (in seconds) to wait for a response
392 before exiting the send call if blocking is used.
393 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
394 callback -- Optional reference to a stream handler function. Will
395 be executed when a reply stanza is received.
397 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
398 iq['pubsub_owner']['affiliations']['node'] = node
399 return iq.send(block=block, callback=callback, timeout=timeout)
401 def delete_node(self, jid, node, ifrom=None, block=True,
402 callback=None, timeout=None):
404 Delete a a pubsub node.
406 Arguments:
407 jid -- The JID of the pubsub service.
408 node -- The node to delete.
409 ifrom -- Specify the sender's JID.
410 block -- Specify if the send call will block until a response
411 is received, or a timeout occurs. Defaults to True.
412 timeout -- The length of time (in seconds) to wait for a response
413 before exiting the send call if blocking is used.
414 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
415 callback -- Optional reference to a stream handler function. Will
416 be executed when a reply stanza is received.
418 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
419 iq['pubsub_owner']['delete']['node'] = node
420 return iq.send(block=block, callback=callback, timeout=timeout)
422 def set_node_config(self, jid, node, config, ifrom=None, block=True,
423 callback=None, timeout=None):
424 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
425 iq['pubsub_owner']['configure']['node'] = node
426 iq['pubsub_owner']['configure'].append(config)
427 return iq.send(block=block, callback=callback, timeout=timeout)
429 def publish(self, jid, node, id=None, payload=None, options=None,
430 ifrom=None, block=True, callback=None, timeout=None):
432 Add a new item to a node, or edit an existing item.
434 For services that support it, you can use the publish command
435 as an event signal by not including an ID or payload.
437 When including a payload and you do not provide an ID then
438 the service will generally create an ID for you.
440 Publish options may be specified, and how those options
441 are processed is left to the service, such as treating
442 the options as preconditions that the node's settings
443 must match.
445 Arguments:
446 jid -- The JID of the pubsub service.
447 node -- The node to publish the item to.
448 id -- Optionally specify the ID of the item.
449 payload -- The item content to publish.
450 options -- A form of publish options.
451 ifrom -- Specify the sender's JID.
452 block -- Specify if the send call will block until a response
453 is received, or a timeout occurs. Defaults to True.
454 timeout -- The length of time (in seconds) to wait for a response
455 before exiting the send call if blocking is used.
456 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
457 callback -- Optional reference to a stream handler function. Will
458 be executed when a reply stanza is received.
460 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
461 iq['pubsub']['publish']['node'] = node
462 if id is not None:
463 iq['pubsub']['publish']['item']['id'] = id
464 if payload is not None:
465 iq['pubsub']['publish']['item']['payload'] = payload
466 iq['pubsub']['publish_options'] = options
467 return iq.send(block=block, callback=callback, timeout=timeout)
469 def retract(self, jid, node, id, notify=None, ifrom=None, block=True,
470 callback=None, timeout=None):
472 Delete a single item from a node.
474 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
476 iq['pubsub']['retract']['node'] = node
477 iq['pubsub']['retract']['notify'] = notify
478 iq['pubsub']['retract']['item']['id'] = id
479 return iq.send(block=block, callback=callback, timeout=timeout)
481 def purge(self, jid, node, ifrom=None, block=True, callback=None,
482 timeout=None):
484 Remove all items from a node.
486 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
487 iq['pubsub_owner']['purge']['node'] = node
488 return iq.send(block=block, callback=callback, timeout=timeout)
490 def get_nodes(self, *args, **kwargs):
492 Discover the nodes provided by a Pubsub service, using disco.
494 return self.xmpp['xep_0030'].get_items(*args, **kwargs)
496 def get_item(self, jid, node, item_id, ifrom=None, block=True,
497 callback=None, timeout=None):
499 Retrieve the content of an individual item.
501 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
502 item = stanza.Item()
503 item['id'] = item_id
504 iq['pubsub']['items']['node'] = node
505 iq['pubsub']['items'].append(item)
506 return iq.send(block=block, callback=callback, timeout=timeout)
508 def get_items(self, jid, node, item_ids=None, max_items=None,
509 iterator=False, ifrom=None, block=False,
510 callback=None, timeout=None):
512 Request the contents of a node's items.
514 The desired items can be specified, or a query for the last
515 few published items can be used.
517 Pubsub services may use result set management for nodes with
518 many items, so an iterator can be returned if needed.
520 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='get')
521 iq['pubsub']['items']['node'] = node
522 iq['pubsub']['items']['max_items'] = max_items
524 if item_ids is not None:
525 for item_id in item_ids:
526 item = stanza.Item()
527 item['id'] = item_id
528 iq['pubsub']['items'].append(item)
530 if iterator:
531 return self.xmpp['xep_0059'].iterate(iq, 'pubsub')
532 else:
533 return iq.send(block=block, callback=callback, timeout=timeout)
535 def get_item_ids(self, jid, node, ifrom=None, block=True,
536 callback=None, timeout=None, iterator=False):
538 Retrieve the ItemIDs hosted by a given node, using disco.
540 return self.xmpp['xep_0030'].get_items(jid, node,
541 ifrom=ifrom,
542 block=block,
543 callback=callback,
544 timeout=timeout,
545 iterator=iterator)
547 def modify_affiliations(self, jid, node, affiliations=None, ifrom=None,
548 block=True, callback=None, timeout=None):
549 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
550 iq['pubsub_owner']['affiliations']['node'] = node
552 if affiliations is None:
553 affiliations = []
555 for jid, affiliation in affiliations:
556 aff = stanza.OwnerAffiliation()
557 aff['jid'] = jid
558 aff['affiliation'] = affiliation
559 iq['pubsub_owner']['affiliations'].append(aff)
561 return iq.send(block=block, callback=callback, timeout=timeout)
563 def modify_subscriptions(self, jid, node, subscriptions=None, ifrom=None,
564 block=True, callback=None, timeout=None):
565 iq = self.xmpp.Iq(sto=jid, sfrom=ifrom, stype='set')
566 iq['pubsub_owner']['subscriptions']['node'] = node
568 if subscriptions is None:
569 subscriptions = []
571 for jid, subscription in subscriptions:
572 sub = stanza.OwnerSubscription()
573 sub['jid'] = jid
574 sub['subscription'] = subscription
575 iq['pubsub_owner']['subscriptions'].append(sub)
577 return iq.send(block=block, callback=callback, timeout=timeout)