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