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
This commit is contained in:
Goffi 2024-07-29 03:30:58 +02:00
commit 31c80d3626

View file

@ -96,6 +96,10 @@ class XEP_0166:
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 @@ class XEP_0166:
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 @@ class XEP_0166:
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 @@ class XEP_0166:
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 @@ class XEP_0166:
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 @@ class XEP_0166:
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 @@ class XEP_0166:
@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 @@ class XEP_0166:
)
results.append(result)
if action == self.A_CONTENT_ADD:
del session["contents_new"]
return results
async def on_session_initiate(
@ -1303,6 +1338,52 @@ class XEP_0166:
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