comparison libervia/backend/plugins/plugin_xep_0308.py @ 4198:b1207332cea2

plugin XEP-0308: fix ID used, recipients and author check.
author Goffi <goffi@goffi.org>
date Wed, 13 Dec 2023 22:00:25 +0100
parents a1f7040b5a15
children 0d7bb4df2343
comparison
equal deleted inserted replaced
4197:9cda0347e0ac 4198:b1207332cea2
14 14
15 # You should have received a copy of the GNU Affero General Public License 15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>. 16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 17
18 import time 18 import time
19 import uuid
19 20
20 from sqlalchemy.orm.attributes import flag_modified 21 from sqlalchemy.orm.attributes import flag_modified
21 from twisted.internet import defer 22 from twisted.internet import defer
22 from twisted.words.protocols.jabber import xmlstream 23 from twisted.words.protocols.jabber import xmlstream
23 from twisted.words.protocols.jabber import jid 24 from twisted.words.protocols.jabber import jid
81 stmt = ( 82 stmt = (
82 select(History) 83 select(History)
83 .where( 84 .where(
84 History.profile_id == profile_id, 85 History.profile_id == profile_id,
85 History.source == from_jid.userhost(), 86 History.source == from_jid.userhost(),
87 History.dest == client.jid.userhost(),
86 History.type == message_type, 88 History.type == message_type,
87 ) 89 )
88 .options(joinedload(History.messages)) 90 .options(joinedload(History.messages))
89 .options(joinedload(History.subjects)) 91 .options(joinedload(History.subjects))
90 .options(joinedload(History.thread)) 92 .options(joinedload(History.thread))
152 self, 154 self,
153 client: SatXMPPEntity, 155 client: SatXMPPEntity,
154 message_elt: domish.Element, 156 message_elt: domish.Element,
155 post_treat: defer.Deferred, 157 post_treat: defer.Deferred,
156 ) -> bool: 158 ) -> bool:
159 from_jid = jid.JID(message_elt["from"])
157 replace_elt = next(message_elt.elements(NS_MESSAGE_CORRECT, "replace"), None) 160 replace_elt = next(message_elt.elements(NS_MESSAGE_CORRECT, "replace"), None)
158 if not replace_elt: 161 if not replace_elt:
159 return True 162 return True
160 try: 163 try:
161 replace_id = replace_elt["id"].strip() 164 replace_id = replace_elt["id"].strip()
165 log.warning(f"Invalid message correction: {message_elt.toXml()}") 168 log.warning(f"Invalid message correction: {message_elt.toXml()}")
166 else: 169 else:
167 edited_history = await self.get_last_history(client, message_elt) 170 edited_history = await self.get_last_history(client, message_elt)
168 if edited_history is None: 171 if edited_history is None:
169 log.warning( 172 log.warning(
170 f"No message found from {message_elt['from']}, can't correct " 173 f"No message found from {from_jid.full()}, can't correct "
171 f"anything: {message_elt.toXml()}" 174 f"anything: {message_elt.toXml()}"
172 ) 175 )
173 return False 176 return True
174 if edited_history.extra.get("message_id") != replace_id: 177 check_id = edited_history.origin_id or edited_history.extra.get("message_id")
178 if check_id != replace_id:
175 log.warning( 179 log.warning(
176 "Can't apply correction: it doesn't reference the last one: " 180 "Can't apply correction: it doesn't reference the last one:\n"
177 f"{message_elt.toXml}" 181 f"{message_elt.toXml()}"
178 ) 182 )
179 return False 183 return True
184 # TODO: this only accept same full JID, if we do more than last message
185 # correction, we may want to accept different resource for non groupchat
186 # messages (in case of reconnection with resource change for instance).
187 if edited_history.source_jid != from_jid:
188 log.warning(
189 "Can't apply correction: correction doesn't come from same author "
190 f"{edited_history.source_jid.full()}:\n{message_elt.toXml()}"
191 )
192 return True
193
180 previous_message_data = edited_history.serialise() 194 previous_message_data = edited_history.serialise()
181 message_data = client.messageProt.parse_message(message_elt) 195 message_data = client.messageProt.parse_message(message_elt)
182 if not message_data["message"] and not message_data["subject"]: 196 if not message_data["message"] and not message_data["subject"]:
183 log.warning( 197 log.warning(
184 "Message correction doesn't have body not subject, we can't edit " 198 "Message correction doesn't have body nor subject, we can't edit "
185 "anything" 199 "anything"
186 ) 200 )
187 return False 201 return False
188 202
189 await self.update_history( 203 await self.update_history(
225 raise exceptions.NotFound( 239 raise exceptions.NotFound(
226 f"message to edit not found in database ({message_id})" 240 f"message to edit not found in database ({message_id})"
227 ) 241 )
228 if edited_history.type == C.MESS_TYPE_GROUPCHAT: 242 if edited_history.type == C.MESS_TYPE_GROUPCHAT:
229 is_group_chat = True 243 is_group_chat = True
230 peer_jid = edited_history.dest_jid 244 # In the case of group chat, our message is sent by ourself with our room JID.
245 peer_jid = edited_history.source_jid
231 else: 246 else:
232 is_group_chat = False 247 is_group_chat = False
233 peer_jid = jid.JID(edited_history.dest) 248 peer_jid = jid.JID(edited_history.dest)
234 history_data = await self.host.memory.history_get( 249 history_data = await self.host.memory.history_get(
235 client.jid, peer_jid, limit=1, profile=client.profile 250 client.jid, peer_jid, limit=1, profile=client.profile
236 ) 251 )
237 if not history_data: 252 if not history_data:
238 raise exceptions.NotFound( 253 raise exceptions.NotFound(
239 "No message found in conversation with {peer_jid.full()}" 254 "No message found in conversation with {peer_jid.userhost()}"
240 ) 255 )
241 last_mess = history_data[0] 256 last_mess = history_data[0]
242 if last_mess[0] != message_id: 257 if last_mess[0] != message_id:
243 raise ValueError( 258 raise ValueError(
244 f"{message_id} is not the last message of the discussion, we can't edit " 259 f"{message_id} is not the last message of the discussion, we can't edit "
258 # message will be updated and signal sent on reception in group chat 273 # message will be updated and signal sent on reception in group chat
259 store = not is_group_chat 274 store = not is_group_chat
260 ) 275 )
261 276
262 serialised = edited_history.serialise() 277 serialised = edited_history.serialise()
263 serialised["from"] = jid.JID(serialised["from"]) 278 serialised["uid"] = str(uuid.uuid4())
264 serialised["to"] = jid.JID(serialised["to"]) 279 serialised["from"] = client.jid
280 # for group chat, we want to send the correction to the room
281 serialised["to"] = peer_jid.userhostJID() if is_group_chat else peer_jid
265 282
266 message_elt = client.generate_message_xml(serialised)["xml"] 283 message_elt = client.generate_message_xml(serialised)["xml"]
267 replace_elt = message_elt.addElement((NS_MESSAGE_CORRECT, "replace")) 284 replace_elt = message_elt.addElement((NS_MESSAGE_CORRECT, "replace"))
268 replace_elt["id"] = message_id 285 replace_elt["id"] = edited_history.origin_id
269 self._h.add_hint_elements(message_elt, [self._h.HINT_STORE]) 286 self._h.add_hint_elements(message_elt, [self._h.HINT_STORE])
270 client.send(message_elt) 287 client.send(message_elt)
271 288
272 def _message_edit(self, message_id: str, edit_data_s: str, profile: str) -> None: 289 def _message_edit(self, message_id: str, edit_data_s: str, profile: str) -> None:
273 client = self.host.get_client(profile) 290 client = self.host.get_client(profile)