comparison src/plugins/plugin_xep_0260.py @ 1631:25906c0dbc63

plugin XEP-0260, XEP-0261: fallback from S5B to IBB is implemented
author Goffi <goffi@goffi.org>
date Thu, 19 Nov 2015 18:15:35 +0100
parents c668081eba1c
children d2e023da2983
comparison
equal deleted inserted replaced
1630:c25f63215632 1631:25906c0dbc63
40 "name": "Jingle SOCKS5 Bytestreams", 40 "name": "Jingle SOCKS5 Bytestreams",
41 "import_name": "XEP-0260", 41 "import_name": "XEP-0260",
42 "type": "XEP", 42 "type": "XEP",
43 "protocols": ["XEP-0260"], 43 "protocols": ["XEP-0260"],
44 "dependencies": ["XEP-0166", "XEP-0065"], 44 "dependencies": ["XEP-0166", "XEP-0065"],
45 "recommendations": ["XEP-0261"], # needed for fallback
45 "main": "XEP_0260", 46 "main": "XEP_0260",
46 "handler": "yes", 47 "handler": "yes",
47 "description": _("""Implementation of Jingle SOCKS5 Bytestreams""") 48 "description": _("""Implementation of Jingle SOCKS5 Bytestreams""")
48 } 49 }
49 50
58 def __init__(self, host): 59 def __init__(self, host):
59 log.info(_("plugin Jingle SOCKS5 Bytestreams")) 60 log.info(_("plugin Jingle SOCKS5 Bytestreams"))
60 self.host = host 61 self.host = host
61 self._j = host.plugins["XEP-0166"] # shortcut to access jingle 62 self._j = host.plugins["XEP-0166"] # shortcut to access jingle
62 self._s5b = host.plugins["XEP-0065"] # and socks5 bytestream 63 self._s5b = host.plugins["XEP-0065"] # and socks5 bytestream
64 try:
65 self._jingle_ibb = host.plugins["XEP-0261"]
66 except KeyError:
67 self._jingle_ibb = None
63 self._j.registerTransport(NS_JINGLE_S5B, self._j.TRANSPORT_STREAMING, self, 100) 68 self._j.registerTransport(NS_JINGLE_S5B, self._j.TRANSPORT_STREAMING, self, 100)
64 69
65 def getHandler(self, profile): 70 def getHandler(self, profile):
66 return XEP_0260_handler() 71 return XEP_0260_handler()
67 72
138 """ 143 """
139 # now that the proxy is activated, we have to inform other peer 144 # now that the proxy is activated, we have to inform other peer
140 iq_elt, transport_elt = self._j.buildAction(self._j.A_TRANSPORT_INFO, session, content_name, profile) 145 iq_elt, transport_elt = self._j.buildAction(self._j.A_TRANSPORT_INFO, session, content_name, profile)
141 activated_elt = transport_elt.addElement('activated') 146 activated_elt = transport_elt.addElement('activated')
142 activated_elt['cid'] = candidate.id 147 activated_elt['cid'] = candidate.id
143 iq_elt.send 148 iq_elt.send()
144 149
145 def _proxyActivatedEb(self, stanza_error, candidate, session, content_name, profile): 150 def _proxyActivatedEb(self, stanza_error, candidate, session, content_name, profile):
146 """Called when activation error has been received from proxy 151 """Called when activation error has been received from proxy
147 152
148 cf XEP-0260 § 2.4 153 cf XEP-0260 § 2.4
149 """ 154 """
150 # TODO: fallback to IBB 155 # TODO: fallback to IBB
151 # now that the proxy is activated, we have to inform other peer 156 # now that the proxy is activated, we have to inform other peer
152 iq_elt, transport_elt = self._j.buildAction(self._j.A_TRANSPORT_INFO, session, content_name, profile) 157 iq_elt, transport_elt = self._j.buildAction(self._j.A_TRANSPORT_INFO, session, content_name, profile)
153 transport_elt.addElement('proxy-error') 158 transport_elt.addElement('proxy-error')
154 iq_elt.send 159 iq_elt.send()
155 return stanza_error 160 log.warning(u"Can't activate proxy, we need to fallback to IBB")
161 client = self.host.getClient(profile)
162 self.doFallback(session, content_name, client)
156 163
157 def _foundPeerCandidate(self, candidate, session, transport_data, content_name, client): 164 def _foundPeerCandidate(self, candidate, session, transport_data, content_name, client):
158 """Called when the best candidate from other peer is found 165 """Called when the best candidate from other peer is found
159 166
160 @param candidate(XEP_0065.Candidate, None): selected candidate, 167 @param candidate(XEP_0065.Candidate, None): selected candidate,
218 else: 225 else:
219 choosed_candidate = max(best_candidate, peer_best_candidate, key=lambda c:c.priority) 226 choosed_candidate = max(best_candidate, peer_best_candidate, key=lambda c:c.priority)
220 227
221 if choosed_candidate is None: 228 if choosed_candidate is None:
222 log.warning(u"Socks5 negociation failed, we need to fallback to IBB") 229 log.warning(u"Socks5 negociation failed, we need to fallback to IBB")
230 self.doFallback(session, content_name, client)
223 else: 231 else:
224 if choosed_candidate == peer_best_candidate: 232 if choosed_candidate == peer_best_candidate:
225 # peer_best_candidate was choosed from the candidates we have sent 233 # peer_best_candidate was choosed from the candidates we have sent
226 # so our_candidate is true if choosed_candidate is peer_best_candidate 234 # so our_candidate is true if choosed_candidate is peer_best_candidate
227 our_candidate = True 235 our_candidate = True
318 def jingleHandler(self, action, session, content_name, transport_elt, profile): 326 def jingleHandler(self, action, session, content_name, transport_elt, profile):
319 client = self.host.getClient(profile) 327 client = self.host.getClient(profile)
320 content_data = session['contents'][content_name] 328 content_data = session['contents'][content_name]
321 transport_data = content_data['transport_data'] 329 transport_data = content_data['transport_data']
322 330
323 if action in (self._j.A_ACCEPTED_ACK,): 331 if action in (self._j.A_ACCEPTED_ACK, self._j.A_PREPARE_RESPONDER):
324 pass 332 pass
325 333
326 elif action == self._j.A_SESSION_ACCEPT: 334 elif action == self._j.A_SESSION_ACCEPT:
327 # initiator side, we select a candidate in the ones sent by responder 335 # initiator side, we select a candidate in the ones sent by responder
328 assert 'peer_candidates' not in transport_data 336 assert 'peer_candidates' not in transport_data
373 method(candidate_elt, session, content_name, transport_data, client) 381 method(candidate_elt, session, content_name, transport_data, client)
374 break 382 break
375 383
376 if candidate_elt is None: 384 if candidate_elt is None:
377 log.warning(u"Unexpected transport element: {}".format(transport_elt.toXml())) 385 log.warning(u"Unexpected transport element: {}".format(transport_elt.toXml()))
378 386 elif action == self._j.A_DESTROY:
387 # the transport is remplaced (fallback ?). We need mainly to stop kill XEP-0065 session
388 # note that sid argument is not necessary for sessions created by this plugin
389 self._s5b.killSession(None, transport_data['session_hash'], None, client)
379 else: 390 else:
380 log.warning(u"FIXME: unmanaged action {}".format(action)) 391 log.warning(u"FIXME: unmanaged action {}".format(action))
381 392
382 defer.returnValue(transport_elt) 393 defer.returnValue(transport_elt)
394
395 def _doFallback(self, feature_checked, session, content_name, client):
396 """Do the fallback, method called once feature is checked
397
398 @param feature_checked(bool): True if other peer can do IBB
399 """
400 if not feature_checked:
401 log.warning(u"Other peer can't manage jingle IBB, be have to terminate the session")
402 self._j.terminate(self._j.REASON_CONNECTIVITY_ERROR, session, client.profile)
403 else:
404 self._j.transportReplace(self._jingle_ibb.NAMESPACE, session, content_name, client.profile)
405
406 def doFallback(self, session, content_name, client):
407 """Fallback to IBB transport, used in last resort
408
409 @param session(dict): session data
410 @param content_name(unicode): name of the current content
411 @param client(unicode): %(doc_client)s
412 """
413 if session['role'] != self._j.ROLE_INITIATOR:
414 # only initiator must do the fallback, see XEP-0260 §3
415 return
416 if self._jingle_ibb is None:
417 log.warning(u"Jingle IBB (XEP-0261) plugin is not available, we have to close the session")
418 self._j.terminate(self._j.REASON_CONNECTIVITY_ERROR, session, client.profile)
419 else:
420 d = self.host.hasFeature(self._jingle_ibb.NAMESPACE, session['peer_jid'], client.profile)
421 d.addCallback(self._doFallback, session, content_name, client)
422 return d
383 423
384 424
385 class XEP_0260_handler(XMPPHandler): 425 class XEP_0260_handler(XMPPHandler):
386 implements(iwokkel.IDisco) 426 implements(iwokkel.IDisco)
387 427