Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_xep_0166/__init__.py @ 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 | 0d7bb4df2343 |
children | e9971a4b0627 |
comparison
equal
deleted
inserted
replaced
4287:ff88a807852d | 4288:f46891f2c9cb |
---|---|
94 | 94 |
95 A_SESSION_INITIATE: Final = "session-initiate" | 95 A_SESSION_INITIATE: Final = "session-initiate" |
96 A_SESSION_ACCEPT: Final = "session-accept" | 96 A_SESSION_ACCEPT: Final = "session-accept" |
97 A_SESSION_TERMINATE: Final = "session-terminate" | 97 A_SESSION_TERMINATE: Final = "session-terminate" |
98 A_SESSION_INFO: Final = "session-info" | 98 A_SESSION_INFO: Final = "session-info" |
99 A_CONTENT_ADD: Final = "content-add" | |
100 A_CONTENT_MODIFY: Final = "content-modify" | |
101 A_CONTENT_REJECT: Final = "content-reject" | |
102 A_CONTENT_REMOVE: Final = "content-remove" | |
99 A_TRANSPORT_REPLACE: Final = "transport-replace" | 103 A_TRANSPORT_REPLACE: Final = "transport-replace" |
100 A_TRANSPORT_ACCEPT: Final = "transport-accept" | 104 A_TRANSPORT_ACCEPT: Final = "transport-accept" |
101 A_TRANSPORT_REJECT: Final = "transport-reject" | 105 A_TRANSPORT_REJECT: Final = "transport-reject" |
102 A_TRANSPORT_INFO: Final = "transport-info" | 106 A_TRANSPORT_INFO: Final = "transport-info" |
103 | 107 |
476 "The <iq> element provided doesn't have a <jingle> element" | 480 "The <iq> element provided doesn't have a <jingle> element" |
477 ) | 481 ) |
478 else: | 482 else: |
479 iq_elt, jingle_elt = self._build_jingle_elt(client, session, action) | 483 iq_elt, jingle_elt = self._build_jingle_elt(client, session, action) |
480 # 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 | 484 # 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 |
481 content_data = session["contents"][content_name] | 485 if action.startswith("content-"): |
486 creator = session["role"] | |
487 transport_namespace = None | |
488 else: | |
489 content_data = session["contents"][content_name] | |
490 creator = content_data["creator"] | |
491 transport_namespace = content_data["transport"].namespace | |
492 | |
482 content_elt = jingle_elt.addElement("content") | 493 content_elt = jingle_elt.addElement("content") |
483 content_elt["name"] = content_name | 494 content_elt["name"] = content_name |
484 content_elt["creator"] = content_data["creator"] | 495 content_elt["creator"] = creator |
485 | 496 |
486 if context_elt is not None: | 497 if context_elt is not None: |
487 if context_elt.parent is None: | 498 if context_elt.parent is None: |
488 content_elt.addChild(context_elt) | 499 content_elt.addChild(context_elt) |
489 elif action == XEP_0166.A_TRANSPORT_INFO: | 500 elif action == XEP_0166.A_TRANSPORT_INFO: |
490 context_elt = transport_elt = content_elt.addElement( | 501 context_elt = transport_elt = content_elt.addElement( |
491 "transport", content_data["transport"].namespace | 502 "transport", transport_namespace |
492 ) | 503 ) |
493 else: | 504 else: |
494 raise exceptions.InternalError(f"unmanaged action {action}") | 505 raise exceptions.InternalError(f"unmanaged action {action}") |
495 | 506 |
496 return iq_elt, context_elt | 507 return iq_elt, context_elt |
511 try: | 522 try: |
512 return self._applications[namespace] | 523 return self._applications[namespace] |
513 except KeyError: | 524 except KeyError: |
514 raise exceptions.NotFound(f"No application registered for {namespace}") | 525 raise exceptions.NotFound(f"No application registered for {namespace}") |
515 | 526 |
516 def get_content_data(self, content: dict, content_idx: int) -> ContentData: | 527 def get_content_data( |
517 """ "Retrieve application and its argument from content""" | 528 self, content: dict, content_idx: int | None = None |
529 ) -> ContentData: | |
530 """Retrieve application and its argument from content""" | |
518 app_ns = content["app_ns"] | 531 app_ns = content["app_ns"] |
519 try: | 532 try: |
520 application = self.get_application(app_ns) | 533 application = self.get_application(app_ns) |
521 except exceptions.NotFound as e: | 534 except exceptions.NotFound as e: |
522 raise exceptions.InternalError(str(e)) | 535 raise exceptions.InternalError(str(e)) |
524 app_kwargs = content.get("app_kwargs", {}) | 537 app_kwargs = content.get("app_kwargs", {}) |
525 transport_data = content.get("transport_data", {}) | 538 transport_data = content.get("transport_data", {}) |
526 try: | 539 try: |
527 content_name = content["name"] | 540 content_name = content["name"] |
528 except KeyError: | 541 except KeyError: |
542 if content_idx is None: | |
543 raise exceptions.InternalError( | |
544 '"content_idx" must be set if "content[\'name\']" is not set.' | |
545 ) | |
529 content_name = content["name"] = str(content_idx) | 546 content_name = content["name"] = str(content_idx) |
530 return ContentData( | 547 return ContentData( |
531 application, app_args, app_kwargs, transport_data, content_name | 548 application, app_args, app_kwargs, transport_data, content_name |
532 ) | 549 ) |
550 | |
551 def get_transport( | |
552 self, | |
553 client: SatXMPPEntity, | |
554 content: dict, | |
555 content_data: ContentData, | |
556 ) -> TransportData: | |
557 """Find a suitable transport for given content""" | |
558 transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING) | |
559 for transport in self._type_transports[transport_type]: | |
560 if transport.handler.is_usable(client, content_data): | |
561 break | |
562 else: | |
563 raise exceptions.InternalError( | |
564 "No transport registered for {}".format(transport_type) | |
565 ) | |
566 return transport | |
533 | 567 |
534 async def initiate( | 568 async def initiate( |
535 self, | 569 self, |
536 client: SatXMPPEntity, | 570 client: SatXMPPEntity, |
537 peer_jid: jid.JID, | 571 peer_jid: jid.JID, |
591 for content_idx, content in enumerate(contents): | 625 for content_idx, content in enumerate(contents): |
592 # we get the application plugin | 626 # we get the application plugin |
593 content_data = self.get_content_data(content, content_idx) | 627 content_data = self.get_content_data(content, content_idx) |
594 | 628 |
595 # and the transport plugin | 629 # and the transport plugin |
596 transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING) | 630 transport = self.get_transport(client, content, content_data) |
597 for transport in self._type_transports[transport_type]: | |
598 if transport.handler.is_usable(client, content_data): | |
599 break | |
600 else: | |
601 raise exceptions.InternalError( | |
602 "No transport registered for {}".format(transport_type) | |
603 ) | |
604 | 631 |
605 # we build the session data for this content | 632 # we build the session data for this content |
606 application_data = {} | 633 application_data = {} |
607 transport_data = content_data.transport_data | 634 transport_data = content_data.transport_data |
608 session_content = { | 635 session_content = { |
799 await self.on_session_terminate(client, request, jingle_elt, session) | 826 await self.on_session_terminate(client, request, jingle_elt, session) |
800 elif action == XEP_0166.A_SESSION_ACCEPT: | 827 elif action == XEP_0166.A_SESSION_ACCEPT: |
801 await self.on_session_accept(client, request, jingle_elt, session) | 828 await self.on_session_accept(client, request, jingle_elt, session) |
802 elif action == XEP_0166.A_SESSION_INFO: | 829 elif action == XEP_0166.A_SESSION_INFO: |
803 await self.on_session_info(client, request, jingle_elt, session) | 830 await self.on_session_info(client, request, jingle_elt, session) |
831 elif action == XEP_0166.A_CONTENT_ADD: | |
832 await self.on_content_add(client, request, jingle_elt, session) | |
804 elif action == XEP_0166.A_TRANSPORT_INFO: | 833 elif action == XEP_0166.A_TRANSPORT_INFO: |
805 self.on_transport_info(client, request, jingle_elt, session) | 834 self.on_transport_info(client, request, jingle_elt, session) |
806 elif action == XEP_0166.A_TRANSPORT_REPLACE: | 835 elif action == XEP_0166.A_TRANSPORT_REPLACE: |
807 await self.on_transport_replace(client, request, jingle_elt, session) | 836 await self.on_transport_replace(client, request, jingle_elt, session) |
808 elif action == XEP_0166.A_TRANSPORT_ACCEPT: | 837 elif action == XEP_0166.A_TRANSPORT_ACCEPT: |
992 @param force_element: if elements is False, it is used as element parameter | 1021 @param force_element: if elements is False, it is used as element parameter |
993 else it is ignored | 1022 else it is ignored |
994 @return : list of launched methods results | 1023 @return : list of launched methods results |
995 @raise exceptions.NotFound: method is not implemented | 1024 @raise exceptions.NotFound: method is not implemented |
996 """ | 1025 """ |
997 contents_dict = session["contents"] | 1026 if action == self.A_CONTENT_ADD: |
1027 contents_dict = session["contents_new"] | |
1028 else: | |
1029 contents_dict = session["contents"] | |
998 results = [] | 1030 results = [] |
999 for content_name, content_data in contents_dict.items(): | 1031 for content_name, content_data in contents_dict.items(): |
1000 for method_name, handler_key, default_cb, elt_name in ( | 1032 for method_name, handler_key, default_cb, elt_name in ( |
1001 (app_method_name, "application", app_default_cb, "desc_elt"), | 1033 (app_method_name, "application", app_default_cb, "desc_elt"), |
1002 (transp_method_name, "transport", transp_default_cb, "transport_elt"), | 1034 (transp_method_name, "transport", transp_default_cb, "transport_elt"), |
1021 result = await utils.as_deferred( | 1053 result = await utils.as_deferred( |
1022 method, client, action, session, content_name, elt | 1054 method, client, action, session, content_name, elt |
1023 ) | 1055 ) |
1024 results.append(result) | 1056 results.append(result) |
1025 | 1057 |
1058 if action == self.A_CONTENT_ADD: | |
1059 del session["contents_new"] | |
1060 | |
1026 return results | 1061 return results |
1027 | 1062 |
1028 async def on_session_initiate( | 1063 async def on_session_initiate( |
1029 self, | 1064 self, |
1030 client: SatXMPPEntity, | 1065 client: SatXMPPEntity, |
1300 ) | 1335 ) |
1301 except Exception: | 1336 except Exception: |
1302 log.exception("Error while managing session info") | 1337 log.exception("Error while managing session info") |
1303 else: | 1338 else: |
1304 client.send(xmlstream.toResponse(request, "result")) | 1339 client.send(xmlstream.toResponse(request, "result")) |
1340 | |
1341 async def on_content_add( | |
1342 self, | |
1343 client: SatXMPPEntity, | |
1344 request: domish.Element, | |
1345 jingle_elt: domish.Element, | |
1346 session: Dict[str, Any], | |
1347 ) -> None: | |
1348 """Called on content-add action | |
1349 | |
1350 The "jingle_request_confirmation" method of each application will be called | |
1351 (or self.jingle_request_confirmation_default if the former doesn't exist). | |
1352 The session is only accepted if all application are confirmed. | |
1353 The application must manage itself multiple contents scenari (e.g. audio/video). | |
1354 @param client: %(doc_client)s | |
1355 @param request(domish.Element): full request | |
1356 @param jingle_elt(domish.Element): <jingle> element | |
1357 @param session(dict): session data | |
1358 """ | |
1359 return | |
1360 try: | |
1361 contents_dict = self._parse_elements( | |
1362 jingle_elt, | |
1363 {"id": session["id"], "contents": {}}, | |
1364 request, | |
1365 client, | |
1366 True, | |
1367 XEP_0166.ROLE_INITIATOR, | |
1368 ) | |
1369 except exceptions.CancelError: | |
1370 return | |
1371 | |
1372 if not contents_dict: | |
1373 # there MUST be at least one content | |
1374 self.sendError(client, "bad-request", session["id"], request) | |
1375 return | |
1376 | |
1377 session["contents_new"] = contents_dict | |
1378 | |
1379 # at this point we can send the <iq/> result to confirm reception of the request | |
1380 client.send(xmlstream.toResponse(request, "result")) | |
1381 | |
1382 assert "jingle_elt" not in session | |
1383 session["jingle_elt"] = jingle_elt | |
1384 | |
1385 await self._call_plugins(client, XEP_0166.A_CONTENT_ADD, session, delete=False) | |
1305 | 1386 |
1306 async def on_transport_replace(self, client, request, jingle_elt, session): | 1387 async def on_transport_replace(self, client, request, jingle_elt, session): |
1307 """A transport change is requested | 1388 """A transport change is requested |
1308 | 1389 |
1309 The request is parsed, and jingle_handler is called on concerned transport plugin(s) | 1390 The request is parsed, and jingle_handler is called on concerned transport plugin(s) |