comparison src/plugins/plugin_xep_0166.py @ 1616:1e05b776a55b

plugin XEP-0166: session-info action handling
author Goffi <goffi@goffi.org>
date Tue, 17 Nov 2015 19:40:37 +0100
parents a1e5bcd9a6eb
children d05f9179fe22
comparison
equal deleted inserted replaced
1615:a1e5bcd9a6eb 1616:1e05b776a55b
23 from sat.tools import xml_tools 23 from sat.tools import xml_tools
24 log = getLogger(__name__) 24 log = getLogger(__name__)
25 from sat.core import exceptions 25 from sat.core import exceptions
26 from twisted.words.protocols.jabber import jid 26 from twisted.words.protocols.jabber import jid
27 from twisted.internet import defer 27 from twisted.internet import defer
28 # from wokkel import disco, iwokkel, data_form, compat
29 from wokkel import disco, iwokkel 28 from wokkel import disco, iwokkel
30 from twisted.words.protocols.jabber import error 29 from twisted.words.protocols.jabber import error
31 from twisted.words.protocols.jabber import xmlstream 30 from twisted.words.protocols.jabber import xmlstream
32 from twisted.python import failure 31 from twisted.python import failure
33 from collections import namedtuple 32 from collections import namedtuple
72 REASON_FAILED_APPLICATION='failed-application' 71 REASON_FAILED_APPLICATION='failed-application'
73 REASON_FAILED_TRANSPORT='failed-transport' 72 REASON_FAILED_TRANSPORT='failed-transport'
74 A_SESSION_INITIATE = "session-initiate" 73 A_SESSION_INITIATE = "session-initiate"
75 A_SESSION_ACCEPT = "session-accept" 74 A_SESSION_ACCEPT = "session-accept"
76 A_SESSION_TERMINATE = "session-terminate" 75 A_SESSION_TERMINATE = "session-terminate"
76 A_SESSION_INFO = "session-info"
77 A_TRANSPORT_INFO = "transport-info" 77 A_TRANSPORT_INFO = "transport-info"
78 # non standard actions 78 # non standard actions
79 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer 79 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer
80 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer 80 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer
81 A_ACCEPTED_ACK = "accepted-ack" # session accepted ack has been received from initiator 81 A_ACCEPTED_ACK = "accepted-ack" # session accepted ack has been received from initiator
254 transport_elt['sid'] = content_data['transport_data']['sid'] 254 transport_elt['sid'] = content_data['transport_data']['sid']
255 else: 255 else:
256 raise exceptions.InternalError(u"unmanaged action {}".format(action)) 256 raise exceptions.InternalError(u"unmanaged action {}".format(action))
257 257
258 return iq_elt, context_elt 258 return iq_elt, context_elt
259
260 def buildSessionInfo(self, session, profile=C.PROF_KEY_NONE):
261 """Build a session-info action
262
263 @param session(dict): jingle session data
264 @param profile: %(doc_profile)s
265 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <jingle> element
266 """
267 client = self.host.getClient(profile)
268 return self._buildJingleElt(client, session, XEP_0166.A_SESSION_INFO)
259 269
260 @defer.inlineCallbacks 270 @defer.inlineCallbacks
261 def initiate(self, peer_jid, contents, profile=C.PROF_KEY_NONE): 271 def initiate(self, peer_jid, contents, profile=C.PROF_KEY_NONE):
262 """Send a session initiation request 272 """Send a session initiation request
263 273
437 self.onSessionInitiate(client, request, jingle_elt, session) 447 self.onSessionInitiate(client, request, jingle_elt, session)
438 elif action == XEP_0166.A_SESSION_TERMINATE: 448 elif action == XEP_0166.A_SESSION_TERMINATE:
439 self.onSessionTerminate(client, request, jingle_elt, session) 449 self.onSessionTerminate(client, request, jingle_elt, session)
440 elif action == XEP_0166.A_SESSION_ACCEPT: 450 elif action == XEP_0166.A_SESSION_ACCEPT:
441 self.onSessionAccept(client, request, jingle_elt, session) 451 self.onSessionAccept(client, request, jingle_elt, session)
452 elif action == XEP_0166.A_SESSION_INFO:
453 self.onSessionInfo(client, request, jingle_elt, session)
442 elif action == XEP_0166.A_TRANSPORT_INFO: 454 elif action == XEP_0166.A_TRANSPORT_INFO:
443 self.onTransportInfo(client, request, jingle_elt, session) 455 self.onTransportInfo(client, request, jingle_elt, session)
444 else: 456 else:
445 raise exceptions.InternalError(u"Unknown action {}".format(action)) 457 raise exceptions.InternalError(u"Unknown action {}".format(action))
446 458
539 if 'transport_elt' in content_data: 551 if 'transport_elt' in content_data:
540 raise exceptions.InternalError(u"desc_elt should not exist at this point") 552 raise exceptions.InternalError(u"desc_elt should not exist at this point")
541 553
542 content_data['transport_elt'] = transport_elt 554 content_data['transport_elt'] = transport_elt
543 555
544 def _callPlugins(self, action, session, app_method_name='jingleHandler', transp_method_name='jingleHandler', app_default_cb=None, transp_default_cb=None, delete=True, elements=True, profile=C.PROF_KEY_NONE): 556 def _callPlugins(self, action, session, app_method_name='jingleHandler', transp_method_name='jingleHandler',
557 app_default_cb=None, transp_default_cb=None, delete=True,
558 elements=True, force_element=None, profile=C.PROF_KEY_NONE):
545 """Call application and transport plugin methods for all contents 559 """Call application and transport plugin methods for all contents
546 560
547 @param action(unicode): jingle action name 561 @param action(unicode): jingle action name
548 @param session(dict): jingle session data 562 @param session(dict): jingle session data
549 @param app_method_name(unicode, None): name of the method to call for applications 563 @param app_method_name(unicode, None): name of the method to call for applications
555 @param transp_default_cb(callable, None): default callback to use if plugin has not transp_method_name 569 @param transp_default_cb(callable, None): default callback to use if plugin has not transp_method_name
556 None to raise an exception instead 570 None to raise an exception instead
557 @param delete(bool): if True, remove desc_elt and transport_elt from session 571 @param delete(bool): if True, remove desc_elt and transport_elt from session
558 ignored if elements is False 572 ignored if elements is False
559 @param elements(bool): True if elements(desc_elt and tranport_elt) must be managed 573 @param elements(bool): True if elements(desc_elt and tranport_elt) must be managed
560 must be True if _callPlugins is use in a request, and False if it used after a request (i.e. on <iq> result or error) 574 must be True if _callPlugins is used in a request, and False if it used after a request (i.e. on <iq> result or error)
575 @param force_element(None, domish.Element, object): if elements is None, it is used as element parameter
576 else it is ignored
561 @param profile(unicode): %(doc_profile)s 577 @param profile(unicode): %(doc_profile)s
562 @return (list[defer.Deferred]): list of launched Deferred 578 @return (list[defer.Deferred]): list of launched Deferred
579 @raise exceptions.NotFound: method is not implemented
563 """ 580 """
564 contents_dict = session['contents'] 581 contents_dict = session['contents']
565 defers_list = [] 582 defers_list = []
566 for content_name, content_data in contents_dict.iteritems(): 583 for content_name, content_data in contents_dict.iteritems():
567 for method_name, handler_key, default_cb, elt_name in ( 584 for method_name, handler_key, default_cb, elt_name in (
575 method = getattr(handler, method_name) 592 method = getattr(handler, method_name)
576 except AttributeError: 593 except AttributeError:
577 if default_cb is not None: 594 if default_cb is not None:
578 method = default_cb 595 method = default_cb
579 else: 596 else:
580 raise exceptions.InternalError(u'{} not implemented !'.format(method_name)) 597 raise exceptions.NotFound(u'{} not implemented !'.format(method_name))
581 finally: 598 if elements:
582 if elements: 599 elt = content_data.pop(elt_name) if delete else content_data[elt_name]
583 elt = content_data.pop(elt_name) if delete else content_data[elt_name] 600 else:
584 else: 601 elt = force_element
585 elt = None 602 d = defer.maybeDeferred(method, action, session, content_name, elt, profile)
586 d = defer.maybeDeferred(method, action, session, content_name, elt, profile)
587 defers_list.append(d) 603 defers_list.append(d)
588 604
589 return defers_list 605 return defers_list
590 606
591 def onSessionInitiate(self, client, request, jingle_elt, session): 607 def onSessionInitiate(self, client, request, jingle_elt, session):
713 negociate_dlist = defer.DeferredList(negociate_defers) 729 negociate_dlist = defer.DeferredList(negociate_defers)
714 730
715 # after negociations we start the transfer 731 # after negociations we start the transfer
716 negociate_dlist.addCallback(lambda dummy: self._callPlugins(XEP_0166.A_START, session, app_method_name=None, elements=False, profile=client.profile)) 732 negociate_dlist.addCallback(lambda dummy: self._callPlugins(XEP_0166.A_START, session, app_method_name=None, elements=False, profile=client.profile))
717 733
734 def _onSessionCb(self, result, client, request, jingle_elt, session):
735 client.xmlstream.send(xmlstream.toResponse(request, 'result'))
736
737 def _onSessionEb(self, failure_, client, request, jingle_elt, session):
738 log.error(u"Error while handling onSessionInfo: {}".format(failure_.value))
739 # XXX: only error managed so far, maybe some applications/transports need more
740 self.sendError('feature-not-implemented', None, request, 'unsupported-info', client.profile)
741
742 def onSessionInfo(self, client, request, jingle_elt, session):
743 """Method called when a session-info action is received from other peer
744
745 This method is only called for initiator
746 @param client: %(doc_client)s
747 @param request(domish.Element): full <iq> request
748 @param jingle_elt(domish.Element): the <jingle> element
749 @param session(dict): session data
750 """
751 if not jingle_elt.children:
752 # this is a session ping, see XEP-0166 ยง6.8
753 client.xmlstream.send(xmlstream.toResponse(request, 'result'))
754 return
755
756 try:
757 # XXX: session-info is most likely only used for application, so we don't call transport plugins
758 # if a future transport use it, this behaviour must be adapted
759 defers = self._callPlugins(XEP_0166.A_SESSION_INFO, session, 'jingleSessionInfo', None,
760 elements=False, force_element=jingle_elt, profile=client.profile)
761 except exceptions.NotFound as e:
762 self._onSessionEb(failure.Failure(e), client, request, jingle_elt, session)
763 return
764
765 dlist = defer.DeferredList(defers, fireOnOneErrback=True)
766 dlist.addCallback(self._onSessionCb, client, request, jingle_elt, session)
767 dlist.addErrback(self._onSessionCb, client, request, jingle_elt, session)
768
718 def onTransportInfo(self, client, request, jingle_elt, session): 769 def onTransportInfo(self, client, request, jingle_elt, session):
719 """Method called when a transport-info action is received from other peer 770 """Method called when a transport-info action is received from other peer
720 771
721 The request is parsed, and jingleHandler is called on concerned transport plugin(s) 772 The request is parsed, and jingleHandler is called on concerned transport plugin(s)
722 @param client: %(doc_client)s 773 @param client: %(doc_client)s