diff --git a/libervia/backend/plugins/plugin_xep_0308.py b/libervia/backend/plugins/plugin_xep_0308.py index 32df912f..90acd98e 100644 --- a/libervia/backend/plugins/plugin_xep_0308.py +++ b/libervia/backend/plugins/plugin_xep_0308.py @@ -163,58 +163,73 @@ class XEP_0308: replace_elt = next(message_elt.elements(NS_MESSAGE_CORRECT, "replace"), None) if not replace_elt: return True - try: - replace_id = replace_elt["id"].strip() - if not replace_id: - raise KeyError - except KeyError: + replace_id = (replace_elt.getAttribute("id") or "").strip() + if not replace_id: log.warning(f"Invalid message correction: {message_elt.toXml()}") - else: + return False + # look for the corrected message by id (this also handles our own messages, e.g. + # corrections from another device) + message_type = message_elt.getAttribute("type", C.MESS_TYPE_NORMAL) + edited_history = await self.host.memory.storage.get_history_from_xmpp_id( + client, replace_id, message_type + ) + if edited_history is None: + # Fallback: last message from the sender (history without usable id) edited_history = await self.get_last_history(client, message_elt) if edited_history is None: log.warning( - f"No message found from {from_jid.full()}, can't correct " - f"anything: {message_elt.toXml()}" + f"No message found to correct ({replace_id}), ignoring " + f"correction: {message_elt.toXml()}" ) - return True + return False check_id = edited_history.origin_id or edited_history.extra.get("message_id") if check_id != replace_id: log.warning( "Can't apply correction: it doesn't reference the last one:\n" f"{message_elt.toXml()}" ) - return True - # TODO: this only accept same full JID, if we do more than last message - # correction, we may want to accept different resource for non groupchat - # messages (in case of reconnection with resource change for instance). - if edited_history.source_jid != from_jid: - log.warning( - "Can't apply correction: correction doesn't come from same author " - f"{edited_history.source_jid.full()}:\n{message_elt.toXml()}" - ) - return True - - previous_message_data = edited_history.serialise() - message_data = await client.messageProt.parse_message(message_elt) - if not message_data["message"] and not message_data["subject"]: - log.warning( - "Message correction doesn't have body nor subject, we can't edit " - "anything" - ) return False - - await self.update_history( - client, - edited_history, - message_data.get("received_timestamp") or message_data["timestamp"], - message_data["message"], - message_data["subject"], - message_data["extra"], - previous_message_data["message"], - previous_message_data["subject"], - previous_message_data.get("extra"), + # for groupchat, the correction must come from the same occupant, for other types, + # the resource may have changed (e.g. reconnection) + same_author = ( + edited_history.source_jid == from_jid + if edited_history.type == C.MESS_TYPE_GROUPCHAT + else edited_history.source_jid.userhost() == from_jid.userhost() + ) + if not same_author: + log.warning( + "Can't apply correction: correction doesn't come from same author " + f"{edited_history.source_jid.full()}:\n{message_elt.toXml()}" ) + return False + previous_message_data = edited_history.serialise() + message_data = await client.messageProt.parse_message(message_elt) + if not message_data["message"] and not message_data["subject"]: + log.warning( + "Message correction doesn't have body nor subject, we can't edit anything" + ) + return False + if ( + message_data["message"] == previous_message_data["message"] + and message_data["subject"] == previous_message_data["subject"] + ): + # content is already up to date (e.g. our own correction redelivered by MAM + # after reconnection) + log.debug(f"Ignoring already applied correction: {message_elt.toXml()}") + return False + + await self.update_history( + client, + edited_history, + message_data.get("received_timestamp") or message_data["timestamp"], + message_data["message"], + message_data["subject"], + message_data["extra"], + previous_message_data["message"], + previous_message_data["subject"], + previous_message_data.get("extra"), + ) return False async def message_edit( @@ -248,7 +263,8 @@ class XEP_0308: peer_jid = edited_history.source_jid else: is_group_chat = False - peer_jid = jid.JID(edited_history.dest) + # keep the destination resource (e.g. room/nick for a MUC private message) + peer_jid = edited_history.dest_jid history_data = await self.host.memory.history_get( client.jid, peer_jid, limit=1, profile=client.profile )