summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sleekxmpp/plugins/xep_0060/pubsub.py111
-rw-r--r--tests/test_stream_xep_0060.py106
2 files changed, 183 insertions, 34 deletions
diff --git a/sleekxmpp/plugins/xep_0060/pubsub.py b/sleekxmpp/plugins/xep_0060/pubsub.py
index 55362068..7d372222 100644
--- a/sleekxmpp/plugins/xep_0060/pubsub.py
+++ b/sleekxmpp/plugins/xep_0060/pubsub.py
@@ -1,16 +1,22 @@
-from __future__ import with_statement
-from sleekxmpp.plugins import base
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
import logging
-#from xml.etree import cElementTree as ET
-from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET
+
+from sleekxmpp.plugins.base import base_plugin
from sleekxmpp.plugins.xep_0060 import stanza
-from sleekxmpp.plugins.xep_0004 import Form
log = logging.getLogger(__name__)
-class xep_0060(base.base_plugin):
+class xep_0060(base_plugin):
+
"""
XEP-0060 Publish Subscribe
"""
@@ -18,44 +24,81 @@ class xep_0060(base.base_plugin):
def plugin_init(self):
self.xep = '0060'
self.description = 'Publish-Subscribe'
-
- def create_node(self, jid, node, config=None, ntype=None):
- iq = IQ(sto=jid, stype='set', sfrom=self.xmpp.jid)
+ self.stanza = stanza
+
+ def create_node(self, jid, node, config=None, ntype=None, ifrom=None,
+ block=True, callback=None, timeout=None):
+ """
+ Create and configure a new pubsub node.
+
+ A server MAY use a different name for the node than the one provided,
+ so be sure to check the result stanza for a server assigned name.
+
+ If no configuration form is provided, the node will be created using
+ the server's default configuration. To get the default configuration
+ use get_node_config().
+
+ Arguments:
+ jid -- The JID of the pubsub service.
+ node -- Optional name of the node to create. If no name is
+ provided, the server MAY generate a node ID for you.
+ The server can also assign a different name than the
+ one you provide; check the result stanza to see if
+ the server assigned a name.
+ config -- Optional XEP-0004 data form of configuration settings.
+ ntype -- The type of node to create. Servers typically default
+ to using 'leaf' if no type is provided.
+ 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.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ iq = self.xmpp.Iq(sto=jid, stype='set')
+ if ifrom:
+ iq['from'] = ifrom
iq['pubsub']['create']['node'] = node
- if ntype is None:
- ntype = 'leaf'
+
if config is not None:
- if 'FORM_TYPE' in submitform.field:
- config.field['FORM_TYPE'].setValue('http://jabber.org/protocol/pubsub#node_config')
+ form_type = 'http://jabber.org/protocol/pubsub#node_config'
+ if 'FORM_TYPE' in config['fields']:
+ config.field['FORM_TYPE']['value'] = form_type
else:
- config.addField('FORM_TYPE', 'hidden', value='http://jabber.org/protocol/pubsub#node_config')
- if 'pubsub#node_type' in submitform.field:
- config.field['pubsub#node_type'].setValue(ntype)
- else:
- config.addField('pubsub#node_type', value=ntype)
- iq['pubsub']['configure']['form'] = config
- return iq.send()
+ config.add_field(var='FORM_TYPE',
+ ftype='hidden',
+ value=form_type)
+ if ntype:
+ if 'pubsub#node_type' in config['fields']:
+ config.field['pubsub#node_type']['value'] = ntype
+ else:
+ config.add_field(var='pubsub#node_type', value=ntype)
+ iq['pubsub']['configure'].append(config)
+
+ return iq.send(block=block, callback=callback, timeout=timeout)
def subscribe(self, jid, node, bare=True, subscribee=None):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='set')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='set')
iq['pubsub']['subscribe']['node'] = node
if subscribee is None:
if bare:
- iq['pubsub']['subscribe']['jid'] = self.xmpp.jid.bare
+ iq['pubsub']['subscribe']['jid'] = self.xmpp.boundjid.bare
else:
- iq['pubsub']['subscribe']['jid'] = self.xmpp.jid.full
+ iq['pubsub']['subscribe']['jid'] = self.xmpp.boundjid.full
else:
iq['pubsub']['subscribe']['jid'] = subscribee
return iq.send()
def unsubscribe(self, jid, node, subid=None, bare=True, subscribee=None):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='set')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='set')
iq['pubsub']['unsubscribe']['node'] = node
if subscribee is None:
if bare:
- iq['pubsub']['unsubscribe']['jid'] = self.xmpp.jid.bare
+ iq['pubsub']['unsubscribe']['jid'] = self.xmpp.boundjid.bare
else:
- iq['pubsub']['unsubscribe']['jid'] = self.xmpp.jid.full
+ iq['pubsub']['unsubscribe']['jid'] = self.xmpp.boundjid.full
else:
iq['pubsub']['unsubscribe']['jid'] = subscribee
if subid is not None:
@@ -63,7 +106,7 @@ class xep_0060(base.base_plugin):
return iq.send()
def get_node_config(self, jid, node=None): # if no node, then grab default
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='get')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='get')
if node is None:
iq['pubsub_owner']['default']
else:
@@ -71,28 +114,28 @@ class xep_0060(base.base_plugin):
return iq.send()
def get_node_subscriptions(self, jid, node):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='get')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='get')
iq['pubsub_owner']['subscriptions']['node'] = node
return iq.send()
def get_node_affiliations(self, jid, node):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='get')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='get')
iq['pubsub_owner']['affiliations']['node'] = node
return iq.send()
def delete_node(self, jid, node):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='get')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='get')
iq['pubsub_owner']['delete']['node'] = node
return iq.send()
def set_node_config(self, jid, node, config):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='set')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='set')
iq['pubsub_owner']['configure']['node'] = node
iq['pubsub_owner']['configure']['config'] = config
return iq.send()
def publish(self, jid, node, items=[]):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='set')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='set')
iq['pubsub']['publish']['node'] = node
for id, payload in items:
item = stanza.pubsub.Item()
@@ -103,7 +146,7 @@ class xep_0060(base.base_plugin):
return iq.send()
def retract(self, jid, node, item):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='set')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='set')
iq['pubsub']['retract']['node'] = node
item = stanza.pubsub.Item()
item['id'] = item
@@ -117,7 +160,7 @@ class xep_0060(base.base_plugin):
return self.xmpp.plugin['xep_0030'].get_items(jid, node)
def modify_affiliation(self, jid, node, affiliation, user_jid=None):
- iq = IQ(sto=jid, sfrom=self.xmpp.jid, stype='set')
+ iq = self.xmpp.Iq(sto=jid, sfrom=self.xmpp.boundjid, stype='set')
iq['pubsub_owner']['affiliations']
aff = stanza.pubsub.Affiliation()
aff['node'] = node
diff --git a/tests/test_stream_xep_0060.py b/tests/test_stream_xep_0060.py
new file mode 100644
index 00000000..cb93bbf6
--- /dev/null
+++ b/tests/test_stream_xep_0060.py
@@ -0,0 +1,106 @@
+import sys
+import time
+import threading
+
+from sleekxmpp.test import *
+
+
+class TestStreamPubsub(SleekTest):
+
+ """
+ Test using the XEP-0030 plugin.
+ """
+
+ def setUp(self):
+ self.stream_start()
+
+ def tearDown(self):
+ self.stream_close()
+
+ def testCreateInstantNode(self):
+ """Test creating an instant node"""
+ t = threading.Thread(name='create_node',
+ target=self.xmpp['xep_0060'].create_node,
+ args=('pubsub.example.com', None))
+ t.start()
+
+ self.send("""
+ <iq type="set" id="1" to="pubsub.example.com">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <create />
+ </pubsub>
+ </iq>
+ """)
+
+ self.recv("""
+ <iq type="result" id="1"
+ to="tester@localhost" from="pubsub.example.com">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <create node="25e3d37dabbab9541f7523321421edc5bfeb2dae" />
+ </pubsub>
+ </iq>
+ """)
+
+ t.join()
+
+ def testCreateNodeNoConfig(self):
+ """Test creating a node without a config"""
+ t = threading.Thread(name='create_node',
+ target=self.xmpp['xep_0060'].create_node,
+ args=('pubsub.example.com', 'princely_musings'))
+ t.start()
+
+ self.send("""
+ <iq type="set" id="1" to="pubsub.example.com">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <create node="princely_musings" />
+ </pubsub>
+ </iq>
+ """)
+
+ self.recv("""
+ <iq type="result" id="1"
+ to="tester@localhost" from="pubsub.example.com" />
+ """)
+
+ t.join()
+
+ def testCreateNodeConfig(self):
+ """Test creating a node with a config"""
+ form = self.xmpp['xep_0004'].stanza.Form()
+ form['type'] = 'submit'
+ form.add_field(var='pubsub#access_model', value='whitelist')
+
+ t = threading.Thread(name='create_node',
+ target=self.xmpp['xep_0060'].create_node,
+ args=('pubsub.example.com', 'princely_musings'),
+ kwargs={'config': form})
+ t.start()
+
+ self.send("""
+ <iq type="set" id="1" to="pubsub.example.com">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <create node="princely_musings" />
+ <configure>
+ <x xmlns="jabber:x:data" type="submit">
+ <field var="pubsub#access_model">
+ <value>whitelist</value>
+ </field>
+ <field var="FORM_TYPE">
+ <value>http://jabber.org/protocol/pubsub#node_config</value>
+ </field>
+ </x>
+ </configure>
+ </pubsub>
+ </iq>
+ """)
+
+ self.recv("""
+ <iq type="result" id="1"
+ to="tester@localhost" from="pubsub.example.com" />
+ """)
+
+ t.join()
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamPubsub)