# HG changeset patch
# User Goffi <goffi@goffi.org>
# Date 1702501225 -3600
# Node ID 7eda7cb8a15c7ffddd2336b00aa01a0e435f7d6f
# Parent  b1207332cea272ea1771e0a4ed8d48b2dcd04407
plugin XEP-0424: fix ID used and recipients

diff -r b1207332cea2 -r 7eda7cb8a15c libervia/backend/plugins/plugin_xep_0424.py
--- 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)]