diff sat/plugins/plugin_xep_0060.py @ 3934:e345d93fb6e5

plugin OXPS: OpenPGP for XMPP Pubsub implementation: OpenPGP for XMPP Pubsub (https://xmpp.org/extensions/inbox/pubsub-encryption.html, currently a protoXEP) is implemented and activated when `encrypted` is set to `True` in pubsub's `extra` data. On item retrieval, the decryption is transparent if the key is known, except if the `decrypt` key in `extra` is set to `False` (notably useful when one wants to checks that data is well encrypted). Methods and corresponding bridge methods have been implemented to manage shared secrets (to share, revoke or rotate the secrets). plugin XEP-0060's `XEP-0060_publish` trigger point as been move before actual publish so item can be modified (here e2ee) by the triggers. A new `XEP-0060_items` trigger point has also been added. `encrypted` flag can be used with plugin XEP-0277's microblog data rel 380
author Goffi <goffi@goffi.org>
date Sat, 15 Oct 2022 20:36:53 +0200
parents cecf45416403
children cd4d95b3fed3
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0060.py	Tue Sep 20 16:22:18 2022 +0200
+++ b/sat/plugins/plugin_xep_0060.py	Sat Oct 15 20:36:53 2022 +0200
@@ -19,7 +19,7 @@
 
 from collections import namedtuple
 from functools import reduce
-from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, Callable
+from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union
 import urllib.error
 import urllib.parse
 import urllib.request
@@ -658,25 +658,33 @@
         nodeIdentifier: str,
         items: Optional[List[domish.Element]] = None,
         options: Optional[dict] = None,
-        sender: Optional[jid.JID] = None
+        sender: Optional[jid.JID] = None,
+        extra: Optional[Dict[str, Any]] = None
     ) -> domish.Element:
         """Publish pubsub items
 
         @param sender: sender of the request,
             client.jid will be used if nto set
+        @param extra: extra data
+            not used directly by ``publish``, but may be used in triggers
         @return: IQ result stanza
+        @trigger XEP-0060_publish: called just before publication.
+            if it returns False, extra must have a "iq_result_elt" key set with
+            domish.Element to return.
         """
         if sender is None:
             sender = client.jid
+        if extra is None:
+            extra = {}
+        if not await self.host.trigger.asyncPoint(
+            "XEP-0060_publish", client, service, nodeIdentifier, items, options, sender,
+            extra
+        ):
+            return extra["iq_result_elt"]
         iq_result_elt = await client.pubsub_client.publish(
             service, nodeIdentifier, items, sender,
             options=options
         )
-
-        await self.host.trigger.asyncPoint(
-            "XEP-0060_publish", client, service, nodeIdentifier, items, options,
-            iq_result_elt
-        )
         return iq_result_elt
 
     def _unwrapMAMMessage(self, message_elt):
@@ -768,7 +776,7 @@
         try:
             mam_query = extra["mam"]
         except KeyError:
-            d = client.pubsub_client.items(
+            d = defer.ensureDeferred(client.pubsub_client.items(
                 service = service,
                 nodeIdentifier = node,
                 maxItems = max_items,
@@ -776,8 +784,9 @@
                 sender = None,
                 itemIdentifiers = item_ids,
                 orderBy = extra.get(C.KEY_ORDER_BY),
-                rsm_request = rsm_request
-            )
+                rsm_request = rsm_request,
+                extra = extra
+            ))
             # we have no MAM data here, so we add None
             d.addErrback(sat_defer.stanza2NotFound)
             d.addTimeout(TIMEOUT, reactor)
@@ -1652,6 +1661,32 @@
     def connectionInitialized(self):
         rsm.PubSubClient.connectionInitialized(self)
 
+    async def items(
+        self,
+        service: Optional[jid.JID],
+        nodeIdentifier: str,
+        maxItems: Optional[int] = None,
+        subscriptionIdentifier: Optional[str] = None,
+        sender: Optional[jid.JID] = None,
+        itemIdentifiers: Optional[Set[str]] = None,
+        orderBy: Optional[List[str]] = None,
+        rsm_request: Optional[rsm.RSMRequest] = None,
+        extra: Optional[Dict[str, Any]] = None,
+    ):
+        if extra is None:
+            extra = {}
+        items, rsm_response = await super().items(
+            service, nodeIdentifier, maxItems, subscriptionIdentifier, sender,
+            itemIdentifiers, orderBy, rsm_request
+        )
+        # items must be returned, thus this async point can't stop the workflow (but it
+        # can modify returned items)
+        await self.host.trigger.asyncPoint(
+            "XEP-0060_items", self.parent, service, nodeIdentifier, items, rsm_response,
+            extra
+        )
+        return items, rsm_response
+
     def _getNodeCallbacks(self, node, event):
         """Generate callbacks from given node and event