diff sat/plugins/plugin_xep_0166.py @ 3969:8e7d5796fb23

plugin XEP-0391: implement XEP-0391 (Jingle Encrypted Transports) + XEP-0396 (JET-OMEMO): rel 378
author Goffi <goffi@goffi.org>
date Mon, 31 Oct 2022 04:09:34 +0100
parents bbf92ef05f38
children 524856bd7b19
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0166.py	Mon Oct 31 04:04:32 2022 +0100
+++ b/sat/plugins/plugin_xep_0166.py	Mon Oct 31 04:09:34 2022 +0100
@@ -17,21 +17,25 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
-import uuid
+from collections import namedtuple
 import time
-from typing import Tuple
-from collections import namedtuple
-from zope.interface import implementer
-from twisted.words.protocols.jabber import jid
+from typing import Any, Dict, Tuple
+import uuid
+
 from twisted.internet import defer
 from twisted.internet import reactor
+from twisted.python import failure
+from twisted.words.protocols.jabber import jid
 from twisted.words.protocols.jabber import error
 from twisted.words.protocols.jabber import xmlstream
-from twisted.python import failure
+from twisted.words.xish import domish
 from wokkel import disco, iwokkel
+from zope.interface import implementer
+
 from sat.core import exceptions
-from sat.core.i18n import _, D_
 from sat.core.constants import Const as C
+from sat.core.core_types import SatXMPPEntity
+from sat.core.i18n import D_, _
 from sat.core.log import getLogger
 from sat.tools import xml_tools
 from sat.tools import utils
@@ -65,7 +69,8 @@
 TransportData = namedtuple("TransportData", ("namespace", "handler", "priority"))
 
 
-class XEP_0166(object):
+class XEP_0166:
+    namespace = NS_JINGLE
     ROLE_INITIATOR = "initiator"
     ROLE_RESPONDER = "responder"
     TRANSPORT_DATAGRAM = "UDP"
@@ -372,7 +377,7 @@
             content_name = content["name"] = str(uuid.uuid4())
         return application, app_args, app_kwargs, content_name
 
-    async def initiate(self, client, peer_jid, contents):
+    async def initiate(self, client, peer_jid, contents, encrypted=False):
         """Send a session initiation request
 
         @param peer_jid(jid.JID): jid to establith session with
@@ -387,6 +392,8 @@
                     default to BOTH (see XEP-0166 ยง7.3)
                 - app_args(list): args to pass to the application plugin
                 - app_kwargs(dict): keyword args to pass to the application plugin
+        @param encrypted: if True, session must be encrypted and "encryption" must be set
+            to all content data of session
         @return (unicode): jingle session id
         """
         assert contents  # there must be at least one content
@@ -471,6 +478,18 @@
             )
             content_elt.addChild(transport_elt)
 
+        if not await self.host.trigger.asyncPoint(
+            "XEP-0166_initiate_elt_built",
+            client, session, iq_elt, jingle_elt
+        ):
+            return
+        if encrypted:
+            for content in session["contents"].values():
+                if "encryption" not in content:
+                    raise exceptions.EncryptionError(
+                        "Encryption is requested, but no encryption has been set"
+                    )
+
         try:
             await iq_elt.send()
         except Exception as e:
@@ -514,7 +533,14 @@
 
     ## jingle events ##
 
-    def _onJingleRequest(self, request, client):
+    def _on_jingle_request(self, request: domish.Element, client: SatXMPPEntity) -> None:
+        defer.ensureDeferred(self.on_jingle_request(client, request))
+
+    async def on_jingle_request(
+        self,
+        client: SatXMPPEntity,
+        request: domish.Element
+    ) -> None:
         """Called when any jingle request is received
 
         The request will then be dispatched to appropriate method
@@ -594,7 +620,7 @@
                 raise exceptions.InternalError
 
         if action == XEP_0166.A_SESSION_INITIATE:
-            self.onSessionInitiate(client, request, jingle_elt, session)
+            await self.onSessionInitiate(client, request, jingle_elt, session)
         elif action == XEP_0166.A_SESSION_TERMINATE:
             self.onSessionTerminate(client, request, jingle_elt, session)
         elif action == XEP_0166.A_SESSION_ACCEPT:
@@ -796,7 +822,13 @@
 
         return defers_list
 
-    def onSessionInitiate(self, client, request, jingle_elt, session):
+    async def onSessionInitiate(
+        self,
+        client: SatXMPPEntity,
+        request: domish.Element,
+        jingle_elt: domish.Element,
+        session: Dict[str, Any]
+    ) -> None:
         """Called on session-initiate action
 
         The "jingleRequestConfirmation" method of each application will be called
@@ -829,6 +861,13 @@
         # at this point we can send the <iq/> result to confirm reception of the request
         client.send(xmlstream.toResponse(request, "result"))
 
+
+        if not await self.host.trigger.asyncPoint(
+            "XEP-0166_on_session_initiate",
+            client, session, request, jingle_elt
+        ):
+            return
+
         # we now request each application plugin confirmation
         # and if all are accepted, we can accept the session
         confirm_defers = self._callPlugins(
@@ -1199,7 +1238,7 @@
 
     def connectionInitialized(self):
         self.xmlstream.addObserver(
-            JINGLE_REQUEST, self.plugin_parent._onJingleRequest, client=self.parent
+            JINGLE_REQUEST, self.plugin_parent._on_jingle_request, client=self.parent
         )
 
     def getDiscoInfo(self, requestor, target, nodeIdentifier=""):