diff sat/plugins/plugin_xep_0065.py @ 2927:69e4716d6268

plugins (jingle) file transfer: use initial "from" attribute as local jid instead of client.jid: while client.jid is fine in a client context, for components it's not the right jid to use: it is the jid of the component itself while the file transfer/jingle session entity may be established with this jid + a local part (e.g. if client is files.example.net, session may be established with louise@files.example.net, in which case "from" is louise@files.example.net, while client.jid will be files.example.net). As a consequence, using client.jid was causing trouble with components. This patch fixes it for jingle and plugins linked to file transfer by keeping a "local_jid" variable in the session, where the jid from the original "from" attribute is used.
author Goffi <goffi@goffi.org>
date Sun, 28 Apr 2019 08:55:13 +0200
parents 003b8b4b56a7
children ab2696e34d29
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0065.py	Sun Apr 28 08:55:13 2019 +0200
+++ b/sat/plugins/plugin_xep_0065.py	Sun Apr 28 08:55:13 2019 +0200
@@ -273,7 +273,7 @@
             raise exceptions.InternalError(u"Unknown {} type !".format(self.type))
         return 2 ** 16 * multiplier + self._local_priority
 
-    def activate(self, sid, peer_jid, client):
+    def activate(self, client, sid, peer_jid, local_jid):
         """Activate the proxy candidate
 
         Send activation request as explained in XEP-0065 ยง 6.3.5
@@ -284,6 +284,7 @@
         """
         assert self.type == XEP_0065.TYPE_PROXY
         iq_elt = client.IQ()
+        iq_elt["from"] = local_jid.full()
         iq_elt["to"] = self.jid.full()
         query_elt = iq_elt.addElement((NS_BS, "query"))
         query_elt["sid"] = sid
@@ -801,20 +802,22 @@
         return self._server_factory
 
     @defer.inlineCallbacks
-    def getProxy(self, client):
+    def getProxy(self, client, local_jid):
         """Return the proxy available for this profile
 
         cache is used between clients using the same server
+        @param local_jid(jid.JID): same as for [getCandidates]
         @return ((D)(ProxyInfos, None)): Found proxy infos,
             or None if not acceptable proxy is found
+        @raise exceptions.NotFound: no Proxy found
         """
 
         def notFound(server):
             log.info(u"No proxy found on this server")
             self._cache_proxies[server] = None
-            defer.returnValue(None)
+            raise exceptions.NotFound
 
-        server = client.jid.host
+        server = client.host if client.is_component else client.jid.host
         try:
             defer.returnValue(self._cache_proxies[server])
         except KeyError:
@@ -826,6 +829,7 @@
         except (defer.CancelledError, StopIteration, KeyError):
             notFound(server)
         iq_elt = client.IQ("get")
+        iq_elt["from"] = local_jid.full()
         iq_elt["to"] = proxy.full()
         iq_elt.addElement((NS_BS, "query"))
 
@@ -885,14 +889,21 @@
         defer.returnValue((local_port, self._external_port, local_ips, external_ip))
 
     @defer.inlineCallbacks
-    def getCandidates(self, client):
+    def getCandidates(self, client, local_jid):
         """Return a list of our stream candidates
 
+        @param local_jid(jid.JID): jid to use as local jid
+            This is needed for client which can be addressed with a different jid than
+            client.jid if a local part is used (e.g. piotr@file.example.net where
+            client.jid would be file.example.net)
         @return (D(list[Candidate])): list of candidates, ordered by priority
         """
         server_factory = yield self.getSocks5ServerFactory()
         local_port, ext_port, local_ips, external_ip = yield self._getNetworkData(client)
-        proxy = yield self.getProxy(client)
+        try:
+            proxy = yield self.getProxy(client, local_jid)
+        except exceptions.NotFound:
+            proxy = None
 
         # its time to gather the candidates
         candidates = []
@@ -907,7 +918,7 @@
                 local_port,
                 XEP_0065.TYPE_DIRECT,
                 PRIORITY_BEST_DIRECT,
-                client.jid,
+                local_jid,
                 priority_local=True,
                 factory=server_factory,
             )
@@ -919,7 +930,7 @@
                     local_port,
                     XEP_0065.TYPE_DIRECT,
                     PRIORITY_DIRECT,
-                    client.jid,
+                    local_jid,
                     priority_local=True,
                     factory=server_factory,
                 )
