Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0166.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 26edcf3a30eb |
children | 378188abe941 |
comparison
equal
deleted
inserted
replaced
2623:49533de4540b | 2624:56f94936df1e |
---|---|
19 | 19 |
20 from sat.core.i18n import _, D_ | 20 from sat.core.i18n import _, D_ |
21 from sat.core.constants import Const as C | 21 from sat.core.constants import Const as C |
22 from sat.core.log import getLogger | 22 from sat.core.log import getLogger |
23 from sat.tools import xml_tools | 23 from sat.tools import xml_tools |
24 | |
24 log = getLogger(__name__) | 25 log = getLogger(__name__) |
25 from sat.core import exceptions | 26 from sat.core import exceptions |
26 from twisted.words.protocols.jabber import jid | 27 from twisted.words.protocols.jabber import jid |
27 from twisted.internet import defer | 28 from twisted.internet import defer |
28 from twisted.internet import reactor | 29 from twisted.internet import reactor |
35 import time | 36 import time |
36 | 37 |
37 from zope.interface import implements | 38 from zope.interface import implements |
38 | 39 |
39 | 40 |
40 | |
41 IQ_SET = '/iq[@type="set"]' | 41 IQ_SET = '/iq[@type="set"]' |
42 NS_JINGLE = "urn:xmpp:jingle:1" | 42 NS_JINGLE = "urn:xmpp:jingle:1" |
43 NS_JINGLE_ERROR = "urn:xmpp:jingle:errors:1" | 43 NS_JINGLE_ERROR = "urn:xmpp:jingle:errors:1" |
44 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]' | 44 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]' |
45 STATE_PENDING = "PENDING" | 45 STATE_PENDING = "PENDING" |
53 C.PI_TYPE: "XEP", | 53 C.PI_TYPE: "XEP", |
54 C.PI_MODES: C.PLUG_MODE_BOTH, | 54 C.PI_MODES: C.PLUG_MODE_BOTH, |
55 C.PI_PROTOCOLS: ["XEP-0166"], | 55 C.PI_PROTOCOLS: ["XEP-0166"], |
56 C.PI_MAIN: "XEP_0166", | 56 C.PI_MAIN: "XEP_0166", |
57 C.PI_HANDLER: "yes", | 57 C.PI_HANDLER: "yes", |
58 C.PI_DESCRIPTION: _("""Implementation of Jingle""") | 58 C.PI_DESCRIPTION: _("""Implementation of Jingle"""), |
59 } | 59 } |
60 | 60 |
61 | 61 |
62 ApplicationData = namedtuple('ApplicationData', ('namespace', 'handler')) | 62 ApplicationData = namedtuple("ApplicationData", ("namespace", "handler")) |
63 TransportData = namedtuple('TransportData', ('namespace', 'handler', 'priority')) | 63 TransportData = namedtuple("TransportData", ("namespace", "handler", "priority")) |
64 | 64 |
65 | 65 |
66 class XEP_0166(object): | 66 class XEP_0166(object): |
67 ROLE_INITIATOR = "initiator" | 67 ROLE_INITIATOR = "initiator" |
68 ROLE_RESPONDER = "responder" | 68 ROLE_RESPONDER = "responder" |
69 TRANSPORT_DATAGRAM='UDP' | 69 TRANSPORT_DATAGRAM = "UDP" |
70 TRANSPORT_STREAMING='TCP' | 70 TRANSPORT_STREAMING = "TCP" |
71 REASON_SUCCESS='success' | 71 REASON_SUCCESS = "success" |
72 REASON_DECLINE='decline' | 72 REASON_DECLINE = "decline" |
73 REASON_FAILED_APPLICATION='failed-application' | 73 REASON_FAILED_APPLICATION = "failed-application" |
74 REASON_FAILED_TRANSPORT='failed-transport' | 74 REASON_FAILED_TRANSPORT = "failed-transport" |
75 REASON_CONNECTIVITY_ERROR='connectivity-error' | 75 REASON_CONNECTIVITY_ERROR = "connectivity-error" |
76 A_SESSION_INITIATE = "session-initiate" | 76 A_SESSION_INITIATE = "session-initiate" |
77 A_SESSION_ACCEPT = "session-accept" | 77 A_SESSION_ACCEPT = "session-accept" |
78 A_SESSION_TERMINATE = "session-terminate" | 78 A_SESSION_TERMINATE = "session-terminate" |
79 A_SESSION_INFO = "session-info" | 79 A_SESSION_INFO = "session-info" |
80 A_TRANSPORT_REPLACE = "transport-replace" | 80 A_TRANSPORT_REPLACE = "transport-replace" |
81 A_TRANSPORT_ACCEPT = "transport-accept" | 81 A_TRANSPORT_ACCEPT = "transport-accept" |
82 A_TRANSPORT_REJECT = "transport-reject" | 82 A_TRANSPORT_REJECT = "transport-reject" |
83 A_TRANSPORT_INFO = "transport-info" | 83 A_TRANSPORT_INFO = "transport-info" |
84 # non standard actions | 84 # non standard actions |
85 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer | 85 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer |
86 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer | 86 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer |
87 A_ACCEPTED_ACK = "accepted-ack" # session accepted ack has been received from initiator | 87 A_ACCEPTED_ACK = ( |
88 A_START = "start" # application can start | 88 "accepted-ack" |
89 A_DESTROY = "destroy" # called when a transport is destroyed (e.g. because it is remplaced). Used to do cleaning operations | 89 ) # session accepted ack has been received from initiator |
90 A_START = "start" # application can start | |
91 A_DESTROY = ( | |
92 "destroy" | |
93 ) # called when a transport is destroyed (e.g. because it is remplaced). Used to do cleaning operations | |
90 | 94 |
91 def __init__(self, host): | 95 def __init__(self, host): |
92 log.info(_("plugin Jingle initialization")) | 96 log.info(_("plugin Jingle initialization")) |
93 self.host = host | 97 self.host = host |
94 self._applications = {} # key: namespace, value: application data | 98 self._applications = {} # key: namespace, value: application data |
95 self._transports = {} # key: namespace, value: transport data | 99 self._transports = {} # key: namespace, value: transport data |
96 # we also keep transports by type, they are then sorted by priority | 100 # we also keep transports by type, they are then sorted by priority |
97 self._type_transports = { XEP_0166.TRANSPORT_DATAGRAM: [], | 101 self._type_transports = { |
98 XEP_0166.TRANSPORT_STREAMING: [], | 102 XEP_0166.TRANSPORT_DATAGRAM: [], |
99 } | 103 XEP_0166.TRANSPORT_STREAMING: [], |
104 } | |
100 | 105 |
101 def profileConnected(self, client): | 106 def profileConnected(self, client): |
102 client.jingle_sessions = {} # key = sid, value = session_data | 107 client.jingle_sessions = {} # key = sid, value = session_data |
103 | 108 |
104 def getHandler(self, client): | 109 def getHandler(self, client): |
113 log.debug(u"Jingle session id [{}] deleted".format(sid)) | 118 log.debug(u"Jingle session id [{}] deleted".format(sid)) |
114 | 119 |
115 ## helpers methods to build stanzas ## | 120 ## helpers methods to build stanzas ## |
116 | 121 |
117 def _buildJingleElt(self, client, session, action): | 122 def _buildJingleElt(self, client, session, action): |
118 iq_elt = client.IQ('set') | 123 iq_elt = client.IQ("set") |
119 iq_elt['from'] = client.jid.full() | 124 iq_elt["from"] = client.jid.full() |
120 iq_elt['to'] = session['peer_jid'].full() | 125 iq_elt["to"] = session["peer_jid"].full() |
121 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE) | 126 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE) |
122 jingle_elt["sid"] = session['id'] | 127 jingle_elt["sid"] = session["id"] |
123 jingle_elt['action'] = action | 128 jingle_elt["action"] = action |
124 return iq_elt, jingle_elt | 129 return iq_elt, jingle_elt |
125 | 130 |
126 def sendError(self, client, error_condition, sid, request, jingle_condition=None): | 131 def sendError(self, client, error_condition, sid, request, jingle_condition=None): |
127 """Send error stanza | 132 """Send error stanza |
128 | 133 |
132 @param jingle_condition(None, unicode): if not None, additional jingle-specific error information | 137 @param jingle_condition(None, unicode): if not None, additional jingle-specific error information |
133 """ | 138 """ |
134 iq_elt = error.StanzaError(error_condition).toResponse(request) | 139 iq_elt = error.StanzaError(error_condition).toResponse(request) |
135 if jingle_condition is not None: | 140 if jingle_condition is not None: |
136 iq_elt.error.addElement((NS_JINGLE_ERROR, jingle_condition)) | 141 iq_elt.error.addElement((NS_JINGLE_ERROR, jingle_condition)) |
137 if error.STANZA_CONDITIONS[error_condition]['type'] == 'cancel' and sid: | 142 if error.STANZA_CONDITIONS[error_condition]["type"] == "cancel" and sid: |
138 self._delSession(client, sid) | 143 self._delSession(client, sid) |
139 log.warning(u"Error while managing jingle session, cancelling: {condition}".format(error_condition)) | 144 log.warning( |
145 u"Error while managing jingle session, cancelling: {condition}".format( | |
146 error_condition | |
147 ) | |
148 ) | |
140 client.send(iq_elt) | 149 client.send(iq_elt) |
141 | 150 |
142 def _terminateEb(self, failure_): | 151 def _terminateEb(self, failure_): |
143 log.warning(_(u"Error while terminating session: {msg}").format(msg=failure_)) | 152 log.warning(_(u"Error while terminating session: {msg}").format(msg=failure_)) |
144 | 153 |
148 send the session-terminate action, and delete the session data | 157 send the session-terminate action, and delete the session data |
149 @param reason(unicode, list[domish.Element]): if unicode, will be transformed to an element | 158 @param reason(unicode, list[domish.Element]): if unicode, will be transformed to an element |
150 if a list of element, add them as children of the <reason/> element | 159 if a list of element, add them as children of the <reason/> element |
151 @param session(dict): data of the session | 160 @param session(dict): data of the session |
152 """ | 161 """ |
153 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_TERMINATE) | 162 iq_elt, jingle_elt = self._buildJingleElt( |
154 reason_elt = jingle_elt.addElement('reason') | 163 client, session, XEP_0166.A_SESSION_TERMINATE |
164 ) | |
165 reason_elt = jingle_elt.addElement("reason") | |
155 if isinstance(reason, basestring): | 166 if isinstance(reason, basestring): |
156 reason_elt.addElement(reason) | 167 reason_elt.addElement(reason) |
157 else: | 168 else: |
158 for elt in reason: | 169 for elt in reason: |
159 reason_elt.addChild(elt) | 170 reason_elt.addChild(elt) |
160 self._delSession(client, session['id']) | 171 self._delSession(client, session["id"]) |
161 d = iq_elt.send() | 172 d = iq_elt.send() |
162 d.addErrback(self._terminateEb) | 173 d.addErrback(self._terminateEb) |
163 return d | 174 return d |
164 | 175 |
165 ## errors which doesn't imply a stanza sending ## | 176 ## errors which doesn't imply a stanza sending ## |
168 """Called when we got an <iq/> error | 179 """Called when we got an <iq/> error |
169 | 180 |
170 @param failure_(failure.Failure): the exceptions raised | 181 @param failure_(failure.Failure): the exceptions raised |
171 @param sid(unicode): jingle session id | 182 @param sid(unicode): jingle session id |
172 """ | 183 """ |
173 log.warning(u"Error while sending jingle <iq/> stanza: {failure_}".format(failure_=failure_.value)) | 184 log.warning( |
185 u"Error while sending jingle <iq/> stanza: {failure_}".format( | |
186 failure_=failure_.value | |
187 ) | |
188 ) | |
174 self._delSession(client, sid) | 189 self._delSession(client, sid) |
175 | 190 |
176 def _jingleErrorCb(self, fail, sid, request, client): | 191 def _jingleErrorCb(self, fail, sid, request, client): |
177 """Called when something is going wrong while parsing jingle request | 192 """Called when something is going wrong while parsing jingle request |
178 | 193 |
183 @param request(domsih.Element): jingle request | 198 @param request(domsih.Element): jingle request |
184 @param client: %(doc_client)s | 199 @param client: %(doc_client)s |
185 """ | 200 """ |
186 log.warning("Error while processing jingle request") | 201 log.warning("Error while processing jingle request") |
187 if isinstance(fail, exceptions.DataError): | 202 if isinstance(fail, exceptions.DataError): |
188 self.sendError(client, 'bad-request', sid, request) | 203 self.sendError(client, "bad-request", sid, request) |
189 else: | 204 else: |
190 log.error("Unmanaged jingle exception") | 205 log.error("Unmanaged jingle exception") |
191 self._delSession(client, sid) | 206 self._delSession(client, sid) |
192 raise fail | 207 raise fail |
193 | 208 |
209 called on several action to negociate the application or transport | 224 called on several action to negociate the application or transport |
210 - jingleTerminate: called on session terminate, with reason_elt | 225 - jingleTerminate: called on session terminate, with reason_elt |
211 May be used to clean session | 226 May be used to clean session |
212 """ | 227 """ |
213 if namespace in self._applications: | 228 if namespace in self._applications: |
214 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace)) | 229 raise exceptions.ConflictError( |
215 self._applications[namespace] = ApplicationData(namespace=namespace, handler=handler) | 230 u"Trying to register already registered namespace {}".format(namespace) |
231 ) | |
232 self._applications[namespace] = ApplicationData( | |
233 namespace=namespace, handler=handler | |
234 ) | |
216 log.debug(u"new jingle application registered") | 235 log.debug(u"new jingle application registered") |
217 | 236 |
218 def registerTransport(self, namespace, transport_type, handler, priority=0): | 237 def registerTransport(self, namespace, transport_type, handler, priority=0): |
219 """Register a transport plugin | 238 """Register a transport plugin |
220 | 239 |
225 - jingleSessionInit(client, self, session, content_name[, *args, **kwargs]): must return the domish.Element used for initial content | 244 - jingleSessionInit(client, self, session, content_name[, *args, **kwargs]): must return the domish.Element used for initial content |
226 - jingleHandler(client, self, action, session, content_name, transport_elt): | 245 - jingleHandler(client, self, action, session, content_name, transport_elt): |
227 called on several action to negociate the application or transport | 246 called on several action to negociate the application or transport |
228 @param priority(int): priority of this transport | 247 @param priority(int): priority of this transport |
229 """ | 248 """ |
230 assert transport_type in (XEP_0166.TRANSPORT_DATAGRAM, XEP_0166.TRANSPORT_STREAMING) | 249 assert transport_type in ( |
250 XEP_0166.TRANSPORT_DATAGRAM, | |
251 XEP_0166.TRANSPORT_STREAMING, | |
252 ) | |
231 if namespace in self._transports: | 253 if namespace in self._transports: |
232 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace)) | 254 raise exceptions.ConflictError( |
233 transport_data = TransportData(namespace=namespace, handler=handler, priority=priority) | 255 u"Trying to register already registered namespace {}".format(namespace) |
256 ) | |
257 transport_data = TransportData( | |
258 namespace=namespace, handler=handler, priority=priority | |
259 ) | |
234 self._type_transports[transport_type].append(transport_data) | 260 self._type_transports[transport_type].append(transport_data) |
235 self._type_transports[transport_type].sort(key=lambda transport_data: transport_data.priority, reverse=True) | 261 self._type_transports[transport_type].sort( |
262 key=lambda transport_data: transport_data.priority, reverse=True | |
263 ) | |
236 self._transports[namespace] = transport_data | 264 self._transports[namespace] = transport_data |
237 log.debug(u"new jingle transport registered") | 265 log.debug(u"new jingle transport registered") |
238 | 266 |
239 @defer.inlineCallbacks | 267 @defer.inlineCallbacks |
240 def transportReplace(self, client, transport_ns, session, content_name): | 268 def transportReplace(self, client, transport_ns, session, content_name): |
245 @param content_name(unicode): name of the content | 273 @param content_name(unicode): name of the content |
246 """ | 274 """ |
247 # XXX: for now we replace the transport before receiving confirmation from other peer | 275 # XXX: for now we replace the transport before receiving confirmation from other peer |
248 # this is acceptable because we terminate the session if transport is rejected. | 276 # this is acceptable because we terminate the session if transport is rejected. |
249 # this behavious may change in the future. | 277 # this behavious may change in the future. |
250 content_data= session['contents'][content_name] | 278 content_data = session["contents"][content_name] |
251 transport_data = content_data['transport_data'] | 279 transport_data = content_data["transport_data"] |
252 try: | 280 try: |
253 transport = self._transports[transport_ns] | 281 transport = self._transports[transport_ns] |
254 except KeyError: | 282 except KeyError: |
255 raise exceptions.InternalError(u"Unkown transport") | 283 raise exceptions.InternalError(u"Unkown transport") |
256 yield content_data['transport'].handler.jingleHandler(client, XEP_0166.A_DESTROY, session, content_name, None) | 284 yield content_data["transport"].handler.jingleHandler( |
257 content_data['transport'] = transport | 285 client, XEP_0166.A_DESTROY, session, content_name, None |
286 ) | |
287 content_data["transport"] = transport | |
258 transport_data.clear() | 288 transport_data.clear() |
259 | 289 |
260 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_TRANSPORT_REPLACE) | 290 iq_elt, jingle_elt = self._buildJingleElt( |
261 content_elt = jingle_elt.addElement('content') | 291 client, session, XEP_0166.A_TRANSPORT_REPLACE |
262 content_elt['name'] = content_name | 292 ) |
263 content_elt['creator'] = content_data['creator'] | 293 content_elt = jingle_elt.addElement("content") |
294 content_elt["name"] = content_name | |
295 content_elt["creator"] = content_data["creator"] | |
264 | 296 |
265 transport_elt = transport.handler.jingleSessionInit(client, session, content_name) | 297 transport_elt = transport.handler.jingleSessionInit(client, session, content_name) |
266 content_elt.addChild(transport_elt) | 298 content_elt.addChild(transport_elt) |
267 iq_elt.send() | 299 iq_elt.send() |
268 | 300 |
277 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <transport> or <description> element, according to action | 309 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <transport> or <description> element, according to action |
278 """ | 310 """ |
279 # we first build iq, jingle and content element which are the same in every cases | 311 # we first build iq, jingle and content element which are the same in every cases |
280 iq_elt, jingle_elt = self._buildJingleElt(client, session, action) | 312 iq_elt, jingle_elt = self._buildJingleElt(client, session, action) |
281 # FIXME: XEP-0260 § 2.3 Ex 5 has an initiator attribute, but it should not according to XEP-0166 §7.1 table 1, must be checked | 313 # FIXME: XEP-0260 § 2.3 Ex 5 has an initiator attribute, but it should not according to XEP-0166 §7.1 table 1, must be checked |
282 content_data= session['contents'][content_name] | 314 content_data = session["contents"][content_name] |
283 content_elt = jingle_elt.addElement('content') | 315 content_elt = jingle_elt.addElement("content") |
284 content_elt['name'] = content_name | 316 content_elt["name"] = content_name |
285 content_elt['creator'] = content_data['creator'] | 317 content_elt["creator"] = content_data["creator"] |
286 | 318 |
287 if action == XEP_0166.A_TRANSPORT_INFO: | 319 if action == XEP_0166.A_TRANSPORT_INFO: |
288 context_elt = transport_elt = content_elt.addElement('transport', content_data['transport'].namespace) | 320 context_elt = transport_elt = content_elt.addElement( |
289 transport_elt['sid'] = content_data['transport_data']['sid'] | 321 "transport", content_data["transport"].namespace |
322 ) | |
323 transport_elt["sid"] = content_data["transport_data"]["sid"] | |
290 else: | 324 else: |
291 raise exceptions.InternalError(u"unmanaged action {}".format(action)) | 325 raise exceptions.InternalError(u"unmanaged action {}".format(action)) |
292 | 326 |
293 return iq_elt, context_elt | 327 return iq_elt, context_elt |
294 | 328 |
296 """Build a session-info action | 330 """Build a session-info action |
297 | 331 |
298 @param session(dict): jingle session data | 332 @param session(dict): jingle session data |
299 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <jingle> element | 333 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <jingle> element |
300 """ | 334 """ |
301 return self._buildJingleElt(client, session, XEP_0166.A_SESSION_INFO) | 335 return self._buildJingleElt(client, session, XEP_0166.A_SESSION_INFO) |
302 | 336 |
303 @defer.inlineCallbacks | 337 @defer.inlineCallbacks |
304 def initiate(self, client, peer_jid, contents): | 338 def initiate(self, client, peer_jid, contents): |
305 """Send a session initiation request | 339 """Send a session initiation request |
306 | 340 |
316 default to BOTH (see XEP-0166 §7.3) | 350 default to BOTH (see XEP-0166 §7.3) |
317 - app_args(list): args to pass to the application plugin | 351 - app_args(list): args to pass to the application plugin |
318 - app_kwargs(dict): keyword args to pass to the application plugin | 352 - app_kwargs(dict): keyword args to pass to the application plugin |
319 @return D(unicode): jingle session id | 353 @return D(unicode): jingle session id |
320 """ | 354 """ |
321 assert contents # there must be at least one content | 355 assert contents # there must be at least one content |
322 if peer_jid == client.jid: | 356 if peer_jid == client.jid: |
323 raise ValueError(_(u"You can't do a jingle session with yourself")) | 357 raise ValueError(_(u"You can't do a jingle session with yourself")) |
324 initiator = client.jid | 358 initiator = client.jid |
325 sid = unicode(uuid.uuid4()) | 359 sid = unicode(uuid.uuid4()) |
326 # TODO: session cleaning after timeout ? | 360 # TODO: session cleaning after timeout ? |
327 session = client.jingle_sessions[sid] = {'id': sid, | 361 session = client.jingle_sessions[sid] = { |
328 'state': STATE_PENDING, | 362 "id": sid, |
329 'initiator': initiator, | 363 "state": STATE_PENDING, |
330 'role': XEP_0166.ROLE_INITIATOR, | 364 "initiator": initiator, |
331 'peer_jid': peer_jid, | 365 "role": XEP_0166.ROLE_INITIATOR, |
332 'started': time.time(), | 366 "peer_jid": peer_jid, |
333 'contents': {} | 367 "started": time.time(), |
334 } | 368 "contents": {}, |
335 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_INITIATE) | 369 } |
370 iq_elt, jingle_elt = self._buildJingleElt( | |
371 client, session, XEP_0166.A_SESSION_INITIATE | |
372 ) | |
336 jingle_elt["initiator"] = initiator.full() | 373 jingle_elt["initiator"] = initiator.full() |
337 | 374 |
338 contents_dict = session['contents'] | 375 contents_dict = session["contents"] |
339 | 376 |
340 for content in contents: | 377 for content in contents: |
341 # we get the application plugin | 378 # we get the application plugin |
342 app_ns = content['app_ns'] | 379 app_ns = content["app_ns"] |
343 try: | 380 try: |
344 application = self._applications[app_ns] | 381 application = self._applications[app_ns] |
345 except KeyError: | 382 except KeyError: |
346 raise exceptions.InternalError(u"No application registered for {}".format(app_ns)) | 383 raise exceptions.InternalError( |
384 u"No application registered for {}".format(app_ns) | |
385 ) | |
347 | 386 |
348 # and the transport plugin | 387 # and the transport plugin |
349 transport_type = content.get('transport_type', XEP_0166.TRANSPORT_STREAMING) | 388 transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING) |
350 try: | 389 try: |
351 transport = self._type_transports[transport_type][0] | 390 transport = self._type_transports[transport_type][0] |
352 except IndexError: | 391 except IndexError: |
353 raise exceptions.InternalError(u"No transport registered for {}".format(transport_type)) | 392 raise exceptions.InternalError( |
393 u"No transport registered for {}".format(transport_type) | |
394 ) | |
354 | 395 |
355 # we build the session data | 396 # we build the session data |
356 content_data = {'application': application, | 397 content_data = { |
357 'application_data': {}, | 398 "application": application, |
358 'transport': transport, | 399 "application_data": {}, |
359 'transport_data': {}, | 400 "transport": transport, |
360 'creator': XEP_0166.ROLE_INITIATOR, | 401 "transport_data": {}, |
361 'senders': content.get('senders', 'both'), | 402 "creator": XEP_0166.ROLE_INITIATOR, |
362 } | 403 "senders": content.get("senders", "both"), |
404 } | |
363 try: | 405 try: |
364 content_name = content['name'] | 406 content_name = content["name"] |
365 except KeyError: | 407 except KeyError: |
366 content_name = unicode(uuid.uuid4()) | 408 content_name = unicode(uuid.uuid4()) |
367 else: | 409 else: |
368 if content_name in contents_dict: | 410 if content_name in contents_dict: |
369 raise exceptions.InternalError('There is already a content with this name') | 411 raise exceptions.InternalError( |
412 "There is already a content with this name" | |
413 ) | |
370 contents_dict[content_name] = content_data | 414 contents_dict[content_name] = content_data |
371 | 415 |
372 # we construct the content element | 416 # we construct the content element |
373 content_elt = jingle_elt.addElement('content') | 417 content_elt = jingle_elt.addElement("content") |
374 content_elt['creator'] = content_data['creator'] | 418 content_elt["creator"] = content_data["creator"] |
375 content_elt['name'] = content_name | 419 content_elt["name"] = content_name |
376 try: | 420 try: |
377 content_elt['senders'] = content['senders'] | 421 content_elt["senders"] = content["senders"] |
378 except KeyError: | 422 except KeyError: |
379 pass | 423 pass |
380 | 424 |
381 # then the description element | 425 # then the description element |
382 app_args = content.get('app_args', []) | 426 app_args = content.get("app_args", []) |
383 app_kwargs = content.get('app_kwargs', {}) | 427 app_kwargs = content.get("app_kwargs", {}) |
384 desc_elt = yield application.handler.jingleSessionInit(client, session, content_name, *app_args, **app_kwargs) | 428 desc_elt = yield application.handler.jingleSessionInit( |
429 client, session, content_name, *app_args, **app_kwargs | |
430 ) | |
385 content_elt.addChild(desc_elt) | 431 content_elt.addChild(desc_elt) |
386 | 432 |
387 # and the transport one | 433 # and the transport one |
388 transport_elt = yield transport.handler.jingleSessionInit(client, session, content_name) | 434 transport_elt = yield transport.handler.jingleSessionInit( |
435 client, session, content_name | |
436 ) | |
389 content_elt.addChild(transport_elt) | 437 content_elt.addChild(transport_elt) |
390 | 438 |
391 try: | 439 try: |
392 yield iq_elt.send() | 440 yield iq_elt.send() |
393 except Exception as e: | 441 except Exception as e: |
408 if there is no more content, then session is terminated | 456 if there is no more content, then session is terminated |
409 @param session(dict): jingle session | 457 @param session(dict): jingle session |
410 @param content_name(unicode): name of the content terminated | 458 @param content_name(unicode): name of the content terminated |
411 @param reason(unicode): reason of the termination | 459 @param reason(unicode): reason of the termination |
412 """ | 460 """ |
413 contents = session['contents'] | 461 contents = session["contents"] |
414 del contents[content_name] | 462 del contents[content_name] |
415 if not contents: | 463 if not contents: |
416 self.terminate(client, reason, session) | 464 self.terminate(client, reason, session) |
417 | 465 |
418 ## defaults methods called when plugin doesn't have them ## | 466 ## defaults methods called when plugin doesn't have them ## |
419 | 467 |
420 def jingleRequestConfirmationDefault(self, client, action, session, content_name, desc_elt): | 468 def jingleRequestConfirmationDefault( |
469 self, client, action, session, content_name, desc_elt | |
470 ): | |
421 """This method request confirmation for a jingle session""" | 471 """This method request confirmation for a jingle session""" |
422 log.debug(u"Using generic jingle confirmation method") | 472 log.debug(u"Using generic jingle confirmation method") |
423 return xml_tools.deferConfirm(self.host, _(CONFIRM_TXT).format(entity=session['peer_jid'].full()), _('Confirm Jingle session'), profile=client.profile) | 473 return xml_tools.deferConfirm( |
474 self.host, | |
475 _(CONFIRM_TXT).format(entity=session["peer_jid"].full()), | |
476 _("Confirm Jingle session"), | |
477 profile=client.profile, | |
478 ) | |
424 | 479 |
425 ## jingle events ## | 480 ## jingle events ## |
426 | 481 |
427 def _onJingleRequest(self, request, client): | 482 def _onJingleRequest(self, request, client): |
428 """Called when any jingle request is received | 483 """Called when any jingle request is received |
430 The request will then be dispatched to appropriate method | 485 The request will then be dispatched to appropriate method |
431 according to current state | 486 according to current state |
432 @param request(domish.Element): received IQ request | 487 @param request(domish.Element): received IQ request |
433 """ | 488 """ |
434 request.handled = True | 489 request.handled = True |
435 jingle_elt = request.elements(NS_JINGLE, 'jingle').next() | 490 jingle_elt = request.elements(NS_JINGLE, "jingle").next() |
436 | 491 |
437 # first we need the session id | 492 # first we need the session id |
438 try: | 493 try: |
439 sid = jingle_elt['sid'] | 494 sid = jingle_elt["sid"] |
440 if not sid: | 495 if not sid: |
441 raise KeyError | 496 raise KeyError |
442 except KeyError: | 497 except KeyError: |
443 log.warning(u"Received jingle request has no sid attribute") | 498 log.warning(u"Received jingle request has no sid attribute") |
444 self.sendError(client, 'bad-request', None, request) | 499 self.sendError(client, "bad-request", None, request) |
445 return | 500 return |
446 | 501 |
447 # then the action | 502 # then the action |
448 try: | 503 try: |
449 action = jingle_elt['action'] | 504 action = jingle_elt["action"] |
450 if not action: | 505 if not action: |
451 raise KeyError | 506 raise KeyError |
452 except KeyError: | 507 except KeyError: |
453 log.warning(u"Received jingle request has no action") | 508 log.warning(u"Received jingle request has no action") |
454 self.sendError(client, 'bad-request', None, request) | 509 self.sendError(client, "bad-request", None, request) |
455 return | 510 return |
456 | 511 |
457 peer_jid = jid.JID(request['from']) | 512 peer_jid = jid.JID(request["from"]) |
458 | 513 |
459 # we get or create the session | 514 # we get or create the session |
460 try: | 515 try: |
461 session = client.jingle_sessions[sid] | 516 session = client.jingle_sessions[sid] |
462 except KeyError: | 517 except KeyError: |
463 if action == XEP_0166.A_SESSION_INITIATE: | 518 if action == XEP_0166.A_SESSION_INITIATE: |
464 pass | 519 pass |
465 elif action == XEP_0166.A_SESSION_TERMINATE: | 520 elif action == XEP_0166.A_SESSION_TERMINATE: |
466 log.debug(u"ignoring session terminate action (inexisting session id): {request_id} [{profile}]".format( | 521 log.debug( |
467 request_id=sid, | 522 u"ignoring session terminate action (inexisting session id): {request_id} [{profile}]".format( |
468 profile = client.profile)) | 523 request_id=sid, profile=client.profile |
524 ) | |
525 ) | |
469 return | 526 return |
470 else: | 527 else: |
471 log.warning(u"Received request for an unknown session id: {request_id} [{profile}]".format( | 528 log.warning( |
472 request_id=sid, | 529 u"Received request for an unknown session id: {request_id} [{profile}]".format( |
473 profile = client.profile)) | 530 request_id=sid, profile=client.profile |
474 self.sendError(client, 'item-not-found', None, request, 'unknown-session') | 531 ) |
532 ) | |
533 self.sendError(client, "item-not-found", None, request, "unknown-session") | |
475 return | 534 return |
476 | 535 |
477 session = client.jingle_sessions[sid] = {'id': sid, | 536 session = client.jingle_sessions[sid] = { |
478 'state': STATE_PENDING, | 537 "id": sid, |
479 'initiator': peer_jid, | 538 "state": STATE_PENDING, |
480 'role': XEP_0166.ROLE_RESPONDER, | 539 "initiator": peer_jid, |
481 'peer_jid': peer_jid, | 540 "role": XEP_0166.ROLE_RESPONDER, |
482 'started': time.time(), | 541 "peer_jid": peer_jid, |
483 } | 542 "started": time.time(), |
543 } | |
484 else: | 544 else: |
485 if session['peer_jid'] != peer_jid: | 545 if session["peer_jid"] != peer_jid: |
486 log.warning(u"sid conflict ({}), the jid doesn't match. Can be a collision, a hack attempt, or a bad sid generation".format(sid)) | 546 log.warning( |
487 self.sendError(client, 'service-unavailable', sid, request) | 547 u"sid conflict ({}), the jid doesn't match. Can be a collision, a hack attempt, or a bad sid generation".format( |
548 sid | |
549 ) | |
550 ) | |
551 self.sendError(client, "service-unavailable", sid, request) | |
488 return | 552 return |
489 if session['id'] != sid: | 553 if session["id"] != sid: |
490 log.error(u"session id doesn't match") | 554 log.error(u"session id doesn't match") |
491 self.sendError(client, 'service-unavailable', sid, request) | 555 self.sendError(client, "service-unavailable", sid, request) |
492 raise exceptions.InternalError | 556 raise exceptions.InternalError |
493 | 557 |
494 if action == XEP_0166.A_SESSION_INITIATE: | 558 if action == XEP_0166.A_SESSION_INITIATE: |
495 self.onSessionInitiate(client, request, jingle_elt, session) | 559 self.onSessionInitiate(client, request, jingle_elt, session) |
496 elif action == XEP_0166.A_SESSION_TERMINATE: | 560 elif action == XEP_0166.A_SESSION_TERMINATE: |
510 else: | 574 else: |
511 raise exceptions.InternalError(u"Unknown action {}".format(action)) | 575 raise exceptions.InternalError(u"Unknown action {}".format(action)) |
512 | 576 |
513 ## Actions callbacks ## | 577 ## Actions callbacks ## |
514 | 578 |
515 def _parseElements(self, jingle_elt, session, request, client, new=False, creator=ROLE_INITIATOR, with_application=True, with_transport=True): | 579 def _parseElements( |
580 self, | |
581 jingle_elt, | |
582 session, | |
583 request, | |
584 client, | |
585 new=False, | |
586 creator=ROLE_INITIATOR, | |
587 with_application=True, | |
588 with_transport=True, | |
589 ): | |
516 """Parse contents elements and fill contents_dict accordingly | 590 """Parse contents elements and fill contents_dict accordingly |
517 | 591 |
518 after the parsing, contents_dict will containt handlers, "desc_elt" and "transport_elt" | 592 after the parsing, contents_dict will containt handlers, "desc_elt" and "transport_elt" |
519 @param jingle_elt(domish.Element): parent <jingle> element, containing one or more <content> | 593 @param jingle_elt(domish.Element): parent <jingle> element, containing one or more <content> |
520 @param session(dict): session data | 594 @param session(dict): session data |
525 @param creator(unicode): only used if new is True: creating pear (see § 7.3) | 599 @param creator(unicode): only used if new is True: creating pear (see § 7.3) |
526 @param with_application(bool): if True, raise an error if there is no <description> element else ignore it | 600 @param with_application(bool): if True, raise an error if there is no <description> element else ignore it |
527 @param with_transport(bool): if True, raise an error if there is no <transport> element else ignore it | 601 @param with_transport(bool): if True, raise an error if there is no <transport> element else ignore it |
528 @raise exceptions.CancelError: the error is treated and the calling method can cancel the treatment (i.e. return) | 602 @raise exceptions.CancelError: the error is treated and the calling method can cancel the treatment (i.e. return) |
529 """ | 603 """ |
530 contents_dict = session['contents'] | 604 contents_dict = session["contents"] |
531 content_elts = jingle_elt.elements(NS_JINGLE, 'content') | 605 content_elts = jingle_elt.elements(NS_JINGLE, "content") |
532 | 606 |
533 for content_elt in content_elts: | 607 for content_elt in content_elts: |
534 name = content_elt['name'] | 608 name = content_elt["name"] |
535 | 609 |
536 if new: | 610 if new: |
537 # the content must not exist, we check it | 611 # the content must not exist, we check it |
538 if not name or name in contents_dict: | 612 if not name or name in contents_dict: |
539 self.sendError(client, 'bad-request', session['id'], request) | 613 self.sendError(client, "bad-request", session["id"], request) |
540 raise exceptions.CancelError | 614 raise exceptions.CancelError |
541 content_data = contents_dict[name] = {'creator': creator, | 615 content_data = contents_dict[name] = { |
542 'senders': content_elt.attributes.get('senders', 'both'), | 616 "creator": creator, |
543 } | 617 "senders": content_elt.attributes.get("senders", "both"), |
618 } | |
544 else: | 619 else: |
545 # the content must exist, we check it | 620 # the content must exist, we check it |
546 try: | 621 try: |
547 content_data = contents_dict[name] | 622 content_data = contents_dict[name] |
548 except KeyError: | 623 except KeyError: |
549 log.warning(u"Other peer try to access an unknown content") | 624 log.warning(u"Other peer try to access an unknown content") |
550 self.sendError(client, 'bad-request', session['id'], request) | 625 self.sendError(client, "bad-request", session["id"], request) |
551 raise exceptions.CancelError | 626 raise exceptions.CancelError |
552 | 627 |
553 # application | 628 # application |
554 if with_application: | 629 if with_application: |
555 desc_elt = content_elt.description | 630 desc_elt = content_elt.description |
556 if not desc_elt: | 631 if not desc_elt: |
557 self.sendError(client, 'bad-request', session['id'], request) | 632 self.sendError(client, "bad-request", session["id"], request) |
558 raise exceptions.CancelError | 633 raise exceptions.CancelError |
559 | 634 |
560 if new: | 635 if new: |
561 # the content is new, we need to check and link the application | 636 # the content is new, we need to check and link the application |
562 app_ns = desc_elt.uri | 637 app_ns = desc_elt.uri |
563 if not app_ns or app_ns == NS_JINGLE: | 638 if not app_ns or app_ns == NS_JINGLE: |
564 self.sendError(client, 'bad-request', session['id'], request) | 639 self.sendError(client, "bad-request", session["id"], request) |
565 raise exceptions.CancelError | 640 raise exceptions.CancelError |
566 | 641 |
567 try: | 642 try: |
568 application = self._applications[app_ns] | 643 application = self._applications[app_ns] |
569 except KeyError: | 644 except KeyError: |
570 log.warning(u"Unmanaged application namespace [{}]".format(app_ns)) | 645 log.warning( |
571 self.sendError(client, 'service-unavailable', session['id'], request) | 646 u"Unmanaged application namespace [{}]".format(app_ns) |
647 ) | |
648 self.sendError( | |
649 client, "service-unavailable", session["id"], request | |
650 ) | |
572 raise exceptions.CancelError | 651 raise exceptions.CancelError |
573 | 652 |
574 content_data['application'] = application | 653 content_data["application"] = application |
575 content_data['application_data'] = {} | 654 content_data["application_data"] = {} |
576 else: | 655 else: |
577 # the content exists, we check that we have not a former desc_elt | 656 # the content exists, we check that we have not a former desc_elt |
578 if 'desc_elt' in content_data: | 657 if "desc_elt" in content_data: |
579 raise exceptions.InternalError(u"desc_elt should not exist at this point") | 658 raise exceptions.InternalError( |
580 | 659 u"desc_elt should not exist at this point" |
581 content_data['desc_elt'] = desc_elt | 660 ) |
661 | |
662 content_data["desc_elt"] = desc_elt | |
582 | 663 |
583 # transport | 664 # transport |
584 if with_transport: | 665 if with_transport: |
585 transport_elt = content_elt.transport | 666 transport_elt = content_elt.transport |
586 if not transport_elt: | 667 if not transport_elt: |
587 self.sendError(client, 'bad-request', session['id'], request) | 668 self.sendError(client, "bad-request", session["id"], request) |
588 raise exceptions.CancelError | 669 raise exceptions.CancelError |
589 | 670 |
590 if new: | 671 if new: |
591 # the content is new, we need to check and link the transport | 672 # the content is new, we need to check and link the transport |
592 transport_ns = transport_elt.uri | 673 transport_ns = transport_elt.uri |
593 if not app_ns or app_ns == NS_JINGLE: | 674 if not app_ns or app_ns == NS_JINGLE: |
594 self.sendError(client, 'bad-request', session['id'], request) | 675 self.sendError(client, "bad-request", session["id"], request) |
595 raise exceptions.CancelError | 676 raise exceptions.CancelError |
596 | 677 |
597 try: | 678 try: |
598 transport = self._transports[transport_ns] | 679 transport = self._transports[transport_ns] |
599 except KeyError: | 680 except KeyError: |
600 raise exceptions.InternalError(u"No transport registered for namespace {}".format(transport_ns)) | 681 raise exceptions.InternalError( |
601 content_data['transport'] = transport | 682 u"No transport registered for namespace {}".format( |
602 content_data['transport_data'] = {} | 683 transport_ns |
684 ) | |
685 ) | |
686 content_data["transport"] = transport | |
687 content_data["transport_data"] = {} | |
603 else: | 688 else: |
604 # the content exists, we check that we have not a former transport_elt | 689 # the content exists, we check that we have not a former transport_elt |
605 if 'transport_elt' in content_data: | 690 if "transport_elt" in content_data: |
606 raise exceptions.InternalError(u"transport_elt should not exist at this point") | 691 raise exceptions.InternalError( |
607 | 692 u"transport_elt should not exist at this point" |
608 content_data['transport_elt'] = transport_elt | 693 ) |
694 | |
695 content_data["transport_elt"] = transport_elt | |
609 | 696 |
610 def _ignore(self, client, action, session, content_name, elt): | 697 def _ignore(self, client, action, session, content_name, elt): |
611 """Dummy method used when not exception must be raised if a method is not implemented in _callPlugins | 698 """Dummy method used when not exception must be raised if a method is not implemented in _callPlugins |
612 | 699 |
613 must be used as app_default_cb and/or transp_default_cb | 700 must be used as app_default_cb and/or transp_default_cb |
614 """ | 701 """ |
615 return elt | 702 return elt |
616 | 703 |
617 def _callPlugins(self, client, action, session, app_method_name='jingleHandler', | 704 def _callPlugins( |
618 transp_method_name='jingleHandler', | 705 self, |
619 app_default_cb=None, transp_default_cb=None, delete=True, | 706 client, |
620 elements=True, force_element=None): | 707 action, |
708 session, | |
709 app_method_name="jingleHandler", | |
710 transp_method_name="jingleHandler", | |
711 app_default_cb=None, | |
712 transp_default_cb=None, | |
713 delete=True, | |
714 elements=True, | |
715 force_element=None, | |
716 ): | |
621 """Call application and transport plugin methods for all contents | 717 """Call application and transport plugin methods for all contents |
622 | 718 |
623 @param action(unicode): jingle action name | 719 @param action(unicode): jingle action name |
624 @param session(dict): jingle session data | 720 @param session(dict): jingle session data |
625 @param app_method_name(unicode, None): name of the method to call for applications | 721 @param app_method_name(unicode, None): name of the method to call for applications |
638 @param force_element(None, domish.Element, object): if elements is False, it is used as element parameter | 734 @param force_element(None, domish.Element, object): if elements is False, it is used as element parameter |
639 else it is ignored | 735 else it is ignored |
640 @return (list[defer.Deferred]): list of launched Deferred | 736 @return (list[defer.Deferred]): list of launched Deferred |
641 @raise exceptions.NotFound: method is not implemented | 737 @raise exceptions.NotFound: method is not implemented |
642 """ | 738 """ |
643 contents_dict = session['contents'] | 739 contents_dict = session["contents"] |
644 defers_list = [] | 740 defers_list = [] |
645 for content_name, content_data in contents_dict.iteritems(): | 741 for content_name, content_data in contents_dict.iteritems(): |
646 for method_name, handler_key, default_cb, elt_name in ( | 742 for method_name, handler_key, default_cb, elt_name in ( |
647 (app_method_name, 'application', app_default_cb, 'desc_elt'), | 743 (app_method_name, "application", app_default_cb, "desc_elt"), |
648 (transp_method_name, 'transport', transp_default_cb, 'transport_elt')): | 744 (transp_method_name, "transport", transp_default_cb, "transport_elt"), |
745 ): | |
649 if method_name is None: | 746 if method_name is None: |
650 continue | 747 continue |
651 | 748 |
652 handler = content_data[handler_key].handler | 749 handler = content_data[handler_key].handler |
653 try: | 750 try: |
654 method = getattr(handler, method_name) | 751 method = getattr(handler, method_name) |
655 except AttributeError: | 752 except AttributeError: |
656 if default_cb is None: | 753 if default_cb is None: |
657 raise exceptions.NotFound(u'{} not implemented !'.format(method_name)) | 754 raise exceptions.NotFound( |
755 u"{} not implemented !".format(method_name) | |
756 ) | |
658 else: | 757 else: |
659 method = default_cb | 758 method = default_cb |
660 if elements: | 759 if elements: |
661 elt = content_data.pop(elt_name) if delete else content_data[elt_name] | 760 elt = content_data.pop(elt_name) if delete else content_data[elt_name] |
662 else: | 761 else: |
663 elt = force_element | 762 elt = force_element |
664 d = defer.maybeDeferred(method, client, action, session, content_name, elt) | 763 d = defer.maybeDeferred( |
764 method, client, action, session, content_name, elt | |
765 ) | |
665 defers_list.append(d) | 766 defers_list.append(d) |
666 | 767 |
667 return defers_list | 768 return defers_list |
668 | 769 |
669 def onSessionInitiate(self, client, request, jingle_elt, session): | 770 def onSessionInitiate(self, client, request, jingle_elt, session): |
676 @param client: %(doc_client)s | 777 @param client: %(doc_client)s |
677 @param request(domish.Element): full request | 778 @param request(domish.Element): full request |
678 @param jingle_elt(domish.Element): <jingle> element | 779 @param jingle_elt(domish.Element): <jingle> element |
679 @param session(dict): session data | 780 @param session(dict): session data |
680 """ | 781 """ |
681 if 'contents' in session: | 782 if "contents" in session: |
682 raise exceptions.InternalError("Contents dict should not already exist at this point") | 783 raise exceptions.InternalError( |
683 session['contents'] = contents_dict = {} | 784 "Contents dict should not already exist at this point" |
684 | 785 ) |
685 try: | 786 session["contents"] = contents_dict = {} |
686 self._parseElements(jingle_elt, session, request, client, True, XEP_0166.ROLE_INITIATOR) | 787 |
788 try: | |
789 self._parseElements( | |
790 jingle_elt, session, request, client, True, XEP_0166.ROLE_INITIATOR | |
791 ) | |
687 except exceptions.CancelError: | 792 except exceptions.CancelError: |
688 return | 793 return |
689 | 794 |
690 if not contents_dict: | 795 if not contents_dict: |
691 # there MUST be at least one content | 796 # there MUST be at least one content |
692 self.sendError(client, 'bad-request', session['id'], request) | 797 self.sendError(client, "bad-request", session["id"], request) |
693 return | 798 return |
694 | 799 |
695 # at this point we can send the <iq/> result to confirm reception of the request | 800 # at this point we can send the <iq/> result to confirm reception of the request |
696 client.send(xmlstream.toResponse(request, 'result')) | 801 client.send(xmlstream.toResponse(request, "result")) |
697 | 802 |
698 # we now request each application plugin confirmation | 803 # we now request each application plugin confirmation |
699 # and if all are accepted, we can accept the session | 804 # and if all are accepted, we can accept the session |
700 confirm_defers = self._callPlugins(client, XEP_0166.A_SESSION_INITIATE, session, 'jingleRequestConfirmation', None, self.jingleRequestConfirmationDefault, delete=False) | 805 confirm_defers = self._callPlugins( |
806 client, | |
807 XEP_0166.A_SESSION_INITIATE, | |
808 session, | |
809 "jingleRequestConfirmation", | |
810 None, | |
811 self.jingleRequestConfirmationDefault, | |
812 delete=False, | |
813 ) | |
701 | 814 |
702 confirm_dlist = defer.gatherResults(confirm_defers) | 815 confirm_dlist = defer.gatherResults(confirm_defers) |
703 confirm_dlist.addCallback(self._confirmationCb, session, jingle_elt, client) | 816 confirm_dlist.addCallback(self._confirmationCb, session, jingle_elt, client) |
704 confirm_dlist.addErrback(self._jingleErrorCb, session['id'], request, client) | 817 confirm_dlist.addErrback(self._jingleErrorCb, session["id"], request, client) |
705 | 818 |
706 def _confirmationCb(self, confirm_results, session, jingle_elt, client): | 819 def _confirmationCb(self, confirm_results, session, jingle_elt, client): |
707 """Method called when confirmation from user has been received | 820 """Method called when confirmation from user has been received |
708 | 821 |
709 This method is only called for the responder | 822 This method is only called for the responder |
714 """ | 827 """ |
715 confirmed = all(confirm_results) | 828 confirmed = all(confirm_results) |
716 if not confirmed: | 829 if not confirmed: |
717 return self.terminate(client, XEP_0166.REASON_DECLINE, session) | 830 return self.terminate(client, XEP_0166.REASON_DECLINE, session) |
718 | 831 |
719 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_ACCEPT) | 832 iq_elt, jingle_elt = self._buildJingleElt( |
720 jingle_elt['responder'] = client.jid.full() | 833 client, session, XEP_0166.A_SESSION_ACCEPT |
834 ) | |
835 jingle_elt["responder"] = client.jid.full() | |
721 | 836 |
722 # contents | 837 # contents |
723 | 838 |
724 def addElement(domish_elt, content_elt): | 839 def addElement(domish_elt, content_elt): |
725 content_elt.addChild(domish_elt) | 840 content_elt.addChild(domish_elt) |
726 | 841 |
727 defers_list = [] | 842 defers_list = [] |
728 | 843 |
729 for content_name, content_data in session['contents'].iteritems(): | 844 for content_name, content_data in session["contents"].iteritems(): |
730 content_elt = jingle_elt.addElement('content') | 845 content_elt = jingle_elt.addElement("content") |
731 content_elt['creator'] = XEP_0166.ROLE_INITIATOR | 846 content_elt["creator"] = XEP_0166.ROLE_INITIATOR |
732 content_elt['name'] = content_name | 847 content_elt["name"] = content_name |
733 | 848 |
734 application = content_data['application'] | 849 application = content_data["application"] |
735 app_session_accept_cb = application.handler.jingleHandler | 850 app_session_accept_cb = application.handler.jingleHandler |
736 | 851 |
737 app_d = defer.maybeDeferred(app_session_accept_cb, client, | 852 app_d = defer.maybeDeferred( |
738 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('desc_elt')) | 853 app_session_accept_cb, |
854 client, | |
855 XEP_0166.A_SESSION_INITIATE, | |
856 session, | |
857 content_name, | |
858 content_data.pop("desc_elt"), | |
859 ) | |
739 app_d.addCallback(addElement, content_elt) | 860 app_d.addCallback(addElement, content_elt) |
740 defers_list.append(app_d) | 861 defers_list.append(app_d) |
741 | 862 |
742 transport = content_data['transport'] | 863 transport = content_data["transport"] |
743 transport_session_accept_cb = transport.handler.jingleHandler | 864 transport_session_accept_cb = transport.handler.jingleHandler |
744 | 865 |
745 transport_d = defer.maybeDeferred(transport_session_accept_cb, client, | 866 transport_d = defer.maybeDeferred( |
746 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('transport_elt')) | 867 transport_session_accept_cb, |
868 client, | |
869 XEP_0166.A_SESSION_INITIATE, | |
870 session, | |
871 content_name, | |
872 content_data.pop("transport_elt"), | |
873 ) | |
747 transport_d.addCallback(addElement, content_elt) | 874 transport_d.addCallback(addElement, content_elt) |
748 defers_list.append(transport_d) | 875 defers_list.append(transport_d) |
749 | 876 |
750 d_list = defer.DeferredList(defers_list) | 877 d_list = defer.DeferredList(defers_list) |
751 d_list.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_PREPARE_RESPONDER, session, app_method_name=None, elements=False)) | 878 d_list.addCallback( |
879 lambda dummy: self._callPlugins( | |
880 client, | |
881 XEP_0166.A_PREPARE_RESPONDER, | |
882 session, | |
883 app_method_name=None, | |
884 elements=False, | |
885 ) | |
886 ) | |
752 d_list.addCallback(lambda dummy: iq_elt.send()) | 887 d_list.addCallback(lambda dummy: iq_elt.send()) |
888 | |
753 def changeState(dummy, session): | 889 def changeState(dummy, session): |
754 session['state'] = STATE_ACTIVE | 890 session["state"] = STATE_ACTIVE |
755 | 891 |
756 d_list.addCallback(changeState, session) | 892 d_list.addCallback(changeState, session) |
757 d_list.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_ACCEPTED_ACK, session, elements=False)) | 893 d_list.addCallback( |
758 d_list.addErrback(self._iqError, session['id'], client) | 894 lambda dummy: self._callPlugins( |
895 client, XEP_0166.A_ACCEPTED_ACK, session, elements=False | |
896 ) | |
897 ) | |
898 d_list.addErrback(self._iqError, session["id"], client) | |
759 return d_list | 899 return d_list |
760 | 900 |
761 def onSessionTerminate(self, client, request, jingle_elt, session): | 901 def onSessionTerminate(self, client, request, jingle_elt, session): |
762 # TODO: check reason, display a message to user if needed | 902 # TODO: check reason, display a message to user if needed |
763 log.debug("Jingle Session {} terminated".format(session['id'])) | 903 log.debug("Jingle Session {} terminated".format(session["id"])) |
764 try: | 904 try: |
765 reason_elt = jingle_elt.elements(NS_JINGLE, 'reason').next() | 905 reason_elt = jingle_elt.elements(NS_JINGLE, "reason").next() |
766 except StopIteration: | 906 except StopIteration: |
767 log.warning(u"No reason given for session termination") | 907 log.warning(u"No reason given for session termination") |
768 reason_elt = jingle_elt.addElement('reason') | 908 reason_elt = jingle_elt.addElement("reason") |
769 | 909 |
770 terminate_defers = self._callPlugins(client, XEP_0166.A_SESSION_TERMINATE, session, 'jingleTerminate', 'jingleTerminate', self._ignore, self._ignore, elements=False, force_element=reason_elt) | 910 terminate_defers = self._callPlugins( |
911 client, | |
912 XEP_0166.A_SESSION_TERMINATE, | |
913 session, | |
914 "jingleTerminate", | |
915 "jingleTerminate", | |
916 self._ignore, | |
917 self._ignore, | |
918 elements=False, | |
919 force_element=reason_elt, | |
920 ) | |
771 terminate_dlist = defer.DeferredList(terminate_defers) | 921 terminate_dlist = defer.DeferredList(terminate_defers) |
772 | 922 |
773 terminate_dlist.addCallback(lambda dummy: self._delSession(client, session['id'])) | 923 terminate_dlist.addCallback(lambda dummy: self._delSession(client, session["id"])) |
774 client.send(xmlstream.toResponse(request, 'result')) | 924 client.send(xmlstream.toResponse(request, "result")) |
775 | 925 |
776 def onSessionAccept(self, client, request, jingle_elt, session): | 926 def onSessionAccept(self, client, request, jingle_elt, session): |
777 """Method called once session is accepted | 927 """Method called once session is accepted |
778 | 928 |
779 This method is only called for initiator | 929 This method is only called for initiator |
780 @param client: %(doc_client)s | 930 @param client: %(doc_client)s |
781 @param request(domish.Element): full <iq> request | 931 @param request(domish.Element): full <iq> request |
782 @param jingle_elt(domish.Element): the <jingle> element | 932 @param jingle_elt(domish.Element): the <jingle> element |
783 @param session(dict): session data | 933 @param session(dict): session data |
784 """ | 934 """ |
785 log.debug(u"Jingle session {} has been accepted".format(session['id'])) | 935 log.debug(u"Jingle session {} has been accepted".format(session["id"])) |
786 | 936 |
787 try: | 937 try: |
788 self._parseElements(jingle_elt, session, request, client) | 938 self._parseElements(jingle_elt, session, request, client) |
789 except exceptions.CancelError: | 939 except exceptions.CancelError: |
790 return | 940 return |
791 | 941 |
792 # at this point we can send the <iq/> result to confirm reception of the request | 942 # at this point we can send the <iq/> result to confirm reception of the request |
793 client.send(xmlstream.toResponse(request, 'result')) | 943 client.send(xmlstream.toResponse(request, "result")) |
794 # and change the state | 944 # and change the state |
795 session['state'] = STATE_ACTIVE | 945 session["state"] = STATE_ACTIVE |
796 | 946 |
797 negociate_defers = [] | 947 negociate_defers = [] |
798 negociate_defers = self._callPlugins(client, XEP_0166.A_SESSION_ACCEPT, session) | 948 negociate_defers = self._callPlugins(client, XEP_0166.A_SESSION_ACCEPT, session) |
799 | 949 |
800 negociate_dlist = defer.DeferredList(negociate_defers) | 950 negociate_dlist = defer.DeferredList(negociate_defers) |
801 | 951 |
802 # after negociations we start the transfer | 952 # after negociations we start the transfer |
803 negociate_dlist.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_START, session, app_method_name=None, elements=False)) | 953 negociate_dlist.addCallback( |
954 lambda dummy: self._callPlugins( | |
955 client, XEP_0166.A_START, session, app_method_name=None, elements=False | |
956 ) | |
957 ) | |
804 | 958 |
805 def _onSessionCb(self, result, client, request, jingle_elt, session): | 959 def _onSessionCb(self, result, client, request, jingle_elt, session): |
806 client.send(xmlstream.toResponse(request, 'result')) | 960 client.send(xmlstream.toResponse(request, "result")) |
807 | 961 |
808 def _onSessionEb(self, failure_, client, request, jingle_elt, session): | 962 def _onSessionEb(self, failure_, client, request, jingle_elt, session): |
809 log.error(u"Error while handling onSessionInfo: {}".format(failure_.value)) | 963 log.error(u"Error while handling onSessionInfo: {}".format(failure_.value)) |
810 # XXX: only error managed so far, maybe some applications/transports need more | 964 # XXX: only error managed so far, maybe some applications/transports need more |
811 self.sendError(client, 'feature-not-implemented', None, request, 'unsupported-info') | 965 self.sendError( |
966 client, "feature-not-implemented", None, request, "unsupported-info" | |
967 ) | |
812 | 968 |
813 def onSessionInfo(self, client, request, jingle_elt, session): | 969 def onSessionInfo(self, client, request, jingle_elt, session): |
814 """Method called when a session-info action is received from other peer | 970 """Method called when a session-info action is received from other peer |
815 | 971 |
816 This method is only called for initiator | 972 This method is only called for initiator |
819 @param jingle_elt(domish.Element): the <jingle> element | 975 @param jingle_elt(domish.Element): the <jingle> element |
820 @param session(dict): session data | 976 @param session(dict): session data |
821 """ | 977 """ |
822 if not jingle_elt.children: | 978 if not jingle_elt.children: |
823 # this is a session ping, see XEP-0166 §6.8 | 979 # this is a session ping, see XEP-0166 §6.8 |
824 client.send(xmlstream.toResponse(request, 'result')) | 980 client.send(xmlstream.toResponse(request, "result")) |
825 return | 981 return |
826 | 982 |
827 try: | 983 try: |
828 # XXX: session-info is most likely only used for application, so we don't call transport plugins | 984 # XXX: session-info is most likely only used for application, so we don't call transport plugins |
829 # if a future transport use it, this behaviour must be adapted | 985 # if a future transport use it, this behaviour must be adapted |
830 defers = self._callPlugins(client, XEP_0166.A_SESSION_INFO, session, 'jingleSessionInfo', None, | 986 defers = self._callPlugins( |
831 elements=False, force_element=jingle_elt) | 987 client, |
988 XEP_0166.A_SESSION_INFO, | |
989 session, | |
990 "jingleSessionInfo", | |
991 None, | |
992 elements=False, | |
993 force_element=jingle_elt, | |
994 ) | |
832 except exceptions.NotFound as e: | 995 except exceptions.NotFound as e: |
833 self._onSessionEb(failure.Failure(e), client, request, jingle_elt, session) | 996 self._onSessionEb(failure.Failure(e), client, request, jingle_elt, session) |
834 return | 997 return |
835 | 998 |
836 dlist = defer.DeferredList(defers, fireOnOneErrback=True) | 999 dlist = defer.DeferredList(defers, fireOnOneErrback=True) |
847 @param jingle_elt(domish.Element): the <jingle> element | 1010 @param jingle_elt(domish.Element): the <jingle> element |
848 @param session(dict): session data | 1011 @param session(dict): session data |
849 """ | 1012 """ |
850 log.debug(u"Other peer wants to replace the transport") | 1013 log.debug(u"Other peer wants to replace the transport") |
851 try: | 1014 try: |
852 self._parseElements(jingle_elt, session, request, client, with_application=False) | 1015 self._parseElements( |
1016 jingle_elt, session, request, client, with_application=False | |
1017 ) | |
853 except exceptions.CancelError: | 1018 except exceptions.CancelError: |
854 defer.returnValue(None) | 1019 defer.returnValue(None) |
855 | 1020 |
856 client.send(xmlstream.toResponse(request, 'result')) | 1021 client.send(xmlstream.toResponse(request, "result")) |
857 | 1022 |
858 content_name = None | 1023 content_name = None |
859 to_replace = [] | 1024 to_replace = [] |
860 | 1025 |
861 for content_name, content_data in session['contents'].iteritems(): | 1026 for content_name, content_data in session["contents"].iteritems(): |
862 try: | 1027 try: |
863 transport_elt = content_data.pop('transport_elt') | 1028 transport_elt = content_data.pop("transport_elt") |
864 except KeyError: | 1029 except KeyError: |
865 continue | 1030 continue |
866 transport_ns = transport_elt.uri | 1031 transport_ns = transport_elt.uri |
867 try: | 1032 try: |
868 transport = self._transports[transport_ns] | 1033 transport = self._transports[transport_ns] |
869 except KeyError: | 1034 except KeyError: |
870 log.warning(u"Other peer want to replace current transport with an unknown one: {}".format(transport_ns)) | 1035 log.warning( |
1036 u"Other peer want to replace current transport with an unknown one: {}".format( | |
1037 transport_ns | |
1038 ) | |
1039 ) | |
871 content_name = None | 1040 content_name = None |
872 break | 1041 break |
873 to_replace.append((content_name, content_data, transport, transport_elt)) | 1042 to_replace.append((content_name, content_data, transport, transport_elt)) |
874 | 1043 |
875 if content_name is None: | 1044 if content_name is None: |
876 # wa can't accept the replacement | 1045 # wa can't accept the replacement |
877 iq_elt, reject_jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_TRANSPORT_REJECT) | 1046 iq_elt, reject_jingle_elt = self._buildJingleElt( |
1047 client, session, XEP_0166.A_TRANSPORT_REJECT | |
1048 ) | |
878 for child in jingle_elt.children: | 1049 for child in jingle_elt.children: |
879 reject_jingle_elt.addChild(child) | 1050 reject_jingle_elt.addChild(child) |
880 | 1051 |
881 iq_elt.send() | 1052 iq_elt.send() |
882 defer.returnValue(None) | 1053 defer.returnValue(None) |
883 | 1054 |
884 # at this point, everything is alright and we can replace the transport(s) | 1055 # at this point, everything is alright and we can replace the transport(s) |
885 # this is similar to an session-accept action, but for transports only | 1056 # this is similar to an session-accept action, but for transports only |
886 iq_elt, accept_jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_TRANSPORT_ACCEPT) | 1057 iq_elt, accept_jingle_elt = self._buildJingleElt( |
1058 client, session, XEP_0166.A_TRANSPORT_ACCEPT | |
1059 ) | |
887 for content_name, content_data, transport, transport_elt in to_replace: | 1060 for content_name, content_data, transport, transport_elt in to_replace: |
888 # we can now actually replace the transport | 1061 # we can now actually replace the transport |
889 yield content_data['transport'].handler.jingleHandler(client, XEP_0166.A_DESTROY, session, content_name, None) | 1062 yield content_data["transport"].handler.jingleHandler( |
890 content_data['transport'] = transport | 1063 client, XEP_0166.A_DESTROY, session, content_name, None |
891 content_data['transport_data'].clear() | 1064 ) |
1065 content_data["transport"] = transport | |
1066 content_data["transport_data"].clear() | |
892 # and build the element | 1067 # and build the element |
893 content_elt = accept_jingle_elt.addElement('content') | 1068 content_elt = accept_jingle_elt.addElement("content") |
894 content_elt['name'] = content_name | 1069 content_elt["name"] = content_name |
895 content_elt['creator'] = content_data['creator'] | 1070 content_elt["creator"] = content_data["creator"] |
896 # we notify the transport and insert its <transport/> in the answer | 1071 # we notify the transport and insert its <transport/> in the answer |
897 accept_transport_elt = yield transport.handler.jingleHandler(client, XEP_0166.A_TRANSPORT_REPLACE, session, content_name, transport_elt) | 1072 accept_transport_elt = yield transport.handler.jingleHandler( |
1073 client, XEP_0166.A_TRANSPORT_REPLACE, session, content_name, transport_elt | |
1074 ) | |
898 content_elt.addChild(accept_transport_elt) | 1075 content_elt.addChild(accept_transport_elt) |
899 # there is no confirmation needed here, so we can directly prepare it | 1076 # there is no confirmation needed here, so we can directly prepare it |
900 yield transport.handler.jingleHandler(client, XEP_0166.A_PREPARE_RESPONDER, session, content_name, None) | 1077 yield transport.handler.jingleHandler( |
1078 client, XEP_0166.A_PREPARE_RESPONDER, session, content_name, None | |
1079 ) | |
901 | 1080 |
902 iq_elt.send() | 1081 iq_elt.send() |
903 | 1082 |
904 def onTransportAccept(self, client, request, jingle_elt, session): | 1083 def onTransportAccept(self, client, request, jingle_elt, session): |
905 """Method called once transport replacement is accepted | 1084 """Method called once transport replacement is accepted |
910 @param session(dict): session data | 1089 @param session(dict): session data |
911 """ | 1090 """ |
912 log.debug(u"new transport has been accepted") | 1091 log.debug(u"new transport has been accepted") |
913 | 1092 |
914 try: | 1093 try: |
915 self._parseElements(jingle_elt, session, request, client, with_application=False) | 1094 self._parseElements( |
1095 jingle_elt, session, request, client, with_application=False | |
1096 ) | |
916 except exceptions.CancelError: | 1097 except exceptions.CancelError: |
917 return | 1098 return |
918 | 1099 |
919 # at this point we can send the <iq/> result to confirm reception of the request | 1100 # at this point we can send the <iq/> result to confirm reception of the request |
920 client.send(xmlstream.toResponse(request, 'result')) | 1101 client.send(xmlstream.toResponse(request, "result")) |
921 | 1102 |
922 negociate_defers = [] | 1103 negociate_defers = [] |
923 negociate_defers = self._callPlugins(client, XEP_0166.A_TRANSPORT_ACCEPT, session, app_method_name=None) | 1104 negociate_defers = self._callPlugins( |
1105 client, XEP_0166.A_TRANSPORT_ACCEPT, session, app_method_name=None | |
1106 ) | |
924 | 1107 |
925 negociate_dlist = defer.DeferredList(negociate_defers) | 1108 negociate_dlist = defer.DeferredList(negociate_defers) |
926 | 1109 |
927 # after negociations we start the transfer | 1110 # after negociations we start the transfer |
928 negociate_dlist.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_START, session, app_method_name=None, elements=False)) | 1111 negociate_dlist.addCallback( |
1112 lambda dummy: self._callPlugins( | |
1113 client, XEP_0166.A_START, session, app_method_name=None, elements=False | |
1114 ) | |
1115 ) | |
929 | 1116 |
930 def onTransportReject(self, client, request, jingle_elt, session): | 1117 def onTransportReject(self, client, request, jingle_elt, session): |
931 """Method called when a transport replacement is refused | 1118 """Method called when a transport replacement is refused |
932 | 1119 |
933 @param client: %(doc_client)s | 1120 @param client: %(doc_client)s |
935 @param jingle_elt(domish.Element): the <jingle> element | 1122 @param jingle_elt(domish.Element): the <jingle> element |
936 @param session(dict): session data | 1123 @param session(dict): session data |
937 """ | 1124 """ |
938 # XXX: for now, we terminate the session in case of transport-reject | 1125 # XXX: for now, we terminate the session in case of transport-reject |
939 # this behaviour may change in the future | 1126 # this behaviour may change in the future |
940 self.terminate(client, 'failed-transport', session) | 1127 self.terminate(client, "failed-transport", session) |
941 | 1128 |
942 def onTransportInfo(self, client, request, jingle_elt, session): | 1129 def onTransportInfo(self, client, request, jingle_elt, session): |
943 """Method called when a transport-info action is received from other peer | 1130 """Method called when a transport-info action is received from other peer |
944 | 1131 |
945 The request is parsed, and jingleHandler is called on concerned transport plugin(s) | 1132 The request is parsed, and jingleHandler is called on concerned transport plugin(s) |
946 @param client: %(doc_client)s | 1133 @param client: %(doc_client)s |
947 @param request(domish.Element): full <iq> request | 1134 @param request(domish.Element): full <iq> request |
948 @param jingle_elt(domish.Element): the <jingle> element | 1135 @param jingle_elt(domish.Element): the <jingle> element |
949 @param session(dict): session data | 1136 @param session(dict): session data |
950 """ | 1137 """ |
951 log.debug(u"Jingle session {} has been accepted".format(session['id'])) | 1138 log.debug(u"Jingle session {} has been accepted".format(session["id"])) |
952 | 1139 |
953 try: | 1140 try: |
954 self._parseElements(jingle_elt, session, request, client, with_application=False) | 1141 self._parseElements( |
1142 jingle_elt, session, request, client, with_application=False | |
1143 ) | |
955 except exceptions.CancelError: | 1144 except exceptions.CancelError: |
956 return | 1145 return |
957 | 1146 |
958 # The parsing was OK, we send the <iq> result | 1147 # The parsing was OK, we send the <iq> result |
959 client.send(xmlstream.toResponse(request, 'result')) | 1148 client.send(xmlstream.toResponse(request, "result")) |
960 | 1149 |
961 for content_name, content_data in session['contents'].iteritems(): | 1150 for content_name, content_data in session["contents"].iteritems(): |
962 try: | 1151 try: |
963 transport_elt = content_data.pop('transport_elt') | 1152 transport_elt = content_data.pop("transport_elt") |
964 except KeyError: | 1153 except KeyError: |
965 continue | 1154 continue |
966 else: | 1155 else: |
967 content_data['transport'].handler.jingleHandler(client, XEP_0166.A_TRANSPORT_INFO, session, content_name, transport_elt) | 1156 content_data["transport"].handler.jingleHandler( |
1157 client, | |
1158 XEP_0166.A_TRANSPORT_INFO, | |
1159 session, | |
1160 content_name, | |
1161 transport_elt, | |
1162 ) | |
968 | 1163 |
969 | 1164 |
970 class XEP_0166_handler(xmlstream.XMPPHandler): | 1165 class XEP_0166_handler(xmlstream.XMPPHandler): |
971 implements(iwokkel.IDisco) | 1166 implements(iwokkel.IDisco) |
972 | 1167 |
973 def __init__(self, plugin_parent): | 1168 def __init__(self, plugin_parent): |
974 self.plugin_parent = plugin_parent | 1169 self.plugin_parent = plugin_parent |
975 | 1170 |
976 def connectionInitialized(self): | 1171 def connectionInitialized(self): |
977 self.xmlstream.addObserver(JINGLE_REQUEST, self.plugin_parent._onJingleRequest, client=self.parent) | 1172 self.xmlstream.addObserver( |
978 | 1173 JINGLE_REQUEST, self.plugin_parent._onJingleRequest, client=self.parent |
979 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 1174 ) |
1175 | |
1176 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | |
980 return [disco.DiscoFeature(NS_JINGLE)] | 1177 return [disco.DiscoFeature(NS_JINGLE)] |
981 | 1178 |
982 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | 1179 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |
983 return [] | 1180 return [] |