comparison src/plugins/plugin_xep_0166.py @ 1556:cbfbe028d099

plugin XEP-0166, XEP-0234, XEP-0261: - transport and application data are now managed in separate dictionaries - new client.IQ method is used - new buildAction helper method for applications and transports - "role" is now stored in session_data - "senders" is now stored in content_data - plugin XEP-0166: "transport-info" action is now managed - plugin XEP-0166: application namespace and handler are now managed in a namedtuple - plugin XEP-0234: <range/> element is added by responder if not already present
author Goffi <goffi@goffi.org>
date Mon, 02 Nov 2015 22:02:41 +0100
parents 0209f8d35873
children 268fda4236ca
comparison
equal deleted inserted replaced
1555:eb8aae35085b 1556:cbfbe028d099
26 from twisted.words.protocols.jabber import jid 26 from twisted.words.protocols.jabber import jid
27 # from twisted.words.protocols import jabber 27 # from twisted.words.protocols import jabber
28 # from twisted.words.xish import domish 28 # from twisted.words.xish import domish
29 from twisted.internet import defer 29 from twisted.internet import defer
30 # from wokkel import disco, iwokkel, data_form, compat 30 # from wokkel import disco, iwokkel, data_form, compat
31 from wokkel import disco, iwokkel, compat 31 from wokkel import disco, iwokkel
32 from twisted.words.protocols.jabber import error 32 from twisted.words.protocols.jabber import error
33 from twisted.words.protocols.jabber import xmlstream 33 from twisted.words.protocols.jabber import xmlstream
34 # from sat.core import exceptions 34 # from sat.core import exceptions
35 # from sat.memory.memory import Sessions 35 # from sat.memory.memory import Sessions
36 # from uuid import uuid4 36 # from uuid import uuid4
47 NS_JINGLE = "urn:xmpp:jingle:1" 47 NS_JINGLE = "urn:xmpp:jingle:1"
48 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]' 48 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]'
49 STATE_PENDING = "PENDING" 49 STATE_PENDING = "PENDING"
50 STATE_ACTIVE = "ACTIVE" 50 STATE_ACTIVE = "ACTIVE"
51 STATE_ENDED = "ENDED" 51 STATE_ENDED = "ENDED"
52 INITIATOR = "initiator"
53 RESPONDER = "responder"
54 CONFIRM_TXT = D_("{entity} want to start a jingle session with you, do you accept ?") 52 CONFIRM_TXT = D_("{entity} want to start a jingle session with you, do you accept ?")
55 53
56 PLUGIN_INFO = { 54 PLUGIN_INFO = {
57 "name": "Jingle", 55 "name": "Jingle",
58 "import_name": "XEP-0166", 56 "import_name": "XEP-0166",
62 "handler": "yes", 60 "handler": "yes",
63 "description": _("""Implementation of Jingle""") 61 "description": _("""Implementation of Jingle""")
64 } 62 }
65 63
66 64
65 ApplicationData = namedtuple('ApplicationData', ('namespace', 'handler'))
67 TransportData = namedtuple('TransportData', ('namespace', 'handler', 'priority')) 66 TransportData = namedtuple('TransportData', ('namespace', 'handler', 'priority'))
68 67
69 68
70 class XEP_0166(object): 69 class XEP_0166(object):
70 ROLE_INITIATOR = "initiator"
71 ROLE_RESPONDER = "responder"
71 TRANSPORT_DATAGRAM='UDP' 72 TRANSPORT_DATAGRAM='UDP'
72 TRANSPORT_STREAMING='TCP' 73 TRANSPORT_STREAMING='TCP'
73 REASON_SUCCESS='success' 74 REASON_SUCCESS='success'
74 REASON_DECLINE='decline' 75 REASON_DECLINE='decline'
75 REASON_FAILED_APPLICATION='failed-application' 76 REASON_FAILED_APPLICATION='failed-application'
76 REASON_FAILED_TRANSPORT='failed-transport' 77 REASON_FAILED_TRANSPORT='failed-transport'
77 A_SESSION_INITIATE = "session-initiate" 78 A_SESSION_INITIATE = "session-initiate"
78 A_SESSION_ACCEPT = "session-accept" 79 A_SESSION_ACCEPT = "session-accept"
79 A_SESSION_TERMINATE = "session-terminate" 80 A_SESSION_TERMINATE = "session-terminate"
81 A_TRANSPORT_INFO = "transport-info"
80 # non standard actions 82 # non standard actions
81 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer 83 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer
82 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer 84 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer
83 A_ACCEPTED_ACK = "accepted-ack" # session accepted ack has been received from initiator 85 A_ACCEPTED_ACK = "accepted-ack" # session accepted ack has been received from initiator
84 A_START = "start" # application can start 86 A_START = "start" # application can start
109 log.debug(u"Jingle session id [{}] deleted".format(sid)) 111 log.debug(u"Jingle session id [{}] deleted".format(sid))
110 112
111 ## helpers methods to build stanzas ## 113 ## helpers methods to build stanzas ##
112 114
113 def _buildJingleElt(self, client, session, action): 115 def _buildJingleElt(self, client, session, action):
114 iq_elt = compat.IQ(client.xmlstream, 'set') 116 iq_elt = client.IQ('set')
115 iq_elt['from'] = client.jid.full() 117 iq_elt['from'] = client.jid.full()
116 iq_elt['to'] = session['to_jid'].full() 118 iq_elt['to'] = session['to_jid'].full()
117 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE) 119 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE)
118 jingle_elt["sid"] = session['id'] 120 jingle_elt["sid"] = session['id']
119 jingle_elt['action'] = action 121 jingle_elt['action'] = action
202 - jingleHandler(self, action, session, content_name, transport_elt, profile): 204 - jingleHandler(self, action, session, content_name, transport_elt, profile):
203 called on several action to negociate the application or transport 205 called on several action to negociate the application or transport
204 """ 206 """
205 if namespace in self._applications: 207 if namespace in self._applications:
206 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace)) 208 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace))
207 self._applications[namespace] = handler 209 self._applications[namespace] = ApplicationData(namespace=namespace, handler=handler)
210 log.debug(u"new jingle application registered")
208 211
209 def registerTransport(self, namespace, transport_type, handler, priority=0): 212 def registerTransport(self, namespace, transport_type, handler, priority=0):
210 """Register a transport plugin 213 """Register a transport plugin
211 214
212 @param namespace(unicode): the XML namespace used for this transport 215 @param namespace(unicode): the XML namespace used for this transport
221 assert transport_type in (XEP_0166.TRANSPORT_DATAGRAM, XEP_0166.TRANSPORT_STREAMING) 224 assert transport_type in (XEP_0166.TRANSPORT_DATAGRAM, XEP_0166.TRANSPORT_STREAMING)
222 if namespace in self._transports: 225 if namespace in self._transports:
223 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace)) 226 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace))
224 transport_data = TransportData(namespace=namespace, handler=handler, priority=priority) 227 transport_data = TransportData(namespace=namespace, handler=handler, priority=priority)
225 self._type_transports[transport_type].append(transport_data) 228 self._type_transports[transport_type].append(transport_data)
226 self._type_transports[transport_type].sort(key=lambda transport_data: transport_data.priority) 229 self._type_transports[transport_type].sort(key=lambda transport_data: transport_data.priority, reverse=True)
227 self._transports[namespace] = transport_data 230 self._transports[namespace] = transport_data
228 log.debug(u"new jingle transport registered") 231 log.debug(u"new jingle transport registered")
232
233 def buildAction(self, action, session, content_name, profile=C.PROF_KEY_NONE):
234 """Build an element according to requested action
235
236 @param action(unicode): a jingle action (see XEP-0166 §7.2),
237 session-* actions are not managed here
238 @param session(dict): jingle session data
239 @param content_name(unicode): name of the content terminated
240 @param profile: %(doc_profile)s
241 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <transport> or <description> element, according to action
242 """
243 client = self.host.getClient(profile)
244
245 # we first build iq, jingle and content element which are the same in every cases
246 iq_elt, jingle_elt = self._buildJingleElt(client, session, action)
247 # 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
248 content_data= session['contents'][content_name]
249 content_elt = jingle_elt.addElement('content')
250 content_elt['name'] = content_name
251 content_elt['creator'] = content_data['creator']
252
253 if action == XEP_0166.A_TRANSPORT_INFO:
254 context_elt = transport_elt = content_elt.addElement('transport', content_data['transport'].namespace)
255 transport_elt['sid'] = content_data['transport_data']['sid']
256 else:
257 raise exceptions.InternalError(u"unmanaged action {}".format(action))
258
259 return iq_elt, context_elt
229 260
230 @defer.inlineCallbacks 261 @defer.inlineCallbacks
231 def initiate(self, to_jid, contents, profile=C.PROF_KEY_NONE): 262 def initiate(self, to_jid, contents, profile=C.PROF_KEY_NONE):
232 """Send a session initiation request 263 """Send a session initiation request
233 264
237 - app_ns(unicode): namespace of the application 268 - app_ns(unicode): namespace of the application
238 the following keys are optional: 269 the following keys are optional:
239 - transport_type(unicode): type of transport to use (see XEP-0166 §8) 270 - transport_type(unicode): type of transport to use (see XEP-0166 §8)
240 default to TRANSPORT_STREAMING 271 default to TRANSPORT_STREAMING
241 - name(unicode): name of the content 272 - name(unicode): name of the content
273 - senders(unicode): One of XEP_0166.ROLE_INITIATOR, XEP_0166.ROLE_RESPONDER, both or none
274 default to BOTH (see XEP-0166 §7.3)
242 - app_args(list): args to pass to the application plugin 275 - app_args(list): args to pass to the application plugin
243 - app_kwargs(dict): keyword args to pass to the application plugin 276 - app_kwargs(dict): keyword args to pass to the application plugin
244 @param profile: %(doc_profile)s 277 @param profile: %(doc_profile)s
245 """ 278 """
246 assert contents # there must be at least one content 279 assert contents # there must be at least one content
249 sid = unicode(uuid.uuid4()) 282 sid = unicode(uuid.uuid4())
250 # TODO: session cleaning after timeout ? 283 # TODO: session cleaning after timeout ?
251 session = client.jingle_sessions[sid] = {'id': sid, 284 session = client.jingle_sessions[sid] = {'id': sid,
252 'state': STATE_PENDING, 285 'state': STATE_PENDING,
253 'initiator': initiator, 286 'initiator': initiator,
287 'role': XEP_0166.ROLE_INITIATOR,
254 'to_jid': to_jid, 288 'to_jid': to_jid,
255 'started': time.time(), 289 'started': time.time(),
256 'contents': {} 290 'contents': {}
257 } 291 }
258 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_INITIATE) 292 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_INITIATE)
262 296
263 for content in contents: 297 for content in contents:
264 # we get the application plugin 298 # we get the application plugin
265 app_ns = content['app_ns'] 299 app_ns = content['app_ns']
266 try: 300 try:
267 application_handler = self._applications[app_ns] 301 application = self._applications[app_ns]
268 except KeyError: 302 except KeyError:
269 raise exceptions.InternalError(u"No application registered for {}".format(app_ns)) 303 raise exceptions.InternalError(u"No application registered for {}".format(app_ns))
270 304
271 # and the transport plugin 305 # and the transport plugin
272 transport_type = content.get('transport_type', XEP_0166.TRANSPORT_STREAMING) 306 transport_type = content.get('transport_type', XEP_0166.TRANSPORT_STREAMING)
273 try: 307 try:
274 transport_handler = self._type_transports[transport_type][0].handler 308 transport = self._type_transports[transport_type][0]
275 except IndexError: 309 except IndexError:
276 raise exceptions.InternalError(u"No transport registered for {}".format(transport_type)) 310 raise exceptions.InternalError(u"No transport registered for {}".format(transport_type))
277 311
278 # we build the session data 312 # we build the session data
279 content_data = {'application': application_handler, 313 content_data = {'application': application,
280 'transport': transport_handler, 314 'application_data': {},
281 'creator': INITIATOR, 315 'transport': transport,
316 'transport_data': {},
317 'creator': XEP_0166.ROLE_INITIATOR,
318 'senders': content.get('senders', 'both'),
282 } 319 }
283 try: 320 try:
284 content_name = content['name'] 321 content_name = content['name']
285 except KeyError: 322 except KeyError:
286 content_name = unicode(uuid.uuid4()) 323 content_name = unicode(uuid.uuid4())
291 328
292 # we construct the content element 329 # we construct the content element
293 content_elt = jingle_elt.addElement('content') 330 content_elt = jingle_elt.addElement('content')
294 content_elt['creator'] = content_data['creator'] 331 content_elt['creator'] = content_data['creator']
295 content_elt['name'] = content_name 332 content_elt['name'] = content_name
333 try:
334 content_elt['senders'] = content['senders']
335 except KeyError:
336 pass
296 337
297 # then the description element 338 # then the description element
298 app_args = content.get('app_args', []) 339 app_args = content.get('app_args', [])
299 app_kwargs = content.get('app_kwargs', {}) 340 app_kwargs = content.get('app_kwargs', {})
300 app_kwargs['profile'] = profile 341 app_kwargs['profile'] = profile
301 desc_elt = yield application_handler.jingleSessionInit(session, content_name, *app_args, **app_kwargs) 342 desc_elt = yield application.handler.jingleSessionInit(session, content_name, *app_args, **app_kwargs)
302 content_elt.addChild(desc_elt) 343 content_elt.addChild(desc_elt)
303 344
304 # and the transport one 345 # and the transport one
305 transport_elt = yield transport_handler.jingleSessionInit(session, content_name, profile) 346 transport_elt = yield transport.handler.jingleSessionInit(session, content_name, profile)
306 content_elt.addChild(transport_elt) 347 content_elt.addChild(transport_elt)
307 348
308 d = iq_elt.send() 349 d = iq_elt.send()
309 d.addErrback(self._iqError, sid, client) 350 d.addErrback(self._iqError, sid, client)
310 yield d 351 yield d
323 if not contents: 364 if not contents:
324 self.terminate(reason, session, profile) 365 self.terminate(reason, session, profile)
325 366
326 ## defaults methods called when plugin doesn't have them ## 367 ## defaults methods called when plugin doesn't have them ##
327 368
328 def jingleRequestConfirmationDefault(self, session, desc_elt, profile): 369 def jingleRequestConfirmationDefault(self, action, session, content_name, desc_elt, profile):
329 """This method request confirmation for a jingle session""" 370 """This method request confirmation for a jingle session"""
330 log.debug(u"Using generic jingle confirmation method") 371 log.debug(u"Using generic jingle confirmation method")
331 return xml_tools.deferConfirm(self.host, _(CONFIRM_TXT).format(entity=session['to_jid'].full()), _('Confirm Jingle session'), profile=profile) 372 return xml_tools.deferConfirm(self.host, _(CONFIRM_TXT).format(entity=session['to_jid'].full()), _('Confirm Jingle session'), profile=profile)
332 373
333 ## jingle events ## 374 ## jingle events ##
371 session = client.jingle_sessions[sid] 412 session = client.jingle_sessions[sid]
372 except KeyError: 413 except KeyError:
373 session = client.jingle_sessions[sid] = {'id': sid, 414 session = client.jingle_sessions[sid] = {'id': sid,
374 'state': STATE_PENDING, 415 'state': STATE_PENDING,
375 'initiator': to_jid, 416 'initiator': to_jid,
417 'role': XEP_0166.ROLE_RESPONDER,
376 'to_jid': to_jid, 418 'to_jid': to_jid,
377 'started': time.time(), 419 'started': time.time(),
378 } 420 }
379 else: 421 else:
380 if session['to_jid'] != to_jid: 422 if session['to_jid'] != to_jid:
390 self.onSessionInitiate(client, request, jingle_elt, session) 432 self.onSessionInitiate(client, request, jingle_elt, session)
391 elif action == XEP_0166.A_SESSION_TERMINATE: 433 elif action == XEP_0166.A_SESSION_TERMINATE:
392 self.onSessionTerminate(client, request, jingle_elt, session) 434 self.onSessionTerminate(client, request, jingle_elt, session)
393 elif action == XEP_0166.A_SESSION_ACCEPT: 435 elif action == XEP_0166.A_SESSION_ACCEPT:
394 self.onSessionAccept(client, request, jingle_elt, session) 436 self.onSessionAccept(client, request, jingle_elt, session)
437 elif action == XEP_0166.A_TRANSPORT_INFO:
438 self.onTransportInfo(client, request, jingle_elt, session)
395 else: 439 else:
396 raise exceptions.InternalError(u"Unknown action {}".format(session['state'])) 440 raise exceptions.InternalError(u"Unknown action {}".format(action))
397 441
398 ## Actions callbacks ## 442 ## Actions callbacks ##
399 443
400 def _parseElements(self, jingle_elt, session, request, client, new=False, creator=INITIATOR): 444 def _parseElements(self, jingle_elt, session, request, client, new=False, creator=ROLE_INITIATOR, with_application=True, with_transport=True):
401 """Parse contents elements and fill contents_dict accordingly 445 """Parse contents elements and fill contents_dict accordingly
402 446
403 after the parsing, contents_dict will containt handlers, "desc_elt" and "transport_elt" 447 after the parsing, contents_dict will containt handlers, "desc_elt" and "transport_elt"
404 @param jingle_elt(domish.Element): parent <jingle> element, containing one or more <content> 448 @param jingle_elt(domish.Element): parent <jingle> element, containing one or more <content>
405 @param contents_dict(dict): session data for contents, the key is the name of the content 449 @param session(dict): session data
450 @param request(domish.Element): the whole request
451 @param client: %(doc_client)s
406 @param new(bool): if new the content is new and must be created, 452 @param new(bool): if new the content is new and must be created,
407 else the content must exists, and session data will be filled 453 else the content must exists, and session data will be filled
408 @param creator(unicode): only used if new is True: creating pear (see § 7.3) 454 @param creator(unicode): only used if new is True: creating pear (see § 7.3)
455 @param with_application(bool): if True, raise an error if there is no <description> element else ignore it
456 @param with_transport(bool): if True, raise an error if there is no <transport> element else ignore it
409 @raise exceptions.CancelError: the error is treated the calling method can cancel the treatment (i.e. return) 457 @raise exceptions.CancelError: the error is treated the calling method can cancel the treatment (i.e. return)
410 """ 458 """
411 contents_dict = session['contents'] 459 contents_dict = session['contents']
412 content_elts = jingle_elt.elements(NS_JINGLE, 'content') 460 content_elts = jingle_elt.elements(NS_JINGLE, 'content')
413 461
417 if new: 465 if new:
418 # the content must not exist, we check it 466 # the content must not exist, we check it
419 if not name or name in contents_dict: 467 if not name or name in contents_dict:
420 self.sendError('bad-request', session['id'], request, client.profile) 468 self.sendError('bad-request', session['id'], request, client.profile)
421 raise exceptions.CancelError 469 raise exceptions.CancelError
422 content_data = contents_dict[name] = {'creator': creator} 470 content_data = contents_dict[name] = {'creator': creator,
471 'senders': content_elt.attributes.get('senders', 'both'),
472 }
423 else: 473 else:
424 # the content must exist, we check it 474 # the content must exist, we check it
425 try: 475 try:
426 content_data = contents_dict[name] 476 content_data = contents_dict[name]
427 except KeyError: 477 except KeyError:
428 log.warning(u"Other peer try to access an unknown content") 478 log.warning(u"Other peer try to access an unknown content")
429 self.sendError('bad-request', session['id'], request, client.profile) 479 self.sendError('bad-request', session['id'], request, client.profile)
430 raise exceptions.CancelError 480 raise exceptions.CancelError
431 481
432 # application 482 # application
433 desc_elt = content_elt.description 483 if with_application:
434 if not desc_elt: 484 desc_elt = content_elt.description
435 self.sendError('bad-request', session['id'], request, client.profile) 485 if not desc_elt:
436 raise exceptions.CancelError
437
438 if new:
439 # the content is new, we need to check and link the application_handler
440 app_ns = desc_elt.uri
441 if not app_ns or app_ns == NS_JINGLE:
442 self.sendError('bad-request', session['id'], request, client.profile) 486 self.sendError('bad-request', session['id'], request, client.profile)
443 raise exceptions.CancelError 487 raise exceptions.CancelError
444 488
445 try: 489 if new:
446 application_handler = self._applications[app_ns] 490 # the content is new, we need to check and link the application
447 except KeyError: 491 app_ns = desc_elt.uri
448 log.warning(u"Unmanaged application namespace [{}]".format(app_ns)) 492 if not app_ns or app_ns == NS_JINGLE:
449 self.sendError('service-unavailable', session['id'], request, client.profile) 493 self.sendError('bad-request', session['id'], request, client.profile)
450 raise exceptions.CancelError 494 raise exceptions.CancelError
451 495
452 content_data['application'] = application_handler 496 try:
453 else: 497 application = self._applications[app_ns]
454 # the content exists, we check that we have not a former desc_elt 498 except KeyError:
455 if 'desc_elt' in content_data: 499 log.warning(u"Unmanaged application namespace [{}]".format(app_ns))
456 raise exceptions.InternalError(u"desc_elt should not exist at this point") 500 self.sendError('service-unavailable', session['id'], request, client.profile)
457 501 raise exceptions.CancelError
458 content_data['desc_elt'] = desc_elt 502
503 content_data['application'] = application
504 content_data['application_data'] = {}
505 else:
506 # the content exists, we check that we have not a former desc_elt
507 if 'desc_elt' in content_data:
508 raise exceptions.InternalError(u"desc_elt should not exist at this point")
509
510 content_data['desc_elt'] = desc_elt
459 511
460 # transport 512 # transport
461 transport_elt = content_elt.transport 513 if with_transport:
462 if not transport_elt: 514 transport_elt = content_elt.transport
463 self.sendError('bad-request', session['id'], request, client.profile) 515 if not transport_elt:
464 raise exceptions.CancelError
465
466 if new:
467 # the content is new, we need to check and link the transport_handler
468 transport_ns = transport_elt.uri
469 if not app_ns or app_ns == NS_JINGLE:
470 self.sendError('bad-request', session['id'], request, client.profile) 516 self.sendError('bad-request', session['id'], request, client.profile)
471 raise exceptions.CancelError 517 raise exceptions.CancelError
472 518
473 try: 519 if new:
474 transport_handler = self._transports[transport_ns].handler 520 # the content is new, we need to check and link the transport
475 except KeyError: 521 transport_ns = transport_elt.uri
476 raise exceptions.InternalError(u"No transport registered for namespace {}".format(transport_ns)) 522 if not app_ns or app_ns == NS_JINGLE:
477 content_data['transport'] = transport_handler 523 self.sendError('bad-request', session['id'], request, client.profile)
478 else: 524 raise exceptions.CancelError
479 # the content exists, we check that we have not a former transport_elt 525
480 if 'transport_elt' in content_data: 526 try:
481 raise exceptions.InternalError(u"desc_elt should not exist at this point") 527 transport = self._transports[transport_ns]
482 528 except KeyError:
483 content_data['transport_elt'] = transport_elt 529 raise exceptions.InternalError(u"No transport registered for namespace {}".format(transport_ns))
530 content_data['transport'] = transport
531 content_data['transport_data'] = {}
532 else:
533 # the content exists, we check that we have not a former transport_elt
534 if 'transport_elt' in content_data:
535 raise exceptions.InternalError(u"desc_elt should not exist at this point")
536
537 content_data['transport_elt'] = transport_elt
484 538
485 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): 539 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):
486 """Call application and transport plugin methods for all contents 540 """Call application and transport plugin methods for all contents
487 541
488 @param action(unicode): jingle action name 542 @param action(unicode): jingle action name
509 (app_method_name, 'application', app_default_cb, 'desc_elt'), 563 (app_method_name, 'application', app_default_cb, 'desc_elt'),
510 (transp_method_name, 'transport', transp_default_cb, 'transport_elt')): 564 (transp_method_name, 'transport', transp_default_cb, 'transport_elt')):
511 if method_name is None: 565 if method_name is None:
512 continue 566 continue
513 567
514 handler = content_data[handler_key] 568 handler = content_data[handler_key].handler
515 try: 569 try:
516 method = getattr(handler, method_name) 570 method = getattr(handler, method_name)
517 except AttributeError: 571 except AttributeError:
518 if default_cb is not None: 572 if default_cb is not None:
519 method = default_cb 573 method = default_cb
544 if 'contents' in session: 598 if 'contents' in session:
545 raise exceptions.InternalError("Contents dict should not already exist at this point") 599 raise exceptions.InternalError("Contents dict should not already exist at this point")
546 session['contents'] = contents_dict = {} 600 session['contents'] = contents_dict = {}
547 601
548 try: 602 try:
549 self._parseElements(jingle_elt, session, request, client, True, INITIATOR) 603 self._parseElements(jingle_elt, session, request, client, True, XEP_0166.ROLE_INITIATOR)
550 except exceptions.CancelError: 604 except exceptions.CancelError:
551 return 605 return
552 606
553 if not contents_dict: 607 if not contents_dict:
554 # there MUST be at least one content 608 # there MUST be at least one content
589 643
590 defers_list = [] 644 defers_list = []
591 645
592 for content_name, content_data in session['contents'].iteritems(): 646 for content_name, content_data in session['contents'].iteritems():
593 content_elt = jingle_elt.addElement('content') 647 content_elt = jingle_elt.addElement('content')
594 content_elt['creator'] = INITIATOR 648 content_elt['creator'] = XEP_0166.ROLE_INITIATOR
595 content_elt['name'] = content_name 649 content_elt['name'] = content_name
596 650
597 application_handler = content_data['application'] 651 application = content_data['application']
598 app_session_accept_cb = application_handler.jingleHandler 652 app_session_accept_cb = application.handler.jingleHandler
599 653
600 app_d = defer.maybeDeferred(app_session_accept_cb, 654 app_d = defer.maybeDeferred(app_session_accept_cb,
601 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('desc_elt'), client.profile) 655 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('desc_elt'), client.profile)
602 app_d.addCallback(addElement, content_elt) 656 app_d.addCallback(addElement, content_elt)
603 defers_list.append(app_d) 657 defers_list.append(app_d)
604 658
605 transport_handler = content_data['transport'] 659 transport = content_data['transport']
606 transport_session_accept_cb = transport_handler.jingleHandler 660 transport_session_accept_cb = transport.handler.jingleHandler
607 661
608 transport_d = defer.maybeDeferred(transport_session_accept_cb, 662 transport_d = defer.maybeDeferred(transport_session_accept_cb,
609 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('transport_elt'), client.profile) 663 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('transport_elt'), client.profile)
610 transport_d.addCallback(addElement, content_elt) 664 transport_d.addCallback(addElement, content_elt)
611 defers_list.append(transport_d) 665 defers_list.append(transport_d)
626 log.debug("Jingle Session {} terminated".format(session['id'])) 680 log.debug("Jingle Session {} terminated".format(session['id']))
627 self._delSession(client, session['id']) 681 self._delSession(client, session['id'])
628 client.xmlstream.send(xmlstream.toResponse(request, 'result')) 682 client.xmlstream.send(xmlstream.toResponse(request, 'result'))
629 683
630 def onSessionAccept(self, client, request, jingle_elt, session): 684 def onSessionAccept(self, client, request, jingle_elt, session):
631 """Method called one sesion is accepted 685 """Method called once session is accepted
632 686
633 This method is only called for initiator 687 This method is only called for initiator
634 @param client: %(doc_client)s 688 @param client: %(doc_client)s
635 @param request(domish.Element): full <iq> request 689 @param request(domish.Element): full <iq> request
636 @param jingle_elt(domish.Element): the <jingle> element 690 @param jingle_elt(domish.Element): the <jingle> element
654 negociate_dlist = defer.DeferredList(negociate_defers) 708 negociate_dlist = defer.DeferredList(negociate_defers)
655 709
656 # after negociations we start the transfer 710 # after negociations we start the transfer
657 negociate_dlist.addCallback(lambda dummy: self._callPlugins(XEP_0166.A_START, session, app_method_name=None, elements=False, profile=client.profile)) 711 negociate_dlist.addCallback(lambda dummy: self._callPlugins(XEP_0166.A_START, session, app_method_name=None, elements=False, profile=client.profile))
658 712
713 def onTransportInfo(self, client, request, jingle_elt, session):
714 """Method called when a transport-info action is received from other peer
715
716 The request is parsed, and jingleHandler is called on concerned transport plugin(s)
717 @param client: %(doc_client)s
718 @param request(domish.Element): full <iq> request
719 @param jingle_elt(domish.Element): the <jingle> element
720 @param session(dict): session data
721 """
722 log.debug(u"Jingle session {} has been accepted".format(session['id']))
723
724 try:
725 self._parseElements(jingle_elt, session, request, client, with_application=False)
726 except exceptions.CancelError:
727 return
728
729 # The parsing was OK, we send the <iq> result
730 client.xmlstream.send(xmlstream.toResponse(request, 'result'))
731
732 for content_name, content_data in session['contents'].iteritems():
733 try:
734 transport_elt = content_data.pop('transport_elt')
735 except KeyError:
736 continue
737 else:
738 content_data['transport'].handler.jingleHandler(XEP_0166.A_TRANSPORT_INFO, session, content_name, transport_elt, client.profile)
739
659 740
660 class XEP_0166_handler(xmlstream.XMPPHandler): 741 class XEP_0166_handler(xmlstream.XMPPHandler):
661 implements(iwokkel.IDisco) 742 implements(iwokkel.IDisco)
662 743
663 def __init__(self, plugin_parent): 744 def __init__(self, plugin_parent):