comparison sat/plugins/plugin_xep_0166.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 378188abe941
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
19 19
20 from sat.core.i18n import _, D_ 20 from sat.core.i18n import _, D_
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
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
24 log = getLogger(__name__) 25 log = getLogger(__name__)
25 from sat.core import exceptions 26 from sat.core import exceptions
26 from twisted.words.protocols.jabber import jid 27 from twisted.words.protocols.jabber import jid
27 from twisted.internet import defer 28 from twisted.internet import defer
28 from twisted.internet import reactor 29 from twisted.internet import reactor
35 import time 36 import time
36 37
37 from zope.interface import implements 38 from zope.interface import implements
38 39
39 40
40
41 IQ_SET = '/iq[@type="set"]' 41 IQ_SET = '/iq[@type="set"]'
42 NS_JINGLE = "urn:xmpp:jingle:1" 42 NS_JINGLE = "urn:xmpp:jingle:1"
43 NS_JINGLE_ERROR = "urn:xmpp:jingle:errors:1" 43 NS_JINGLE_ERROR = "urn:xmpp:jingle:errors:1"
44 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]' 44 JINGLE_REQUEST = IQ_SET + '/jingle[@xmlns="' + NS_JINGLE + '"]'
45 STATE_PENDING = "PENDING" 45 STATE_PENDING = "PENDING"
53 C.PI_TYPE: "XEP", 53 C.PI_TYPE: "XEP",
54 C.PI_MODES: C.PLUG_MODE_BOTH, 54 C.PI_MODES: C.PLUG_MODE_BOTH,
55 C.PI_PROTOCOLS: ["XEP-0166"], 55 C.PI_PROTOCOLS: ["XEP-0166"],
56 C.PI_MAIN: "XEP_0166", 56 C.PI_MAIN: "XEP_0166",
57 C.PI_HANDLER: "yes", 57 C.PI_HANDLER: "yes",
58 C.PI_DESCRIPTION: _("""Implementation of Jingle""") 58 C.PI_DESCRIPTION: _("""Implementation of Jingle"""),
59 } 59 }
60 60
61 61
62 ApplicationData = namedtuple('ApplicationData', ('namespace', 'handler')) 62 ApplicationData = namedtuple("ApplicationData", ("namespace", "handler"))
63 TransportData = namedtuple('TransportData', ('namespace', 'handler', 'priority')) 63 TransportData = namedtuple("TransportData", ("namespace", "handler", "priority"))
64 64
65 65
66 class XEP_0166(object): 66 class XEP_0166(object):
67 ROLE_INITIATOR = "initiator" 67 ROLE_INITIATOR = "initiator"
68 ROLE_RESPONDER = "responder" 68 ROLE_RESPONDER = "responder"
69 TRANSPORT_DATAGRAM='UDP' 69 TRANSPORT_DATAGRAM = "UDP"
70 TRANSPORT_STREAMING='TCP' 70 TRANSPORT_STREAMING = "TCP"
71 REASON_SUCCESS='success' 71 REASON_SUCCESS = "success"
72 REASON_DECLINE='decline' 72 REASON_DECLINE = "decline"
73 REASON_FAILED_APPLICATION='failed-application' 73 REASON_FAILED_APPLICATION = "failed-application"
74 REASON_FAILED_TRANSPORT='failed-transport' 74 REASON_FAILED_TRANSPORT = "failed-transport"
75 REASON_CONNECTIVITY_ERROR='connectivity-error' 75 REASON_CONNECTIVITY_ERROR = "connectivity-error"
76 A_SESSION_INITIATE = "session-initiate" 76 A_SESSION_INITIATE = "session-initiate"
77 A_SESSION_ACCEPT = "session-accept" 77 A_SESSION_ACCEPT = "session-accept"
78 A_SESSION_TERMINATE = "session-terminate" 78 A_SESSION_TERMINATE = "session-terminate"
79 A_SESSION_INFO = "session-info" 79 A_SESSION_INFO = "session-info"
80 A_TRANSPORT_REPLACE = "transport-replace" 80 A_TRANSPORT_REPLACE = "transport-replace"
81 A_TRANSPORT_ACCEPT = "transport-accept" 81 A_TRANSPORT_ACCEPT = "transport-accept"
82 A_TRANSPORT_REJECT = "transport-reject" 82 A_TRANSPORT_REJECT = "transport-reject"
83 A_TRANSPORT_INFO = "transport-info" 83 A_TRANSPORT_INFO = "transport-info"
84 # non standard actions 84 # non standard actions
85 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer 85 A_PREPARE_INITIATOR = "prepare-initiator" # initiator must prepare tranfer
86 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer 86 A_PREPARE_RESPONDER = "prepare-responder" # responder must prepare tranfer
87 A_ACCEPTED_ACK = "accepted-ack" # session accepted ack has been received from initiator 87 A_ACCEPTED_ACK = (
88 A_START = "start" # application can start 88 "accepted-ack"
89 A_DESTROY = "destroy" # called when a transport is destroyed (e.g. because it is remplaced). Used to do cleaning operations 89 ) # session accepted ack has been received from initiator
90 A_START = "start" # application can start
91 A_DESTROY = (
92 "destroy"
93 ) # called when a transport is destroyed (e.g. because it is remplaced). Used to do cleaning operations
90 94
91 def __init__(self, host): 95 def __init__(self, host):
92 log.info(_("plugin Jingle initialization")) 96 log.info(_("plugin Jingle initialization"))
93 self.host = host 97 self.host = host
94 self._applications = {} # key: namespace, value: application data 98 self._applications = {} # key: namespace, value: application data
95 self._transports = {} # key: namespace, value: transport data 99 self._transports = {} # key: namespace, value: transport data
96 # we also keep transports by type, they are then sorted by priority 100 # we also keep transports by type, they are then sorted by priority
97 self._type_transports = { XEP_0166.TRANSPORT_DATAGRAM: [], 101 self._type_transports = {
98 XEP_0166.TRANSPORT_STREAMING: [], 102 XEP_0166.TRANSPORT_DATAGRAM: [],
99 } 103 XEP_0166.TRANSPORT_STREAMING: [],
104 }
100 105
101 def profileConnected(self, client): 106 def profileConnected(self, client):
102 client.jingle_sessions = {} # key = sid, value = session_data 107 client.jingle_sessions = {} # key = sid, value = session_data
103 108
104 def getHandler(self, client): 109 def getHandler(self, client):
113 log.debug(u"Jingle session id [{}] deleted".format(sid)) 118 log.debug(u"Jingle session id [{}] deleted".format(sid))
114 119
115 ## helpers methods to build stanzas ## 120 ## helpers methods to build stanzas ##
116 121
117 def _buildJingleElt(self, client, session, action): 122 def _buildJingleElt(self, client, session, action):
118 iq_elt = client.IQ('set') 123 iq_elt = client.IQ("set")
119 iq_elt['from'] = client.jid.full() 124 iq_elt["from"] = client.jid.full()
120 iq_elt['to'] = session['peer_jid'].full() 125 iq_elt["to"] = session["peer_jid"].full()
121 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE) 126 jingle_elt = iq_elt.addElement("jingle", NS_JINGLE)
122 jingle_elt["sid"] = session['id'] 127 jingle_elt["sid"] = session["id"]
123 jingle_elt['action'] = action 128 jingle_elt["action"] = action
124 return iq_elt, jingle_elt 129 return iq_elt, jingle_elt
125 130
126 def sendError(self, client, error_condition, sid, request, jingle_condition=None): 131 def sendError(self, client, error_condition, sid, request, jingle_condition=None):
127 """Send error stanza 132 """Send error stanza
128 133
132 @param jingle_condition(None, unicode): if not None, additional jingle-specific error information 137 @param jingle_condition(None, unicode): if not None, additional jingle-specific error information
133 """ 138 """
134 iq_elt = error.StanzaError(error_condition).toResponse(request) 139 iq_elt = error.StanzaError(error_condition).toResponse(request)
135 if jingle_condition is not None: 140 if jingle_condition is not None:
136 iq_elt.error.addElement((NS_JINGLE_ERROR, jingle_condition)) 141 iq_elt.error.addElement((NS_JINGLE_ERROR, jingle_condition))
137 if error.STANZA_CONDITIONS[error_condition]['type'] == 'cancel' and sid: 142 if error.STANZA_CONDITIONS[error_condition]["type"] == "cancel" and sid:
138 self._delSession(client, sid) 143 self._delSession(client, sid)
139 log.warning(u"Error while managing jingle session, cancelling: {condition}".format(error_condition)) 144 log.warning(
145 u"Error while managing jingle session, cancelling: {condition}".format(
146 error_condition
147 )
148 )
140 client.send(iq_elt) 149 client.send(iq_elt)
141 150
142 def _terminateEb(self, failure_): 151 def _terminateEb(self, failure_):
143 log.warning(_(u"Error while terminating session: {msg}").format(msg=failure_)) 152 log.warning(_(u"Error while terminating session: {msg}").format(msg=failure_))
144 153
148 send the session-terminate action, and delete the session data 157 send the session-terminate action, and delete the session data
149 @param reason(unicode, list[domish.Element]): if unicode, will be transformed to an element 158 @param reason(unicode, list[domish.Element]): if unicode, will be transformed to an element
150 if a list of element, add them as children of the <reason/> element 159 if a list of element, add them as children of the <reason/> element
151 @param session(dict): data of the session 160 @param session(dict): data of the session
152 """ 161 """
153 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_TERMINATE) 162 iq_elt, jingle_elt = self._buildJingleElt(
154 reason_elt = jingle_elt.addElement('reason') 163 client, session, XEP_0166.A_SESSION_TERMINATE
164 )
165 reason_elt = jingle_elt.addElement("reason")
155 if isinstance(reason, basestring): 166 if isinstance(reason, basestring):
156 reason_elt.addElement(reason) 167 reason_elt.addElement(reason)
157 else: 168 else:
158 for elt in reason: 169 for elt in reason:
159 reason_elt.addChild(elt) 170 reason_elt.addChild(elt)
160 self._delSession(client, session['id']) 171 self._delSession(client, session["id"])
161 d = iq_elt.send() 172 d = iq_elt.send()
162 d.addErrback(self._terminateEb) 173 d.addErrback(self._terminateEb)
163 return d 174 return d
164 175
165 ## errors which doesn't imply a stanza sending ## 176 ## errors which doesn't imply a stanza sending ##
168 """Called when we got an <iq/> error 179 """Called when we got an <iq/> error
169 180
170 @param failure_(failure.Failure): the exceptions raised 181 @param failure_(failure.Failure): the exceptions raised
171 @param sid(unicode): jingle session id 182 @param sid(unicode): jingle session id
172 """ 183 """
173 log.warning(u"Error while sending jingle <iq/> stanza: {failure_}".format(failure_=failure_.value)) 184 log.warning(
185 u"Error while sending jingle <iq/> stanza: {failure_}".format(
186 failure_=failure_.value
187 )
188 )
174 self._delSession(client, sid) 189 self._delSession(client, sid)
175 190
176 def _jingleErrorCb(self, fail, sid, request, client): 191 def _jingleErrorCb(self, fail, sid, request, client):
177 """Called when something is going wrong while parsing jingle request 192 """Called when something is going wrong while parsing jingle request
178 193
183 @param request(domsih.Element): jingle request 198 @param request(domsih.Element): jingle request
184 @param client: %(doc_client)s 199 @param client: %(doc_client)s
185 """ 200 """
186 log.warning("Error while processing jingle request") 201 log.warning("Error while processing jingle request")
187 if isinstance(fail, exceptions.DataError): 202 if isinstance(fail, exceptions.DataError):
188 self.sendError(client, 'bad-request', sid, request) 203 self.sendError(client, "bad-request", sid, request)
189 else: 204 else:
190 log.error("Unmanaged jingle exception") 205 log.error("Unmanaged jingle exception")
191 self._delSession(client, sid) 206 self._delSession(client, sid)
192 raise fail 207 raise fail
193 208
209 called on several action to negociate the application or transport 224 called on several action to negociate the application or transport
210 - jingleTerminate: called on session terminate, with reason_elt 225 - jingleTerminate: called on session terminate, with reason_elt
211 May be used to clean session 226 May be used to clean session
212 """ 227 """
213 if namespace in self._applications: 228 if namespace in self._applications:
214 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace)) 229 raise exceptions.ConflictError(
215 self._applications[namespace] = ApplicationData(namespace=namespace, handler=handler) 230 u"Trying to register already registered namespace {}".format(namespace)
231 )
232 self._applications[namespace] = ApplicationData(
233 namespace=namespace, handler=handler
234 )
216 log.debug(u"new jingle application registered") 235 log.debug(u"new jingle application registered")
217 236
218 def registerTransport(self, namespace, transport_type, handler, priority=0): 237 def registerTransport(self, namespace, transport_type, handler, priority=0):
219 """Register a transport plugin 238 """Register a transport plugin
220 239
225 - jingleSessionInit(client, self, session, content_name[, *args, **kwargs]): must return the domish.Element used for initial content 244 - jingleSessionInit(client, self, session, content_name[, *args, **kwargs]): must return the domish.Element used for initial content
226 - jingleHandler(client, self, action, session, content_name, transport_elt): 245 - jingleHandler(client, self, action, session, content_name, transport_elt):
227 called on several action to negociate the application or transport 246 called on several action to negociate the application or transport
228 @param priority(int): priority of this transport 247 @param priority(int): priority of this transport
229 """ 248 """
230 assert transport_type in (XEP_0166.TRANSPORT_DATAGRAM, XEP_0166.TRANSPORT_STREAMING) 249 assert transport_type in (
250 XEP_0166.TRANSPORT_DATAGRAM,
251 XEP_0166.TRANSPORT_STREAMING,
252 )
231 if namespace in self._transports: 253 if namespace in self._transports:
232 raise exceptions.ConflictError(u"Trying to register already registered namespace {}".format(namespace)) 254 raise exceptions.ConflictError(
233 transport_data = TransportData(namespace=namespace, handler=handler, priority=priority) 255 u"Trying to register already registered namespace {}".format(namespace)
256 )
257 transport_data = TransportData(
258 namespace=namespace, handler=handler, priority=priority
259 )
234 self._type_transports[transport_type].append(transport_data) 260 self._type_transports[transport_type].append(transport_data)
235 self._type_transports[transport_type].sort(key=lambda transport_data: transport_data.priority, reverse=True) 261 self._type_transports[transport_type].sort(
262 key=lambda transport_data: transport_data.priority, reverse=True
263 )
236 self._transports[namespace] = transport_data 264 self._transports[namespace] = transport_data
237 log.debug(u"new jingle transport registered") 265 log.debug(u"new jingle transport registered")
238 266
239 @defer.inlineCallbacks 267 @defer.inlineCallbacks
240 def transportReplace(self, client, transport_ns, session, content_name): 268 def transportReplace(self, client, transport_ns, session, content_name):
245 @param content_name(unicode): name of the content 273 @param content_name(unicode): name of the content
246 """ 274 """
247 # XXX: for now we replace the transport before receiving confirmation from other peer 275 # XXX: for now we replace the transport before receiving confirmation from other peer
248 # this is acceptable because we terminate the session if transport is rejected. 276 # this is acceptable because we terminate the session if transport is rejected.
249 # this behavious may change in the future. 277 # this behavious may change in the future.
250 content_data= session['contents'][content_name] 278 content_data = session["contents"][content_name]
251 transport_data = content_data['transport_data'] 279 transport_data = content_data["transport_data"]
252 try: 280 try:
253 transport = self._transports[transport_ns] 281 transport = self._transports[transport_ns]
254 except KeyError: 282 except KeyError:
255 raise exceptions.InternalError(u"Unkown transport") 283 raise exceptions.InternalError(u"Unkown transport")
256 yield content_data['transport'].handler.jingleHandler(client, XEP_0166.A_DESTROY, session, content_name, None) 284 yield content_data["transport"].handler.jingleHandler(
257 content_data['transport'] = transport 285 client, XEP_0166.A_DESTROY, session, content_name, None
286 )
287 content_data["transport"] = transport
258 transport_data.clear() 288 transport_data.clear()
259 289
260 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_TRANSPORT_REPLACE) 290 iq_elt, jingle_elt = self._buildJingleElt(
261 content_elt = jingle_elt.addElement('content') 291 client, session, XEP_0166.A_TRANSPORT_REPLACE
262 content_elt['name'] = content_name 292 )
263 content_elt['creator'] = content_data['creator'] 293 content_elt = jingle_elt.addElement("content")
294 content_elt["name"] = content_name
295 content_elt["creator"] = content_data["creator"]
264 296
265 transport_elt = transport.handler.jingleSessionInit(client, session, content_name) 297 transport_elt = transport.handler.jingleSessionInit(client, session, content_name)
266 content_elt.addChild(transport_elt) 298 content_elt.addChild(transport_elt)
267 iq_elt.send() 299 iq_elt.send()
268 300
277 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <transport> or <description> element, according to action 309 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <transport> or <description> element, according to action
278 """ 310 """
279 # we first build iq, jingle and content element which are the same in every cases 311 # we first build iq, jingle and content element which are the same in every cases
280 iq_elt, jingle_elt = self._buildJingleElt(client, session, action) 312 iq_elt, jingle_elt = self._buildJingleElt(client, session, action)
281 # 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 313 # 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
282 content_data= session['contents'][content_name] 314 content_data = session["contents"][content_name]
283 content_elt = jingle_elt.addElement('content') 315 content_elt = jingle_elt.addElement("content")
284 content_elt['name'] = content_name 316 content_elt["name"] = content_name
285 content_elt['creator'] = content_data['creator'] 317 content_elt["creator"] = content_data["creator"]
286 318
287 if action == XEP_0166.A_TRANSPORT_INFO: 319 if action == XEP_0166.A_TRANSPORT_INFO:
288 context_elt = transport_elt = content_elt.addElement('transport', content_data['transport'].namespace) 320 context_elt = transport_elt = content_elt.addElement(
289 transport_elt['sid'] = content_data['transport_data']['sid'] 321 "transport", content_data["transport"].namespace
322 )
323 transport_elt["sid"] = content_data["transport_data"]["sid"]
290 else: 324 else:
291 raise exceptions.InternalError(u"unmanaged action {}".format(action)) 325 raise exceptions.InternalError(u"unmanaged action {}".format(action))
292 326
293 return iq_elt, context_elt 327 return iq_elt, context_elt
294 328
296 """Build a session-info action 330 """Build a session-info action
297 331
298 @param session(dict): jingle session data 332 @param session(dict): jingle session data
299 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <jingle> element 333 @return (tuple[domish.Element, domish.Element]): parent <iq> element, <jingle> element
300 """ 334 """
301 return self._buildJingleElt(client, session, XEP_0166.A_SESSION_INFO) 335 return self._buildJingleElt(client, session, XEP_0166.A_SESSION_INFO)
302 336
303 @defer.inlineCallbacks 337 @defer.inlineCallbacks
304 def initiate(self, client, peer_jid, contents): 338 def initiate(self, client, peer_jid, contents):
305 """Send a session initiation request 339 """Send a session initiation request
306 340
316 default to BOTH (see XEP-0166 §7.3) 350 default to BOTH (see XEP-0166 §7.3)
317 - app_args(list): args to pass to the application plugin 351 - app_args(list): args to pass to the application plugin
318 - app_kwargs(dict): keyword args to pass to the application plugin 352 - app_kwargs(dict): keyword args to pass to the application plugin
319 @return D(unicode): jingle session id 353 @return D(unicode): jingle session id
320 """ 354 """
321 assert contents # there must be at least one content 355 assert contents # there must be at least one content
322 if peer_jid == client.jid: 356 if peer_jid == client.jid:
323 raise ValueError(_(u"You can't do a jingle session with yourself")) 357 raise ValueError(_(u"You can't do a jingle session with yourself"))
324 initiator = client.jid 358 initiator = client.jid
325 sid = unicode(uuid.uuid4()) 359 sid = unicode(uuid.uuid4())
326 # TODO: session cleaning after timeout ? 360 # TODO: session cleaning after timeout ?
327 session = client.jingle_sessions[sid] = {'id': sid, 361 session = client.jingle_sessions[sid] = {
328 'state': STATE_PENDING, 362 "id": sid,
329 'initiator': initiator, 363 "state": STATE_PENDING,
330 'role': XEP_0166.ROLE_INITIATOR, 364 "initiator": initiator,
331 'peer_jid': peer_jid, 365 "role": XEP_0166.ROLE_INITIATOR,
332 'started': time.time(), 366 "peer_jid": peer_jid,
333 'contents': {} 367 "started": time.time(),
334 } 368 "contents": {},
335 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_INITIATE) 369 }
370 iq_elt, jingle_elt = self._buildJingleElt(
371 client, session, XEP_0166.A_SESSION_INITIATE
372 )
336 jingle_elt["initiator"] = initiator.full() 373 jingle_elt["initiator"] = initiator.full()
337 374
338 contents_dict = session['contents'] 375 contents_dict = session["contents"]
339 376
340 for content in contents: 377 for content in contents:
341 # we get the application plugin 378 # we get the application plugin
342 app_ns = content['app_ns'] 379 app_ns = content["app_ns"]
343 try: 380 try:
344 application = self._applications[app_ns] 381 application = self._applications[app_ns]
345 except KeyError: 382 except KeyError:
346 raise exceptions.InternalError(u"No application registered for {}".format(app_ns)) 383 raise exceptions.InternalError(
384 u"No application registered for {}".format(app_ns)
385 )
347 386
348 # and the transport plugin 387 # and the transport plugin
349 transport_type = content.get('transport_type', XEP_0166.TRANSPORT_STREAMING) 388 transport_type = content.get("transport_type", XEP_0166.TRANSPORT_STREAMING)
350 try: 389 try:
351 transport = self._type_transports[transport_type][0] 390 transport = self._type_transports[transport_type][0]
352 except IndexError: 391 except IndexError:
353 raise exceptions.InternalError(u"No transport registered for {}".format(transport_type)) 392 raise exceptions.InternalError(
393 u"No transport registered for {}".format(transport_type)
394 )
354 395
355 # we build the session data 396 # we build the session data
356 content_data = {'application': application, 397 content_data = {
357 'application_data': {}, 398 "application": application,
358 'transport': transport, 399 "application_data": {},
359 'transport_data': {}, 400 "transport": transport,
360 'creator': XEP_0166.ROLE_INITIATOR, 401 "transport_data": {},
361 'senders': content.get('senders', 'both'), 402 "creator": XEP_0166.ROLE_INITIATOR,
362 } 403 "senders": content.get("senders", "both"),
404 }
363 try: 405 try:
364 content_name = content['name'] 406 content_name = content["name"]
365 except KeyError: 407 except KeyError:
366 content_name = unicode(uuid.uuid4()) 408 content_name = unicode(uuid.uuid4())
367 else: 409 else:
368 if content_name in contents_dict: 410 if content_name in contents_dict:
369 raise exceptions.InternalError('There is already a content with this name') 411 raise exceptions.InternalError(
412 "There is already a content with this name"
413 )
370 contents_dict[content_name] = content_data 414 contents_dict[content_name] = content_data
371 415
372 # we construct the content element 416 # we construct the content element
373 content_elt = jingle_elt.addElement('content') 417 content_elt = jingle_elt.addElement("content")
374 content_elt['creator'] = content_data['creator'] 418 content_elt["creator"] = content_data["creator"]
375 content_elt['name'] = content_name 419 content_elt["name"] = content_name
376 try: 420 try:
377 content_elt['senders'] = content['senders'] 421 content_elt["senders"] = content["senders"]
378 except KeyError: 422 except KeyError:
379 pass 423 pass
380 424
381 # then the description element 425 # then the description element
382 app_args = content.get('app_args', []) 426 app_args = content.get("app_args", [])
383 app_kwargs = content.get('app_kwargs', {}) 427 app_kwargs = content.get("app_kwargs", {})
384 desc_elt = yield application.handler.jingleSessionInit(client, session, content_name, *app_args, **app_kwargs) 428 desc_elt = yield application.handler.jingleSessionInit(
429 client, session, content_name, *app_args, **app_kwargs
430 )
385 content_elt.addChild(desc_elt) 431 content_elt.addChild(desc_elt)
386 432
387 # and the transport one 433 # and the transport one
388 transport_elt = yield transport.handler.jingleSessionInit(client, session, content_name) 434 transport_elt = yield transport.handler.jingleSessionInit(
435 client, session, content_name
436 )
389 content_elt.addChild(transport_elt) 437 content_elt.addChild(transport_elt)
390 438
391 try: 439 try:
392 yield iq_elt.send() 440 yield iq_elt.send()
393 except Exception as e: 441 except Exception as e:
408 if there is no more content, then session is terminated 456 if there is no more content, then session is terminated
409 @param session(dict): jingle session 457 @param session(dict): jingle session
410 @param content_name(unicode): name of the content terminated 458 @param content_name(unicode): name of the content terminated
411 @param reason(unicode): reason of the termination 459 @param reason(unicode): reason of the termination
412 """ 460 """
413 contents = session['contents'] 461 contents = session["contents"]
414 del contents[content_name] 462 del contents[content_name]
415 if not contents: 463 if not contents:
416 self.terminate(client, reason, session) 464 self.terminate(client, reason, session)
417 465
418 ## defaults methods called when plugin doesn't have them ## 466 ## defaults methods called when plugin doesn't have them ##
419 467
420 def jingleRequestConfirmationDefault(self, client, action, session, content_name, desc_elt): 468 def jingleRequestConfirmationDefault(
469 self, client, action, session, content_name, desc_elt
470 ):
421 """This method request confirmation for a jingle session""" 471 """This method request confirmation for a jingle session"""
422 log.debug(u"Using generic jingle confirmation method") 472 log.debug(u"Using generic jingle confirmation method")
423 return xml_tools.deferConfirm(self.host, _(CONFIRM_TXT).format(entity=session['peer_jid'].full()), _('Confirm Jingle session'), profile=client.profile) 473 return xml_tools.deferConfirm(
474 self.host,
475 _(CONFIRM_TXT).format(entity=session["peer_jid"].full()),
476 _("Confirm Jingle session"),
477 profile=client.profile,
478 )
424 479
425 ## jingle events ## 480 ## jingle events ##
426 481
427 def _onJingleRequest(self, request, client): 482 def _onJingleRequest(self, request, client):
428 """Called when any jingle request is received 483 """Called when any jingle request is received
430 The request will then be dispatched to appropriate method 485 The request will then be dispatched to appropriate method
431 according to current state 486 according to current state
432 @param request(domish.Element): received IQ request 487 @param request(domish.Element): received IQ request
433 """ 488 """
434 request.handled = True 489 request.handled = True
435 jingle_elt = request.elements(NS_JINGLE, 'jingle').next() 490 jingle_elt = request.elements(NS_JINGLE, "jingle").next()
436 491
437 # first we need the session id 492 # first we need the session id
438 try: 493 try:
439 sid = jingle_elt['sid'] 494 sid = jingle_elt["sid"]
440 if not sid: 495 if not sid:
441 raise KeyError 496 raise KeyError
442 except KeyError: 497 except KeyError:
443 log.warning(u"Received jingle request has no sid attribute") 498 log.warning(u"Received jingle request has no sid attribute")
444 self.sendError(client, 'bad-request', None, request) 499 self.sendError(client, "bad-request", None, request)
445 return 500 return
446 501
447 # then the action 502 # then the action
448 try: 503 try:
449 action = jingle_elt['action'] 504 action = jingle_elt["action"]
450 if not action: 505 if not action:
451 raise KeyError 506 raise KeyError
452 except KeyError: 507 except KeyError:
453 log.warning(u"Received jingle request has no action") 508 log.warning(u"Received jingle request has no action")
454 self.sendError(client, 'bad-request', None, request) 509 self.sendError(client, "bad-request", None, request)
455 return 510 return
456 511
457 peer_jid = jid.JID(request['from']) 512 peer_jid = jid.JID(request["from"])
458 513
459 # we get or create the session 514 # we get or create the session
460 try: 515 try:
461 session = client.jingle_sessions[sid] 516 session = client.jingle_sessions[sid]
462 except KeyError: 517 except KeyError:
463 if action == XEP_0166.A_SESSION_INITIATE: 518 if action == XEP_0166.A_SESSION_INITIATE:
464 pass 519 pass
465 elif action == XEP_0166.A_SESSION_TERMINATE: 520 elif action == XEP_0166.A_SESSION_TERMINATE:
466 log.debug(u"ignoring session terminate action (inexisting session id): {request_id} [{profile}]".format( 521 log.debug(
467 request_id=sid, 522 u"ignoring session terminate action (inexisting session id): {request_id} [{profile}]".format(
468 profile = client.profile)) 523 request_id=sid, profile=client.profile
524 )
525 )
469 return 526 return
470 else: 527 else:
471 log.warning(u"Received request for an unknown session id: {request_id} [{profile}]".format( 528 log.warning(
472 request_id=sid, 529 u"Received request for an unknown session id: {request_id} [{profile}]".format(
473 profile = client.profile)) 530 request_id=sid, profile=client.profile
474 self.sendError(client, 'item-not-found', None, request, 'unknown-session') 531 )
532 )
533 self.sendError(client, "item-not-found", None, request, "unknown-session")
475 return 534 return
476 535
477 session = client.jingle_sessions[sid] = {'id': sid, 536 session = client.jingle_sessions[sid] = {
478 'state': STATE_PENDING, 537 "id": sid,
479 'initiator': peer_jid, 538 "state": STATE_PENDING,
480 'role': XEP_0166.ROLE_RESPONDER, 539 "initiator": peer_jid,
481 'peer_jid': peer_jid, 540 "role": XEP_0166.ROLE_RESPONDER,
482 'started': time.time(), 541 "peer_jid": peer_jid,
483 } 542 "started": time.time(),
543 }
484 else: 544 else:
485 if session['peer_jid'] != peer_jid: 545 if session["peer_jid"] != peer_jid:
486 log.warning(u"sid conflict ({}), the jid doesn't match. Can be a collision, a hack attempt, or a bad sid generation".format(sid)) 546 log.warning(
487 self.sendError(client, 'service-unavailable', sid, request) 547 u"sid conflict ({}), the jid doesn't match. Can be a collision, a hack attempt, or a bad sid generation".format(
548 sid
549 )
550 )
551 self.sendError(client, "service-unavailable", sid, request)
488 return 552 return
489 if session['id'] != sid: 553 if session["id"] != sid:
490 log.error(u"session id doesn't match") 554 log.error(u"session id doesn't match")
491 self.sendError(client, 'service-unavailable', sid, request) 555 self.sendError(client, "service-unavailable", sid, request)
492 raise exceptions.InternalError 556 raise exceptions.InternalError
493 557
494 if action == XEP_0166.A_SESSION_INITIATE: 558 if action == XEP_0166.A_SESSION_INITIATE:
495 self.onSessionInitiate(client, request, jingle_elt, session) 559 self.onSessionInitiate(client, request, jingle_elt, session)
496 elif action == XEP_0166.A_SESSION_TERMINATE: 560 elif action == XEP_0166.A_SESSION_TERMINATE:
510 else: 574 else:
511 raise exceptions.InternalError(u"Unknown action {}".format(action)) 575 raise exceptions.InternalError(u"Unknown action {}".format(action))
512 576
513 ## Actions callbacks ## 577 ## Actions callbacks ##
514 578
515 def _parseElements(self, jingle_elt, session, request, client, new=False, creator=ROLE_INITIATOR, with_application=True, with_transport=True): 579 def _parseElements(
580 self,
581 jingle_elt,
582 session,
583 request,
584 client,
585 new=False,
586 creator=ROLE_INITIATOR,
587 with_application=True,
588 with_transport=True,
589 ):
516 """Parse contents elements and fill contents_dict accordingly 590 """Parse contents elements and fill contents_dict accordingly
517 591
518 after the parsing, contents_dict will containt handlers, "desc_elt" and "transport_elt" 592 after the parsing, contents_dict will containt handlers, "desc_elt" and "transport_elt"
519 @param jingle_elt(domish.Element): parent <jingle> element, containing one or more <content> 593 @param jingle_elt(domish.Element): parent <jingle> element, containing one or more <content>
520 @param session(dict): session data 594 @param session(dict): session data
525 @param creator(unicode): only used if new is True: creating pear (see § 7.3) 599 @param creator(unicode): only used if new is True: creating pear (see § 7.3)
526 @param with_application(bool): if True, raise an error if there is no <description> element else ignore it 600 @param with_application(bool): if True, raise an error if there is no <description> element else ignore it
527 @param with_transport(bool): if True, raise an error if there is no <transport> element else ignore it 601 @param with_transport(bool): if True, raise an error if there is no <transport> element else ignore it
528 @raise exceptions.CancelError: the error is treated and the calling method can cancel the treatment (i.e. return) 602 @raise exceptions.CancelError: the error is treated and the calling method can cancel the treatment (i.e. return)
529 """ 603 """
530 contents_dict = session['contents'] 604 contents_dict = session["contents"]
531 content_elts = jingle_elt.elements(NS_JINGLE, 'content') 605 content_elts = jingle_elt.elements(NS_JINGLE, "content")
532 606
533 for content_elt in content_elts: 607 for content_elt in content_elts:
534 name = content_elt['name'] 608 name = content_elt["name"]
535 609
536 if new: 610 if new:
537 # the content must not exist, we check it 611 # the content must not exist, we check it
538 if not name or name in contents_dict: 612 if not name or name in contents_dict:
539 self.sendError(client, 'bad-request', session['id'], request) 613 self.sendError(client, "bad-request", session["id"], request)
540 raise exceptions.CancelError 614 raise exceptions.CancelError
541 content_data = contents_dict[name] = {'creator': creator, 615 content_data = contents_dict[name] = {
542 'senders': content_elt.attributes.get('senders', 'both'), 616 "creator": creator,
543 } 617 "senders": content_elt.attributes.get("senders", "both"),
618 }
544 else: 619 else:
545 # the content must exist, we check it 620 # the content must exist, we check it
546 try: 621 try:
547 content_data = contents_dict[name] 622 content_data = contents_dict[name]
548 except KeyError: 623 except KeyError:
549 log.warning(u"Other peer try to access an unknown content") 624 log.warning(u"Other peer try to access an unknown content")
550 self.sendError(client, 'bad-request', session['id'], request) 625 self.sendError(client, "bad-request", session["id"], request)
551 raise exceptions.CancelError 626 raise exceptions.CancelError
552 627
553 # application 628 # application
554 if with_application: 629 if with_application:
555 desc_elt = content_elt.description 630 desc_elt = content_elt.description
556 if not desc_elt: 631 if not desc_elt:
557 self.sendError(client, 'bad-request', session['id'], request) 632 self.sendError(client, "bad-request", session["id"], request)
558 raise exceptions.CancelError 633 raise exceptions.CancelError
559 634
560 if new: 635 if new:
561 # the content is new, we need to check and link the application 636 # the content is new, we need to check and link the application
562 app_ns = desc_elt.uri 637 app_ns = desc_elt.uri
563 if not app_ns or app_ns == NS_JINGLE: 638 if not app_ns or app_ns == NS_JINGLE:
564 self.sendError(client, 'bad-request', session['id'], request) 639 self.sendError(client, "bad-request", session["id"], request)
565 raise exceptions.CancelError 640 raise exceptions.CancelError
566 641
567 try: 642 try:
568 application = self._applications[app_ns] 643 application = self._applications[app_ns]
569 except KeyError: 644 except KeyError:
570 log.warning(u"Unmanaged application namespace [{}]".format(app_ns)) 645 log.warning(
571 self.sendError(client, 'service-unavailable', session['id'], request) 646 u"Unmanaged application namespace [{}]".format(app_ns)
647 )
648 self.sendError(
649 client, "service-unavailable", session["id"], request
650 )
572 raise exceptions.CancelError 651 raise exceptions.CancelError
573 652
574 content_data['application'] = application 653 content_data["application"] = application
575 content_data['application_data'] = {} 654 content_data["application_data"] = {}
576 else: 655 else:
577 # the content exists, we check that we have not a former desc_elt 656 # the content exists, we check that we have not a former desc_elt
578 if 'desc_elt' in content_data: 657 if "desc_elt" in content_data:
579 raise exceptions.InternalError(u"desc_elt should not exist at this point") 658 raise exceptions.InternalError(
580 659 u"desc_elt should not exist at this point"
581 content_data['desc_elt'] = desc_elt 660 )
661
662 content_data["desc_elt"] = desc_elt
582 663
583 # transport 664 # transport
584 if with_transport: 665 if with_transport:
585 transport_elt = content_elt.transport 666 transport_elt = content_elt.transport
586 if not transport_elt: 667 if not transport_elt:
587 self.sendError(client, 'bad-request', session['id'], request) 668 self.sendError(client, "bad-request", session["id"], request)
588 raise exceptions.CancelError 669 raise exceptions.CancelError
589 670
590 if new: 671 if new:
591 # the content is new, we need to check and link the transport 672 # the content is new, we need to check and link the transport
592 transport_ns = transport_elt.uri 673 transport_ns = transport_elt.uri
593 if not app_ns or app_ns == NS_JINGLE: 674 if not app_ns or app_ns == NS_JINGLE:
594 self.sendError(client, 'bad-request', session['id'], request) 675 self.sendError(client, "bad-request", session["id"], request)
595 raise exceptions.CancelError 676 raise exceptions.CancelError
596 677
597 try: 678 try:
598 transport = self._transports[transport_ns] 679 transport = self._transports[transport_ns]
599 except KeyError: 680 except KeyError:
600 raise exceptions.InternalError(u"No transport registered for namespace {}".format(transport_ns)) 681 raise exceptions.InternalError(
601 content_data['transport'] = transport 682 u"No transport registered for namespace {}".format(
602 content_data['transport_data'] = {} 683 transport_ns
684 )
685 )
686 content_data["transport"] = transport
687 content_data["transport_data"] = {}
603 else: 688 else:
604 # the content exists, we check that we have not a former transport_elt 689 # the content exists, we check that we have not a former transport_elt
605 if 'transport_elt' in content_data: 690 if "transport_elt" in content_data:
606 raise exceptions.InternalError(u"transport_elt should not exist at this point") 691 raise exceptions.InternalError(
607 692 u"transport_elt should not exist at this point"
608 content_data['transport_elt'] = transport_elt 693 )
694
695 content_data["transport_elt"] = transport_elt
609 696
610 def _ignore(self, client, action, session, content_name, elt): 697 def _ignore(self, client, action, session, content_name, elt):
611 """Dummy method used when not exception must be raised if a method is not implemented in _callPlugins 698 """Dummy method used when not exception must be raised if a method is not implemented in _callPlugins
612 699
613 must be used as app_default_cb and/or transp_default_cb 700 must be used as app_default_cb and/or transp_default_cb
614 """ 701 """
615 return elt 702 return elt
616 703
617 def _callPlugins(self, client, action, session, app_method_name='jingleHandler', 704 def _callPlugins(
618 transp_method_name='jingleHandler', 705 self,
619 app_default_cb=None, transp_default_cb=None, delete=True, 706 client,
620 elements=True, force_element=None): 707 action,
708 session,
709 app_method_name="jingleHandler",
710 transp_method_name="jingleHandler",
711 app_default_cb=None,
712 transp_default_cb=None,
713 delete=True,
714 elements=True,
715 force_element=None,
716 ):
621 """Call application and transport plugin methods for all contents 717 """Call application and transport plugin methods for all contents
622 718
623 @param action(unicode): jingle action name 719 @param action(unicode): jingle action name
624 @param session(dict): jingle session data 720 @param session(dict): jingle session data
625 @param app_method_name(unicode, None): name of the method to call for applications 721 @param app_method_name(unicode, None): name of the method to call for applications
638 @param force_element(None, domish.Element, object): if elements is False, it is used as element parameter 734 @param force_element(None, domish.Element, object): if elements is False, it is used as element parameter
639 else it is ignored 735 else it is ignored
640 @return (list[defer.Deferred]): list of launched Deferred 736 @return (list[defer.Deferred]): list of launched Deferred
641 @raise exceptions.NotFound: method is not implemented 737 @raise exceptions.NotFound: method is not implemented
642 """ 738 """
643 contents_dict = session['contents'] 739 contents_dict = session["contents"]
644 defers_list = [] 740 defers_list = []
645 for content_name, content_data in contents_dict.iteritems(): 741 for content_name, content_data in contents_dict.iteritems():
646 for method_name, handler_key, default_cb, elt_name in ( 742 for method_name, handler_key, default_cb, elt_name in (
647 (app_method_name, 'application', app_default_cb, 'desc_elt'), 743 (app_method_name, "application", app_default_cb, "desc_elt"),
648 (transp_method_name, 'transport', transp_default_cb, 'transport_elt')): 744 (transp_method_name, "transport", transp_default_cb, "transport_elt"),
745 ):
649 if method_name is None: 746 if method_name is None:
650 continue 747 continue
651 748
652 handler = content_data[handler_key].handler 749 handler = content_data[handler_key].handler
653 try: 750 try:
654 method = getattr(handler, method_name) 751 method = getattr(handler, method_name)
655 except AttributeError: 752 except AttributeError:
656 if default_cb is None: 753 if default_cb is None:
657 raise exceptions.NotFound(u'{} not implemented !'.format(method_name)) 754 raise exceptions.NotFound(
755 u"{} not implemented !".format(method_name)
756 )
658 else: 757 else:
659 method = default_cb 758 method = default_cb
660 if elements: 759 if elements:
661 elt = content_data.pop(elt_name) if delete else content_data[elt_name] 760 elt = content_data.pop(elt_name) if delete else content_data[elt_name]
662 else: 761 else:
663 elt = force_element 762 elt = force_element
664 d = defer.maybeDeferred(method, client, action, session, content_name, elt) 763 d = defer.maybeDeferred(
764 method, client, action, session, content_name, elt
765 )
665 defers_list.append(d) 766 defers_list.append(d)
666 767
667 return defers_list 768 return defers_list
668 769
669 def onSessionInitiate(self, client, request, jingle_elt, session): 770 def onSessionInitiate(self, client, request, jingle_elt, session):
676 @param client: %(doc_client)s 777 @param client: %(doc_client)s
677 @param request(domish.Element): full request 778 @param request(domish.Element): full request
678 @param jingle_elt(domish.Element): <jingle> element 779 @param jingle_elt(domish.Element): <jingle> element
679 @param session(dict): session data 780 @param session(dict): session data
680 """ 781 """
681 if 'contents' in session: 782 if "contents" in session:
682 raise exceptions.InternalError("Contents dict should not already exist at this point") 783 raise exceptions.InternalError(
683 session['contents'] = contents_dict = {} 784 "Contents dict should not already exist at this point"
684 785 )
685 try: 786 session["contents"] = contents_dict = {}
686 self._parseElements(jingle_elt, session, request, client, True, XEP_0166.ROLE_INITIATOR) 787
788 try:
789 self._parseElements(
790 jingle_elt, session, request, client, True, XEP_0166.ROLE_INITIATOR
791 )
687 except exceptions.CancelError: 792 except exceptions.CancelError:
688 return 793 return
689 794
690 if not contents_dict: 795 if not contents_dict:
691 # there MUST be at least one content 796 # there MUST be at least one content
692 self.sendError(client, 'bad-request', session['id'], request) 797 self.sendError(client, "bad-request", session["id"], request)
693 return 798 return
694 799
695 # at this point we can send the <iq/> result to confirm reception of the request 800 # at this point we can send the <iq/> result to confirm reception of the request
696 client.send(xmlstream.toResponse(request, 'result')) 801 client.send(xmlstream.toResponse(request, "result"))
697 802
698 # we now request each application plugin confirmation 803 # we now request each application plugin confirmation
699 # and if all are accepted, we can accept the session 804 # and if all are accepted, we can accept the session
700 confirm_defers = self._callPlugins(client, XEP_0166.A_SESSION_INITIATE, session, 'jingleRequestConfirmation', None, self.jingleRequestConfirmationDefault, delete=False) 805 confirm_defers = self._callPlugins(
806 client,
807 XEP_0166.A_SESSION_INITIATE,
808 session,
809 "jingleRequestConfirmation",
810 None,
811 self.jingleRequestConfirmationDefault,
812 delete=False,
813 )
701 814
702 confirm_dlist = defer.gatherResults(confirm_defers) 815 confirm_dlist = defer.gatherResults(confirm_defers)
703 confirm_dlist.addCallback(self._confirmationCb, session, jingle_elt, client) 816 confirm_dlist.addCallback(self._confirmationCb, session, jingle_elt, client)
704 confirm_dlist.addErrback(self._jingleErrorCb, session['id'], request, client) 817 confirm_dlist.addErrback(self._jingleErrorCb, session["id"], request, client)
705 818
706 def _confirmationCb(self, confirm_results, session, jingle_elt, client): 819 def _confirmationCb(self, confirm_results, session, jingle_elt, client):
707 """Method called when confirmation from user has been received 820 """Method called when confirmation from user has been received
708 821
709 This method is only called for the responder 822 This method is only called for the responder
714 """ 827 """
715 confirmed = all(confirm_results) 828 confirmed = all(confirm_results)
716 if not confirmed: 829 if not confirmed:
717 return self.terminate(client, XEP_0166.REASON_DECLINE, session) 830 return self.terminate(client, XEP_0166.REASON_DECLINE, session)
718 831
719 iq_elt, jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_SESSION_ACCEPT) 832 iq_elt, jingle_elt = self._buildJingleElt(
720 jingle_elt['responder'] = client.jid.full() 833 client, session, XEP_0166.A_SESSION_ACCEPT
834 )
835 jingle_elt["responder"] = client.jid.full()
721 836
722 # contents 837 # contents
723 838
724 def addElement(domish_elt, content_elt): 839 def addElement(domish_elt, content_elt):
725 content_elt.addChild(domish_elt) 840 content_elt.addChild(domish_elt)
726 841
727 defers_list = [] 842 defers_list = []
728 843
729 for content_name, content_data in session['contents'].iteritems(): 844 for content_name, content_data in session["contents"].iteritems():
730 content_elt = jingle_elt.addElement('content') 845 content_elt = jingle_elt.addElement("content")
731 content_elt['creator'] = XEP_0166.ROLE_INITIATOR 846 content_elt["creator"] = XEP_0166.ROLE_INITIATOR
732 content_elt['name'] = content_name 847 content_elt["name"] = content_name
733 848
734 application = content_data['application'] 849 application = content_data["application"]
735 app_session_accept_cb = application.handler.jingleHandler 850 app_session_accept_cb = application.handler.jingleHandler
736 851
737 app_d = defer.maybeDeferred(app_session_accept_cb, client, 852 app_d = defer.maybeDeferred(
738 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('desc_elt')) 853 app_session_accept_cb,
854 client,
855 XEP_0166.A_SESSION_INITIATE,
856 session,
857 content_name,
858 content_data.pop("desc_elt"),
859 )
739 app_d.addCallback(addElement, content_elt) 860 app_d.addCallback(addElement, content_elt)
740 defers_list.append(app_d) 861 defers_list.append(app_d)
741 862
742 transport = content_data['transport'] 863 transport = content_data["transport"]
743 transport_session_accept_cb = transport.handler.jingleHandler 864 transport_session_accept_cb = transport.handler.jingleHandler
744 865
745 transport_d = defer.maybeDeferred(transport_session_accept_cb, client, 866 transport_d = defer.maybeDeferred(
746 XEP_0166.A_SESSION_INITIATE, session, content_name, content_data.pop('transport_elt')) 867 transport_session_accept_cb,
868 client,
869 XEP_0166.A_SESSION_INITIATE,
870 session,
871 content_name,
872 content_data.pop("transport_elt"),
873 )
747 transport_d.addCallback(addElement, content_elt) 874 transport_d.addCallback(addElement, content_elt)
748 defers_list.append(transport_d) 875 defers_list.append(transport_d)
749 876
750 d_list = defer.DeferredList(defers_list) 877 d_list = defer.DeferredList(defers_list)
751 d_list.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_PREPARE_RESPONDER, session, app_method_name=None, elements=False)) 878 d_list.addCallback(
879 lambda dummy: self._callPlugins(
880 client,
881 XEP_0166.A_PREPARE_RESPONDER,
882 session,
883 app_method_name=None,
884 elements=False,
885 )
886 )
752 d_list.addCallback(lambda dummy: iq_elt.send()) 887 d_list.addCallback(lambda dummy: iq_elt.send())
888
753 def changeState(dummy, session): 889 def changeState(dummy, session):
754 session['state'] = STATE_ACTIVE 890 session["state"] = STATE_ACTIVE
755 891
756 d_list.addCallback(changeState, session) 892 d_list.addCallback(changeState, session)
757 d_list.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_ACCEPTED_ACK, session, elements=False)) 893 d_list.addCallback(
758 d_list.addErrback(self._iqError, session['id'], client) 894 lambda dummy: self._callPlugins(
895 client, XEP_0166.A_ACCEPTED_ACK, session, elements=False
896 )
897 )
898 d_list.addErrback(self._iqError, session["id"], client)
759 return d_list 899 return d_list
760 900
761 def onSessionTerminate(self, client, request, jingle_elt, session): 901 def onSessionTerminate(self, client, request, jingle_elt, session):
762 # TODO: check reason, display a message to user if needed 902 # TODO: check reason, display a message to user if needed
763 log.debug("Jingle Session {} terminated".format(session['id'])) 903 log.debug("Jingle Session {} terminated".format(session["id"]))
764 try: 904 try:
765 reason_elt = jingle_elt.elements(NS_JINGLE, 'reason').next() 905 reason_elt = jingle_elt.elements(NS_JINGLE, "reason").next()
766 except StopIteration: 906 except StopIteration:
767 log.warning(u"No reason given for session termination") 907 log.warning(u"No reason given for session termination")
768 reason_elt = jingle_elt.addElement('reason') 908 reason_elt = jingle_elt.addElement("reason")
769 909
770 terminate_defers = self._callPlugins(client, XEP_0166.A_SESSION_TERMINATE, session, 'jingleTerminate', 'jingleTerminate', self._ignore, self._ignore, elements=False, force_element=reason_elt) 910 terminate_defers = self._callPlugins(
911 client,
912 XEP_0166.A_SESSION_TERMINATE,
913 session,
914 "jingleTerminate",
915 "jingleTerminate",
916 self._ignore,
917 self._ignore,
918 elements=False,
919 force_element=reason_elt,
920 )
771 terminate_dlist = defer.DeferredList(terminate_defers) 921 terminate_dlist = defer.DeferredList(terminate_defers)
772 922
773 terminate_dlist.addCallback(lambda dummy: self._delSession(client, session['id'])) 923 terminate_dlist.addCallback(lambda dummy: self._delSession(client, session["id"]))
774 client.send(xmlstream.toResponse(request, 'result')) 924 client.send(xmlstream.toResponse(request, "result"))
775 925
776 def onSessionAccept(self, client, request, jingle_elt, session): 926 def onSessionAccept(self, client, request, jingle_elt, session):
777 """Method called once session is accepted 927 """Method called once session is accepted
778 928
779 This method is only called for initiator 929 This method is only called for initiator
780 @param client: %(doc_client)s 930 @param client: %(doc_client)s
781 @param request(domish.Element): full <iq> request 931 @param request(domish.Element): full <iq> request
782 @param jingle_elt(domish.Element): the <jingle> element 932 @param jingle_elt(domish.Element): the <jingle> element
783 @param session(dict): session data 933 @param session(dict): session data
784 """ 934 """
785 log.debug(u"Jingle session {} has been accepted".format(session['id'])) 935 log.debug(u"Jingle session {} has been accepted".format(session["id"]))
786 936
787 try: 937 try:
788 self._parseElements(jingle_elt, session, request, client) 938 self._parseElements(jingle_elt, session, request, client)
789 except exceptions.CancelError: 939 except exceptions.CancelError:
790 return 940 return
791 941
792 # at this point we can send the <iq/> result to confirm reception of the request 942 # at this point we can send the <iq/> result to confirm reception of the request
793 client.send(xmlstream.toResponse(request, 'result')) 943 client.send(xmlstream.toResponse(request, "result"))
794 # and change the state 944 # and change the state
795 session['state'] = STATE_ACTIVE 945 session["state"] = STATE_ACTIVE
796 946
797 negociate_defers = [] 947 negociate_defers = []
798 negociate_defers = self._callPlugins(client, XEP_0166.A_SESSION_ACCEPT, session) 948 negociate_defers = self._callPlugins(client, XEP_0166.A_SESSION_ACCEPT, session)
799 949
800 negociate_dlist = defer.DeferredList(negociate_defers) 950 negociate_dlist = defer.DeferredList(negociate_defers)
801 951
802 # after negociations we start the transfer 952 # after negociations we start the transfer
803 negociate_dlist.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_START, session, app_method_name=None, elements=False)) 953 negociate_dlist.addCallback(
954 lambda dummy: self._callPlugins(
955 client, XEP_0166.A_START, session, app_method_name=None, elements=False
956 )
957 )
804 958
805 def _onSessionCb(self, result, client, request, jingle_elt, session): 959 def _onSessionCb(self, result, client, request, jingle_elt, session):
806 client.send(xmlstream.toResponse(request, 'result')) 960 client.send(xmlstream.toResponse(request, "result"))
807 961
808 def _onSessionEb(self, failure_, client, request, jingle_elt, session): 962 def _onSessionEb(self, failure_, client, request, jingle_elt, session):
809 log.error(u"Error while handling onSessionInfo: {}".format(failure_.value)) 963 log.error(u"Error while handling onSessionInfo: {}".format(failure_.value))
810 # XXX: only error managed so far, maybe some applications/transports need more 964 # XXX: only error managed so far, maybe some applications/transports need more
811 self.sendError(client, 'feature-not-implemented', None, request, 'unsupported-info') 965 self.sendError(
966 client, "feature-not-implemented", None, request, "unsupported-info"
967 )
812 968
813 def onSessionInfo(self, client, request, jingle_elt, session): 969 def onSessionInfo(self, client, request, jingle_elt, session):
814 """Method called when a session-info action is received from other peer 970 """Method called when a session-info action is received from other peer
815 971
816 This method is only called for initiator 972 This method is only called for initiator
819 @param jingle_elt(domish.Element): the <jingle> element 975 @param jingle_elt(domish.Element): the <jingle> element
820 @param session(dict): session data 976 @param session(dict): session data
821 """ 977 """
822 if not jingle_elt.children: 978 if not jingle_elt.children:
823 # this is a session ping, see XEP-0166 §6.8 979 # this is a session ping, see XEP-0166 §6.8
824 client.send(xmlstream.toResponse(request, 'result')) 980 client.send(xmlstream.toResponse(request, "result"))
825 return 981 return
826 982
827 try: 983 try:
828 # XXX: session-info is most likely only used for application, so we don't call transport plugins 984 # XXX: session-info is most likely only used for application, so we don't call transport plugins
829 # if a future transport use it, this behaviour must be adapted 985 # if a future transport use it, this behaviour must be adapted
830 defers = self._callPlugins(client, XEP_0166.A_SESSION_INFO, session, 'jingleSessionInfo', None, 986 defers = self._callPlugins(
831 elements=False, force_element=jingle_elt) 987 client,
988 XEP_0166.A_SESSION_INFO,
989 session,
990 "jingleSessionInfo",
991 None,
992 elements=False,
993 force_element=jingle_elt,
994 )
832 except exceptions.NotFound as e: 995 except exceptions.NotFound as e:
833 self._onSessionEb(failure.Failure(e), client, request, jingle_elt, session) 996 self._onSessionEb(failure.Failure(e), client, request, jingle_elt, session)
834 return 997 return
835 998
836 dlist = defer.DeferredList(defers, fireOnOneErrback=True) 999 dlist = defer.DeferredList(defers, fireOnOneErrback=True)
847 @param jingle_elt(domish.Element): the <jingle> element 1010 @param jingle_elt(domish.Element): the <jingle> element
848 @param session(dict): session data 1011 @param session(dict): session data
849 """ 1012 """
850 log.debug(u"Other peer wants to replace the transport") 1013 log.debug(u"Other peer wants to replace the transport")
851 try: 1014 try:
852 self._parseElements(jingle_elt, session, request, client, with_application=False) 1015 self._parseElements(
1016 jingle_elt, session, request, client, with_application=False
1017 )
853 except exceptions.CancelError: 1018 except exceptions.CancelError:
854 defer.returnValue(None) 1019 defer.returnValue(None)
855 1020
856 client.send(xmlstream.toResponse(request, 'result')) 1021 client.send(xmlstream.toResponse(request, "result"))
857 1022
858 content_name = None 1023 content_name = None
859 to_replace = [] 1024 to_replace = []
860 1025
861 for content_name, content_data in session['contents'].iteritems(): 1026 for content_name, content_data in session["contents"].iteritems():
862 try: 1027 try:
863 transport_elt = content_data.pop('transport_elt') 1028 transport_elt = content_data.pop("transport_elt")
864 except KeyError: 1029 except KeyError:
865 continue 1030 continue
866 transport_ns = transport_elt.uri 1031 transport_ns = transport_elt.uri
867 try: 1032 try:
868 transport = self._transports[transport_ns] 1033 transport = self._transports[transport_ns]
869 except KeyError: 1034 except KeyError:
870 log.warning(u"Other peer want to replace current transport with an unknown one: {}".format(transport_ns)) 1035 log.warning(
1036 u"Other peer want to replace current transport with an unknown one: {}".format(
1037 transport_ns
1038 )
1039 )
871 content_name = None 1040 content_name = None
872 break 1041 break
873 to_replace.append((content_name, content_data, transport, transport_elt)) 1042 to_replace.append((content_name, content_data, transport, transport_elt))
874 1043
875 if content_name is None: 1044 if content_name is None:
876 # wa can't accept the replacement 1045 # wa can't accept the replacement
877 iq_elt, reject_jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_TRANSPORT_REJECT) 1046 iq_elt, reject_jingle_elt = self._buildJingleElt(
1047 client, session, XEP_0166.A_TRANSPORT_REJECT
1048 )
878 for child in jingle_elt.children: 1049 for child in jingle_elt.children:
879 reject_jingle_elt.addChild(child) 1050 reject_jingle_elt.addChild(child)
880 1051
881 iq_elt.send() 1052 iq_elt.send()
882 defer.returnValue(None) 1053 defer.returnValue(None)
883 1054
884 # at this point, everything is alright and we can replace the transport(s) 1055 # at this point, everything is alright and we can replace the transport(s)
885 # this is similar to an session-accept action, but for transports only 1056 # this is similar to an session-accept action, but for transports only
886 iq_elt, accept_jingle_elt = self._buildJingleElt(client, session, XEP_0166.A_TRANSPORT_ACCEPT) 1057 iq_elt, accept_jingle_elt = self._buildJingleElt(
1058 client, session, XEP_0166.A_TRANSPORT_ACCEPT
1059 )
887 for content_name, content_data, transport, transport_elt in to_replace: 1060 for content_name, content_data, transport, transport_elt in to_replace:
888 # we can now actually replace the transport 1061 # we can now actually replace the transport
889 yield content_data['transport'].handler.jingleHandler(client, XEP_0166.A_DESTROY, session, content_name, None) 1062 yield content_data["transport"].handler.jingleHandler(
890 content_data['transport'] = transport 1063 client, XEP_0166.A_DESTROY, session, content_name, None
891 content_data['transport_data'].clear() 1064 )
1065 content_data["transport"] = transport
1066 content_data["transport_data"].clear()
892 # and build the element 1067 # and build the element
893 content_elt = accept_jingle_elt.addElement('content') 1068 content_elt = accept_jingle_elt.addElement("content")
894 content_elt['name'] = content_name 1069 content_elt["name"] = content_name
895 content_elt['creator'] = content_data['creator'] 1070 content_elt["creator"] = content_data["creator"]
896 # we notify the transport and insert its <transport/> in the answer 1071 # we notify the transport and insert its <transport/> in the answer
897 accept_transport_elt = yield transport.handler.jingleHandler(client, XEP_0166.A_TRANSPORT_REPLACE, session, content_name, transport_elt) 1072 accept_transport_elt = yield transport.handler.jingleHandler(
1073 client, XEP_0166.A_TRANSPORT_REPLACE, session, content_name, transport_elt
1074 )
898 content_elt.addChild(accept_transport_elt) 1075 content_elt.addChild(accept_transport_elt)
899 # there is no confirmation needed here, so we can directly prepare it 1076 # there is no confirmation needed here, so we can directly prepare it
900 yield transport.handler.jingleHandler(client, XEP_0166.A_PREPARE_RESPONDER, session, content_name, None) 1077 yield transport.handler.jingleHandler(
1078 client, XEP_0166.A_PREPARE_RESPONDER, session, content_name, None
1079 )
901 1080
902 iq_elt.send() 1081 iq_elt.send()
903 1082
904 def onTransportAccept(self, client, request, jingle_elt, session): 1083 def onTransportAccept(self, client, request, jingle_elt, session):
905 """Method called once transport replacement is accepted 1084 """Method called once transport replacement is accepted
910 @param session(dict): session data 1089 @param session(dict): session data
911 """ 1090 """
912 log.debug(u"new transport has been accepted") 1091 log.debug(u"new transport has been accepted")
913 1092
914 try: 1093 try:
915 self._parseElements(jingle_elt, session, request, client, with_application=False) 1094 self._parseElements(
1095 jingle_elt, session, request, client, with_application=False
1096 )
916 except exceptions.CancelError: 1097 except exceptions.CancelError:
917 return 1098 return
918 1099
919 # at this point we can send the <iq/> result to confirm reception of the request 1100 # at this point we can send the <iq/> result to confirm reception of the request
920 client.send(xmlstream.toResponse(request, 'result')) 1101 client.send(xmlstream.toResponse(request, "result"))
921 1102
922 negociate_defers = [] 1103 negociate_defers = []
923 negociate_defers = self._callPlugins(client, XEP_0166.A_TRANSPORT_ACCEPT, session, app_method_name=None) 1104 negociate_defers = self._callPlugins(
1105 client, XEP_0166.A_TRANSPORT_ACCEPT, session, app_method_name=None
1106 )
924 1107
925 negociate_dlist = defer.DeferredList(negociate_defers) 1108 negociate_dlist = defer.DeferredList(negociate_defers)
926 1109
927 # after negociations we start the transfer 1110 # after negociations we start the transfer
928 negociate_dlist.addCallback(lambda dummy: self._callPlugins(client, XEP_0166.A_START, session, app_method_name=None, elements=False)) 1111 negociate_dlist.addCallback(
1112 lambda dummy: self._callPlugins(
1113 client, XEP_0166.A_START, session, app_method_name=None, elements=False
1114 )
1115 )
929 1116
930 def onTransportReject(self, client, request, jingle_elt, session): 1117 def onTransportReject(self, client, request, jingle_elt, session):
931 """Method called when a transport replacement is refused 1118 """Method called when a transport replacement is refused
932 1119
933 @param client: %(doc_client)s 1120 @param client: %(doc_client)s
935 @param jingle_elt(domish.Element): the <jingle> element 1122 @param jingle_elt(domish.Element): the <jingle> element
936 @param session(dict): session data 1123 @param session(dict): session data
937 """ 1124 """
938 # XXX: for now, we terminate the session in case of transport-reject 1125 # XXX: for now, we terminate the session in case of transport-reject
939 # this behaviour may change in the future 1126 # this behaviour may change in the future
940 self.terminate(client, 'failed-transport', session) 1127 self.terminate(client, "failed-transport", session)
941 1128
942 def onTransportInfo(self, client, request, jingle_elt, session): 1129 def onTransportInfo(self, client, request, jingle_elt, session):
943 """Method called when a transport-info action is received from other peer 1130 """Method called when a transport-info action is received from other peer
944 1131
945 The request is parsed, and jingleHandler is called on concerned transport plugin(s) 1132 The request is parsed, and jingleHandler is called on concerned transport plugin(s)
946 @param client: %(doc_client)s 1133 @param client: %(doc_client)s
947 @param request(domish.Element): full <iq> request 1134 @param request(domish.Element): full <iq> request
948 @param jingle_elt(domish.Element): the <jingle> element 1135 @param jingle_elt(domish.Element): the <jingle> element
949 @param session(dict): session data 1136 @param session(dict): session data
950 """ 1137 """
951 log.debug(u"Jingle session {} has been accepted".format(session['id'])) 1138 log.debug(u"Jingle session {} has been accepted".format(session["id"]))
952 1139
953 try: 1140 try:
954 self._parseElements(jingle_elt, session, request, client, with_application=False) 1141 self._parseElements(
1142 jingle_elt, session, request, client, with_application=False
1143 )
955 except exceptions.CancelError: 1144 except exceptions.CancelError:
956 return 1145 return
957 1146
958 # The parsing was OK, we send the <iq> result 1147 # The parsing was OK, we send the <iq> result
959 client.send(xmlstream.toResponse(request, 'result')) 1148 client.send(xmlstream.toResponse(request, "result"))
960 1149
961 for content_name, content_data in session['contents'].iteritems(): 1150 for content_name, content_data in session["contents"].iteritems():
962 try: 1151 try:
963 transport_elt = content_data.pop('transport_elt') 1152 transport_elt = content_data.pop("transport_elt")
964 except KeyError: 1153 except KeyError:
965 continue 1154 continue
966 else: 1155 else:
967 content_data['transport'].handler.jingleHandler(client, XEP_0166.A_TRANSPORT_INFO, session, content_name, transport_elt) 1156 content_data["transport"].handler.jingleHandler(
1157 client,
1158 XEP_0166.A_TRANSPORT_INFO,
1159 session,
1160 content_name,
1161 transport_elt,
1162 )
968 1163
969 1164
970 class XEP_0166_handler(xmlstream.XMPPHandler): 1165 class XEP_0166_handler(xmlstream.XMPPHandler):
971 implements(iwokkel.IDisco) 1166 implements(iwokkel.IDisco)
972 1167
973 def __init__(self, plugin_parent): 1168 def __init__(self, plugin_parent):
974 self.plugin_parent = plugin_parent 1169 self.plugin_parent = plugin_parent
975 1170
976 def connectionInitialized(self): 1171 def connectionInitialized(self):
977 self.xmlstream.addObserver(JINGLE_REQUEST, self.plugin_parent._onJingleRequest, client=self.parent) 1172 self.xmlstream.addObserver(
978 1173 JINGLE_REQUEST, self.plugin_parent._onJingleRequest, client=self.parent
979 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 1174 )
1175
1176 def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
980 return [disco.DiscoFeature(NS_JINGLE)] 1177 return [disco.DiscoFeature(NS_JINGLE)]
981 1178
982 def getDiscoItems(self, requestor, target, nodeIdentifier=''): 1179 def getDiscoItems(self, requestor, target, nodeIdentifier=""):
983 return [] 1180 return []