diff sat/plugins/plugin_xep_0329.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents db0890c9c7db
children 95e2fd14a761
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0329.py	Wed Jul 31 11:31:22 2019 +0200
+++ b/sat/plugins/plugin_xep_0329.py	Tue Aug 13 19:08:41 2019 +0200
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 # SAT plugin for File Information Sharing (XEP-0329)
@@ -26,7 +26,7 @@
 from sat.tools import stream
 from sat.tools.common import regex
 from wokkel import disco, iwokkel
-from zope.interface import implements
+from zope.interface import implementer
 from twisted.words.protocols.jabber import xmlstream
 from twisted.words.protocols.jabber import jid
 from twisted.words.protocols.jabber import error as jabber_error
@@ -45,17 +45,17 @@
     C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300", "XEP-0106"],
     C.PI_MAIN: "XEP_0329",
     C.PI_HANDLER: "yes",
-    C.PI_DESCRIPTION: _(u"""Implementation of File Information Sharing"""),
+    C.PI_DESCRIPTION: _("""Implementation of File Information Sharing"""),
 }
 
 NS_FIS = "urn:xmpp:fis:0"
 
 IQ_FIS_REQUEST = C.IQ_GET + '/query[@xmlns="' + NS_FIS + '"]'
-SINGLE_FILES_DIR = u"files"
-TYPE_VIRTUAL = u"virtual"
-TYPE_PATH = u"path"
+SINGLE_FILES_DIR = "files"
+TYPE_VIRTUAL = "virtual"
+TYPE_PATH = "path"
 SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL)
-KEY_TYPE = u"type"
+KEY_TYPE = "type"
 
 
 class ShareNode(object):
@@ -66,14 +66,14 @@
     def __init__(self, name, parent, type_, access, path=None):
         assert type_ in SHARE_TYPES
         if name is not None:
-            if name == u".." or u"/" in name or u"\\" in name:
+            if name == ".." or "/" in name or "\\" in name:
                 log.warning(
-                    _(u"path change chars found in name [{name}], hack attempt?").format(
+                    _("path change chars found in name [{name}], hack attempt?").format(
                         name=name
                     )
                 )
-                if name == u"..":
-                    name = u"--"
+                if name == "..":
+                    name = "--"
                 else:
                     name = regex.pathEscape(name)
         self.name = name
@@ -89,7 +89,7 @@
             assert name is None
         if path is not None:
             if type_ != TYPE_PATH:
-                raise exceptions.InternalError(_(u"path can only be set on path nodes"))
+                raise exceptions.InternalError(_("path can only be set on path nodes"))
             self._path = path
 
     @property
@@ -106,10 +106,10 @@
         return self.children.__iter__()
 
     def iteritems(self):
-        return self.children.iteritems()
+        return iter(self.children.items())
 
     def itervalues(self):
-        return self.children.itervalues()
+        return iter(self.children.values())
 
     def getOrCreate(self, name, type_=TYPE_VIRTUAL, access=None):
         """Get a node or create a virtual node and return it"""
@@ -123,7 +123,7 @@
 
     def addChild(self, node):
         if node.parent is not None:
-            raise exceptions.ConflictError(_(u"a node can't have several parents"))
+            raise exceptions.ConflictError(_("a node can't have several parents"))
         node.parent = self
         self.children[node.name] = node
 
@@ -132,10 +132,10 @@
             del self.parent.children[self.name]
         except TypeError:
             raise exceptions.InternalError(
-                u"trying to remove a node from inexisting parent"
+                "trying to remove a node from inexisting parent"
             )
         except KeyError:
-            raise exceptions.InternalError(u"node not found in parent's children")
+            raise exceptions.InternalError("node not found in parent's children")
         self.parent = None
 
     def _checkNodePermission(self, client, node, perms, peer_jid):
@@ -146,7 +146,7 @@
         @param peer_jid(jid.JID): entity which try to access the node
         @return (bool): True if entity can access
         """
-        file_data = {u"access": self.access, u"owner": client.jid.userhostJID()}
+        file_data = {"access": self.access, "owner": client.jid.userhostJID()}
         try:
             self.host.memory.checkFilePermission(file_data, peer_jid, perms)
         except exceptions.PermissionError:
@@ -189,16 +189,16 @@
         @raise exceptions.DataError: path is invalid
         @raise NotFound: path lead to a non existing file/directory
         """
-        path_elts = filter(None, path.split(u"/"))
+        path_elts = [_f for _f in path.split("/") if _f]
 
-        if u".." in path_elts:
+        if ".." in path_elts:
             log.warning(_(
-                u'parent dir ("..") found in path, hack attempt? path is {path} '
-                u'[{profile}]').format(path=path, profile=client.profile))
-            raise exceptions.PermissionError(u"illegal path elements")
+                'parent dir ("..") found in path, hack attempt? path is {path} '
+                '[{profile}]').format(path=path, profile=client.profile))
+            raise exceptions.PermissionError("illegal path elements")
 
         if not path_elts:
-            raise exceptions.DataError(_(u"path is invalid: {path}").format(path=path))
+            raise exceptions.DataError(_("path is invalid: {path}").format(path=path))
 
         node = client._XEP_0329_root_node
 
@@ -212,9 +212,9 @@
                 break
 
         if not node.checkPermissions(client, peer_jid, perms=perms):
-            raise exceptions.PermissionError(u"permission denied")
+            raise exceptions.PermissionError("permission denied")
 
-        return node, u"/".join(path_elts)
+        return node, "/".join(path_elts)
 
     def findByLocalPath(self, path):
         """retrieve nodes linking to local path
@@ -230,13 +230,13 @@
 
     def _getSharedPaths(self, node, paths):
         if node.type == TYPE_VIRTUAL:
-            for node in node.itervalues():
+            for node in node.values():
                 self._getSharedPaths(node, paths)
         elif node.type == TYPE_PATH:
             paths.setdefault(node.path, []).append(node)
         else:
             raise exceptions.InternalError(
-                u"unknown node type: {type}".format(type=node.type)
+                "unknown node type: {type}".format(type=node.type)
             )
 
     def getSharedPaths(self):
@@ -267,7 +267,7 @@
             in_sign="ssa{ss}s",
             out_sign="aa{ss}",
             method=self._listFiles,
-            async=True,
+            async_=True,
         )
         host.bridge.addMethod(
             "FISLocalSharesGet",
@@ -320,21 +320,21 @@
             return True, None
 
         try:
-            name = file_data[u"name"]
+            name = file_data["name"]
         except KeyError:
             return True, None
-        assert u"/" not in name
+        assert "/" not in name
 
-        path = file_data.get(u"path")
+        path = file_data.get("path")
         if path is not None:
             # we have a path, we can follow it to find node
             try:
-                node, rem_path = ShareNode.find(client, path, session[u"peer_jid"])
+                node, rem_path = ShareNode.find(client, path, session["peer_jid"])
             except (exceptions.PermissionError, exceptions.NotFound):
                 #  no file, or file not allowed, we continue normal workflow
                 return True, None
             except exceptions.DataError:
-                log.warning(_(u"invalid path: {path}").format(path=path))
+                log.warning(_("invalid path: {path}").format(path=path))
                 return True, None
 
             if node.type == TYPE_VIRTUAL:
@@ -348,7 +348,7 @@
                 path = os.path.join(node.path, rem_path, name)
             else:
                 raise exceptions.InternalError(
-                    u"unknown type: {type}".format(type=node.type)
+                    "unknown type: {type}".format(type=node.type)
                 )
             if not os.path.exists(path):
                 return True, None
@@ -360,25 +360,25 @@
             except KeyError:
                 return True, None
 
-            for path, shared_file in name_data.iteritems():
+            for path, shared_file in name_data.items():
                 if True:  #  FIXME: filters are here
                     break
             else:
                 return True, None
-            parent_node = shared_file[u"parent"]
-            if not parent_node.checkPermissions(client, session[u"peer_jid"]):
+            parent_node = shared_file["parent"]
+            if not parent_node.checkPermissions(client, session["peer_jid"]):
                 log.warning(
                     _(
-                        u"{peer_jid} requested a file (s)he can't access [{profile}]"
-                    ).format(peer_jid=session[u"peer_jid"], profile=client.profile)
+                        "{peer_jid} requested a file (s)he can't access [{profile}]"
+                    ).format(peer_jid=session["peer_jid"], profile=client.profile)
                 )
                 return True, None
-            size = shared_file[u"size"]
+            size = shared_file["size"]
 
-        file_data[u"size"] = size
-        file_elt.addElement(u"size", content=unicode(size))
-        hash_algo = file_data[u"hash_algo"] = self._h.getDefaultAlgo()
-        hasher = file_data[u"hash_hasher"] = self._h.getHasher(hash_algo)
+        file_data["size"] = size
+        file_elt.addElement("size", content=str(size))
+        hash_algo = file_data["hash_algo"] = self._h.getDefaultAlgo()
+        hasher = file_data["hash_hasher"] = self._h.getHasher(hash_algo)
         file_elt.addChild(self._h.buildHashUsedElt(hash_algo))
         content_data["stream_object"] = stream.FileStreamObject(
             self.host,
@@ -401,7 +401,7 @@
             d = defer.maybeDeferred(files_from_node_cb, client, iq_elt, node)
         d.addErrback(
             lambda failure_: log.error(
-                _(u"error while retrieving files: {msg}").format(msg=failure_)
+                _("error while retrieving files: {msg}").format(msg=failure_)
             )
         )
 
@@ -452,7 +452,7 @@
                     name = name.decode("utf-8", "strict")
                 except UnicodeDecodeError as e:
                     log.warning(
-                        _(u"ignoring invalid unicode name ({name}): {msg}").format(
+                        _("ignoring invalid unicode name ({name}): {msg}").format(
                             name=name.decode("utf-8", "replace"), msg=e
                         )
                     )
@@ -462,7 +462,7 @@
 
     def _virtualNodeHandler(self, client, peer_jid, iq_elt, query_elt, node):
         """Fill query_elt for virtual nodes"""
-        for name, child_node in node.iteritems():
+        for name, child_node in node.items():
             if not child_node.checkPermissions(client, peer_jid, check_parents=False):
                 continue
             node_type = child_node.type
@@ -473,14 +473,14 @@
                 self._addPathData(client, query_elt, child_node.path, child_node)
             else:
                 raise exceptions.InternalError(
-                    _(u"unexpected type: {type}").format(type=node_type)
+                    _("unexpected type: {type}").format(type=node_type)
                 )
 
     def _getRootNodesCb(self, client, iq_elt):
         peer_jid = jid.JID(iq_elt["from"])
         iq_result_elt = xmlstream.toResponse(iq_elt, "result")
         query_elt = iq_result_elt.addElement((NS_FIS, "query"))
-        for name, node in client._XEP_0329_root_node.iteritems():
+        for name, node in client._XEP_0329_root_node.items():
             if not node.checkPermissions(client, peer_jid, check_parents=False):
                 continue
             directory_elt = query_elt.addElement("directory")
@@ -489,7 +489,7 @@
 
     def _getFilesFromNodeCb(self, client, iq_elt, node_path):
         """Main method to retrieve files/directories from a node_path"""
-        peer_jid = jid.JID(iq_elt[u"from"])
+        peer_jid = jid.JID(iq_elt["from"])
         try:
             node, path = ShareNode.find(client, node_path, peer_jid)
         except (exceptions.PermissionError, exceptions.NotFound):
@@ -501,7 +501,7 @@
         peer_jid = jid.JID(iq_elt["from"])
         iq_result_elt = xmlstream.toResponse(iq_elt, "result")
         query_elt = iq_result_elt.addElement((NS_FIS, "query"))
-        query_elt[u"node"] = node_path
+        query_elt["node"] = node_path
 
         # we now fill query_elt according to node_type
         if node_type == TYPE_PATH:
@@ -512,7 +512,7 @@
             self._virtualNodeHandler(client, peer_jid, iq_elt, query_elt, node)
         else:
             raise exceptions.InternalError(
-                _(u"unknown node type: {type}").format(type=node_type)
+                _("unknown node type: {type}").format(type=node_type)
             )
 
         client.send(iq_result_elt)
@@ -533,7 +533,7 @@
         to_jid = jid.JID(iq_elt['to'])
         if to_jid.user:
             user = self.host.plugins['XEP-0106'].unescape(to_jid.user)
-            if u'@' in user:
+            if '@' in user:
                 # a full jid is specified
                 owner = jid.JID(user)
             else:
@@ -551,16 +551,16 @@
         files_data = yield self.host.memory.getFiles(
             client,
             peer_jid=peer_jid,
-            parent=u"",
+            parent="",
             type_=C.FILE_TYPE_DIRECTORY,
             owner=owner,
         )
         iq_result_elt = xmlstream.toResponse(iq_elt, "result")
         query_elt = iq_result_elt.addElement((NS_FIS, "query"))
         for file_data in files_data:
-            name = file_data[u"name"]
-            directory_elt = query_elt.addElement(u"directory")
-            directory_elt[u"name"] = name
+            name = file_data["name"]
+            directory_elt = query_elt.addElement("directory")
+            directory_elt["name"] = name
         client.send(iq_result_elt)
 
     @defer.inlineCallbacks
@@ -584,19 +584,19 @@
             self._iqError(client, iq_elt, condition='not-allowed')
             return
         except Exception as e:
-            log.error(u"internal server error: {e}".format(e=e))
+            log.error("internal server error: {e}".format(e=e))
             self._iqError(client, iq_elt, condition='internal-server-error')
             return
         iq_result_elt = xmlstream.toResponse(iq_elt, "result")
         query_elt = iq_result_elt.addElement((NS_FIS, "query"))
-        query_elt[u"node"] = node_path
+        query_elt["node"] = node_path
         if not self.host.trigger.point(
-            u"XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data
+            "XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data
         ):
             return
         for file_data in files_data:
             file_elt = self._jf.buildFileElementFromDict(
-                file_data, modified=file_data.get(u"modified", file_data[u"created"])
+                file_data, modified=file_data.get("modified", file_data["created"])
             )
             query_elt.addChild(file_elt)
         client.send(iq_result_elt)
@@ -617,14 +617,14 @@
                     file_data = self._jf.parseFileElement(elt)
                 except exceptions.DataError:
                     continue
-                file_data[u"type"] = C.FILE_TYPE_FILE
+                file_data["type"] = C.FILE_TYPE_FILE
             elif elt.name == "directory" and elt.uri == NS_FIS:
                 # we have a directory
 
                 file_data = {"name": elt["name"], "type": C.FILE_TYPE_DIRECTORY}
             else:
                 log.warning(
-                    _(u"unexpected element, ignoring: {elt}").format(elt=elt.toXml())
+                    _("unexpected element, ignoring: {elt}").format(elt=elt.toXml())
                 )
                 continue
             files.append(file_data)
@@ -634,9 +634,9 @@
 
     def _serializeData(self, files_data):
         for file_data in files_data:
-            for key, value in file_data.iteritems():
+            for key, value in file_data.items():
                 file_data[key] = (
-                    json.dumps(value) if key in ("extra",) else unicode(value)
+                    json.dumps(value) if key in ("extra",) else str(value)
                 )
         return files_data
 
@@ -670,7 +670,7 @@
         return self.localSharesGet(client)
 
     def localSharesGet(self, client):
-        return client._XEP_0329_root_node.getSharedPaths().keys()
+        return list(client._XEP_0329_root_node.getSharedPaths().keys())
 
     def _sharePath(self, name, path, access, profile):
         client = self.host.getClient(profile)
@@ -681,11 +681,11 @@
         if client.is_component:
             raise exceptions.ClientTypeError
         if not os.path.exists(path):
-            raise ValueError(_(u"This path doesn't exist!"))
-        if not path or not path.strip(u" /"):
-            raise ValueError(_(u"A path need to be specified"))
+            raise ValueError(_("This path doesn't exist!"))
+        if not path or not path.strip(" /"):
+            raise ValueError(_("A path need to be specified"))
         if not isinstance(access, dict):
-            raise ValueError(_(u"access must be a dict"))
+            raise ValueError(_("access must be a dict"))
 
         node = client._XEP_0329_root_node
         node_type = TYPE_PATH
@@ -695,20 +695,20 @@
             node = node.getOrCreate(SINGLE_FILES_DIR)
 
         if not name:
-            name = os.path.basename(path.rstrip(u" /"))
+            name = os.path.basename(path.rstrip(" /"))
             if not name:
-                raise exceptions.InternalError(_(u"Can't find a proper name"))
+                raise exceptions.InternalError(_("Can't find a proper name"))
 
         if name in node or name == SINGLE_FILES_DIR:
             idx = 1
-            new_name = name + "_" + unicode(idx)
+            new_name = name + "_" + str(idx)
             while new_name in node:
                 idx += 1
-                new_name = name + "_" + unicode(idx)
+                new_name = name + "_" + str(idx)
             name = new_name
             log.info(_(
-                u"A directory with this name is already shared, renamed to {new_name} "
-                u"[{profile}]".format( new_name=new_name, profile=client.profile)))
+                "A directory with this name is already shared, renamed to {new_name} "
+                "[{profile}]".format( new_name=new_name, profile=client.profile)))
 
         ShareNode(name=name, parent=node, type_=node_type, access=access, path=path)
         self.host.bridge.FISSharedPathNew(path, name, client.profile)
@@ -725,8 +725,8 @@
         self.host.bridge.FISSharedPathRemoved(path, client.profile)
 
 
+@implementer(iwokkel.IDisco)
 class XEP_0329_handler(xmlstream.XMPPHandler):
-    implements(iwokkel.IDisco)
 
     def __init__(self, plugin_parent):
         self.plugin_parent = plugin_parent