Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0384.py @ 3214:8d92d4d829fb
plugin XEP-0384: use "max_items=1" for devices and bundles nodes:
- max_items = 1 is requested with publish-options when publishing the bundle or the
devices list. If the constraint is failed, a warning will be shown, and the publication
will be done without this option
- fixed return values when devices list is badly formed
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 11 Mar 2020 20:44:49 +0100 |
parents | dcebc585c29f |
children | 806a7936a591 |
comparison
equal
deleted
inserted
replaced
3213:c2f958dde5d2 | 3214:8d92d4d829fb |
---|---|
21 from sat.core.log import getLogger | 21 from sat.core.log import getLogger |
22 from sat.core import exceptions | 22 from sat.core import exceptions |
23 from twisted.internet import defer, reactor | 23 from twisted.internet import defer, reactor |
24 from twisted.words.xish import domish | 24 from twisted.words.xish import domish |
25 from twisted.words.protocols.jabber import jid | 25 from twisted.words.protocols.jabber import jid |
26 from twisted.words.protocols.jabber import error | 26 from twisted.words.protocols.jabber import error as jabber_error |
27 from sat.memory import persistent | 27 from sat.memory import persistent |
28 from functools import partial | 28 from functools import partial |
29 from sat.tools import xml_tools | 29 from sat.tools import xml_tools |
30 import logging | 30 import logging |
31 import random | 31 import random |
395 self._sid = host.plugins.get("XEP-0359") | 395 self._sid = host.plugins.get("XEP-0359") |
396 host.trigger.add("messageReceived", self._messageReceivedTrigger, priority=100050) | 396 host.trigger.add("messageReceived", self._messageReceivedTrigger, priority=100050) |
397 host.trigger.add("sendMessageData", self._sendMessageDataTrigger) | 397 host.trigger.add("sendMessageData", self._sendMessageDataTrigger) |
398 self.host.registerEncryptionPlugin(self, "OMEMO", NS_OMEMO, 100) | 398 self.host.registerEncryptionPlugin(self, "OMEMO", NS_OMEMO, 100) |
399 pep = host.plugins['XEP-0163'] | 399 pep = host.plugins['XEP-0163'] |
400 pep.addPEPEvent("OMEMO_DEVICES", NS_OMEMO_DEVICES, self.onNewDevices) | 400 pep.addPEPEvent( |
401 "OMEMO_DEVICES", NS_OMEMO_DEVICES, | |
402 lambda itemsEvent, profile: defer.ensureDeferred( | |
403 self.onNewDevices(itemsEvent, profile)) | |
404 ) | |
401 | 405 |
402 @defer.inlineCallbacks | 406 @defer.inlineCallbacks |
403 def trustUICb(self, xmlui_data, trust_data, expect_problems=None, | 407 def trustUICb(self, xmlui_data, trust_data, expect_problems=None, |
404 profile=C.PROF_KEY_NONE): | 408 profile=C.PROF_KEY_NONE): |
405 if C.bool(xmlui_data.get('cancelled', 'false')): | 409 if C.bool(xmlui_data.get('cancelled', 'false')): |
568 while device_id in devices: | 572 while device_id in devices: |
569 device_id = random.randint(1, 2**31-1) | 573 device_id = random.randint(1, 2**31-1) |
570 # and we save it | 574 # and we save it |
571 persistent_dict[KEY_DEVICE_ID] = device_id | 575 persistent_dict[KEY_DEVICE_ID] = device_id |
572 | 576 |
577 log.debug(f"our OMEMO device id is {device_id}") | |
578 | |
573 if device_id not in devices: | 579 if device_id not in devices: |
580 log.debug(f"our device id ({device_id}) is not in the list, adding it") | |
574 devices.add(device_id) | 581 devices.add(device_id) |
575 yield self.setDevices(client, devices) | 582 yield defer.ensureDeferred(self.setDevices(client, devices)) |
576 | 583 |
577 all_jids = yield persistent_dict.get(KEY_ALL_JIDS, set()) | 584 all_jids = yield persistent_dict.get(KEY_ALL_JIDS, set()) |
578 | 585 |
579 omemo_storage = OmemoStorage(client, device_id, all_jids, persistent_dict) | 586 omemo_storage = OmemoStorage(client, device_id, all_jids, persistent_dict) |
580 omemo_session = yield OmemoSession.create(client, omemo_storage, device_id) | 587 omemo_session = yield OmemoSession.create(client, omemo_storage, device_id) |
583 client._xep_0384_device_id = device_id | 590 client._xep_0384_device_id = device_id |
584 yield omemo_session.newDeviceList(client.jid, devices) | 591 yield omemo_session.newDeviceList(client.jid, devices) |
585 if omemo_session.republish_bundle: | 592 if omemo_session.republish_bundle: |
586 log.info(_("Saving public bundle for this device ({device_id})").format( | 593 log.info(_("Saving public bundle for this device ({device_id})").format( |
587 device_id=device_id)) | 594 device_id=device_id)) |
588 yield self.setBundle(client, omemo_session.public_bundle, device_id) | 595 yield defer.ensureDeferred( |
596 self.setBundle(client, omemo_session.public_bundle, device_id) | |
597 ) | |
589 client._xep_0384_ready.callback(None) | 598 client._xep_0384_ready.callback(None) |
590 del client._xep_0384_ready | 599 del client._xep_0384_ready |
591 | 600 |
592 ## XMPP PEP nodes manipulation | 601 ## XMPP PEP nodes manipulation |
593 | 602 |
606 if items: | 615 if items: |
607 try: | 616 try: |
608 list_elt = next(items[0].elements(NS_OMEMO, 'list')) | 617 list_elt = next(items[0].elements(NS_OMEMO, 'list')) |
609 except StopIteration: | 618 except StopIteration: |
610 log.warning(_("no list element found in OMEMO devices list")) | 619 log.warning(_("no list element found in OMEMO devices list")) |
611 return | 620 return devices |
612 for device_elt in list_elt.elements(NS_OMEMO, 'device'): | 621 for device_elt in list_elt.elements(NS_OMEMO, 'device'): |
613 try: | 622 try: |
614 device_id = int(device_elt['id']) | 623 device_id = int(device_elt['id']) |
615 except KeyError: | 624 except KeyError: |
616 log.warning(_('device element is missing "id" attribute: {elt}') | 625 log.warning(_('device element is missing "id" attribute: {elt}') |
639 defer.returnValue(set()) | 648 defer.returnValue(set()) |
640 | 649 |
641 devices = self.parseDevices(items) | 650 devices = self.parseDevices(items) |
642 defer.returnValue(devices) | 651 defer.returnValue(devices) |
643 | 652 |
644 def setDevicesEb(self, failure_): | 653 async def setDevices(self, client, devices): |
645 log.warning(_("Can't set devices: {reason}").format(reason=failure_)) | 654 log.debug(f"setting devices with {', '.join(devices)}") |
646 | |
647 def setDevices(self, client, devices): | |
648 list_elt = domish.Element((NS_OMEMO, 'list')) | 655 list_elt = domish.Element((NS_OMEMO, 'list')) |
649 for device in devices: | 656 for device in devices: |
650 device_elt = list_elt.addElement('device') | 657 device_elt = list_elt.addElement('device') |
651 device_elt['id'] = str(device) | 658 device_elt['id'] = str(device) |
652 d = self._p.sendItem( | 659 try: |
653 client, None, NS_OMEMO_DEVICES, list_elt, item_id=self._p.ID_SINGLETON) | 660 await self._p.sendItem( |
654 d.addErrback(self.setDevicesEb) | 661 client, None, NS_OMEMO_DEVICES, list_elt, |
655 return d | 662 item_id=self._p.ID_SINGLETON, |
663 extra={ | |
664 self._p.EXTRA_PUBLISH_OPTIONS: {self._p.OPT_MAX_ITEMS: 1}, | |
665 self._p.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options", | |
666 } | |
667 ) | |
668 except Exception as e: | |
669 log.warning(_("Can't set devices: {reason}").format(reason=e)) | |
656 | 670 |
657 # bundles | 671 # bundles |
658 | 672 |
659 @defer.inlineCallbacks | 673 @defer.inlineCallbacks |
660 def getBundles(self, client, entity_jid, devices_ids): | 674 def getBundles(self, client, entity_jid, devices_ids): |
678 except exceptions.NotFound: | 692 except exceptions.NotFound: |
679 log.warning(_("Bundle missing for device {device_id}") | 693 log.warning(_("Bundle missing for device {device_id}") |
680 .format(device_id=device_id)) | 694 .format(device_id=device_id)) |
681 missing.add(device_id) | 695 missing.add(device_id) |
682 continue | 696 continue |
683 except error.StanzaError as e: | 697 except jabber_error.StanzaError as e: |
684 log.warning(_("Can't get bundle for device {device_id}: {reason}") | 698 log.warning(_("Can't get bundle for device {device_id}: {reason}") |
685 .format(device_id=device_id, reason=e)) | 699 .format(device_id=device_id, reason=e)) |
686 continue | 700 continue |
687 if not items: | 701 if not items: |
688 log.warning(_("no item found in node {node}, can't get public bundle " | 702 log.warning(_("no item found in node {node}, can't get public bundle " |
735 bundles[device_id] = ExtendedPublicBundle.parse(omemo_backend, ik, spk, | 749 bundles[device_id] = ExtendedPublicBundle.parse(omemo_backend, ik, spk, |
736 spkSignature, otpks) | 750 spkSignature, otpks) |
737 | 751 |
738 defer.returnValue((bundles, missing)) | 752 defer.returnValue((bundles, missing)) |
739 | 753 |
740 def setBundleEb(self, failure_): | 754 async def setBundle(self, client, bundle, device_id): |
741 log.warning(_("Can't set bundle: {reason}").format(reason=failure_)) | |
742 | |
743 def setBundle(self, client, bundle, device_id): | |
744 """Set public bundle for this device. | 755 """Set public bundle for this device. |
745 | 756 |
746 @param bundle(ExtendedPublicBundle): bundle to publish | 757 @param bundle(ExtendedPublicBundle): bundle to publish |
747 """ | 758 """ |
748 log.debug(_("updating bundle for {device_id}").format(device_id=device_id)) | 759 log.debug(_("updating bundle for {device_id}").format(device_id=device_id)) |
767 'preKeyPublic', | 778 'preKeyPublic', |
768 content=b64enc(otpk["key"])) | 779 content=b64enc(otpk["key"])) |
769 preKeyPublic_elt['preKeyId'] = str(otpk['id']) | 780 preKeyPublic_elt['preKeyId'] = str(otpk['id']) |
770 | 781 |
771 node = NS_OMEMO_BUNDLE.format(device_id=device_id) | 782 node = NS_OMEMO_BUNDLE.format(device_id=device_id) |
772 d = self._p.sendItem(client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON) | 783 try: |
773 d.addErrback(self.setBundleEb) | 784 await self._p.sendItem( |
774 return d | 785 client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON, |
786 extra={ | |
787 self._p.EXTRA_PUBLISH_OPTIONS: {self._p.OPT_MAX_ITEMS: 1}, | |
788 self._p.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options", | |
789 } | |
790 ) | |
791 except Exception as e: | |
792 log.warning(_("Can't set bundle: {reason}").format(reason=e)) | |
775 | 793 |
776 ## PEP node events callbacks | 794 ## PEP node events callbacks |
777 | 795 |
778 @defer.inlineCallbacks | 796 async def onNewDevices(self, itemsEvent, profile): |
779 def onNewDevices(self, itemsEvent, profile): | |
780 client = self.host.getClient(profile) | 797 client = self.host.getClient(profile) |
781 try: | 798 try: |
782 omemo_session = client._xep_0384_session | 799 omemo_session = client._xep_0384_session |
783 except AttributeError: | 800 except AttributeError: |
784 yield client._xep_0384_ready | 801 await client._xep_0384_ready |
785 omemo_session = client._xep_0384_session | 802 omemo_session = client._xep_0384_session |
786 entity = itemsEvent.sender | 803 entity = itemsEvent.sender |
787 | 804 |
788 devices = self.parseDevices(itemsEvent.items) | 805 devices = self.parseDevices(itemsEvent.items) |
789 omemo_session.newDeviceList(entity, devices) | 806 await omemo_session.newDeviceList(entity, devices) |
790 | 807 |
791 if entity == client.jid.userhostJID(): | 808 if entity == client.jid.userhostJID(): |
792 own_device = client._xep_0384_device_id | 809 own_device = client._xep_0384_device_id |
793 if own_device not in devices: | 810 if own_device not in devices: |
794 log.warning(_("Our own device is missing from devices list, fixing it")) | 811 log.warning(_("Our own device is missing from devices list, fixing it")) |
795 devices.add(own_device) | 812 devices.add(own_device) |
796 yield self.setDevices(client, devices) | 813 await self.setDevices(client, devices) |
797 | 814 |
798 ## triggers | 815 ## triggers |
799 | 816 |
800 @defer.inlineCallbacks | 817 @defer.inlineCallbacks |
801 def handleProblems(self, client, feedback_jid, bundles, expect_problems, problems): | 818 def handleProblems(self, client, feedback_jid, bundles, expect_problems, problems): |
1096 finally: | 1113 finally: |
1097 if omemo_session.republish_bundle: | 1114 if omemo_session.republish_bundle: |
1098 # we don't wait for the Deferred (i.e. no yield) on purpose | 1115 # we don't wait for the Deferred (i.e. no yield) on purpose |
1099 # there is no need to block the whole message workflow while | 1116 # there is no need to block the whole message workflow while |
1100 # updating the bundle | 1117 # updating the bundle |
1101 self.setBundle(client, omemo_session.public_bundle, device_id) | 1118 defer.ensureDeferred( |
1119 self.setBundle(client, omemo_session.public_bundle, device_id) | |
1120 ) | |
1102 | 1121 |
1103 message_elt.children.remove(encrypted_elt) | 1122 message_elt.children.remove(encrypted_elt) |
1104 if plaintext: | 1123 if plaintext: |
1105 message_elt.addElement("body", content=plaintext) | 1124 message_elt.addElement("body", content=plaintext) |
1106 post_treat.addCallback(client.encryption.markAsEncrypted) | 1125 post_treat.addCallback(client.encryption.markAsEncrypted) |