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)