changeset 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 c2f958dde5d2
children bfa1bde97f48
files sat/plugins/plugin_xep_0384.py
diffstat 1 files changed, 46 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0384.py	Wed Mar 11 19:15:48 2020 +0100
+++ b/sat/plugins/plugin_xep_0384.py	Wed Mar 11 20:44:49 2020 +0100
@@ -23,7 +23,7 @@
 from twisted.internet import defer, reactor
 from twisted.words.xish import domish
 from twisted.words.protocols.jabber import jid
-from twisted.words.protocols.jabber import error
+from twisted.words.protocols.jabber import error as jabber_error
 from sat.memory import persistent
 from functools import partial
 from sat.tools import xml_tools
@@ -397,7 +397,11 @@
         host.trigger.add("sendMessageData", self._sendMessageDataTrigger)
         self.host.registerEncryptionPlugin(self, "OMEMO", NS_OMEMO, 100)
         pep = host.plugins['XEP-0163']
-        pep.addPEPEvent("OMEMO_DEVICES", NS_OMEMO_DEVICES, self.onNewDevices)
+        pep.addPEPEvent(
+            "OMEMO_DEVICES", NS_OMEMO_DEVICES,
+            lambda itemsEvent, profile: defer.ensureDeferred(
+                self.onNewDevices(itemsEvent, profile))
+        )
 
     @defer.inlineCallbacks
     def trustUICb(self, xmlui_data, trust_data, expect_problems=None,
@@ -570,9 +574,12 @@
             # and we save it
             persistent_dict[KEY_DEVICE_ID] = device_id
 
+        log.debug(f"our OMEMO device id is {device_id}")
+
         if device_id not in devices:
+            log.debug(f"our device id ({device_id}) is not in the list, adding it")
             devices.add(device_id)
-            yield self.setDevices(client, devices)
+            yield defer.ensureDeferred(self.setDevices(client, devices))
 
         all_jids = yield persistent_dict.get(KEY_ALL_JIDS, set())
 
@@ -585,7 +592,9 @@
         if omemo_session.republish_bundle:
             log.info(_("Saving public bundle for this device ({device_id})").format(
                 device_id=device_id))
-            yield self.setBundle(client, omemo_session.public_bundle, device_id)
+            yield defer.ensureDeferred(
+                self.setBundle(client, omemo_session.public_bundle, device_id)
+            )
         client._xep_0384_ready.callback(None)
         del client._xep_0384_ready
 
@@ -608,7 +617,7 @@
                 list_elt = next(items[0].elements(NS_OMEMO, 'list'))
             except StopIteration:
                 log.warning(_("no list element found in OMEMO devices list"))
-                return
+                return devices
             for device_elt in list_elt.elements(NS_OMEMO, 'device'):
                 try:
                     device_id = int(device_elt['id'])
@@ -641,18 +650,23 @@
         devices = self.parseDevices(items)
         defer.returnValue(devices)
 
-    def setDevicesEb(self, failure_):
-        log.warning(_("Can't set devices: {reason}").format(reason=failure_))
-
-    def setDevices(self, client, devices):
+    async def setDevices(self, client, devices):
+        log.debug(f"setting devices with {', '.join(devices)}")
         list_elt = domish.Element((NS_OMEMO, 'list'))
         for device in devices:
             device_elt = list_elt.addElement('device')
             device_elt['id'] = str(device)
-        d = self._p.sendItem(
-            client, None, NS_OMEMO_DEVICES, list_elt, item_id=self._p.ID_SINGLETON)
-        d.addErrback(self.setDevicesEb)
-        return d
+        try:
+            await self._p.sendItem(
+                client, None, NS_OMEMO_DEVICES, list_elt,
+                item_id=self._p.ID_SINGLETON,
+                extra={
+                    self._p.EXTRA_PUBLISH_OPTIONS: {self._p.OPT_MAX_ITEMS: 1},
+                    self._p.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options",
+                }
+            )
+        except Exception as e:
+            log.warning(_("Can't set devices: {reason}").format(reason=e))
 
     # bundles
 
@@ -680,7 +694,7 @@
                     .format(device_id=device_id))
                 missing.add(device_id)
                 continue
-            except error.StanzaError as e:
+            except jabber_error.StanzaError as e:
                 log.warning(_("Can't get bundle for device {device_id}: {reason}")
                     .format(device_id=device_id, reason=e))
                 continue
@@ -737,10 +751,7 @@
 
         defer.returnValue((bundles, missing))
 
-    def setBundleEb(self, failure_):
-        log.warning(_("Can't set bundle: {reason}").format(reason=failure_))
-
-    def setBundle(self, client, bundle, device_id):
+    async def setBundle(self, client, bundle, device_id):
         """Set public bundle for this device.
 
         @param bundle(ExtendedPublicBundle): bundle to publish
@@ -769,31 +780,37 @@
             preKeyPublic_elt['preKeyId'] = str(otpk['id'])
 
         node = NS_OMEMO_BUNDLE.format(device_id=device_id)
-        d = self._p.sendItem(client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON)
-        d.addErrback(self.setBundleEb)
-        return d
+        try:
+            await self._p.sendItem(
+                client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON,
+                extra={
+                    self._p.EXTRA_PUBLISH_OPTIONS: {self._p.OPT_MAX_ITEMS: 1},
+                    self._p.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options",
+                }
+            )
+        except Exception as e:
+            log.warning(_("Can't set bundle: {reason}").format(reason=e))
 
     ## PEP node events callbacks
 
-    @defer.inlineCallbacks
-    def onNewDevices(self, itemsEvent, profile):
+    async def onNewDevices(self, itemsEvent, profile):
         client = self.host.getClient(profile)
         try:
             omemo_session = client._xep_0384_session
         except AttributeError:
-            yield client._xep_0384_ready
+            await client._xep_0384_ready
             omemo_session = client._xep_0384_session
         entity = itemsEvent.sender
 
         devices = self.parseDevices(itemsEvent.items)
-        omemo_session.newDeviceList(entity, devices)
+        await omemo_session.newDeviceList(entity, devices)
 
         if entity == client.jid.userhostJID():
             own_device = client._xep_0384_device_id
             if own_device not in devices:
                 log.warning(_("Our own device is missing from devices list, fixing it"))
                 devices.add(own_device)
-                yield self.setDevices(client, devices)
+                await self.setDevices(client, devices)
 
     ## triggers
 
@@ -1098,7 +1115,9 @@
                     # we don't wait for the Deferred (i.e. no yield) on purpose
                     # there is no need to block the whole message workflow while
                     # updating the bundle
-                    self.setBundle(client, omemo_session.public_bundle, device_id)
+                    defer.ensureDeferred(
+                        self.setBundle(client, omemo_session.public_bundle, device_id)
+                    )
 
         message_elt.children.remove(encrypted_elt)
         if plaintext: