2 SleekXMPP: The Sleek XMPP Library
3 Copyright (C) 2010 Nathanael C. Fritz
4 This file is part of SleekXMPP.
6 See the file LICENSE for copying permission.
9 from sleekxmpp
.stanza
.rootstanza
import RootStanza
10 from sleekxmpp
.xmlstream
import StanzaBase
, ET
11 from sleekxmpp
.xmlstream
.handler
import Waiter
, Callback
12 from sleekxmpp
.xmlstream
.matcher
import MatchIDSender
, MatcherId
13 from sleekxmpp
.exceptions
import IqTimeout
, IqError
19 XMPP <iq> stanzas, or info/query stanzas, are XMPP's method of
20 requesting and modifying information, similar to HTTP's GET and
23 Each <iq> stanza must have an 'id' value which associates the
24 stanza with the response stanza. XMPP entities must always
25 be given a response <iq> stanza with a type of 'result' after
26 sending a stanza of type 'get' or 'set'.
28 Most uses cases for <iq> stanzas will involve adding a <query>
29 element whose namespace indicates the type of information
30 desired. However, some custom XMPP applications use <iq> stanzas
31 as a carrier stanza for an application-specific protocol instead.
34 <iq to="user@example.com" type="get" id="314">
35 <query xmlns="http://jabber.org/protocol/disco#items" />
38 <iq to="user@localhost" type="result" id="17">
39 <query xmlns='jabber:iq:roster'>
40 <item jid='otheruser@example.net'
43 <group>Friends</group>
49 query -- The namespace of the <query> element if one exists.
52 types -- May be one of: get, set, result, or error.
55 __init__ -- Overrides StanzaBase.__init__.
56 unhandled -- Send error if there are no handlers.
57 set_payload -- Overrides StanzaBase.set_payload.
58 set_query -- Add or modify a <query> element.
59 get_query -- Return the namespace of the <query> element.
60 del_query -- Remove the <query> element.
61 reply -- Overrides StanzaBase.reply
62 send -- Overrides StanzaBase.send
65 namespace
= 'jabber:client'
67 interfaces
= set(('type', 'to', 'from', 'id', 'query'))
68 types
= set(('get', 'result', 'set', 'error'))
71 def __init__(self
, *args
, **kwargs
):
73 Initialize a new <iq> stanza with an 'id' value.
75 Overrides StanzaBase.__init__.
77 StanzaBase
.__init
__(self
, *args
, **kwargs
)
79 if self
.stream
is not None:
80 self
['id'] = self
.stream
.new_id()
86 Send a feature-not-implemented error if the stanza is not handled.
88 Overrides StanzaBase.unhandled.
90 if self
['type'] in ('get', 'set'):
92 self
['error']['condition'] = 'feature-not-implemented'
93 self
['error']['text'] = 'No handlers registered for this request.'
96 def set_payload(self
, value
):
98 Set the XML contents of the <iq> stanza.
101 value -- An XML object to use as the <iq> stanza's contents
104 StanzaBase
.set_payload(self
, value
)
107 def set_query(self
, value
):
109 Add or modify a <query> element.
111 Query elements are differentiated by their namespace.
114 value -- The namespace of the <query> element.
116 query
= self
.xml
.find("{%s}query" % value
)
117 if query
is None and value
:
118 plugin
= self
.plugin_tag_map
.get('{%s}query' % value
, None)
120 self
.enable(plugin
.plugin_attrib
)
123 query
= ET
.Element("{%s}query" % value
)
124 self
.xml
.append(query
)
128 """Return the namespace of the <query> element."""
129 for child
in self
.xml
:
130 if child
.tag
.endswith('query'):
131 ns
= child
.tag
.split('}')[0]
138 """Remove the <query> element."""
139 for child
in self
.xml
:
140 if child
.tag
.endswith('query'):
141 self
.xml
.remove(child
)
144 def reply(self
, clear
=True):
146 Send a reply <iq> stanza.
148 Overrides StanzaBase.reply
150 Sets the 'type' to 'result' in addition to the default
151 StanzaBase.reply behavior.
154 clear -- Indicates if existing content should be
155 removed before replying. Defaults to True.
157 self
['type'] = 'result'
158 StanzaBase
.reply(self
, clear
)
161 def send(self
, block
=True, timeout
=None, callback
=None, now
=False, timeout_callback
=None):
163 Send an <iq> stanza over the XML stream.
165 The send call can optionally block until a response is received or
166 a timeout occurs. Be aware that using blocking in non-threaded event
167 handlers can drastically impact performance. Otherwise, a callback
168 handler can be provided that will be executed when the Iq stanza's
169 result reply is received. Be aware though that that the callback
170 handler will not be executed in its own thread.
172 Using both block and callback is not recommended, and only the
173 callback argument will be used in that case.
175 Overrides StanzaBase.send
178 block -- Specify if the send call will block until a response
179 is received, or a timeout occurs. Defaults to True.
180 timeout -- The length of time (in seconds) to wait for a response
181 before exiting the send call if blocking is used.
182 Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
183 callback -- Optional reference to a stream handler function. Will
184 be executed when a reply stanza is received.
185 now -- Indicates if the send queue should be skipped and send
186 the stanza immediately. Used during stream
187 initialization. Defaults to False.
188 timeout_callback -- Optional reference to a stream handler function.
189 Will be executed when the timeout expires before a
190 response has been received with the originally-sent IQ
191 stanza. Only called if there is a callback parameter
192 (and therefore are in async mode).
195 timeout
= self
.stream
.response_timeout
197 if self
.stream
.session_bind_event
.is_set():
198 matcher
= MatchIDSender({
200 'self': self
.stream
.boundjid
,
204 matcher
= MatcherId(self
['id'])
206 if callback
is not None and self
['type'] in ('get', 'set'):
207 handler_name
= 'IqCallback_%s' % self
['id']
209 self
.callback
= callback
210 self
.timeout_callback
= timeout_callback
211 self
.stream
.schedule('IqTimeout_%s' % self
['id'],
215 handler
= Callback(handler_name
,
220 handler
= Callback(handler_name
,
224 self
.stream
.register_handler(handler
)
225 StanzaBase
.send(self
, now
=now
)
227 elif block
and self
['type'] in ('get', 'set'):
228 waitfor
= Waiter('IqWait_%s' % self
['id'], matcher
)
229 self
.stream
.register_handler(waitfor
)
230 StanzaBase
.send(self
, now
=now
)
231 result
= waitfor
.wait(timeout
)
233 raise IqTimeout(self
)
234 if result
['type'] == 'error':
235 raise IqError(result
)
238 return StanzaBase
.send(self
, now
=now
)
240 def _handle_result(self
, iq
):
241 # we got the IQ, so don't fire the timeout
242 self
.stream
.scheduler
.remove('IqTimeout_%s' % self
['id'])
245 def _fire_timeout(self
):
246 # don't fire the handler for the IQ, if it finally does come in
247 self
.stream
.remove_handler('IqCallback_%s' % self
['id'])
248 self
.timeout_callback(self
)
250 def _set_stanza_values(self
, values
):
252 Set multiple stanza interface values using a dictionary.
254 Stanza plugin values may be set usind nested dictionaries.
256 If the interface 'query' is given, then it will be set
257 last to avoid duplication of the <query /> element.
259 Overrides ElementBase._set_stanza_values.
262 values -- A dictionary mapping stanza interface with values.
263 Plugin interfaces may accept a nested dictionary that
264 will be used recursively.
266 query
= values
.get('query', '')
269 StanzaBase
._set
_stanza
_values
(self
, values
)
270 self
['query'] = query
272 StanzaBase
._set
_stanza
_values
(self
, values
)
276 # To comply with PEP8, method names now use underscores.
277 # Deprecated method names are re-mapped for backwards compatibility.
278 Iq
.setPayload
= Iq
.set_payload
279 Iq
.getQuery
= Iq
.get_query
280 Iq
.setQuery
= Iq
.set_query
281 Iq
.delQuery
= Iq
.del_query