changeset 4199:7eda7cb8a15c

plugin XEP-0424: fix ID used and recipients
author Goffi <goffi@goffi.org>
date Wed, 13 Dec 2023 22:00:25 +0100
parents b1207332cea2
children 5d2de6c1156d
files libervia/backend/plugins/plugin_xep_0424.py
diffstat 1 files changed, 59 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_xep_0424.py	Wed Dec 13 22:00:25 2023 +0100
+++ b/libervia/backend/plugins/plugin_xep_0424.py	Wed Dec 13 22:00:25 2023 +0100
@@ -43,6 +43,7 @@
     C.PI_MODES: C.PLUG_MODE_BOTH,
     C.PI_PROTOCOLS: ["XEP-0424"],
     C.PI_DEPENDENCIES: ["XEP-0334", "XEP-0428"],
+    C.PI_RECOMMENDATIONS: ["XEP-0045"],
     C.PI_MAIN: "XEP_0424",
     C.PI_HANDLER: "yes",
     C.PI_DESCRIPTION: _("""Implementation Message Retraction"""),
@@ -67,12 +68,12 @@
 
 
 class XEP_0424:
-
     def __init__(self, host):
         log.info(f"plugin {PLUGIN_INFO[C.PI_NAME]!r} initialization")
         self.host = host
         host.memory.update_params(PARAMS)
         self._h = host.plugins["XEP-0334"]
