Fix the uses of stanza.reply()

This is relying on the stanzas being copied for each handler. We no
longer do that for performance reasons, so instead of editing the copy
in-place, stanza.reply() now returns a new stanza.
This commit is contained in:
mathieui 2015-02-12 12:17:01 +01:00
parent 8b36e918e8
commit f6b3a0c6cf
No known key found for this signature in database
GPG Key ID: C59F84CEEFD616E3
19 changed files with 83 additions and 84 deletions

View File

@ -93,7 +93,8 @@ class XEP_0009(BasePlugin):
def _item_not_found(self, iq):
payload = iq.get_payload()
iq.reply().error().set_payload(payload)
iq = iq.reply()
iq.error().set_payload(payload)
iq['error']['code'] = '404'
iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found'
@ -101,7 +102,8 @@ class XEP_0009(BasePlugin):
def _undefined_condition(self, iq):
payload = iq.get_payload()
iq.reply().error().set_payload(payload)
iq = iq.reply()
iq.error().set_payload(payload)
iq['error']['code'] = '500'
iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'undefined-condition'
@ -109,7 +111,8 @@ class XEP_0009(BasePlugin):
def _forbidden(self, iq):
payload = iq.get_payload()
iq.reply().error().set_payload(payload)
iq = iq.reply()
iq.error().set_payload(payload)
iq['error']['code'] = '403'
iq['error']['type'] = 'auth'
iq['error']['condition'] = 'forbidden'
@ -117,7 +120,8 @@ class XEP_0009(BasePlugin):
def _recipient_unvailable(self, iq):
payload = iq.get_payload()
iq.reply().error().set_payload(payload)
iq = iq.reply()
error().set_payload(payload)
iq['error']['code'] = '404'
iq['error']['type'] = 'wait'
iq['error']['condition'] = 'recipient-unavailable'

View File

@ -132,8 +132,7 @@ class XEP_0012(BasePlugin):
if not isinstance(iq, Iq):
reply = self.xmpp.Iq()
else:
iq.reply()
reply = iq
reply = iq.reply()
if jid not in self._last_activities:
raise XMPPError('service-unavailable')

View File

@ -67,8 +67,7 @@ class XEP_0027(BasePlugin):
register_stanza_plugin(Message, Encrypted)
self.xmpp.add_event_handler('unverified_signed_presence',
self._handle_unverified_signed_presence,
threaded=True)
self._handle_unverified_signed_presence)
self.xmpp.register_handler(
Callback('Signed Presence',

View File

@ -634,7 +634,7 @@ class XEP_0030(BasePlugin):
info['id'] = iq['id']
info.send()
else:
iq.reply()
iq = iq.reply()
if info:
info = self._fix_default_info(info)
iq.set_payload(info.xml)
@ -674,7 +674,7 @@ class XEP_0030(BasePlugin):
if isinstance(items, Iq):
items.send()
else:
iq.reply()
iq = iq.reply()
if items:
iq.set_payload(items.xml)
iq.send()

View File

@ -191,8 +191,7 @@ class XEP_0047(BasePlugin):
self.window_size)
stream.stream_started.set()
self.api['set_stream'](stream.self_jid, stream.sid, stream.peer_jid, stream)
iq.reply()
iq.send()
iq.reply().send()
self.xmpp.event('ibb_stream_start', stream)
self.xmpp.event('stream:%s:%s' % (sid, stream.peer_jid), stream)

View File

@ -103,8 +103,7 @@ class IBBytestream(object):
self.xmpp.event('ibb_stream_data', {'stream': self, 'data': data})
if isinstance(stanza, Iq):
stanza.reply()
stanza.send()
stanza.reply().send()
def recv(self, *args, **kwargs):
return self.read(block=True)
@ -134,8 +133,7 @@ class IBBytestream(object):
def _closed(self, iq):
self.stream_in_closed.set()
self.stream_out_closed.set()
iq.reply()
iq.send()
iq.reply().send()
self.xmpp.event('ibb_stream_end', self)
def makefile(self, *args, **kwargs):

View File

@ -339,7 +339,7 @@ class XEP_0050(BasePlugin):
for item in payload:
register_stanza_plugin(Command, item.__class__, iterable=True)
iq.reply()
iq = iq.reply()
iq['command']['node'] = session['node']
iq['command']['sessionid'] = session['id']
@ -382,7 +382,7 @@ class XEP_0050(BasePlugin):
if handler:
handler(iq, session)
del self.sessions[sessionid]
iq.reply()
iq = iq.reply()
iq['command']['node'] = node
iq['command']['sessionid'] = sessionid
iq['command']['status'] = 'canceled'
@ -421,7 +421,7 @@ class XEP_0050(BasePlugin):
del self.sessions[sessionid]
iq.reply()
iq = iq.reply()
iq['command']['node'] = node
iq['command']['sessionid'] = sessionid
iq['command']['actions'] = []

View File

@ -127,7 +127,7 @@ class XEP_0054(BasePlugin):
if isinstance(vcard, Iq):
vcard.send()
else:
iq.reply()
iq = iq.reply()
iq.append(vcard)
iq.send()
elif iq['type'] == 'set':

View File

@ -178,7 +178,7 @@ class XEP_0065(BasePlugin):
else:
raise XMPPError(etype='cancel', condition='item-not-found')
iq.reply()
iq = iq.reply()
with self._sessions_lock:
self._sessions[sid] = conn
iq['socks']['sid'] = sid

View File

@ -64,7 +64,7 @@ class XEP_0092(BasePlugin):
Arguments:
iq -- The Iq stanza containing the software version query.
"""
iq.reply()
iq = iq.reply()
iq['software_version']['name'] = self.software_name
iq['software_version']['version'] = self.version
iq['software_version']['os'] = self.os

View File

@ -72,7 +72,7 @@ class XEP_0202(BasePlugin):
Arguments:
iq -- The Iq time request stanza.
"""
iq.reply()
iq = iq.reply()
iq['entity_time']['time'] = self.local_time(iq['to'])
iq.send()

View File

@ -115,7 +115,7 @@ class XEP_0231(BasePlugin):
data.send()
return
iq.reply()
iq = iq.reply()
iq.append(data)
iq.send()

View File

@ -303,11 +303,11 @@ class XEP_0323(BasePlugin):
#print("added session: " + str(self.sessions))
iq.reply()
iq = iq.reply()
iq['accepted']['seqnr'] = seqnr
if not request_delay_sec is None:
iq['accepted']['queued'] = "true"
iq.send(block=False)
iq.send()
self.sessions[session]["node_list"] = process_nodes
@ -327,11 +327,11 @@ class XEP_0323(BasePlugin):
self._threaded_node_request(session, process_fields, req_flags)
else:
iq.reply()
iq = iq.reply()
iq['type'] = 'error'
iq['rejected']['seqnr'] = seqnr
iq['rejected']['error'] = error_msg
iq.send(block=False)
iq.send()
def _threaded_node_request(self, session, process_fields, flags):
"""
@ -515,21 +515,21 @@ class XEP_0323(BasePlugin):
self.sessions[s]["commTimers"][n].cancel()
# Confirm
iq.reply()
iq = iq.reply()
iq['type'] = 'result'
iq['cancelled']['seqnr'] = seqnr
iq.send(block=False)
iq.send()
# Delete session
del self.sessions[s]
return
# Could not find session, send reject
iq.reply()
iq = iq.reply()
iq['type'] = 'error'
iq['rejected']['seqnr'] = seqnr
iq['rejected']['error'] = "Cancel request received, no matching request is active."
iq.send(block=False)
iq.send()
# =================================================================
# Client side (data retriever) API
@ -610,7 +610,7 @@ class XEP_0323(BasePlugin):
iq['req']._set_flags(flags)
self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "seqnr": seqnr, "callback": callback}
iq.send(block=False)
iq.send()
return seqnr
@ -631,7 +631,7 @@ class XEP_0323(BasePlugin):
iq['type'] = "get"
iq['id'] = seqnr
iq['cancel']['seqnr'] = seqnr
iq.send(block=False)
iq.send()
def _get_new_seqnr(self):
""" Returns a unique sequence number (unique across threads) """

View File

@ -273,7 +273,7 @@ class XEP_0325(BasePlugin):
self._threaded_node_request(session, process_fields)
else:
iq.reply()
iq = iq.reply()
iq['type'] = 'error'
iq['setResponse']['responseCode'] = "NotFound"
if missing_node is not None:
@ -282,7 +282,7 @@ class XEP_0325(BasePlugin):
iq['setResponse'].add_data(missing_field)
iq['setResponse']['error']['var'] = "Output"
iq['setResponse']['error']['text'] = error_msg
iq.send(block=False)
iq.send()
def _handle_direct_set(self, msg):
"""
@ -382,7 +382,7 @@ class XEP_0325(BasePlugin):
iq['setResponse'].add_node(nodeId)
iq['setResponse']['error']['var'] = "Output"
iq['setResponse']['error']['text'] = "Timeout."
iq.send(block=False)
iq.send()
## TODO - should we send one timeout per node??
@ -443,7 +443,7 @@ class XEP_0325(BasePlugin):
iq['setResponse'].add_data(error_field)
iq['setResponse']['error']['var'] = error_field
iq['setResponse']['error']['text'] = error_msg
iq.send(block=False)
iq.send()
# Drop communication with this device and check if we are done
self.sessions[session]["nodeDone"][nodeId] = True
@ -463,7 +463,7 @@ class XEP_0325(BasePlugin):
iq['type'] = "result"
iq['id'] = self.sessions[session]['seqnr']
iq['setResponse']['responseCode'] = "OK"
iq.send(block=False)
iq.send()
# The session is complete, delete it
del self.sessions[session]
@ -526,7 +526,7 @@ class XEP_0325(BasePlugin):
iq['set'].add_data(name=name, typename=typename, value=value)
self.sessions[seqnr] = {"from": iq['from'], "to": iq['to'], "callback": callback}
iq.send(block=False)
iq.send()
def set_command(self, from_jid, to_jid, fields, nodeIds=None):
"""

View File

@ -88,10 +88,10 @@ class Iq(RootStanza):
Overrides StanzaBase.unhandled.
"""
if self['type'] in ('get', 'set'):
self.reply()
self['error']['condition'] = 'feature-not-implemented'
self['error']['text'] = 'No handlers registered for this request.'
self.send()
reply = self.reply()
reply['error']['condition'] = 'feature-not-implemented'
reply['error']['text'] = 'No handlers registered for this request.'
reply.send()
def set_payload(self, value):
"""
@ -154,9 +154,9 @@ class Iq(RootStanza):
clear -- Indicates if existing content should be
removed before replying. Defaults to True.
"""
self['type'] = 'result'
StanzaBase.reply(self, clear)
return self
new_iq = StanzaBase.reply(self, clear=clear)
new_iq['type'] = 'result'
return new_iq
def send(self, callback=None, timeout=None, timeout_callback=None):
"""Send an <iq> stanza over the XML stream.

View File

@ -133,21 +133,19 @@ class Message(RootStanza):
clear -- Indicates if existing content should be removed
before replying. Defaults to True.
"""
thread = self['thread']
parent = self['parent_thread']
new_message = StanzaBase.reply(self, clear)
StanzaBase.reply(self, clear)
if self['type'] == 'groupchat':
self['to'] = self['to'].bare
new_message['to'] = new_message['to'].bare
self['thread'] = thread
self['parent_thread'] = parent
new_message['thread'] = self['thread']
new_message['parent_thread'] = self['parent_thread']
del self['id']
del new_message['id']
if body is not None:
self['body'] = body
return self
new_message['body'] = body
return new_message
def get_mucroom(self):
"""

View File

@ -174,8 +174,9 @@ class Presence(RootStanza):
clear -- Indicates if the stanza contents should be removed
before replying. Defaults to True.
"""
new_presence = StanzaBase.reply(self, clear)
if self['type'] == 'unsubscribe':
self['type'] = 'unsubscribed'
new_presence['type'] = 'unsubscribed'
elif self['type'] == 'subscribe':
self['type'] = 'subscribed'
return StanzaBase.reply(self, clear)
new_presence['type'] = 'subscribed'
return new_presence

View File

@ -46,37 +46,37 @@ class RootStanza(StanzaBase):
# locally. Using the condition/text from that error
# response could leak too much information, so we'll
# only use a generic error here.
self.reply()
self['error']['condition'] = 'undefined-condition'
self['error']['text'] = 'External error'
self['error']['type'] = 'cancel'
reply = self.reply()
reply['error']['condition'] = 'undefined-condition'
reply['error']['text'] = 'External error'
reply['error']['type'] = 'cancel'
log.warning('You should catch IqError exceptions')
self.send()
reply.send()
elif isinstance(e, IqTimeout):
self.reply()
self['error']['condition'] = 'remote-server-timeout'
self['error']['type'] = 'wait'
reply = self.reply()
reply['error']['condition'] = 'remote-server-timeout'
reply['error']['type'] = 'wait'
log.warning('You should catch IqTimeout exceptions')
self.send()
reply.send()
elif isinstance(e, XMPPError):
# We raised this deliberately
self.reply(clear=e.clear)
self['error']['condition'] = e.condition
self['error']['text'] = e.text
self['error']['type'] = e.etype
reply = self.reply(clear=e.clear)
reply['error']['condition'] = e.condition
reply['error']['text'] = e.text
reply['error']['type'] = e.etype
if e.extension is not None:
# Extended error tag
extxml = ET.Element("{%s}%s" % (e.extension_ns, e.extension),
e.extension_args)
self['error'].append(extxml)
self.send()
reply['error'].append(extxml)
reply.send()
else:
# We probably didn't raise this on purpose, so send an error stanza
self.reply()
self['error']['condition'] = 'undefined-condition'
self['error']['text'] = "Slixmpp got into trouble."
self['error']['type'] = 'cancel'
self.send()
reply = self.reply()
reply['error']['condition'] = 'undefined-condition'
reply['error']['text'] = "Slixmpp got into trouble."
reply['error']['type'] = 'cancel'
reply.send()
# log the error
log.exception('Error handling {%s}%s stanza',
self.namespace, self.name)

View File

@ -1538,16 +1538,17 @@ class StanzaBase(ElementBase):
:param bool clear: Indicates if the stanza's contents should be
removed. Defaults to ``True``.
"""
new_stanza = copy.copy(self)
# if it's a component, use from
if self.stream and hasattr(self.stream, "is_component") and \
self.stream.is_component:
self['from'], self['to'] = self['to'], self['from']
new_stanza['from'], new_stanza['to'] = self['to'], self['from']
else:
self['to'] = self['from']
del self['from']
new_stanza['to'] = self['from']
del new_stanza['from']
if clear:
self.clear()
return self
new_stanza.clear()
return new_stanza
def error(self):
"""Set the stanza's type to ``'error'``."""