diff sat/plugins/plugin_xep_0234.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 69e4716d6268
children fee60f17ebac
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0234.py	Wed Jul 31 11:31:22 2019 +0200
+++ b/sat/plugins/plugin_xep_0234.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 Jingle File Transfer (XEP-0234)
@@ -24,7 +24,7 @@
 log = getLogger(__name__)
 from sat.core import exceptions
 from wokkel import disco, iwokkel
-from zope.interface import implements
+from zope.interface import implementer
 from sat.tools import utils
 from sat.tools import stream
 from sat.tools.common import date_utils
@@ -55,7 +55,7 @@
     C.PI_DESCRIPTION: _("""Implementation of Jingle File Transfer"""),
 }
 
-EXTRA_ALLOWED = {u"path", u"namespace", u"file_desc", u"file_hash"}
+EXTRA_ALLOWED = {"path", "namespace", "file_desc", "file_hash"}
 Range = namedtuple("Range", ("offset", "length"))
 
 
@@ -72,7 +72,7 @@
         self._j.registerApplication(NS_JINGLE_FT, self)
         self._f = host.plugins["FILE"]
         self._f.register(
-            NS_JINGLE_FT, self.fileJingleSend, priority=10000, method_name=u"Jingle"
+            NS_JINGLE_FT, self.fileJingleSend, priority=10000, method_name="Jingle"
         )
         self._hash = self.host.plugins["XEP-0300"]
         host.bridge.addMethod(
@@ -81,7 +81,7 @@
             in_sign="ssssa{ss}s",
             out_sign="",
             method=self._fileJingleSend,
-            async=True,
+            async_=True,
         )
         host.bridge.addMethod(
             "fileJingleRequest",
@@ -89,7 +89,7 @@
             in_sign="sssssa{ss}s",
             out_sign="s",
             method=self._fileJingleRequest,
-            async=True,
+            async_=True,
         )
 
     def getHandler(self, client):
@@ -102,7 +102,7 @@
         @param content_name(unicode): name of the content
         @return (unicode): unique progress id
         """
-        return u"{}_{}".format(session["id"], content_name)
+        return "{}_{}".format(session["id"], content_name)
 
     # generic methods
 
@@ -127,32 +127,32 @@
         @trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend elements to add
         """
         if file_elt is None:
-            file_elt = domish.Element((NS_JINGLE_FT, u"file"))
+            file_elt = domish.Element((NS_JINGLE_FT, "file"))
         for name, value in (
-            (u"name", name),
-            (u"size", size),
+            ("name", name),
+            ("size", size),
             ("media-type", mime_type),
-            (u"desc", desc),
-            (u"path", path),
-            (u"namespace", namespace),
+            ("desc", desc),
+            ("path", path),
+            ("namespace", namespace),
         ):
             if value is not None:
-                file_elt.addElement(name, content=unicode(value))
+                file_elt.addElement(name, content=str(value))
 
         if modified is not None:
             if isinstance(modified, int):
-                file_elt.addElement(u"date", utils.xmpp_date(modified or None))
+                file_elt.addElement("date", utils.xmpp_date(modified or None))
             else:
-                file_elt.addElement(u"date", modified)
+                file_elt.addElement("date", modified)
         elif "created" in kwargs:
-            file_elt.addElement(u"date", utils.xmpp_date(kwargs.pop("created")))
+            file_elt.addElement("date", utils.xmpp_date(kwargs.pop("created")))
 
-        range_elt = file_elt.addElement(u"range")
+        range_elt = file_elt.addElement("range")
         if transfer_range is not None:
             if transfer_range.offset is not None:
-                range_elt[u"offset"] = transfer_range.offset
+                range_elt["offset"] = transfer_range.offset
             if transfer_range.length is not None:
-                range_elt[u"length"] = transfer_range.length
+                range_elt["length"] = transfer_range.length
         if file_hash is not None:
             if not file_hash:
                 file_elt.addChild(self._hash.buildHashUsedElt())