+        self._m = host.plugins.get("XEP-0045")
         host.register_namespace("message-retract", NS_MESSAGE_RETRACT)
         host.trigger.add("message_received", self._message_received_trigger, 100)
         host.bridge.add_method(
@@ -89,66 +90,65 @@
 
     def _retract(self, message_id: str, profile: str) -> None:
         client = self.host.get_client(profile)
-        return defer.ensureDeferred(
-            self.retract(client, message_id)
-        )
+        return defer.ensureDeferred(self.retract(client, message_id))
 
-    def retract_by_origin_id(
-        self,
-        client: SatXMPPEntity,
-        peer_jid: jid.JID,
-        origin_id: str
+    def send_retract(
+        self, client: SatXMPPEntity, peer_jid: jid.JID, retract_id: str, history: History
     ) -> None:
         """Send a message retraction using origin-id
 
         [retract] should be prefered: internal ID should be used as it is independant of
         XEPs changes. However, in some case messages may not be stored in database
         (notably for some components), and then this method can be used
-        @param origin_id: origin-id as specified in XEP-0359
+
+        @param retract_id: ID (origin or stanza according to XEP-0359) of message to
+            retract
+        @param history: history instance of the message to retract
         """
         message_elt = domish.Element((None, "message"))
         message_elt["from"] = client.jid.full()
         message_elt["to"] = peer_jid.full()
+        message_elt["type"] = history.type
         retract_elt = message_elt.addElement((NS_MESSAGE_RETRACT, "retract"))
-        retract_elt["id"] = origin_id
+        retract_elt["id"] = retract_id
         self.host.plugins["XEP-0428"].add_fallback_elt(
             message_elt,
             "[A message retraction has been requested, but your client doesn't support "
-            "it]"
+            "it]",
         )
         self._h.add_hint_elements(message_elt, [self._h.HINT_STORE])
         client.send(message_elt)
 
-    async def retract_by_history(
-        self,
-        client: SatXMPPEntity,
-        history: History
-    ) -> None:
+    async def retract_by_history(self, client: SatXMPPEntity, history: History) -> None:
         """Send a message retraction using History instance
 
         This method is to use instead of [retract] when the history instance is already
         retrieved. Note that the instance must have messages and subjets loaded
         @param history: history instance of the message to retract
         """
-        try:
-            origin_id = history.origin_id
-        except KeyError:
-            raise exceptions.FeatureNotFound(
-                f"message to retract doesn't have the necessary origin-id, the sending "
-                "client is probably not supporting message retraction."
-            )
+        if history.type == C.MESS_TYPE_GROUPCHAT:
+            is_group_chat = True
+            peer_jid = jid.JID(history.source)
+            retract_id = history.stanza_id
         else:
-            if history.type == C.MESS_TYPE_GROUPCHAT:
-                is_group_chat = True
+            is_group_chat = False
+            peer_jid = jid.JID(history.dest)
+            if self._m is not None and self._m.is_joined_room(client, peer_jid):
+                # it's actually a private MUC message, we need the full JID.
                 peer_jid = history.dest_jid
-            else:
-                is_group_chat = False
-                peer_jid = jid.JID(history.dest)
-            self.retract_by_origin_id(client, peer_jid, origin_id)
-            if not is_group_chat:
-                # retraction will happen when <retract> message will be received in the
-                # chat.
-                await self.retract_db_history(client, history)
+            retract_id = history.origin_id
+
+        if not retract_id:
+            raise exceptions.FeatureNotFound(
+                "Message to retract doesn't have the necessary ID, the sending client is "
+                "probably not supporting message retraction."
+            )
+
+        self.send_retract(client, peer_jid, retract_id, history)
+        if not is_group_chat:
+            # retraction will happen when <retract> message will be received in the
+            # chat.
+            await self.retract_db_history(client, history)
 
     async def retract(
         self,
@@ -165,8 +165,11 @@
         if not message_id:
             raise ValueError("message_id can't be empty")
         history = await self.host.memory.storage.get(
-            client, History, History.uid, message_id,
-            joined_loads=[History.messages, History.subjects, History.thread]
+            client,
+            History,
+            History.uid,
+            message_id,
+            joined_loads=[History.messages, History.subjects, History.thread],
         )
         if history is None:
             raise exceptions.NotFound(
@@ -186,14 +189,14 @@
         keep_history = await self.host.memory.param_get_a_async(
             NAME, CATEGORY, profile_key=client.profile
         )
-        old_version: Dict[str, Any] = {
-            "timestamp": time.time()
-        }
+        old_version: Dict[str, Any] = {"timestamp": time.time()}
         if keep_history:
-            old_version.update({
-                "messages": [m.serialise() for m in history.messages],
-                "subjects": [s.serialise() for s in history.subjects],
-            })
+            old_version.update(
+                {
+                    "messages": [m.serialise() for m in history.messages],
+                    "subjects": [s.serialise() for s in history.subjects],
+                }
+            )
 
         history.extra.setdefault("old_versions", []).append(old_version)
 
@@ -219,7 +222,7 @@
         self,
         client: SatXMPPEntity,
         message_elt: domish.Element,
-        post_treat: defer.Deferred
+        post_treat: defer.Deferred,
     ) -> bool:
         retract_elt = next(message_elt.elements(NS_MESSAGE_RETRACT, "retract"), None)
         if not retract_elt:
@@ -230,17 +233,23 @@
             else:
                 col_id = History.origin_id
             history = await self.host.memory.storage.get(
-                client, History, col_id, retract_elt["id"],
-                joined_loads=[History.messages, History.subjects, History.thread]
+                client,
+                History,
+                col_id,
+                retract_elt["id"],
+                joined_loads=[History.messages, History.subjects, History.thread],
             )
         except KeyError:
             log.warning(f"invalid retract element, missing id: {retract_elt.toXml()}")
             return False
         from_jid = jid.JID(message_elt["from"])
 
-        if (
-            history is not None
-            and history.source_jid.userhostJID() != from_jid.userhostJID()
+        if history is not None and (
+            (history.type == C.MESS_TYPE_GROUPCHAT and history.source_jid != from_jid)
+            or (
+                history.type != C.MESS_TYPE_GROUPCHAT
+                and history.source_jid.userhostJID() != from_jid.userhostJID()
+            )
         ):
             log.warning(
                 f"Received message retraction from {from_jid.full()}, but the message to "
@@ -257,9 +266,7 @@
         if history is None:
             # we check history after the trigger because we may be in a component which
             # doesn't store messages in database.
-            log.warning(
-                f"No message found with given origin-id: {message_elt.toXml()}"
-            )
+            log.warning(f"No message found with given id: {message_elt.toXml()}")
             return False
         log.info(f"[{client.profile}] retracting message {history.uid!r}")
         await self.retract_db_history(client, history)
@@ -268,7 +275,6 @@
 
 @implementer(disco.IDisco)
 class XEP_0424_handler(xmlstream.XMPPHandler):
-
     def getDiscoInfo(self, __, target, nodeIdentifier=""):
         return [disco.DiscoFeature(NS_MESSAGE_RETRACT)]