Mercurial > libervia-backend
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): |