Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0166.py @ 3969:8e7d5796fb23
plugin XEP-0391: implement XEP-0391 (Jingle Encrypted Transports) + XEP-0396 (JET-OMEMO):
rel 378
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 31 Oct 2022 04:09:34 +0100 |
parents | bbf92ef05f38 |
children | 524856bd7b19 |
comparison
equal
deleted
inserted
replaced
3968:0dd79c6cc1d2 | 3969:8e7d5796fb23 |
---|---|
15 | 15 |
16 # You should have received a copy of the GNU Affero General Public License | 16 # You should have received a copy of the GNU Affero General Public License |
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | 18 |
19 | 19 |
20 from collections import namedtuple | |
21 import time | |
22 from typing import Any, Dict, Tuple | |
20 import uuid | 23 import uuid |
21 import time | 24 |
22 from typing import Tuple | |
23 from collections import namedtuple | |
24 from zope.interface import implementer | |
25 from twisted.words.protocols.jabber import jid | |
26 from twisted.internet import defer | 25 from twisted.internet import defer |
27 from twisted.internet import reactor | 26 from twisted.internet import reactor |
27 from twisted.python import failure | |
28 from twisted.words.protocols.jabber import jid | |
28 from twisted.words.protocols.jabber import error | 29 from twisted.words.protocols.jabber import error |
29 from twisted.words.protocols.jabber import xmlstream | 30 from twisted.words.protocols.jabber import xmlstream |
30 from twisted.python import failure | 31 from twisted.words.xish import domish |
31 from wokkel import disco, iwokkel | 32 from wokkel import disco, iwokkel |
33 from zope.interface import implementer | |
34 | |
32 from sat.core import exceptions | 35 from sat.core import exceptions |
33 from sat.core.i18n import _, D_ | |
34 from sat.core.constants import Const as C | 36 from sat.core.constants import Const as C |
37 from sat.core.core_types import SatXMPPEntity | |
38 from sat.core.i18n import D_, _ | |
35 from sat.core.log import getLogger | 39 from sat.core.log import getLogger |
36 from sat.tools import xml_tools | 40 from sat.tools import xml_tools |
37 from sat.tools import utils | 41 from sat.tools import utils |
38 | 42 |
39 | 43 |
63 | 67 |
64 ApplicationData = namedtuple("ApplicationData", ("namespace", "handler")) | 68 ApplicationData = namedtuple("ApplicationData", ("namespace", "handler")) |
65 TransportData = namedtuple("TransportData", ("namespace", "handler", "priority")) | 69 TransportData = namedtuple("TransportData", ("namespace", "handler", "priority")) |
66 | 70 |
67 | 71 |
68 class XEP_0166(object): | 72 class XEP_0166: |
73 namespace = NS_JINGLE | |
69 ROLE_INITIATOR = "initiator" | 74 ROLE_INITIATOR = "initiator" |
70 ROLE_RESPONDER = "responder" | 75 ROLE_RESPONDER = "responder" |
71 TRANSPORT_DATAGRAM = "UDP" | 76 TRANSPORT_DATAGRAM = "UDP" |
72 TRANSPORT_STREAMING = "TCP" | 77 TRANSPORT_STREAMING = "TCP" |
73 REASON_SUCCESS = "success" | 78 REASON_SUCCESS = "success" |
370 content_name = content["name"] | 375 content_name = content["name"] |
371 except KeyError: | 376 except KeyError: |
372 content_name = content["name"] = str(uuid.uuid4()) | 377 content_name = content["name"] = str(uuid.uuid4()) |
373 return application, app_args, app_kwargs, content_name | 378 return application, app_args, app_kwargs, content_name |
374 | 379 |
375 async def initiate(self, client, peer_jid, contents): | 380 async def initiate(self, client, peer_jid, contents, encrypted=False): |
376 """Send a session initiation request | 381 """Send a session initiation request |
377 | 382 |
378 @param peer_jid(jid.JID): jid to establith session with | 383 @param peer_jid(jid.JID): jid to establith session with |
379 @param contents(list[dict]): list of contents to use: | 384 @param contents(list[dict]): list of contents to use: |
380 The dict must have the following keys: | 385 The dict must have the following keys: |
385 - name(unicode): name of the content | 390 - name(unicode): name of the content |
386 - senders(unicode): One of XEP_0166.ROLE_INITIATOR, XEP_0166.ROLE_RESPONDER, both or none | 391 - senders(unicode): One of XEP_0166.ROLE_INITIATOR, XEP_0166.ROLE_RESPONDER, both or none |
387 default to BOTH (see XEP-0166 §7.3) | 392 default to BOTH (see XEP-0166 §7.3) |
388 - app_args(list): args to pass to the application plugin | 393 - app_args(list): args to pass to the application plugin |
389 - app_kwargs(dict): keyword args to pass to the application plugin | 394 - app_kwargs(dict): keyword args to pass to the application plugin |
395 @param encrypted: if True, session must be encrypted and "encryption" must be set | |
396 to all content data of session | |
390 @return (unicode): jingle session id | 397 @return (unicode): jingle session id |
391 """ | 398 """ |
392 assert contents # there must be at least one content | 399 assert contents # there must be at least one content |
393 if (peer_jid == client.jid | 400 if (peer_jid == client.jid |
394 or client.is_component and peer_jid.host == client.jid.host): | 401 or client.is_component and peer_jid.host == client.jid.host): |
469 transport.handler.jingleSessionInit, | 476 transport.handler.jingleSessionInit, |
470 client, session, content_name | 477 client, session, content_name |
471 ) | 478 ) |
472 content_elt.addChild(transport_elt) | 479 content_elt.addChild(transport_elt) |
473 | 480 |
481 if not await self.host.trigger.asyncPoint( | |
482 "XEP-0166_initiate_elt_built", | |
483 client, session, iq_elt, jingle_elt | |
484 ): | |
485 return | |
486 if encrypted: | |
487 for content in session["contents"].values(): | |
488 if "encryption" not in content: | |
489 raise exceptions.EncryptionError( | |
490 "Encryption is requested, but no encryption has been set" | |
491 ) | |
492 | |
474 try: | 493 try: |
475 await iq_elt.send() | 494 await iq_elt.send() |
476 except Exception as e: | 495 except Exception as e: |
477 failure_ = failure.Failure(e) | 496 failure_ = failure.Failure(e) |
478 self._iqError(failure_, sid, client) | 497 self._iqError(failure_, sid, client) |
512 profile=client.profile, | 531 profile=client.profile, |
513 ) | 532 ) |
514 | 533 |
515 ## jingle events ## | 534 ## jingle events ## |
516 | 535 |
517 def _onJingleRequest(self, request, client): | 536 def _on_jingle_request(self, request: domish.Element, client: SatXMPPEntity) -> None: |
537 defer.ensureDeferred(self.on_jingle_request(client, request)) | |
538 | |
539 async def on_jingle_request( | |
540 self, | |
541 client: SatXMPPEntity, | |
542 request: domish.Element | |
543 ) -> None: | |
518 """Called when any jingle request is received | 544 """Called when any jingle request is received |
519 | 545 |
520 The request will then be dispatched to appropriate method | 546 The request will then be dispatched to appropriate method |
521 according to current state | 547 according to current state |
522 @param request(domish.Element): received IQ request | 548 @param request(domish.Element): received IQ request |
592 log.error("session id doesn't match") | 618 log.error("session id doesn't match") |
593 self.sendError(client, "service-unavailable", sid, request) | 619 self.sendError(client, "service-unavailable", sid, request) |
594 raise exceptions.InternalError | 620 raise exceptions.InternalError |
595 | 621 |
596 if action == XEP_0166.A_SESSION_INITIATE: | 622 if action == XEP_0166.A_SESSION_INITIATE: |
597 self.onSessionInitiate(client, request, jingle_elt, session) | 623 await self.onSessionInitiate(client, request, jingle_elt, session) |
598 elif action == XEP_0166.A_SESSION_TERMINATE: | 624 elif action == XEP_0166.A_SESSION_TERMINATE: |
599 self.onSessionTerminate(client, request, jingle_elt, session) | 625 self.onSessionTerminate(client, request, jingle_elt, session) |
600 elif action == XEP_0166.A_SESSION_ACCEPT: | 626 elif action == XEP_0166.A_SESSION_ACCEPT: |
601 self.onSessionAccept(client, request, jingle_elt, session) | 627 self.onSessionAccept(client, request, jingle_elt, session) |
602 elif action == XEP_0166.A_SESSION_INFO: | 628 elif action == XEP_0166.A_SESSION_INFO: |
794 ) | 820 ) |
795 defers_list.append(d) | 821 defers_list.append(d) |
796 | 822 |
797 return defers_list | 823 return defers_list |
798 | 824 |
799 def onSessionInitiate(self, client, request, jingle_elt, session): | 825 async def onSessionInitiate( |
826 self, | |
827 client: SatXMPPEntity, | |
828 request: domish.Element, | |
829 jingle_elt: domish.Element, | |
830 session: Dict[str, Any] | |
831 ) -> None: | |
800 """Called on session-initiate action | 832 """Called on session-initiate action |
801 | 833 |
802 The "jingleRequestConfirmation" method of each application will be called | 834 The "jingleRequestConfirmation" method of each application will be called |
803 (or self.jingleRequestConfirmationDefault if the former doesn't exist). | 835 (or self.jingleRequestConfirmationDefault if the former doesn't exist). |
804 The session is only accepted if all application are confirmed. | 836 The session is only accepted if all application are confirmed. |
826 self.sendError(client, "bad-request", session["id"], request) | 858 self.sendError(client, "bad-request", session["id"], request) |
827 return | 859 return |
828 | 860 |
829 # at this point we can send the <iq/> result to confirm reception of the request | 861 # at this point we can send the <iq/> result to confirm reception of the request |
830 client.send(xmlstream.toResponse(request, "result")) | 862 client.send(xmlstream.toResponse(request, "result")) |
863 | |
864 | |
865 if not await self.host.trigger.asyncPoint( | |
866 "XEP-0166_on_session_initiate", | |
867 client, session, request, jingle_elt | |
868 ): | |
869 return | |
831 | 870 |
832 # we now request each application plugin confirmation | 871 # we now request each application plugin confirmation |
833 # and if all are accepted, we can accept the session | 872 # and if all are accepted, we can accept the session |
834 confirm_defers = self._callPlugins( | 873 confirm_defers = self._callPlugins( |
835 client, | 874 client, |
1197 def __init__(self, plugin_parent): | 1236 def __init__(self, plugin_parent): |
1198 self.plugin_parent = plugin_parent | 1237 self.plugin_parent = plugin_parent |
1199 | 1238 |
1200 def connectionInitialized(self): | 1239 def connectionInitialized(self): |
1201 self.xmlstream.addObserver( | 1240 self.xmlstream.addObserver( |
1202 JINGLE_REQUEST, self.plugin_parent._onJingleRequest, client=self.parent | 1241 JINGLE_REQUEST, self.plugin_parent._on_jingle_request, client=self.parent |
1203 ) | 1242 ) |
1204 | 1243 |
1205 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | 1244 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): |
1206 return [disco.DiscoFeature(NS_JINGLE)] | 1245 return [disco.DiscoFeature(NS_JINGLE)] |
1207 | 1246 |