comparison sat/plugins/plugin_xep_0166.py @ 3404:26a0af6e32c1

plugin XEP-0166: new trigger point + coroutines + helper methods: - a new `XEP-0166_initiate` async trigger point is available - `initate` is now a coroutine - `jingleSessionInit` in applications and transports handlers are now called using `utils.adDeferred` - new `getApplication` helper method, to retrieve application from its namespace - new `getContentData` helper method to retrieve application and its argument from content
author Goffi <goffi@goffi.org>
date Thu, 12 Nov 2020 14:53:15 +0100
parents ac9342f359e9
children be6d91572633
comparison
equal deleted inserted replaced
3403:404d4b29de52 3404:26a0af6e32c1
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 18
19 19
20 import uuid 20 import uuid
21 import time 21 import time
22 from typing import Tuple
22 from collections import namedtuple 23 from collections import namedtuple
23 from zope.interface import implementer 24 from zope.interface import implementer
24 from twisted.words.protocols.jabber import jid 25 from twisted.words.protocols.jabber import jid
25 from twisted.internet import defer 26 from twisted.internet import defer
26 from twisted.internet import reactor 27 from twisted.internet import reactor
334 @param session(dict): jingle session data 335 @param session(dict): jingle session data
335 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <jingle> element 336 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <jingle> element
336 """ 337 """
337 return self._buildJingleElt(client, session, XEP_0166.A_SESSION_INFO) 338 return self._buildJingleElt(client, session, XEP_0166.A_SESSION_INFO)
338 339
339 @defer.inlineCallbacks 340 def getApplication(self, namespace: str) -> object:
340 def initiate(self, client, peer_jid, contents): 341 """Retreive application corresponding to a namespace
342
343 @raise exceptions.NotFound if application can't be found
344 """
345 try:
346 return self._applications[namespace]
347 except KeyError:
348 raise exceptions.NotFound(
349 f"No application registered for {namespace}"
350 )
351
352 def getContentData(self, content: dict) -> Tuple[object, list, dict, str]:
353 """"Retrieve application and its argument from content"""
354 app_ns = content["app_ns"]
355 try:
356 application = self.getApplication(app_ns)
357 except exceptions.NotFound as e:
358 raise exceptions.InternalError(str(e))
359 app_args = content.get("app_args", [])
360 app_kwargs = content.get("app_kwargs", {})
361 try:
362 content_name = content["name"]
363 except KeyError:
364 content_name = content["name"] = str(uuid.uuid4())
365 return application, app_args, app_kwargs, content_name
366
367 async def initiate(self, client, peer_jid, contents):
341 """Send a session initiation request 368 """Send a session initiation request
342 369
343 @param peer_jid(jid.JID): jid to establith session with 370 @param peer_jid(jid.JID): jid to establith session with
344 @param contents(list[dict]): list of contents to use: 371 @param contents(list[dict]): list of contents to use:
345 The dict must have the following keys: 372 The dict must have the following keys:
350 - name(unicode): name of the content 377 - name(unicode): name of the content
351 - senders(unicode): One of XEP_0166.ROLE_INITIATOR, XEP_0166.ROLE_RESPONDER, both or none 378 - senders(unicode): One of XEP_0166.ROLE_INITIATOR, XEP_0166.ROLE_RESPONDER, both or none
352 default to BOTH (see XEP-0166 §7.3) 379 default to BOTH (see XEP-0166 §7.3)
353 - app_args(list): args to pass to the application plugin 380 - app_args(list): args to pass to the application plugin
354 - app_kwargs(dict): keyword args to pass to the application plugin 381 - app_kwargs(dict): keyword args to pass to the application plugin
355 @return D(unicode): jingle session id 382 @return (unicode): jingle session id
356 """ 383 """
357 assert contents # there must be at least one content 384 assert contents # there must be at least one content
358 if (peer_jid == client.jid 385 if (peer_jid == client.jid
359 or client.is_component and peer_jid.host == client.jid.host): 386 or client.is_component and peer_jid.host == client.jid.host):
360 raise ValueError(_("You can't do a jingle session with yourself")) 387 raise ValueError(_("You can't do a jingle session with yourself"))
369 "local_jid": client.jid, 396 "local_jid": client.jid,
370 "peer_jid": peer_jid, 397 "peer_jid": peer_jid,
371 "started": time.time(), 398 "started": time.time(),
372 "contents": {}, 399 "contents": {},
373 } 400 }
401
402 if not await self.host.trigger.asyncPoint(
403 "XEP-0166_initiate",
404 client, session, contents
405 ):
406 return
407
374 iq_elt, jingle_elt = self._buildJingleElt( 408 iq_elt, jingle_elt = self._buildJingleElt(
375 client, session, XEP_0166.A_SESSION_INITIATE 409 client, session, XEP_0166.A_SESSION_INITIATE
376 ) 410 )
377 jingle_elt["initiator"] = initiator.full() 411 jingle_elt["initiator"] = initiator.full()
378 412
379 contents_dict = session["contents"] 413 contents_dict = session["contents"]
380 414
381 for content in contents: 415 for content in contents:
382 # we get the application plugin 416 # we get the application plugin
383 app_ns = content["app_ns"] 417 application, app_args, app_kwargs, content_name = self.getContentData(content)
384 try:
385 application = self._applications[app_ns]
386 except KeyError:
387 raise exceptions.InternalError(
388 "No application registered for {}".format(app_ns)
389 )
390 418
391 # and the transport plugin 419 # and the transport plugin
392 transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING) 420 transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING)
393 try: 421 try:
394 transport = self._type_transports[transport_type][0] 422 transport = self._type_transports[transport_type][0]
404 "transport": transport, 432 "transport": transport,
405 "transport_data": {}, 433 "transport_data": {},
406 "creator": XEP_0166.ROLE_INITIATOR, 434 "creator": XEP_0166.ROLE_INITIATOR,
407 "senders": content.get("senders", "both"), 435 "senders": content.get("senders", "both"),
408 } 436 }
409 try: 437 if content_name in contents_dict:
410 content_name = content["name"] 438 raise exceptions.InternalError(
411 except KeyError: 439 "There is already a content with this name"
412 content_name = str(uuid.uuid4()) 440 )
413 else:
414 if content_name in contents_dict:
415 raise exceptions.InternalError(
416 "There is already a content with this name"
417 )
418 contents_dict[content_name] = content_data 441 contents_dict[content_name] = content_data
419 442
420 # we construct the content element 443 # we construct the content element
421 content_elt = jingle_elt.addElement("content") 444 content_elt = jingle_elt.addElement("content")
422 content_elt["creator"] = content_data["creator"] 445 content_elt["creator"] = content_data["creator"]
425 content_elt["senders"] = content["senders"] 448 content_elt["senders"] = content["senders"]
426 except KeyError: 449 except KeyError:
427 pass 450 pass
428 451
429 # then the description element 452 # then the description element
430 app_args = content.get("app_args", []) 453 desc_elt = await utils.asDeferred(
431 app_kwargs = content.get("app_kwargs", {}) 454 application.handler.jingleSessionInit,
432 desc_elt = yield application.handler.jingleSessionInit(
433 client, session, content_name, *app_args, **app_kwargs 455 client, session, content_name, *app_args, **app_kwargs
434 ) 456 )
435 content_elt.addChild(desc_elt) 457 content_elt.addChild(desc_elt)
436 458
437 # and the transport one 459 # and the transport one
438 transport_elt = yield transport.handler.jingleSessionInit( 460 transport_elt = await utils.asDeferred(
461 transport.handler.jingleSessionInit,
439 client, session, content_name 462 client, session, content_name
440 ) 463 )
441 content_elt.addChild(transport_elt) 464 content_elt.addChild(transport_elt)
442 465
443 try: 466 try:
444 yield iq_elt.send() 467 await iq_elt.send()
445 except Exception as e: 468 except Exception as e:
446 failure_ = failure.Failure(e) 469 failure_ = failure.Failure(e)
447 self._iqError(failure_, sid, client) 470 self._iqError(failure_, sid, client)
448 raise failure_ 471 raise failure_
449 472