@@ -160,7 +160,7 @@
                 file_elt.addChild(self._hash.buildHashElt(file_hash, hash_algo))
         elif hash_algo is not None:
             file_elt.addChild(self._hash.buildHashUsedElt(hash_algo))
-        self.host.trigger.point(u"XEP-0234_buildFileElement", file_elt, extra_args=kwargs)
+        self.host.trigger.point("XEP-0234_buildFileElement", file_elt, extra_args=kwargs)
         if kwargs:
             for kw in kwargs:
                 log.debug("ignored keyword: {}".format(kw))
@@ -204,68 +204,68 @@
         if parent_elt is not None:
             if file_elt is not None:
                 raise exceptions.InternalError(
-                    u"file_elt must be None if parent_elt is set"
+                    "file_elt must be None if parent_elt is set"
                 )
             try:
-                file_elt = next(parent_elt.elements(NS_JINGLE_FT, u"file"))
+                file_elt = next(parent_elt.elements(NS_JINGLE_FT, "file"))
             except StopIteration:
                 raise exceptions.NotFound()
         else:
             if not file_elt or file_elt.uri != NS_JINGLE_FT:
                 raise exceptions.DataError(
-                    u"invalid <file> element: {stanza}".format(stanza=file_elt.toXml())
+                    "invalid <file> element: {stanza}".format(stanza=file_elt.toXml())
                 )
 
         if file_data is None:
             file_data = {}
 
-        for name in (u"name", u"desc", u"path", u"namespace"):
+        for name in ("name", "desc", "path", "namespace"):
             try:
-                file_data[name] = unicode(next(file_elt.elements(NS_JINGLE_FT, name)))
+                file_data[name] = str(next(file_elt.elements(NS_JINGLE_FT, name)))
             except StopIteration:
                 pass
 
-        name = file_data.get(u"name")
-        if name == u"..":
+        name = file_data.get("name")
+        if name == "..":
             # we don't want to go to parent dir when joining to a path
-            name = u"--"
-            file_data[u"name"] = name
-        elif name is not None and u"/" in name or u"\\" in name:
-            file_data[u"name"] = regex.pathEscape(name)
+            name = "--"
+            file_data["name"] = name
+        elif name is not None and "/" in name or "\\" in name:
+            file_data["name"] = regex.pathEscape(name)
 
         try:
-            file_data[u"mime_type"] = unicode(
-                next(file_elt.elements(NS_JINGLE_FT, u"media-type"))
+            file_data["mime_type"] = str(
+                next(file_elt.elements(NS_JINGLE_FT, "media-type"))
             )
         except StopIteration:
             pass
 
         try:
-            file_data[u"size"] = int(
-                unicode(next(file_elt.elements(NS_JINGLE_FT, u"size")))
+            file_data["size"] = int(
+                str(next(file_elt.elements(NS_JINGLE_FT, "size")))
             )
         except StopIteration:
             pass
 
         try:
-            file_data[u"modified"] = date_utils.date_parse(
-                next(file_elt.elements(NS_JINGLE_FT, u"date"))
+            file_data["modified"] = date_utils.date_parse(
+                next(file_elt.elements(NS_JINGLE_FT, "date"))
             )
         except StopIteration:
             pass
 
         try:
-            range_elt = file_elt.elements(NS_JINGLE_FT, u"range").next()
+            range_elt = next(file_elt.elements(NS_JINGLE_FT, "range"))
         except StopIteration:
             pass
         else:
             offset = range_elt.getAttribute("offset")
             length = range_elt.getAttribute("length")
             if offset or length or keep_empty_range:
-                file_data[u"transfer_range"] = Range(offset=offset, length=length)
+                file_data["transfer_range"] = Range(offset=offset, length=length)
 
-        prefix = u"given_" if given else u""
-        hash_algo_key, hash_key = u"hash_algo", prefix + u"file_hash"
+        prefix = "given_" if given else ""
+        hash_algo_key, hash_key = "hash_algo", prefix + "file_hash"
         try:
             file_data[hash_algo_key], file_data[hash_key] = self._hash.parseHashElt(
                 file_elt
@@ -273,7 +273,7 @@
         except exceptions.NotFound:
             pass
 
-        self.host.trigger.point(u"XEP-0234_parseFileElement", file_elt, file_data)
+        self.host.trigger.point("XEP-0234_parseFileElement", file_elt, file_data)
 
         return file_data
 
@@ -379,12 +379,12 @@
             extra = {}
         if file_hash is not None:
             if hash_algo is None:
-                raise ValueError(_(u"hash_algo must be set if file_hash is set"))
+                raise ValueError(_("hash_algo must be set if file_hash is set"))
             extra["file_hash"] = file_hash
             extra["hash_algo"] = hash_algo
         else:
             if hash_algo is not None:
-                raise ValueError(_(u"file_hash must be set if hash_algo is set"))
+                raise ValueError(_("file_hash must be set if hash_algo is set"))
         yield self._j.initiate(
             client,
             peer_jid,
@@ -414,8 +414,8 @@
         else:
             if not EXTRA_ALLOWED.issuperset(extra):
                 raise ValueError(
-                    _(u"only the following keys are allowed in extra: {keys}").format(
-                        keys=u", ".join(EXTRA_ALLOWED)
+                    _("only the following keys are allowed in extra: {keys}").format(
+                        keys=", ".join(EXTRA_ALLOWED)
                     )
                 )
         progress_id_d.callback(self.getProgressId(session, content_name))
@@ -427,38 +427,38 @@
         desc_elt = domish.Element((NS_JINGLE_FT, "description"))
         file_elt = desc_elt.addElement("file")
 
-        if content_data[u"senders"] == self._j.ROLE_INITIATOR:
+        if content_data["senders"] == self._j.ROLE_INITIATOR:
             # we send a file
             if name is None:
                 name = os.path.basename(filepath)
-            file_data[u"date"] = utils.xmpp_date()
-            file_data[u"desc"] = extra.pop(u"file_desc", u"")
-            file_data[u"name"] = name
+            file_data["date"] = utils.xmpp_date()
+            file_data["desc"] = extra.pop("file_desc", "")
+            file_data["name"] = name
             mime_type = mimetypes.guess_type(name, strict=False)[0]
             if mime_type is not None:
-                file_data[u"mime_type"] = mime_type
-            file_data[u"size"] = os.path.getsize(filepath)
-            if u"namespace" in extra:
-                file_data[u"namespace"] = extra[u"namespace"]
-            if u"path" in extra:
-                file_data[u"path"] = extra[u"path"]
-            self.buildFileElementFromDict(file_data, file_elt=file_elt, file_hash=u"")
+                file_data["mime_type"] = mime_type
+            file_data["size"] = os.path.getsize(filepath)
+            if "namespace" in extra:
+                file_data["namespace"] = extra["namespace"]
+            if "path" in extra:
+                file_data["path"] = extra["path"]
+            self.buildFileElementFromDict(file_data, file_elt=file_elt, file_hash="")
         else:
             # we request a file
-            file_hash = extra.pop(u"file_hash", u"")
+            file_hash = extra.pop("file_hash", "")
             if not name and not file_hash:
-                raise ValueError(_(u"you need to provide at least name or file hash"))
+                raise ValueError(_("you need to provide at least name or file hash"))
             if name:
-                file_data[u"name"] = name
+                file_data["name"] = name
             if file_hash:
-                file_data[u"file_hash"] = file_hash
-                file_data[u"hash_algo"] = extra[u"hash_algo"]
+                file_data["file_hash"] = file_hash
+                file_data["hash_algo"] = extra["hash_algo"]
             else:
-                file_data[u"hash_algo"] = self._hash.getDefaultAlgo()
-            if u"namespace" in extra:
-                file_data[u"namespace"] = extra[u"namespace"]
-            if u"path" in extra:
-                file_data[u"path"] = extra[u"path"]
+                file_data["hash_algo"] = self._hash.getDefaultAlgo()
+            if "namespace" in extra:
+                file_data["namespace"] = extra["namespace"]
+            if "path" in extra:
+                file_data["path"] = extra["path"]
             self.buildFileElementFromDict(file_data, file_elt=file_elt)
 
         return desc_elt
@@ -466,13 +466,13 @@
     def jingleRequestConfirmation(self, client, action, session, content_name, desc_elt):
         """This method request confirmation for a jingle session"""
         content_data = session["contents"][content_name]
-        senders = content_data[u"senders"]
+        senders = content_data["senders"]
         if senders not in (self._j.ROLE_INITIATOR, self._j.ROLE_RESPONDER):
-            log.warning(u"Bad sender, assuming initiator")
-            senders = content_data[u"senders"] = self._j.ROLE_INITIATOR
+            log.warning("Bad sender, assuming initiator")
+            senders = content_data["senders"] = self._j.ROLE_INITIATOR
         # first we grab file informations
         try:
-            file_elt = desc_elt.elements(NS_JINGLE_FT, "file").next()
+            file_elt = next(desc_elt.elements(NS_JINGLE_FT, "file"))
         except StopIteration:
             raise failure.Failure(exceptions.DataError)
         file_data = {"progress_id": self.getProgressId(session, content_name)}
@@ -516,7 +516,7 @@
                 )
             defer.returnValue(confirmed)
 
-        log.warning(_(u"File continue is not implemented yet"))
+        log.warning(_("File continue is not implemented yet"))
         defer.returnValue(False)
 
     def _fileReceivingRequestConf(
@@ -545,7 +545,7 @@
         name = file_data["name"]
         if "/" in name or "\\" in name:
             log.warning(
-                u"File name contain path characters, we replace them: {}".format(name)
+                "File name contain path characters, we replace them: {}".format(name)
             )
             file_data["name"] = name.replace("/", "_").replace("\\", "_")
 
@@ -574,9 +574,9 @@
         if action in (self._j.A_ACCEPTED_ACK,):
             pass
         elif action == self._j.A_SESSION_INITIATE:
-            file_elt = desc_elt.elements(NS_JINGLE_FT, "file").next()
+            file_elt = next(desc_elt.elements(NS_JINGLE_FT, "file"))
             try:
-                file_elt.elements(NS_JINGLE_FT, "range").next()
+                next(file_elt.elements(NS_JINGLE_FT, "range"))
             except StopIteration:
                 # initiator doesn't manage <range>, but we do so we advertise it
                 #  FIXME: to be checked
@@ -586,14 +586,14 @@
             assert not "stream_object" in content_data
             file_data = application_data["file_data"]
             file_path = application_data["file_path"]
-            senders = content_data[u"senders"]
-            if senders != session[u"role"]:
+            senders = content_data["senders"]
+            if senders != session["role"]:
                 # we are receiving the file
                 try:
                     # did the responder specified the size of the file?
-                    file_elt = next(desc_elt.elements(NS_JINGLE_FT, u"file"))
-                    size_elt = next(file_elt.elements(NS_JINGLE_FT, u"size"))
-                    size = int(unicode(size_elt))
+                    file_elt = next(desc_elt.elements(NS_JINGLE_FT, "file"))
+                    size_elt = next(file_elt.elements(NS_JINGLE_FT, "size"))
+                    size = int(str(size_elt))
                 except (StopIteration, ValueError):
                     size = None
                 # XXX: hash security is not critical here, so we just take the higher mandatory one
@@ -624,7 +624,7 @@
             args = [client, session, content_name, content_data]
             finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args)
         else:
-            log.warning(u"FIXME: unmanaged action {}".format(action))
+            log.warning("FIXME: unmanaged action {}".format(action))
         return desc_elt
 
     def jingleSessionInfo(self, client, action, session, content_name, jingle_elt):
@@ -644,7 +644,7 @@
                 # we have received the file hash, we need to parse it
                 if content_data["senders"] == session["role"]:
                     log.warning(
-                        u"unexpected checksum received while we are the file sender"
+                        "unexpected checksum received while we are the file sender"
                     )
                     raise exceptions.DataError
                 info_content_name = elt["name"]
@@ -653,13 +653,13 @@
                     return
                 file_data = content_data["application_data"]["file_data"]
                 try:
-                    file_elt = elt.elements((NS_JINGLE_FT, "file")).next()
+                    file_elt = next(elt.elements((NS_JINGLE_FT, "file")))
                 except StopIteration:
                     raise exceptions.DataError
                 algo, file_data["given_file_hash"] = self._hash.parseHashElt(file_elt)
                 if algo != file_data.get("hash_algo"):
                     log.warning(
-                        u"Hash algorithm used in given hash ({peer_algo}) doesn't correspond to the one we have used ({our_algo}) [{profile}]".format(
+                        "Hash algorithm used in given hash ({peer_algo}) doesn't correspond to the one we have used ({our_algo}) [{profile}]".format(
                             peer_algo=algo,
                             our_algo=file_data.get("hash_algo"),
                             profile=client.profile,
@@ -685,7 +685,7 @@
         file_data = content_data["application_data"]["file_data"]
         hasher = file_data["hash_hasher"]
         hash_ = hasher.hexdigest()
-        log.debug(u"Calculated hash: {}".format(hash_))
+        log.debug("Calculated hash: {}".format(hash_))
         iq_elt, jingle_elt = self._j.buildSessionInfo(client, session)
         checksum_elt = jingle_elt.addElement((NS_JINGLE_FT, "checksum"))
         checksum_elt["creator"] = content_data["creator"]
@@ -712,7 +712,7 @@
         if given_hash is None:
             if last_try:
                 log.warning(
-                    u"sender didn't sent hash checksum, we can't check the file [{profile}]".format(
+                    "sender didn't sent hash checksum, we can't check the file [{profile}]".format(
                         profile=client.profile
                     )
                 )
@@ -721,10 +721,10 @@
                 return True
             return False
         hasher = file_data["hash_hasher"]
-        hash_ = hasher.hexdigest()
+        hash_ = hasher.hexdigest().encode('utf-8')
 
         if hash_ == given_hash:
-            log.info(u"Hash checked, file was successfully transfered: {}".format(hash_))
+            log.info("Hash checked, file was successfully transfered: {}".format(hash_))
             progress_metadata = {
                 "hash": hash_,
                 "hash_algo": file_data["hash_algo"],
@@ -732,9 +732,9 @@
             }
             error = None
         else:
-            log.warning(u"Hash mismatch, the file was not transfered correctly")
+            log.warning("Hash mismatch, the file was not transfered correctly")
             progress_metadata = None
-            error = u"Hash mismatch: given={algo}:{given}, calculated={algo}:{our}".format(
+            error = "Hash mismatch: given={algo}:{given}, calculated={algo}:{our}".format(
                 algo=file_data["hash_algo"], given=given_hash, our=hash_
             )
 
@@ -748,7 +748,7 @@
         return True
 
     def _finishedCb(self, __, client, session, content_name, content_data):
-        log.info(u"File transfer terminated")
+        log.info("File transfer terminated")
         if content_data["senders"] != session["role"]:
             # we terminate the session only if we are the receiver,
             # as recommanded in XEP-0234 §2 (after example 6)
@@ -772,15 +772,15 @@
             content_data["stream_object"].close()
 
     def _finishedEb(self, failure, client, session, content_name, content_data):
-        log.warning(u"Error while streaming file: {}".format(failure))
+        log.warning("Error while streaming file: {}".format(failure))
         content_data["stream_object"].close()
         self._j.contentTerminate(
             client, session, content_name, reason=self._j.REASON_FAILED_TRANSPORT
         )
 
 
+@implementer(iwokkel.IDisco)
 class XEP_0234_handler(XMPPHandler):
-    implements(iwokkel.IDisco)
 
     def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
         return [disco.DiscoFeature(NS_JINGLE_FT)]