changeset 4288:f46891f2c9cb

plugin XEP-0166: handle `content-add` action + expose `get_transport`: - `content-add` is now handled at this plugin level (implementation needs to be done in apps and transports plugins). - `get_transport` is now exposed. rel 447
author Goffi <goffi@goffi.org>
date Mon, 29 Jul 2024 03:30:58 +0200
parents ff88a807852d
children 33ecebb2c02f
files libervia/backend/plugins/plugin_xep_0166/__init__.py
diffstat 1 files changed, 95 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_xep_0166/__init__.py	Mon Jul 29 03:30:53 2024 +0200
+++ b/libervia/backend/plugins/plugin_xep_0166/__init__.py	Mon Jul 29 03:30:58 2024 +0200
@@ -96,6 +96,10 @@
     A_SESSION_ACCEPT: Final = "session-accept"
     A_SESSION_TERMINATE: Final = "session-terminate"
     A_SESSION_INFO: Final = "session-info"
+    A_CONTENT_ADD: Final = "content-add"
+    A_CONTENT_MODIFY: Final = "content-modify"
+    A_CONTENT_REJECT: Final = "content-reject"
+    A_CONTENT_REMOVE: Final = "content-remove"
     A_TRANSPORT_REPLACE: Final = "transport-replace"
     A_TRANSPORT_ACCEPT: Final = "transport-accept"
     A_TRANSPORT_REJECT: Final = "transport-reject"
@@ -478,17 +482,24 @@
         else:
             iq_elt, jingle_elt = self._build_jingle_elt(client, session, action)
         # FIXME: XEP-0260 § 2.3 Ex 5 has an initiator attribute, but it should not according to XEP-0166 §7.1 table 1, must be checked
-        content_data = session["contents"][content_name]
+        if action.startswith("content-"):
+            creator = session["role"]
+            transport_namespace = None
+        else:
+            content_data = session["contents"][content_name]
+            creator = content_data["creator"]
+            transport_namespace = content_data["transport"].namespace
+
         content_elt = jingle_elt.addElement("content")
         content_elt["name"] = content_name
-        content_elt["creator"] = content_data["creator"]
+        content_elt["creator"] = creator
 
         if context_elt is not None:
             if context_elt.parent is None:
                 content_elt.addChild(context_elt)
         elif action == XEP_0166.A_TRANSPORT_INFO:
             context_elt = transport_elt = content_elt.addElement(
-                "transport", content_data["transport"].namespace
+                "transport", transport_namespace
             )
         else:
             raise exceptions.InternalError(f"unmanaged action {action}")
@@ -513,8 +524,10 @@
         except KeyError:
             raise exceptions.NotFound(f"No application registered for {namespace}")
 
-    def get_content_data(self, content: dict, content_idx: int) -> ContentData:
-        """ "Retrieve application and its argument from content"""
+    def get_content_data(
+        self, content: dict, content_idx: int | None = None
+    ) -> ContentData:
+        """Retrieve application and its argument from content"""
         app_ns = content["app_ns"]
         try:
             application = self.get_application(app_ns)
@@ -526,11 +539,32 @@
         try:
             content_name = content["name"]
         except KeyError:
+            if content_idx is None:
+                raise exceptions.InternalError(
+                    '"content_idx" must be set if "content[\'name\']" is not set.'
+                )
             content_name = content["name"] = str(content_idx)
         return ContentData(
             application, app_args, app_kwargs, transport_data, content_name
         )
 
+    def get_transport(
+        self,
+        client: SatXMPPEntity,
+        content: dict,
+        content_data: ContentData,
+    ) -> TransportData:
+        """Find a suitable transport for given content"""
+        transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING)
+        for transport in self._type_transports[transport_type]:
+            if transport.handler.is_usable(client, content_data):
+                break
+        else:
+            raise exceptions.InternalError(
+                "No transport registered for {}".format(transport_type)
+            )
+        return transport
+
     async def initiate(
         self,
         client: SatXMPPEntity,
@@ -593,14 +627,7 @@
             content_data = self.get_content_data(content, content_idx)
 
             # and the transport plugin
-            transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING)
-            for transport in self._type_transports[transport_type]:
-                if transport.handler.is_usable(client, content_data):
-                    break
-            else:
-                raise exceptions.InternalError(
-                    "No transport registered for {}".format(transport_type)
-                )
+            transport = self.get_transport(client, content, content_data)
 
             # we build the session data for this content
             application_data = {}
@@ -801,6 +828,8 @@
             await self.on_session_accept(client, request, jingle_elt, session)
         elif action == XEP_0166.A_SESSION_INFO:
             await self.on_session_info(client, request, jingle_elt, session)
+        elif action == XEP_0166.A_CONTENT_ADD:
+            await self.on_content_add(client, request, jingle_elt, session)
         elif action == XEP_0166.A_TRANSPORT_INFO:
             self.on_transport_info(client, request, jingle_elt, session)
         elif action == XEP_0166.A_TRANSPORT_REPLACE:
@@ -994,7 +1023,10 @@
         @return : list of launched methods results
         @raise exceptions.NotFound: method is not implemented
         """
-        contents_dict = session["contents"]
+        if action == self.A_CONTENT_ADD:
+            contents_dict = session["contents_new"]
+        else:
+            contents_dict = session["contents"]
         results = []
         for content_name, content_data in contents_dict.items():
             for method_name, handler_key, default_cb, elt_name in (
@@ -1023,6 +1055,9 @@
                 )
                 results.append(result)
 
+        if action == self.A_CONTENT_ADD:
+            del session["contents_new"]
+
         return results
 
     async def on_session_initiate(
@@ -1303,6 +1338,52 @@
         else:
             client.send(xmlstream.toResponse(request, "result"))
 
+    async def on_content_add(
+        self,
+        client: SatXMPPEntity,
+        request: domish.Element,
+        jingle_elt: domish.Element,
+        session: Dict[str, Any],
+    ) -> None:
+        """Called on content-add action
+
+        The "jingle_request_confirmation" method of each application will be called
+        (or self.jingle_request_confirmation_default if the former doesn't exist).
+        The session is only accepted if all application are confirmed.
+        The application must manage itself multiple contents scenari (e.g. audio/video).
+        @param client: %(doc_client)s
+        @param request(domish.Element): full request
+        @param jingle_elt(domish.Element): <jingle> element
+        @param session(dict): session data
+        """
+        return
+        try:
+            contents_dict = self._parse_elements(
+                jingle_elt,
+                {"id": session["id"], "contents": {}},
+                request,
+                client,
+                True,
+                XEP_0166.ROLE_INITIATOR,
+            )
+        except exceptions.CancelError:
+            return
+
+        if not contents_dict:
+            # there MUST be at least one content
+            self.sendError(client, "bad-request", session["id"], request)
+            return
+
+        session["contents_new"] = contents_dict
+
+        # at this point we can send the <iq/> result to confirm reception of the request
+        client.send(xmlstream.toResponse(request, "result"))
+
+        assert "jingle_elt" not in session
+        session["jingle_elt"] = jingle_elt
+
+        await self._call_plugins(client, XEP_0166.A_CONTENT_ADD, session, delete=False)
+
     async def on_transport_replace(self, client, request, jingle_elt, session):
         """A transport change is requested