summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xexamples/adhoc_user.py3
-rw-r--r--sleekxmpp/plugins/xep_0050/adhoc.py60
-rw-r--r--tests/test_stream_xep_0050.py46
3 files changed, 86 insertions, 23 deletions
diff --git a/examples/adhoc_user.py b/examples/adhoc_user.py
index 738b22cd..5fba78d2 100755
--- a/examples/adhoc_user.py
+++ b/examples/adhoc_user.py
@@ -136,6 +136,7 @@ class CommandUserBot(sleekxmpp.ClientXMPP):
# The session will automatically be cleared if no error
# handler is provided.
self['xep_0050'].terminate_command(session)
+ self.disconnect()
if __name__ == '__main__':
@@ -176,7 +177,7 @@ if __name__ == '__main__':
if opts.other is None:
opts.other = raw_input("JID Providing Commands: ")
if opts.greeting is None:
- opts.other = raw_input("Greeting: ")
+ opts.greeting = raw_input("Greeting: ")
# Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
diff --git a/sleekxmpp/plugins/xep_0050/adhoc.py b/sleekxmpp/plugins/xep_0050/adhoc.py
index dd1c88d6..54be1f86 100644
--- a/sleekxmpp/plugins/xep_0050/adhoc.py
+++ b/sleekxmpp/plugins/xep_0050/adhoc.py
@@ -10,6 +10,7 @@ import logging
import time
from sleekxmpp import Iq
+from sleekxmpp.exceptions import IqError
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.xmlstream import register_stanza_plugin, JID
@@ -91,16 +92,6 @@ class xep_0050(base_plugin):
StanzaPath('iq@type=set/command'),
self._handle_command))
- self.xmpp.register_handler(
- Callback("Ad-Hoc Result",
- StanzaPath('iq@type=result/command'),
- self._handle_command_result))
-
- self.xmpp.register_handler(
- Callback("Ad-Hoc Error",
- StanzaPath('iq@type=error/command'),
- self._handle_command_result))
-
register_stanza_plugin(Iq, stanza.Command)
self.xmpp.add_event_handler('command_execute',
@@ -408,7 +399,7 @@ class xep_0050(base_plugin):
**kwargs)
def send_command(self, jid, node, ifrom=None, action='execute',
- payload=None, sessionid=None, **kwargs):
+ payload=None, sessionid=None, flow=False, **kwargs):
"""
Create and send a command stanza, without using the provided
workflow management APIs.
@@ -422,6 +413,10 @@ class xep_0050(base_plugin):
payload -- Either a list of payload items, or a single
payload item such as a data form.
sessionid -- The current session's ID value.
+ flow -- If True, process the Iq result using the
+ command workflow methods contained in the
+ session instead of returning the response
+ stanza itself. Defaults to False.
block -- Specify if the send call will block until a
response is received, or a timeout occurs.
Defaults to True.
@@ -431,7 +426,7 @@ class xep_0050(base_plugin):
sleekxmpp.xmlstream.RESPONSE_TIMEOUT
callback -- Optional reference to a stream handler
function. Will be executed when a reply
- stanza is received.
+ stanza is received if flow=False.
"""
iq = self.xmpp.Iq()
iq['type'] = 'set'
@@ -447,13 +442,24 @@ class xep_0050(base_plugin):
payload = [payload]
for item in payload:
iq['command'].append(item)
- return iq.send(**kwargs)
+ if not flow:
+ return iq.send(**kwargs)
+ else:
+ if kwargs.get('block', True):
+ try:
+ result = iq.send(**kwargs)
+ except IqError as err:
+ result = err.iq
+ self._handle_command_result(result)
+ else:
+ iq.send(block=False, callback=self._handle_command_result)
- def start_command(self, jid, node, session, ifrom=None):
+ def start_command(self, jid, node, session, ifrom=None, block=False):
"""
Initiate executing a command provided by a remote agent.
- The workflow provided is always non-blocking.
+ The default workflow provided is non-blocking, but a blocking
+ version may be used with block=True.
The provided session dictionary should contain:
next -- A handler for processing the command result.
@@ -465,11 +471,14 @@ class xep_0050(base_plugin):
node -- The node for the desired command.
session -- A dictionary of relevant session data.
ifrom -- Optionally specify the sender's JID.
+ block -- If True, block execution until a result
+ is received. Defaults to False.
"""
session['jid'] = jid
session['node'] = node
session['timestamp'] = time.time()
session['payload'] = None
+ session['block'] = block
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq['to'] = jid
@@ -481,7 +490,14 @@ class xep_0050(base_plugin):
sessionid = 'client:pending_' + iq['id']
session['id'] = sessionid
self.sessions[sessionid] = session
- iq.send(block=False)
+ if session['block']:
+ try:
+ result = iq.send(block=True)
+ except IqError as err:
+ result = err.iq
+ self._handle_command_result(result)
+ else:
+ iq.send(block=False, callback=self._handle_command_result)
def continue_command(self, session):
"""
@@ -499,7 +515,9 @@ class xep_0050(base_plugin):
ifrom=session.get('from', None),
action='next',
payload=session.get('payload', None),
- sessionid=session['id'])
+ sessionid=session['id'],
+ flow=True,
+ block=session['block'])
def cancel_command(self, session):
"""
@@ -517,7 +535,9 @@ class xep_0050(base_plugin):
ifrom=session.get('from', None),
action='cancel',
payload=session.get('payload', None),
- sessionid=session['id'])
+ sessionid=session['id'],
+ flow=True,
+ block=session['block'])
def complete_command(self, session):
"""
@@ -535,7 +555,9 @@ class xep_0050(base_plugin):
ifrom=session.get('from', None),
action='complete',
payload=session.get('payload', None),
- sessionid=session['id'])
+ sessionid=session['id'],
+ flow=True,
+ block=session['block'])
def terminate_command(self, session):
"""
diff --git a/tests/test_stream_xep_0050.py b/tests/test_stream_xep_0050.py
index 11b293c8..1931349d 100644
--- a/tests/test_stream_xep_0050.py
+++ b/tests/test_stream_xep_0050.py
@@ -504,7 +504,7 @@ class TestAdHocCommands(SleekTest):
""")
self.recv("""
- <iq id="1" to="foo@example.com" type="result">
+ <iq id="1" from="foo@example.com" type="result">
<command xmlns="http://jabber.org/protocol/commands"
node="test_client"
sessionid="_sessionid_"
@@ -532,7 +532,7 @@ class TestAdHocCommands(SleekTest):
""")
self.recv("""
- <iq id="2" to="foo@example.com" type="result">
+ <iq id="2" from="foo@example.com" type="result">
<command xmlns="http://jabber.org/protocol/commands"
node="test_client"
sessionid="_sessionid_"
@@ -560,7 +560,7 @@ class TestAdHocCommands(SleekTest):
""")
self.recv("""
- <iq id="3" to="foo@example.com" type="result">
+ <iq id="3" from="foo@example.com" type="result">
<command xmlns="http://jabber.org/protocol/commands"
node="test_client"
sessionid="_sessionid_"
@@ -681,6 +681,46 @@ class TestAdHocCommands(SleekTest):
self.failUnless(results == ['foo'],
'Incomplete command workflow: %s' % results)
+ def testClientAPIErrorStrippedResponse(self):
+ """Test errors that don't include the command substanza."""
+ results = []
+
+ def handle_error(iq, session):
+ for item in session['custom_data']:
+ results.append(item)
+
+ session = {'custom_data': ['foo'],
+ 'error': handle_error}
+
+ self.xmpp['xep_0050'].start_command(
+ 'foo@example.com',
+ 'test_client',
+ session)
+
+ self.send("""
+ <iq id="1" to="foo@example.com" type="set">
+ <command xmlns="http://jabber.org/protocol/commands"
+ node="test_client"
+ action="execute" />
+ </iq>
+ """)
+
+ self.recv("""
+ <iq id="1" to="foo@example.com" type="error">
+ <error type='cancel'>
+ <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' />
+ </error>
+ </iq>
+ """)
+
+ # Give the event queue time to process
+ time.sleep(0.3)
+
+ self.failUnless(results == ['foo'],
+ 'Incomplete command workflow: %s' % results)
+
+
+
suite = unittest.TestLoader().loadTestsFromTestCase(TestAdHocCommands)