Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0260.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | 69e4716d6268 |
children | 9d0df638c8b4 |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT plugin for Jingle (XEP-0260) | 4 # SAT plugin for Jingle (XEP-0260) |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 6 |
22 from sat.core.log import getLogger | 22 from sat.core.log import getLogger |
23 | 23 |
24 log = getLogger(__name__) | 24 log = getLogger(__name__) |
25 from sat.core import exceptions | 25 from sat.core import exceptions |
26 from wokkel import disco, iwokkel | 26 from wokkel import disco, iwokkel |
27 from zope.interface import implements | 27 from zope.interface import implementer |
28 from twisted.words.xish import domish | 28 from twisted.words.xish import domish |
29 from twisted.words.protocols.jabber import jid | 29 from twisted.words.protocols.jabber import jid |
30 from twisted.internet import defer | 30 from twisted.internet import defer |
31 import uuid | 31 import uuid |
32 | 32 |
120 transport_elt["dstaddr"] = session_hash | 120 transport_elt["dstaddr"] = session_hash |
121 if mode is not None: | 121 if mode is not None: |
122 transport_elt["mode"] = "tcp" # XXX: we only manage tcp for now | 122 transport_elt["mode"] = "tcp" # XXX: we only manage tcp for now |
123 | 123 |
124 for candidate in candidates: | 124 for candidate in candidates: |
125 log.debug(u"Adding candidate: {}".format(candidate)) | 125 log.debug("Adding candidate: {}".format(candidate)) |
126 candidate_elt = transport_elt.addElement("candidate", NS_JINGLE_S5B) | 126 candidate_elt = transport_elt.addElement("candidate", NS_JINGLE_S5B) |
127 if candidate.id is None: | 127 if candidate.id is None: |
128 candidate.id = unicode(uuid.uuid4()) | 128 candidate.id = str(uuid.uuid4()) |
129 candidate_elt["cid"] = candidate.id | 129 candidate_elt["cid"] = candidate.id |
130 candidate_elt["host"] = candidate.host | 130 candidate_elt["host"] = candidate.host |
131 candidate_elt["jid"] = candidate.jid.full() | 131 candidate_elt["jid"] = candidate.jid.full() |
132 candidate_elt["port"] = unicode(candidate.port) | 132 candidate_elt["port"] = str(candidate.port) |
133 candidate_elt["priority"] = unicode(candidate.priority) | 133 candidate_elt["priority"] = str(candidate.priority) |
134 candidate_elt["type"] = candidate.type | 134 candidate_elt["type"] = candidate.type |
135 return transport_elt | 135 return transport_elt |
136 | 136 |
137 @defer.inlineCallbacks | 137 @defer.inlineCallbacks |
138 def jingleSessionInit(self, client, session, content_name): | 138 def jingleSessionInit(self, client, session, content_name): |
139 content_data = session["contents"][content_name] | 139 content_data = session["contents"][content_name] |
140 transport_data = content_data["transport_data"] | 140 transport_data = content_data["transport_data"] |
141 sid = transport_data["sid"] = unicode(uuid.uuid4()) | 141 sid = transport_data["sid"] = str(uuid.uuid4()) |
142 session_hash = transport_data["session_hash"] = self._s5b.getSessionHash( | 142 session_hash = transport_data["session_hash"] = self._s5b.getSessionHash( |
143 session[u"local_jid"], session["peer_jid"], sid | 143 session["local_jid"], session["peer_jid"], sid |
144 ) | 144 ) |
145 transport_data["peer_session_hash"] = self._s5b.getSessionHash( | 145 transport_data["peer_session_hash"] = self._s5b.getSessionHash( |
146 session["peer_jid"], session[u"local_jid"], sid | 146 session["peer_jid"], session["local_jid"], sid |
147 ) # requester and target are inversed for peer candidates | 147 ) # requester and target are inversed for peer candidates |
148 transport_data["stream_d"] = self._s5b.registerHash(client, session_hash, None) | 148 transport_data["stream_d"] = self._s5b.registerHash(client, session_hash, None) |
149 candidates = transport_data["candidates"] = yield self._s5b.getCandidates( | 149 candidates = transport_data["candidates"] = yield self._s5b.getCandidates( |
150 client, session["local_jid"]) | 150 client, session["local_jid"]) |
151 mode = "tcp" # XXX: we only manage tcp for now | 151 mode = "tcp" # XXX: we only manage tcp for now |
179 client, self._j.A_TRANSPORT_INFO, session, content_name | 179 client, self._j.A_TRANSPORT_INFO, session, content_name |
180 ) | 180 ) |
181 transport_elt.addElement("proxy-error") | 181 transport_elt.addElement("proxy-error") |
182 iq_elt.send() | 182 iq_elt.send() |
183 log.warning( | 183 log.warning( |
184 u"Can't activate proxy, we need to fallback to IBB: {reason}".format( | 184 "Can't activate proxy, we need to fallback to IBB: {reason}".format( |
185 reason=stanza_error.value.condition | 185 reason=stanza_error.value.condition |
186 ) | 186 ) |
187 ) | 187 ) |
188 self.doFallback(session, content_name, client) | 188 self.doFallback(session, content_name, client) |
189 | 189 |
209 del transport_data["peer_candidates"] | 209 del transport_data["peer_candidates"] |
210 iq_elt, transport_elt = self._j.buildAction( | 210 iq_elt, transport_elt = self._j.buildAction( |
211 client, self._j.A_TRANSPORT_INFO, session, content_name | 211 client, self._j.A_TRANSPORT_INFO, session, content_name |
212 ) | 212 ) |
213 if candidate is None: | 213 if candidate is None: |
214 log.warning(u"Can't connect to any peer candidate") | 214 log.warning("Can't connect to any peer candidate") |
215 candidate_elt = transport_elt.addElement("candidate-error") | 215 candidate_elt = transport_elt.addElement("candidate-error") |
216 else: | 216 else: |
217 log.info(u"Found best peer candidate: {}".format(unicode(candidate))) | 217 log.info("Found best peer candidate: {}".format(str(candidate))) |
218 candidate_elt = transport_elt.addElement("candidate-used") | 218 candidate_elt = transport_elt.addElement("candidate-used") |
219 candidate_elt["cid"] = candidate.id | 219 candidate_elt["cid"] = candidate.id |
220 iq_elt.send() # TODO: check result stanza | 220 iq_elt.send() # TODO: check result stanza |
221 self._checkCandidates(session, content_name, transport_data, client) | 221 self._checkCandidates(session, content_name, transport_data, client) |
222 | 222 |
246 choosed_candidate = best_candidate or peer_best_candidate | 246 choosed_candidate = best_candidate or peer_best_candidate |
247 else: | 247 else: |
248 if best_candidate.priority == peer_best_candidate.priority: | 248 if best_candidate.priority == peer_best_candidate.priority: |
249 # same priority, we choose initiator one according to XEP-0260 §2.4 #4 | 249 # same priority, we choose initiator one according to XEP-0260 §2.4 #4 |
250 log.debug( | 250 log.debug( |
251 u"Candidates have same priority, we select the one choosed by initiator" | 251 "Candidates have same priority, we select the one choosed by initiator" |
252 ) | 252 ) |
253 if session["initiator"] == session[u"local_jid"]: | 253 if session["initiator"] == session["local_jid"]: |
254 choosed_candidate = best_candidate | 254 choosed_candidate = best_candidate |
255 else: | 255 else: |
256 choosed_candidate = peer_best_candidate | 256 choosed_candidate = peer_best_candidate |
257 else: | 257 else: |
258 choosed_candidate = max( | 258 choosed_candidate = max( |
259 best_candidate, peer_best_candidate, key=lambda c: c.priority | 259 best_candidate, peer_best_candidate, key=lambda c: c.priority |
260 ) | 260 ) |
261 | 261 |
262 if choosed_candidate is None: | 262 if choosed_candidate is None: |
263 log.warning(u"Socks5 negociation failed, we need to fallback to IBB") | 263 log.warning("Socks5 negociation failed, we need to fallback to IBB") |
264 self.doFallback(session, content_name, client) | 264 self.doFallback(session, content_name, client) |
265 else: | 265 else: |
266 if choosed_candidate == peer_best_candidate: | 266 if choosed_candidate == peer_best_candidate: |
267 # peer_best_candidate was choosed from the candidates we have sent | 267 # peer_best_candidate was choosed from the candidates we have sent |
268 # so our_candidate is true if choosed_candidate is peer_best_candidate | 268 # so our_candidate is true if choosed_candidate is peer_best_candidate |
274 pass | 274 pass |
275 else: | 275 else: |
276 our_candidate = False | 276 our_candidate = False |
277 | 277 |
278 log.info( | 278 log.info( |
279 u"Socks5 negociation successful, {who} candidate will be used: {candidate}".format( | 279 "Socks5 negociation successful, {who} candidate will be used: {candidate}".format( |
280 who=u"our" if our_candidate else u"other peer", | 280 who="our" if our_candidate else "other peer", |
281 candidate=choosed_candidate, | 281 candidate=choosed_candidate, |
282 ) | 282 ) |
283 ) | 283 ) |
284 del transport_data["best_candidate"] | 284 del transport_data["best_candidate"] |
285 del transport_data["peer_best_candidate"] | 285 del transport_data["peer_best_candidate"] |
319 """Called when it's not possible to start the transfer | 319 """Called when it's not possible to start the transfer |
320 | 320 |
321 Will try to fallback to IBB | 321 Will try to fallback to IBB |
322 """ | 322 """ |
323 try: | 323 try: |
324 reason = unicode(fail.value) | 324 reason = str(fail.value) |
325 except AttributeError: | 325 except AttributeError: |
326 reason = unicode(fail) | 326 reason = str(fail) |
327 log.warning(u"Cant start transfert, we'll try fallback method: {}".format(reason)) | 327 log.warning("Cant start transfert, we'll try fallback method: {}".format(reason)) |
328 self.doFallback(session, content_name, client) | 328 self.doFallback(session, content_name, client) |
329 | 329 |
330 def _candidateInfo( | 330 def _candidateInfo( |
331 self, candidate_elt, session, content_name, transport_data, client | 331 self, candidate_elt, session, content_name, transport_data, client |
332 ): | 332 ): |
345 else: | 345 else: |
346 # candidate-used, one candidate was choosed | 346 # candidate-used, one candidate was choosed |
347 try: | 347 try: |
348 cid = candidate_elt.attributes["cid"] | 348 cid = candidate_elt.attributes["cid"] |
349 except KeyError: | 349 except KeyError: |
350 log.warning(u"No cid found in <candidate-used>") | 350 log.warning("No cid found in <candidate-used>") |
351 raise exceptions.DataError | 351 raise exceptions.DataError |
352 try: | 352 try: |
353 candidate = ( | 353 candidate = next(( |
354 c for c in transport_data["candidates"] if c.id == cid | 354 c for c in transport_data["candidates"] if c.id == cid |
355 ).next() | 355 )) |
356 except StopIteration: | 356 except StopIteration: |
357 log.warning(u"Given cid doesn't correspond to any known candidate !") | 357 log.warning("Given cid doesn't correspond to any known candidate !") |
358 raise exceptions.DataError # TODO: send an error to other peer, and use better exception | 358 raise exceptions.DataError # TODO: send an error to other peer, and use better exception |
359 except KeyError: | 359 except KeyError: |
360 # a transport-info can also be intentionaly sent too early by other peer | 360 # a transport-info can also be intentionaly sent too early by other peer |
361 # but there is little probability | 361 # but there is little probability |
362 log.error( | 362 log.error( |
363 u'"candidates" key doesn\'t exists in transport_data, it should at this point' | 363 '"candidates" key doesn\'t exists in transport_data, it should at this point' |
364 ) | 364 ) |
365 raise exceptions.InternalError | 365 raise exceptions.InternalError |
366 # at this point we have the candidate choosed by other peer | 366 # at this point we have the candidate choosed by other peer |
367 transport_data["peer_best_candidate"] = candidate | 367 transport_data["peer_best_candidate"] = candidate |
368 log.info(u"Other peer best candidate: {}".format(candidate)) | 368 log.info("Other peer best candidate: {}".format(candidate)) |
369 | 369 |
370 del transport_data["candidates"] | 370 del transport_data["candidates"] |
371 self._checkCandidates(session, content_name, transport_data, client) | 371 self._checkCandidates(session, content_name, transport_data, client) |
372 | 372 |
373 def _proxyActivationInfo( | 373 def _proxyActivationInfo( |
383 @param client(unicode): %(doc_client)s | 383 @param client(unicode): %(doc_client)s |
384 """ | 384 """ |
385 try: | 385 try: |
386 activation_d = transport_data.pop("activation_d") | 386 activation_d = transport_data.pop("activation_d") |
387 except KeyError: | 387 except KeyError: |
388 log.warning(u"Received unexpected transport-info for proxy activation") | 388 log.warning("Received unexpected transport-info for proxy activation") |
389 | 389 |
390 if proxy_elt.name == "activated": | 390 if proxy_elt.name == "activated": |
391 activation_d.callback(None) | 391 activation_d.callback(None) |
392 else: | 392 else: |
393 activation_d.errback(ProxyError()) | 393 activation_d.errback(ProxyError()) |
465 (self._candidateInfo, ("candidate-used", "candidate-error")), | 465 (self._candidateInfo, ("candidate-used", "candidate-error")), |
466 (self._proxyActivationInfo, ("activated", "proxy-error")), | 466 (self._proxyActivationInfo, ("activated", "proxy-error")), |
467 ): | 467 ): |
468 for name in names: | 468 for name in names: |
469 try: | 469 try: |
470 candidate_elt = transport_elt.elements(NS_JINGLE_S5B, name).next() | 470 candidate_elt = next(transport_elt.elements(NS_JINGLE_S5B, name)) |
471 except StopIteration: | 471 except StopIteration: |
472 continue | 472 continue |
473 else: | 473 else: |
474 method( | 474 method( |
475 candidate_elt, session, content_name, transport_data, client | 475 candidate_elt, session, content_name, transport_data, client |
476 ) | 476 ) |
477 break | 477 break |
478 | 478 |
479 if candidate_elt is None: | 479 if candidate_elt is None: |
480 log.warning( | 480 log.warning( |
481 u"Unexpected transport element: {}".format(transport_elt.toXml()) | 481 "Unexpected transport element: {}".format(transport_elt.toXml()) |
482 ) | 482 ) |
483 elif action == self._j.A_DESTROY: | 483 elif action == self._j.A_DESTROY: |
484 # the transport is replaced (fallback ?), We need mainly to kill XEP-0065 session. | 484 # the transport is replaced (fallback ?), We need mainly to kill XEP-0065 session. |
485 # note that sid argument is not necessary for sessions created by this plugin | 485 # note that sid argument is not necessary for sessions created by this plugin |
486 self._s5b.killSession(None, transport_data["session_hash"], None, client) | 486 self._s5b.killSession(None, transport_data["session_hash"], None, client) |
487 else: | 487 else: |
488 log.warning(u"FIXME: unmanaged action {}".format(action)) | 488 log.warning("FIXME: unmanaged action {}".format(action)) |
489 | 489 |
490 defer.returnValue(transport_elt) | 490 defer.returnValue(transport_elt) |
491 | 491 |
492 def jingleTerminate(self, client, action, session, content_name, reason_elt): | 492 def jingleTerminate(self, client, action, session, content_name, reason_elt): |
493 if reason_elt.decline: | 493 if reason_elt.decline: |
494 log.debug(u"Session declined, deleting S5B session") | 494 log.debug("Session declined, deleting S5B session") |
495 # we just need to clean the S5B session if it is declined | 495 # we just need to clean the S5B session if it is declined |
496 content_data = session["contents"][content_name] | 496 content_data = session["contents"][content_name] |
497 transport_data = content_data["transport_data"] | 497 transport_data = content_data["transport_data"] |
498 self._s5b.killSession(None, transport_data["session_hash"], None, client) | 498 self._s5b.killSession(None, transport_data["session_hash"], None, client) |
499 | 499 |
502 | 502 |
503 @param feature_checked(bool): True if other peer can do IBB | 503 @param feature_checked(bool): True if other peer can do IBB |
504 """ | 504 """ |
505 if not feature_checked: | 505 if not feature_checked: |
506 log.warning( | 506 log.warning( |
507 u"Other peer can't manage jingle IBB, be have to terminate the session" | 507 "Other peer can't manage jingle IBB, be have to terminate the session" |
508 ) | 508 ) |
509 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) | 509 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) |
510 else: | 510 else: |
511 self._j.transportReplace( | 511 self._j.transportReplace( |
512 client, self._jingle_ibb.NAMESPACE, session, content_name | 512 client, self._jingle_ibb.NAMESPACE, session, content_name |
522 if session["role"] != self._j.ROLE_INITIATOR: | 522 if session["role"] != self._j.ROLE_INITIATOR: |
523 # only initiator must do the fallback, see XEP-0260 §3 | 523 # only initiator must do the fallback, see XEP-0260 §3 |
524 return | 524 return |
525 if self._jingle_ibb is None: | 525 if self._jingle_ibb is None: |
526 log.warning( | 526 log.warning( |
527 u"Jingle IBB (XEP-0261) plugin is not available, we have to close the session" | 527 "Jingle IBB (XEP-0261) plugin is not available, we have to close the session" |
528 ) | 528 ) |
529 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) | 529 self._j.terminate(client, self._j.REASON_CONNECTIVITY_ERROR, session) |
530 else: | 530 else: |
531 d = self.host.hasFeature( | 531 d = self.host.hasFeature( |
532 client, self._jingle_ibb.NAMESPACE, session["peer_jid"] | 532 client, self._jingle_ibb.NAMESPACE, session["peer_jid"] |
533 ) | 533 ) |
534 d.addCallback(self._doFallback, session, content_name, client) | 534 d.addCallback(self._doFallback, session, content_name, client) |
535 return d | 535 return d |
536 | 536 |
537 | 537 |
538 @implementer(iwokkel.IDisco) | |
538 class XEP_0260_handler(XMPPHandler): | 539 class XEP_0260_handler(XMPPHandler): |
539 implements(iwokkel.IDisco) | |
540 | 540 |
541 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | 541 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): |
542 return [disco.DiscoFeature(NS_JINGLE_S5B)] | 542 return [disco.DiscoFeature(NS_JINGLE_S5B)] |
543 | 543 |
544 def getDiscoItems(self, requestor, target, nodeIdentifier=""): | 544 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |