comparison sat/plugins/plugin_xep_0260.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents be6d91572633
children 3900626bc100
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
67 self._s5b = host.plugins["XEP-0065"] # and socks5 bytestream 67 self._s5b = host.plugins["XEP-0065"] # and socks5 bytestream
68 try: 68 try:
69 self._jingle_ibb = host.plugins["XEP-0261"] 69 self._jingle_ibb = host.plugins["XEP-0261"]
70 except KeyError: 70 except KeyError:
71 self._jingle_ibb = None 71 self._jingle_ibb = None
72 self._j.registerTransport(NS_JINGLE_S5B, self._j.TRANSPORT_STREAMING, self, 100) 72 self._j.register_transport(NS_JINGLE_S5B, self._j.TRANSPORT_STREAMING, self, 100)
73 73
74 def getHandler(self, client): 74 def get_handler(self, client):
75 return XEP_0260_handler() 75 return XEP_0260_handler()
76 76
77 def _parseCandidates(self, transport_elt): 77 def _parse_candidates(self, transport_elt):
78 """Parse <candidate> elements 78 """Parse <candidate> elements
79 79
80 @param transport_elt(domish.Element): parent <transport> element 80 @param transport_elt(domish.Element): parent <transport> element
81 @return (list[plugin_xep_0065.Candidate): list of parsed candidates 81 @return (list[plugin_xep_0065.Candidate): list of parsed candidates
82 """ 82 """
94 candidate = self._s5b.Candidate(host, port, type_, priority, jid_, cid) 94 candidate = self._s5b.Candidate(host, port, type_, priority, jid_, cid)
95 candidates.append(candidate) 95 candidates.append(candidate)
96 # self._s5b.registerCandidate(candidate) 96 # self._s5b.registerCandidate(candidate)
97 return candidates 97 return candidates
98 98
99 def _buildCandidates(self, session, candidates, sid, session_hash, client, mode=None): 99 def _build_candidates(self, session, candidates, sid, session_hash, client, mode=None):
100 """Build <transport> element with candidates 100 """Build <transport> element with candidates
101 101
102 @param session(dict): jingle session data 102 @param session(dict): jingle session data
103 @param candidates(iterator[plugin_xep_0065.Candidate]): iterator of candidates to add 103 @param candidates(iterator[plugin_xep_0065.Candidate]): iterator of candidates to add
104 @param sid(unicode): transport stream id 104 @param sid(unicode): transport stream id
133 candidate_elt["priority"] = str(candidate.priority) 133 candidate_elt["priority"] = str(candidate.priority)
134 candidate_elt["type"] = candidate.type 134 candidate_elt["type"] = candidate.type
135 return transport_elt 135 return transport_elt
136 136
137 @defer.inlineCallbacks 137 @defer.inlineCallbacks
138 def jingleSessionInit(self, client, session, content_name): 138 def jingle_session_init(self, client, session, content_name):
139 content_data = session["contents"][content_name] 139 content_data = session["contents"][content_name]
140 transport_data = content_data["transport_data"] 140 transport_data = content_data["transport_data"]
141 sid = transport_data["sid"] = str(uuid.uuid4()) 141 sid = transport_data["sid"] = str(uuid.uuid4())
142 session_hash = transport_data["session_hash"] = self._s5b.getSessionHash( 142 session_hash = transport_data["session_hash"] = self._s5b.get_session_hash(
143 session["local_jid"], session["peer_jid"], sid 143 session["local_jid"], session["peer_jid"], sid
144 ) 144 )
145 transport_data["peer_session_hash"] = self._s5b.getSessionHash( 145 transport_data["peer_session_hash"] = self._s5b.get_session_hash(
146 session["peer_jid"], session["local_jid"], sid 146 session["peer_jid"], session["local_jid"], sid
147 ) # requester and target are inversed for peer candidates 147 ) # requester and target are inversed for peer candidates
148 transport_data["stream_d"] = self._s5b.registerHash(client, session_hash, None) 148 transport_data["stream_d"] = self._s5b.register_hash(client, session_hash, None)
149 candidates = transport_data["candidates"] = yield self._s5b.getCandidates( 149 candidates = transport_data["candidates"] = yield self._s5b.get_candidates(
150 client, session["local_jid"]) 150 client, session["local_jid"])
151 mode = "tcp" # XXX: we only manage tcp for now 151 mode = "tcp" # XXX: we only manage tcp for now
152 transport_elt = self._buildCandidates( 152 transport_elt = self._build_candidates(
153 session, candidates, sid, session_hash, client, mode 153 session, candidates, sid, session_hash, client, mode
154 ) 154 )
155 155
156 defer.returnValue(transport_elt) 156 defer.returnValue(transport_elt)
157 157
158 def _proxyActivatedCb(self, iq_result_elt, client, candidate, session, content_name): 158 def _proxy_activated_cb(self, iq_result_elt, client, candidate, session, content_name):
159 """Called when activation confirmation has been received from proxy 159 """Called when activation confirmation has been received from proxy
160 160
161 cf XEP-0260 § 2.4 161 cf XEP-0260 § 2.4
162 """ 162 """
163 # now that the proxy is activated, we have to inform other peer 163 # now that the proxy is activated, we have to inform other peer
164 iq_elt, transport_elt = self._j.buildAction( 164 iq_elt, transport_elt = self._j.build_action(
165 client, self._j.A_TRANSPORT_INFO, session, content_name 165 client, self._j.A_TRANSPORT_INFO, session, content_name
166 ) 166 )
167 activated_elt = transport_elt.addElement("activated") 167 activated_elt = transport_elt.addElement("activated")
168 activated_elt["cid"] = candidate.id 168 activated_elt["cid"] = candidate.id
169 iq_elt.send() 169 iq_elt.send()
170 170
171 def _proxyActivatedEb(self, stanza_error, client, candidate, session, content_name): 171 def _proxy_activated_eb(self, stanza_error, client, candidate, session, content_name):
172 """Called when activation error has been received from proxy 172 """Called when activation error has been received from proxy
173 173
174 cf XEP-0260 § 2.4 174 cf XEP-0260 § 2.4
175 """ 175 """
176 # TODO: fallback to IBB 176 # TODO: fallback to IBB
177 # now that the proxy is activated, we have to inform other peer 177 # now that the proxy is activated, we have to inform other peer
178 iq_elt, transport_elt = self._j.buildAction( 178 iq_elt, transport_elt = self._j.build_action(
179 client, self._j.A_TRANSPORT_INFO, session, content_name 179 client, self._j.A_TRANSPORT_INFO, session, content_name
180 ) 180 )
181 transport_elt.addElement("proxy-error") 181 transport_elt.addElement("proxy-error")
182 iq_elt.send() 182 iq_elt.send()
183 log.warning( 183 log.warning(
184 "Can't activate proxy, we need to fallback to IBB: {reason}".format( 184 "Can't activate proxy, we need to fallback to IBB: {reason}".format(
185 reason=stanza_error.value.condition 185 reason=stanza_error.value.condition
186 ) 186 )
187 ) 187 )
188 self.doFallback(session, content_name, client) 188 self.do_fallback(session, content_name, client)
189 189
190 def _foundPeerCandidate( 190 def _found_peer_candidate(
191 self, candidate, session, transport_data, content_name, client 191 self, candidate, session, transport_data, content_name, client
192 ): 192 ):
193 """Called when the best candidate from other peer is found 193 """Called when the best candidate from other peer is found
194 194
195 @param candidate(XEP_0065.Candidate, None): selected candidate, 195 @param candidate(XEP_0065.Candidate, None): selected candidate,
205 for c in transport_data["peer_candidates"]: 205 for c in transport_data["peer_candidates"]:
206 if c is None or c is candidate: 206 if c is None or c is candidate:
207 continue 207 continue
208 c.discard() 208 c.discard()
209 del transport_data["peer_candidates"] 209 del transport_data["peer_candidates"]
210 iq_elt, transport_elt = self._j.buildAction( 210 iq_elt, transport_elt = self._j.build_action(
211 client, self._j.A_TRANSPORT_INFO, session, content_name 211 client, self._j.A_TRANSPORT_INFO, session, content_name
212 ) 212 )
213 if candidate is None: 213 if candidate is None:
214 log.warning("Can't connect to any peer candidate") 214 log.warning("Can't connect to any peer candidate")
215 candidate_elt = transport_elt.addElement("candidate-error") 215 candidate_elt = transport_elt.addElement("candidate-error")
216 else: 216 else:
217 log.info("Found best peer candidate: {}".format(str(candidate))) 217 log.info("Found best peer candidate: {}".format(str(candidate)))
218 candidate_elt = transport_elt.addElement("candidate-used") 218 candidate_elt = transport_elt.addElement("candidate-used")
219 candidate_elt["cid"] = candidate.id 219 candidate_elt["cid"] = candidate.id
220 iq_elt.send() # TODO: check result stanza 220 iq_elt.send() # TODO: check result stanza
221 self._checkCandidates(session, content_name, transport_data, client) 221 self._check_candidates(session, content_name, transport_data, client)
222 222
223 def _checkCandidates(self, session, content_name, transport_data, client): 223 def _check_candidates(self, session, content_name, transport_data, client):
224 """Called when a candidate has been choosed 224 """Called when a candidate has been choosed
225 225
226 if we have both candidates, we select one, or fallback to an other transport 226 if we have both candidates, we select one, or fallback to an other transport
227 @param session(dict): session data 227 @param session(dict): session data
228 @param content_name(unicode): name of the current content 228 @param content_name(unicode): name of the current content
259 best_candidate, peer_best_candidate, key=lambda c: c.priority 259 best_candidate, peer_best_candidate, key=lambda c: c.priority
260 ) 260 )
261 261
262 if choosed_candidate is None: 262 if choosed_candidate is None:
263 log.warning("Socks5 negociation failed, we need to fallback to IBB") 263 log.warning("Socks5 negociation failed, we need to fallback to IBB")
264 self.doFallback(session, content_name, client) 264 self.do_fallback(session, content_name, client)
265 else: 265 else:
266 if choosed_candidate == peer_best_candidate: 266 if choosed_candidate == peer_best_candidate:
267 # peer_best_candidate was choosed from the candidates we have sent 267 # peer_best_candidate was choosed from the candidates we have sent
268 # so our_candidate is true if choosed_candidate is peer_best_candidate 268 # so our_candidate is true if choosed_candidate is peer_best_candidate
269 our_candidate = True 269 our_candidate = True
286 286
287 if choosed_candidate.type == self._s5b.TYPE_PROXY: 287 if choosed_candidate.type == self._s5b.TYPE_PROXY:
288 # the stream transfer need to wait for proxy activation 288 # the stream transfer need to wait for proxy activation
289 # (see XEP-0260 § 2.4) 289 # (see XEP-0260 § 2.4)
290 if our_candidate: 290 if our_candidate:
291 d = self._s5b.connectCandidate( 291 d = self._s5b.connect_candidate(
292 client, choosed_candidate, transport_data["session_hash"] 292 client, choosed_candidate, transport_data["session_hash"]
293 ) 293 )
294 d.addCallback( 294 d.addCallback(
295 lambda __: choosed_candidate.activate( 295 lambda __: choosed_candidate.activate(
296 transport_data["sid"], session["peer_jid"], client 296 transport_data["sid"], session["peer_jid"], client
297 ) 297 )
298 ) 298 )
299 args = [client, choosed_candidate, session, content_name] 299 args = [client, choosed_candidate, session, content_name]
300 d.addCallbacks( 300 d.addCallbacks(
301 self._proxyActivatedCb, self._proxyActivatedEb, args, None, args 301 self._proxy_activated_cb, self._proxy_activated_eb, args, None, args
302 ) 302 )
303 else: 303 else:
304 # this Deferred will be called when we'll receive activation confirmation from other peer 304 # this Deferred will be called when we'll receive activation confirmation from other peer
305 d = transport_data["activation_d"] = defer.Deferred() 305 d = transport_data["activation_d"] = defer.Deferred()
306 else: 306 else:
307 d = defer.succeed(None) 307 d = defer.succeed(None)
308 308
309 if content_data["senders"] == session["role"]: 309 if content_data["senders"] == session["role"]:
310 # we can now start the stream transfer (or start it after proxy activation) 310 # we can now start the stream transfer (or start it after proxy activation)
311 d.addCallback( 311 d.addCallback(
312 lambda __: choosed_candidate.startTransfer( 312 lambda __: choosed_candidate.start_transfer(
313 transport_data["session_hash"] 313 transport_data["session_hash"]
314 ) 314 )
315 ) 315 )
316 d.addErrback(self._startEb, session, content_name, client) 316 d.addErrback(self._start_eb, session, content_name, client)
317 317
318 def _startEb(self, fail, session, content_name, client): 318 def _start_eb(self, fail, session, content_name, client):
319 """Called when it's not possible to start the transfer 319 """Called when it's not possible to start the transfer
320 320
321 Will try to fallback to IBB 321 Will try to fallback to IBB
322 """ 322 """
323 try: 323 try:
324 reason = str(fail.value) 324 reason = str(fail.value)
325 except AttributeError: 325 except AttributeError:
326 reason = str(fail) 326 reason = str(fail)
327 log.warning("Cant start transfert, we'll try fallback method: {}".format(reason)) 327 log.warning("Cant start transfert, we'll try fallback method: {}".format(reason))
328 self.doFallback(session, content_name, client) 328 self.do_fallback(session, content_name, client)
329 329
330 def _candidateInfo( 330 def _candidate_info(
331 self, candidate_elt, session, content_name, transport_data, client 331 self, candidate_elt, session, content_name, transport_data, client
332 ): 332 ):
333 """Called when best candidate has been received from peer (or if none is working) 333 """Called when best candidate has been received from peer (or if none is working)
334 334
335 @param candidate_elt(domish.Element): candidate-used or candidate-error element 335 @param candidate_elt(domish.Element): candidate-used or candidate-error element
366 # at this point we have the candidate choosed by other peer 366 # at this point we have the candidate choosed by other peer
367 transport_data["peer_best_candidate"] = candidate 367 transport_data["peer_best_candidate"] = candidate
368 log.info("Other peer best candidate: {}".format(candidate)) 368 log.info("Other peer best candidate: {}".format(candidate))
369 369
370 del transport_data["candidates"] 370 del transport_data["candidates"]
371 self._checkCandidates(session, content_name, transport_data, client) 371 self._check_candidates(session, content_name, transport_data, client)
372 372
373 def _proxyActivationInfo( 373 def _proxy_activation_info(
374 self, proxy_elt, session, content_name, transport_data, client 374 self, proxy_elt, session, content_name, transport_data, client
375 ): 375 ):
376 """Called when proxy has been activated (or has sent an error) 376 """Called when proxy has been activated (or has sent an error)
377 377
378 @param proxy_elt(domish.Element): <activated/> or <proxy-error/> element 378 @param proxy_elt(domish.Element): <activated/> or <proxy-error/> element
391 activation_d.callback(None) 391 activation_d.callback(None)
392 else: 392 else:
393 activation_d.errback(ProxyError()) 393 activation_d.errback(ProxyError())
394 394
395 @defer.inlineCallbacks 395 @defer.inlineCallbacks
396 def jingleHandler(self, client, action, session, content_name, transport_elt): 396 def jingle_handler(self, client, action, session, content_name, transport_elt):
397 content_data = session["contents"][content_name] 397 content_data = session["contents"][content_name]
398 transport_data = content_data["transport_data"] 398 transport_data = content_data["transport_data"]
399 399
400 if action in (self._j.A_ACCEPTED_ACK, self._j.A_PREPARE_RESPONDER): 400 if action in (self._j.A_ACCEPTED_ACK, self._j.A_PREPARE_RESPONDER):
401 pass 401 pass
402 402
403 elif action == self._j.A_SESSION_ACCEPT: 403 elif action == self._j.A_SESSION_ACCEPT:
404 # initiator side, we select a candidate in the ones sent by responder 404 # initiator side, we select a candidate in the ones sent by responder
405 assert "peer_candidates" not in transport_data 405 assert "peer_candidates" not in transport_data
406 transport_data["peer_candidates"] = self._parseCandidates(transport_elt) 406 transport_data["peer_candidates"] = self._parse_candidates(transport_elt)
407 407
408 elif action == self._j.A_START: 408 elif action == self._j.A_START:
409 session_hash = transport_data["session_hash"] 409 session_hash = transport_data["session_hash"]
410 peer_candidates = transport_data["peer_candidates"] 410 peer_candidates = transport_data["peer_candidates"]
411 stream_object = content_data["stream_object"] 411 stream_object = content_data["stream_object"]
412 self._s5b.associateStreamObject(client, session_hash, stream_object) 412 self._s5b.associate_stream_object(client, session_hash, stream_object)
413 stream_d = transport_data.pop("stream_d") 413 stream_d = transport_data.pop("stream_d")
414 stream_d.chainDeferred(content_data["finished_d"]) 414 stream_d.chainDeferred(content_data["finished_d"])
415 peer_session_hash = transport_data["peer_session_hash"] 415 peer_session_hash = transport_data["peer_session_hash"]
416 d = self._s5b.getBestCandidate( 416 d = self._s5b.get_best_candidate(
417 client, peer_candidates, session_hash, peer_session_hash 417 client, peer_candidates, session_hash, peer_session_hash
418 ) 418 )
419 d.addCallback( 419 d.addCallback(
420 self._foundPeerCandidate, session, transport_data, content_name, client 420 self._found_peer_candidate, session, transport_data, content_name, client
421 ) 421 )
422 422
423 elif action == self._j.A_SESSION_INITIATE: 423 elif action == self._j.A_SESSION_INITIATE:
424 # responder side, we select a candidate in the ones sent by initiator 424 # responder side, we select a candidate in the ones sent by initiator
425 # and we give our candidates 425 # and we give our candidates
426 assert "peer_candidates" not in transport_data 426 assert "peer_candidates" not in transport_data
427 sid = transport_data["sid"] = transport_elt["sid"] 427 sid = transport_data["sid"] = transport_elt["sid"]
428 session_hash = transport_data["session_hash"] = self._s5b.getSessionHash( 428 session_hash = transport_data["session_hash"] = self._s5b.get_session_hash(
429 session["local_jid"], session["peer_jid"], sid 429 session["local_jid"], session["peer_jid"], sid
430 ) 430 )
431 peer_session_hash = transport_data[ 431 peer_session_hash = transport_data[
432 "peer_session_hash" 432 "peer_session_hash"
433 ] = self._s5b.getSessionHash( 433 ] = self._s5b.get_session_hash(
434 session["peer_jid"], session["local_jid"], sid 434 session["peer_jid"], session["local_jid"], sid
435 ) # requester and target are inversed for peer candidates 435 ) # requester and target are inversed for peer candidates
436 peer_candidates = transport_data["peer_candidates"] = self._parseCandidates( 436 peer_candidates = transport_data["peer_candidates"] = self._parse_candidates(
437 transport_elt 437 transport_elt
438 ) 438 )
439 stream_object = content_data["stream_object"] 439 stream_object = content_data["stream_object"]
440 stream_d = self._s5b.registerHash(client, session_hash, stream_object) 440 stream_d = self._s5b.register_hash(client, session_hash, stream_object)
441 stream_d.chainDeferred(content_data["finished_d"]) 441 stream_d.chainDeferred(content_data["finished_d"])
442 d = self._s5b.getBestCandidate( 442 d = self._s5b.get_best_candidate(
443 client, peer_candidates, session_hash, peer_session_hash 443 client, peer_candidates, session_hash, peer_session_hash
444 ) 444 )
445 d.addCallback( 445 d.addCallback(
446 self._foundPeerCandidate, session, transport_data, content_name, client 446 self._found_peer_candidate, session, transport_data, content_name, client
447 ) 447 )
448 candidates = yield self._s5b.getCandidates(client, session["local_jid"]) 448 candidates = yield self._s5b.get_candidates(client, session["local_jid"])
449 # we remove duplicate candidates 449 # we remove duplicate candidates
450 candidates = [ 450 candidates = [
451 candidate for candidate in candidates if candidate not in peer_candidates 451 candidate for candidate in candidates if candidate not in peer_candidates
452 ] 452 ]
453 453
454 transport_data["candidates"] = candidates 454 transport_data["candidates"] = candidates
455 # we can now build a new <transport> element with our candidates 455 # we can now build a new <transport> element with our candidates
456 transport_elt = self._buildCandidates( 456 transport_elt = self._build_candidates(
457 session, candidates, sid, session_hash, client 457 session, candidates, sid, session_hash, client
458 ) 458 )
459 459
460 elif action == self._j.A_TRANSPORT_INFO: 460 elif action == self._j.A_TRANSPORT_INFO:
461 # transport-info can be about candidate or proxy activation 461 # transport-info can be about candidate or proxy activation
462 candidate_elt = None 462 candidate_elt = None
463 463
464 for method, names in ( 464 for method, names in (
465 (self._candidateInfo, ("candidate-used", "candidate-error")), 465 (self._candidate_info, ("candidate-used", "candidate-error")),
466 (self._proxyActivationInfo, ("activated", "proxy-error")), 466 (self._proxy_activation_info, ("activated", "proxy-error")),
467 ): 467 ):
468 for name in names: 468 for name in names:
469 try: 469 try:
470 candidate_elt = next(transport_elt.elements(NS_JINGLE_S5B, name)) 470 candidate_elt = next(transport_elt.elements(NS_JINGLE_S5B, name))
471 except StopIteration: 471 except StopIteration:
481 "Unexpected transport element: {}".format(transport_elt.toXml()) 481 "Unexpected transport element: {}".format(transport_elt.toXml())
482 ) 482 )
483 elif action == self._j.A_DESTROY: 483 elif action == self._j.A_DESTROY:
484 # the transport is replaced (fallback ?), We need mainly to kill XEP-0065 session. 484 # the transport is replaced (fallback ?), We need mainly to kill XEP-0065 session.
485 # note that sid argument is not necessary for sessions created by this plugin 485 # note that sid argument is not necessary for sessions created by this plugin
486 self._s5b.killSession(None, transport_data["session_hash"], None, client) 486 self._s5b.kill_session(None, transport_data["session_hash"], None, client)
487 else: 487 else:
488 log.warning("FIXME: unmanaged action {}".format(action)) 488 log.warning("FIXME: unmanaged action {}".format(action))
489 489
490 defer.returnValue(transport_elt) 490 defer.returnValue(transport_elt)
491 491
492 def jingleTerminate(self, client, action, session, content_name, reason_elt): 492 def jingle_terminate(self, client, action, session, content_name, reason_elt):
493 if reason_elt.decline: 493 if reason_elt.decline:
494 log.debug("Session declined, deleting S5B session") 494 log.debug("Session declined, deleting S5B session")
495 # we just need to clean the S5B session if it is declined 495 # we just need to clean the S5B session if it is declined
496 content_data = session["contents"][content_name] 496 content_data = session["contents"][content_name]
497 transport_data = content_data["transport_data"] 497 transport_data = content_data["transport_data"]
498 self._s5b.killSession(None, transport_data["session_hash"], None, client) 498 self._s5b.kill_session(None, transport_data["session_hash"], None, client)
499 499
500 def _doFallback(self, feature_checked, session, content_name, client): 500 def _do_fallback(self, feature_checked, session, content_name, client):
501 """Do the fallback, method called once feature is checked 501 """Do the fallback, method called once feature is checked
502 502
503 @param feature_checked(bool): True if other peer can do IBB 503 @param feature_checked(bool): True if other peer can do IBB
504 """ 504 """
505 if not feature_checked: 505 if not feature_checked:
506 log.warning( 506 log.warning(
507 "Other peer can't manage jingle IBB, be have to terminate the session" 507 "Other peer can't manage jingle IBB, be have to terminate the session"
508 ) 508 )
509 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) 509 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session)
510 else: 510 else:
511 self._j.transportReplace( 511 self._j.transport_replace(
512 client, self._jingle_ibb.NAMESPACE, session, content_name 512 client, self._jingle_ibb.NAMESPACE, session, content_name
513 ) 513 )
514 514
515 def doFallback(self, session, content_name, client): 515 def do_fallback(self, session, content_name, client):
516 """Fallback to IBB transport, used in last resort 516 """Fallback to IBB transport, used in last resort
517 517
518 @param session(dict): session data 518 @param session(dict): session data
519 @param content_name(unicode): name of the current content 519 @param content_name(unicode): name of the current content
520 @param client(unicode): %(doc_client)s 520 @param client(unicode): %(doc_client)s
529 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) 529 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session)
530 else: 530 else:
531 d = self.host.hasFeature( 531 d = self.host.hasFeature(
532 client, self._jingle_ibb.NAMESPACE, session["peer_jid"] 532 client, self._jingle_ibb.NAMESPACE, session["peer_jid"]
533 ) 533 )
534 d.addCallback(self._doFallback, session, content_name, client) 534 d.addCallback(self._do_fallback, session, content_name, client)
535 return d 535 return d
536 536
537 537
538 @implementer(iwokkel.IDisco) 538 @implementer(iwokkel.IDisco)
539 class XEP_0260_handler(XMPPHandler): 539 class XEP_0260_handler(XMPPHandler):