Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0166.py @ 1614:1ced93821c35
plugin XEP-0166: sendError now manage jingle conditions
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 17 Nov 2015 19:37:09 +0100 |
parents | 846a39900fa6 |
children | a1e5bcd9a6eb |
comparison
equal
deleted
inserted
replaced
1613:1c5761cb1bdc | 1614:1ced93821c35 |
---|---|
22 from sat.core.log import getLogger | 22 from sat.core.log import getLogger |
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.words.protocols import jabber | |
28 # from twisted.words.xish import domish | |
29 from twisted.internet import defer | 27 from twisted.internet import defer |
30 # from wokkel import disco, iwokkel, data_form, compat | 28 # from wokkel import disco, iwokkel, data_form, compat |
31 from wokkel import disco, iwokkel | 29 from wokkel import disco, iwokkel |
32 from twisted.words.protocols.jabber import error | 30 from twisted.words.protocols.jabber import error |
33 from twisted.words.protocols.jabber import xmlstream | 31 from twisted.words.protocols.jabber import xmlstream |
34 # from sat.core import exceptions | 32 from twisted.python import failure |
35 # from sat.memory.memory import Sessions | |
36 # from uuid import uuid4 | |
37 # from sat.tools import xml_tools | |
38 from collections import namedtuple | 33 from collections import namedtuple |
39 import uuid | 34 import uuid |
40 import time | 35 import time |
41 | 36 |
42 from zope.interface import implements | 37 from zope.interface import implements |
43 | 38 |
44 | 39 |
45 | 40 |
46 IQ_SET = '/iq[@type="set"]' | 41 IQ_SET = '/iq[@type="set"]' |
47 NS_JINGLE = "urn:xmpp:jingle:1" | 42 NS_JINGLE = "urn:xmpp:jingle:1" |
43 NS_JINGLE_ERROR = "urn:xmpp:jingle:errors:1" | |
48 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]' | 44 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]' |
49 STATE_PENDING = "PENDING" | 45 STATE_PENDING = "PENDING" |
50 STATE_ACTIVE = "ACTIVE" | 46 STATE_ACTIVE = "ACTIVE" |
51 STATE_ENDED = "ENDED" | 47 STATE_ENDED = "ENDED" |
52 CONFIRM_TXT = D_("{entity} want to start a jingle session with you, do you accept ?") | 48 CONFIRM_TXT = D_("{entity} want to start a jingle session with you, do you accept ?") |
119 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE) | 115 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE) |
120 jingle_elt["sid"] = session['id'] | 116 jingle_elt["sid"] = session['id'] |
121 jingle_elt['action'] = action | 117 jingle_elt['action'] = action |
122 return iq_elt, jingle_elt | 118 return iq_elt, jingle_elt |
123 | 119 |
124 def sendError(self, error_condition, sid, request, profile): | 120 def sendError(self, error_condition, sid, request, jingle_condition=None, profile=C.PROF_KEY_NONE): |
125 """Send error stanza | 121 """Send error stanza |
126 | 122 |
127 @param error_condition: one of twisted.words.protocols.jabber.error.STANZA_CONDITIONS keys | 123 @param error_condition: one of twisted.words.protocols.jabber.error.STANZA_CONDITIONS keys |
128 @param sid(unicode,None): jingle session id, or None, if session must not be destroyed | 124 @param sid(unicode,None): jingle session id, or None, if session must not be destroyed |
129 @param request(domish.Element): original request | 125 @param request(domish.Element): original request |
126 @param jingle_condition(None, unicode): if not None, additional jingle-specific error information | |
130 @param profile: %(doc_profile)s | 127 @param profile: %(doc_profile)s |
131 """ | 128 """ |
132 client = self.host.getClient(profile) | 129 client = self.host.getClient(profile) |
133 iq_elt = error.StanzaError(error_condition).toResponse(request) | 130 iq_elt = error.StanzaError(error_condition).toResponse(request) |
131 if jingle_condition is not None: | |
132 iq_elt.error.addElement((NS_JINGLE_ERROR, jingle_condition)) | |
134 if error.STANZA_CONDITIONS[error_condition]['type'] == 'cancel' and sid: | 133 if error.STANZA_CONDITIONS[error_condition]['type'] == 'cancel' and sid: |
135 self._delSession(client, sid) | 134 self._delSession(client, sid) |
136 log.warning(u"Error while managing jingle session, cancelling: {condition}".format(error_condition)) | 135 log.warning(u"Error while managing jingle session, cancelling: {condition}".format(error_condition)) |
137 client.xmlstream.send(iq_elt) | 136 client.xmlstream.send(iq_elt) |
138 | 137 |
157 d = iq_elt.send() | 156 d = iq_elt.send() |
158 return d | 157 return d |
159 | 158 |
160 ## errors which doesn't imply a stanza sending ## | 159 ## errors which doesn't imply a stanza sending ## |
161 | 160 |
162 def _iqError(self, failure, sid, client): | 161 def _iqError(self, failure_, sid, client): |
163 """Called when we got an <iq/> error | 162 """Called when we got an <iq/> error |
164 | 163 |
165 @param failure(failure.Failure): the exceptions raised | 164 @param failure_(failure.Failure): the exceptions raised |
166 @param sid(unicode): jingle session id | 165 @param sid(unicode): jingle session id |
167 @param profile: %(doc_client)s | 166 @param profile: %(doc_client)s |
168 """ | 167 """ |
169 log.warning(u"Error while sending jingle <iq/> stanza: {failure}".format(failure=failure.value)) | 168 log.warning(u"Error while sending jingle <iq/> stanza: {failure_}".format(failure_=failure_.value)) |
170 self._delSession(client, sid) | 169 self._delSession(client, sid) |
171 | 170 |
172 def _jingleErrorCb(self, fail, sid, request, client): | 171 def _jingleErrorCb(self, fail, sid, request, client): |
173 """Called when something is going wrong while parsing jingle request | 172 """Called when something is going wrong while parsing jingle request |
174 | 173 |
179 @param request(domsih.Element): jingle request | 178 @param request(domsih.Element): jingle request |
180 @param client: %(doc_client)s | 179 @param client: %(doc_client)s |
181 """ | 180 """ |
182 log.warning("Error while processing jingle request") | 181 log.warning("Error while processing jingle request") |
183 if isinstance(fail, exceptions.DataError): | 182 if isinstance(fail, exceptions.DataError): |
184 self.sendError('bad-request', sid, request, client.profile) | 183 self.sendError('bad-request', sid, request, profile=client.profile) |
185 else: | 184 else: |
186 log.error("Unmanaged jingle exception") | 185 log.error("Unmanaged jingle exception") |
187 self._delSession(client, sid) | 186 self._delSession(client, sid) |
188 raise fail | 187 raise fail |
189 | 188 |
234 """Build an element according to requested action | 233 """Build an element according to requested action |
235 | 234 |
236 @param action(unicode): a jingle action (see XEP-0166 §7.2), | 235 @param action(unicode): a jingle action (see XEP-0166 §7.2), |
237 session-* actions are not managed here | 236 session-* actions are not managed here |
238 @param session(dict): jingle session data | 237 @param session(dict): jingle session data |
239 @param content_name(unicode): name of the content terminated | 238 @param content_name(unicode): name of the content |
240 @param profile: %(doc_profile)s | 239 @param profile: %(doc_profile)s |
241 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <transport> or <description> element, according to action | 240 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <transport> or <description> element, according to action |
242 """ | 241 """ |
243 client = self.host.getClient(profile) | 242 client = self.host.getClient(profile) |
244 | 243 |
391 sid = jingle_elt['sid'] | 390 sid = jingle_elt['sid'] |
392 if not sid: | 391 if not sid: |
393 raise KeyError | 392 raise KeyError |
394 except KeyError: | 393 except KeyError: |
395 log.warning(u"Received jingle request has no sid attribute") | 394 log.warning(u"Received jingle request has no sid attribute") |
396 self.sendError('bad-request', None, request, profile) | 395 self.sendError('bad-request', None, request, profile=profile) |
397 return | 396 return |
398 | 397 |
399 # then the action | 398 # then the action |
400 try: | 399 try: |
401 action = jingle_elt['action'] | 400 action = jingle_elt['action'] |
402 if not action: | 401 if not action: |
403 raise KeyError | 402 raise KeyError |
404 except KeyError: | 403 except KeyError: |
405 log.warning(u"Received jingle request has no action") | 404 log.warning(u"Received jingle request has no action") |
406 self.sendError('bad-request', None, request, profile) | 405 self.sendError('bad-request', None, request, profile=profile) |
407 return | 406 return |
408 | 407 |
409 peer_jid = jid.JID(request['from']) | 408 peer_jid = jid.JID(request['from']) |
410 | 409 |
411 # we get or create the session | 410 # we get or create the session |
420 'started': time.time(), | 419 'started': time.time(), |
421 } | 420 } |
422 else: | 421 else: |
423 if session['peer_jid'] != peer_jid: | 422 if session['peer_jid'] != peer_jid: |
424 log.warning(u"sid conflict ({}), the jid doesn't match. Can be a collision, a hack attempt, or a bad sid generation".format(sid)) | 423 log.warning(u"sid conflict ({}), the jid doesn't match. Can be a collision, a hack attempt, or a bad sid generation".format(sid)) |
425 self.sendError('service-unavailable', sid, request, profile) | 424 self.sendError('service-unavailable', sid, request, profile=profile) |
426 return | 425 return |
427 if session['id'] != sid: | 426 if session['id'] != sid: |
428 log.error(u"session id doesn't match") | 427 log.error(u"session id doesn't match") |
429 self.sendError('service-unavailable', sid, request, profile) | 428 self.sendError('service-unavailable', sid, request, profile=profile) |
430 raise exceptions.InternalError | 429 raise exceptions.InternalError |
431 | 430 |
432 if action == XEP_0166.A_SESSION_INITIATE: | 431 if action == XEP_0166.A_SESSION_INITIATE: |
433 self.onSessionInitiate(client, request, jingle_elt, session) | 432 self.onSessionInitiate(client, request, jingle_elt, session) |
434 elif action == XEP_0166.A_SESSION_TERMINATE: | 433 elif action == XEP_0166.A_SESSION_TERMINATE: |
464 name = content_elt['name'] | 463 name = content_elt['name'] |
465 | 464 |
466 if new: | 465 if new: |
467 # the content must not exist, we check it | 466 # the content must not exist, we check it |
468 if not name or name in contents_dict: | 467 if not name or name in contents_dict: |
469 self.sendError('bad-request', session['id'], request, client.profile) | 468 self.sendError('bad-request', session['id'], request, profile=client.profile) |
470 raise exceptions.CancelError | 469 raise exceptions.CancelError |
471 content_data = contents_dict[name] = {'creator': creator, | 470 content_data = contents_dict[name] = {'creator': creator, |
472 'senders': content_elt.attributes.get('senders', 'both'), | 471 'senders': content_elt.attributes.get('senders', 'both'), |
473 } | 472 } |
474 else: | 473 else: |
475 # the content must exist, we check it | 474 # the content must exist, we check it |
476 try: | 475 try: |
477 content_data = contents_dict[name] | 476 content_data = contents_dict[name] |
478 except KeyError: | 477 except KeyError: |
479 log.warning(u"Other peer try to access an unknown content") | 478 log.warning(u"Other peer try to access an unknown content") |
480 self.sendError('bad-request', session['id'], request, client.profile) | 479 self.sendError('bad-request', session['id'], request, profile=client.profile) |
481 raise exceptions.CancelError | 480 raise exceptions.CancelError |
482 | 481 |
483 # application | 482 # application |
484 if with_application: | 483 if with_application: |
485 desc_elt = content_elt.description | 484 desc_elt = content_elt.description |
486 if not desc_elt: | 485 if not desc_elt: |
487 self.sendError('bad-request', session['id'], request, client.profile) | 486 self.sendError('bad-request', session['id'], request, profile=client.profile) |
488 raise exceptions.CancelError | 487 raise exceptions.CancelError |
489 | 488 |
490 if new: | 489 if new: |
491 # the content is new, we need to check and link the application | 490 # the content is new, we need to check and link the application |
492 app_ns = desc_elt.uri | 491 app_ns = desc_elt.uri |
493 if not app_ns or app_ns == NS_JINGLE: | 492 if not app_ns or app_ns == NS_JINGLE: |
494 self.sendError('bad-request', session['id'], request, client.profile) | 493 self.sendError('bad-request', session['id'], request, profile=client.profile) |
495 raise exceptions.CancelError | 494 raise exceptions.CancelError |
496 | 495 |
497 try: | 496 try: |
498 application = self._applications[app_ns] | 497 application = self._applications[app_ns] |
499 except KeyError: | 498 except KeyError: |
500 log.warning(u"Unmanaged application namespace [{}]".format(app_ns)) | 499 log.warning(u"Unmanaged application namespace [{}]".format(app_ns)) |
501 self.sendError('service-unavailable', session['id'], request, client.profile) | 500 self.sendError('service-unavailable', session['id'], request, profile=client.profile) |
502 raise exceptions.CancelError | 501 raise exceptions.CancelError |
503 | 502 |
504 content_data['application'] = application | 503 content_data['application'] = application |
505 content_data['application_data'] = {} | 504 content_data['application_data'] = {} |
506 else: | 505 else: |
512 | 511 |
513 # transport | 512 # transport |
514 if with_transport: | 513 if with_transport: |
515 transport_elt = content_elt.transport | 514 transport_elt = content_elt.transport |
516 if not transport_elt: | 515 if not transport_elt: |
517 self.sendError('bad-request', session['id'], request, client.profile) | 516 self.sendError('bad-request', session['id'], request, profile=client.profile) |
518 raise exceptions.CancelError | 517 raise exceptions.CancelError |
519 | 518 |
520 if new: | 519 if new: |
521 # the content is new, we need to check and link the transport | 520 # the content is new, we need to check and link the transport |
522 transport_ns = transport_elt.uri | 521 transport_ns = transport_elt.uri |
523 if not app_ns or app_ns == NS_JINGLE: | 522 if not app_ns or app_ns == NS_JINGLE: |
524 self.sendError('bad-request', session['id'], request, client.profile) | 523 self.sendError('bad-request', session['id'], request, profile=client.profile) |
525 raise exceptions.CancelError | 524 raise exceptions.CancelError |
526 | 525 |
527 try: | 526 try: |
528 transport = self._transports[transport_ns] | 527 transport = self._transports[transport_ns] |
529 except KeyError: | 528 except KeyError: |
605 except exceptions.CancelError: | 604 except exceptions.CancelError: |
606 return | 605 return |
607 | 606 |
608 if not contents_dict: | 607 if not contents_dict: |
609 # there MUST be at least one content | 608 # there MUST be at least one content |
610 self.sendError('bad-request', session['id'], request, client.profile) | 609 self.sendError('bad-request', session['id'], request, profile=client.profile) |
611 return | 610 return |
612 | 611 |
613 # at this point we can send the <iq/> result to confirm reception of the request | 612 # at this point we can send the <iq/> result to confirm reception of the request |
614 client.xmlstream.send(xmlstream.toResponse(request, 'result')) | 613 client.xmlstream.send(xmlstream.toResponse(request, 'result')) |
615 | 614 |