@@ -933,7 +944,7 @@
                     ext_port,
                     XEP_0065.TYPE_ASSISTED,
                     PRIORITY_ASSISTED,
-                    client.jid,
+                    local_jid,
                     priority_local=True,
                     factory=server_factory,
                 )
@@ -1134,24 +1145,26 @@
 
         return failure_
 
-    def startStream(self, client, stream_object, to_jid, sid):
+    def startStream(self, client, stream_object, local_jid, to_jid, sid):
         """Launch the stream workflow
 
         @param streamProducer: stream_object to use
+        @param local_jid(jid.JID): same as for [getCandidates]
         @param to_jid: JID of the recipient
         @param sid: Stream session id
         @param successCb: method to call when stream successfuly finished
         @param failureCb: method to call when something goes wrong
         @return (D): Deferred fired when session is finished
         """
-        session_data = self._createSession(client, stream_object, to_jid, sid, True)
+        session_data = self._createSession(
+            client, stream_object, local_jid, to_jid, sid, True)
 
         session_data[client] = client
 
         def gotCandidates(candidates):
             session_data["candidates"] = candidates
             iq_elt = client.IQ()
-            iq_elt["from"] = client.jid.full()
+            iq_elt["from"] = local_jid.full()
             iq_elt["to"] = to_jid.full()
             query_elt = iq_elt.addElement((NS_BS, "query"))
             query_elt["mode"] = "tcp"
@@ -1165,13 +1178,13 @@
                 log.debug(u"Candidate proposed: {}".format(candidate))
 
             d = iq_elt.send()
-            args = [session_data, client]
+            args = [client, session_data, local_jid]
             d.addCallbacks(self._IQNegotiationCb, self._IQNegotiationEb, args, None, args)
 
-        self.getCandidates(client).addCallback(gotCandidates)
+        self.getCandidates(client, local_jid).addCallback(gotCandidates)
         return session_data[DEFER_KEY]
 
-    def _IQNegotiationCb(self, iq_elt, session_data, client):
+    def _IQNegotiationCb(self, iq_elt, client, session_data, local_jid):
         """Called when the result of open iq is received
 
         @param session_data(dict): data of the session
@@ -1204,7 +1217,7 @@
             d = self.connectCandidate(client, candidate, session_data["hash"])
             d.addCallback(
                 lambda __: candidate.activate(
-                    session_data["id"], session_data["peer_jid"], client
+                    client, session_data["id"], session_data["peer_jid"], local_jid
                 )
             )
             d.addErrback(self._activationEb)
@@ -1216,7 +1229,7 @@
     def _activationEb(self, failure):
         log.warning(u"Proxy activation error: {}".format(failure.value))
 
-    def _IQNegotiationEb(self, stanza_err, session_data, client):
+    def _IQNegotiationEb(self, stanza_err, client, session_data, local_jid):
         log.warning(u"Socks5 transfer failed: {}".format(stanza_err.value))
         # FIXME: must clean session
 
@@ -1227,10 +1240,12 @@
         """
         return self._createSession(*args, **kwargs)[DEFER_KEY]
 
-    def _createSession(self, client, stream_object, to_jid, sid, requester=False):
+    def _createSession(self, client, stream_object, local_jid, to_jid, sid,
+                       requester=False):
         """Called when a bytestream is imminent
 
-        @param stream_object(iface.IStreamProducer): File object where data will be written
+        @param stream_object(iface.IStreamProducer): File object where data will be
+            written
         @param to_jid(jid.JId): jid of the other peer
         @param sid(unicode): session id
         @param initiator(bool): if True, this session is create by initiator
@@ -1239,10 +1254,10 @@
         if sid in client.xep_0065_sid_session:
             raise exceptions.ConflictError(u"A session with this id already exists !")
         if requester:
-            session_hash = getSessionHash(client.jid, to_jid, sid)
+            session_hash = getSessionHash(local_jid, to_jid, sid)
             session_data = self._registerHash(client, session_hash, stream_object)
         else:
-            session_hash = getSessionHash(to_jid, client.jid, sid)
+            session_hash = getSessionHash(to_jid, local_jid, sid)
             session_d = defer.Deferred()
             session_d.addBoth(self.killSession, session_hash, sid, client)
             session_data = client._s5b_sessions[session_hash] = {
@@ -1255,6 +1270,7 @@
         session_data.update(
             {
                 "id": sid,
+                "local_jid": local_jid,
                 "peer_jid": to_jid,
                 "stream_object": stream_object,
                 "hash": session_hash,