# HG changeset patch # User Goffi # Date 1447953335 -3600 # Node ID 25906c0dbc633c9d684223cfb773bb5d96854660 # Parent c25f632156326936d28072e283d2268737e667f7 plugin XEP-0260, XEP-0261: fallback from S5B to IBB is implemented diff -r c25f63215632 -r 25906c0dbc63 src/plugins/plugin_xep_0260.py --- a/src/plugins/plugin_xep_0260.py Thu Nov 19 18:15:35 2015 +0100 +++ b/src/plugins/plugin_xep_0260.py Thu Nov 19 18:15:35 2015 +0100 @@ -42,6 +42,7 @@ "type": "XEP", "protocols": ["XEP-0260"], "dependencies": ["XEP-0166", "XEP-0065"], + "recommendations": ["XEP-0261"], # needed for fallback "main": "XEP_0260", "handler": "yes", "description": _("""Implementation of Jingle SOCKS5 Bytestreams""") @@ -60,6 +61,10 @@ self.host = host self._j = host.plugins["XEP-0166"] # shortcut to access jingle self._s5b = host.plugins["XEP-0065"] # and socks5 bytestream + try: + self._jingle_ibb = host.plugins["XEP-0261"] + except KeyError: + self._jingle_ibb = None self._j.registerTransport(NS_JINGLE_S5B, self._j.TRANSPORT_STREAMING, self, 100) def getHandler(self, profile): @@ -140,7 +145,7 @@ iq_elt, transport_elt = self._j.buildAction(self._j.A_TRANSPORT_INFO, session, content_name, profile) activated_elt = transport_elt.addElement('activated') activated_elt['cid'] = candidate.id - iq_elt.send + iq_elt.send() def _proxyActivatedEb(self, stanza_error, candidate, session, content_name, profile): """Called when activation error has been received from proxy @@ -151,8 +156,10 @@ # now that the proxy is activated, we have to inform other peer iq_elt, transport_elt = self._j.buildAction(self._j.A_TRANSPORT_INFO, session, content_name, profile) transport_elt.addElement('proxy-error') - iq_elt.send - return stanza_error + iq_elt.send() + log.warning(u"Can't activate proxy, we need to fallback to IBB") + client = self.host.getClient(profile) + self.doFallback(session, content_name, client) def _foundPeerCandidate(self, candidate, session, transport_data, content_name, client): """Called when the best candidate from other peer is found @@ -220,6 +227,7 @@ if choosed_candidate is None: log.warning(u"Socks5 negociation failed, we need to fallback to IBB") + self.doFallback(session, content_name, client) else: if choosed_candidate == peer_best_candidate: # peer_best_candidate was choosed from the candidates we have sent @@ -320,7 +328,7 @@ content_data = session['contents'][content_name] transport_data = content_data['transport_data'] - if action in (self._j.A_ACCEPTED_ACK,): + if action in (self._j.A_ACCEPTED_ACK, self._j.A_PREPARE_RESPONDER): pass elif action == self._j.A_SESSION_ACCEPT: @@ -375,12 +383,44 @@ if candidate_elt is None: log.warning(u"Unexpected transport element: {}".format(transport_elt.toXml())) - + elif action == self._j.A_DESTROY: + # the transport is remplaced (fallback ?). We need mainly to stop kill XEP-0065 session + # note that sid argument is not necessary for sessions created by this plugin + self._s5b.killSession(None, transport_data['session_hash'], None, client) else: log.warning(u"FIXME: unmanaged action {}".format(action)) defer.returnValue(transport_elt) + def _doFallback(self, feature_checked, session, content_name, client): + """Do the fallback, method called once feature is checked + + @param feature_checked(bool): True if other peer can do IBB + """ + if not feature_checked: + log.warning(u"Other peer can't manage jingle IBB, be have to terminate the session") + self._j.terminate(self._j.REASON_CONNECTIVITY_ERROR, session, client.profile) + else: + self._j.transportReplace(self._jingle_ibb.NAMESPACE, session, content_name, client.profile) + + def doFallback(self, session, content_name, client): + """Fallback to IBB transport, used in last resort + + @param session(dict): session data + @param content_name(unicode): name of the current content + @param client(unicode): %(doc_client)s + """ + if session['role'] != self._j.ROLE_INITIATOR: + # only initiator must do the fallback, see XEP-0260 ยง3 + return + if self._jingle_ibb is None: + log.warning(u"Jingle IBB (XEP-0261) plugin is not available, we have to close the session") + self._j.terminate(self._j.REASON_CONNECTIVITY_ERROR, session, client.profile) + else: + d = self.host.hasFeature(self._jingle_ibb.NAMESPACE, session['peer_jid'], client.profile) + d.addCallback(self._doFallback, session, content_name, client) + return d + class XEP_0260_handler(XMPPHandler): implements(iwokkel.IDisco) diff -r c25f63215632 -r 25906c0dbc63 src/plugins/plugin_xep_0261.py --- a/src/plugins/plugin_xep_0261.py Thu Nov 19 18:15:35 2015 +0100 +++ b/src/plugins/plugin_xep_0261.py Thu Nov 19 18:15:35 2015 +0100 @@ -46,6 +46,7 @@ class XEP_0261(object): + NAMESPACE = NS_JINGLE_IBB # used by XEP-0260 plugin for transport-replace def __init__(self, host): log.info(_("plugin Jingle In-Band Bytestreams")) @@ -69,9 +70,11 @@ def jingleHandler(self, action, session, content_name, transport_elt, profile): content_data = session['contents'][content_name] transport_data = content_data['transport_data'] - if action in (self._j.A_SESSION_ACCEPT, self._j.A_ACCEPTED_ACK): + if action in (self._j.A_SESSION_ACCEPT, + self._j.A_ACCEPTED_ACK, + self._j.A_TRANSPORT_ACCEPT): pass - elif action == self._j.A_SESSION_INITIATE: + elif action in (self._j.A_SESSION_INITIATE, self._j.A_TRANSPORT_REPLACE): transport_data['sid'] = transport_elt['sid'] elif action in (self._j.A_START, self._j.A_PREPARE_RESPONDER): peer_jid = session['peer_jid']