changeset 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 c25f63215632
children 4f9fa67758eb
files src/plugins/plugin_xep_0260.py src/plugins/plugin_xep_0261.py
diffstat 2 files changed, 50 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- 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)
--- 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']