From de11a00a8e6d448dbe909d5f96f04bdda9d8e8ec Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Wed, 27 Feb 2013 22:09:14 +0100
Subject: Improve XEP-0308 support

- Prevent correction of delayed messages
- Prevent correction of messages by someone else in a MUC (and in a
  private tab)
- Messages with unauthorized corrections (above) or wrong message id
  will be displayed as normal messages

TODO: restrict the corrections to the same fullJID (only in direct
"normal" conversations, because we can know in private an muc tabs, via
the User object)
---
 src/core.py        | 47 +++++++++++++++++++++++++++++++----------------
 src/tabs.py        | 53 +++++++++++++++++++++--------------------------------
 src/text_buffer.py | 10 ++++++++--
 3 files changed, 60 insertions(+), 50 deletions(-)

(limited to 'src')

diff --git a/src/core.py b/src/core.py
index c7fac492..4d1bf45a 100644
--- a/src/core.py
+++ b/src/core.py
@@ -53,7 +53,7 @@ from config import config
 from logger import logger
 from roster import roster
 from contact import Contact, Resource
-from text_buffer import TextBuffer
+from text_buffer import TextBuffer, CorrectionError
 from keyboard import keyboard
 from theming import get_theme
 from fifo import Fifo
@@ -2551,9 +2551,14 @@ class Core(object):
             delayed = False
             date = None
         replaced_id = message['replace']['id']
+        replaced = False
         if replaced_id is not '':
-            conversation.modify_message(body, replaced_id, message['id'])
-        else:
+            try:
+                conversation.modify_message(body, replaced_id, message['id'])
+                replaced = True
+            except CorrectionError:
+                pass
+        if not replaced :
             conversation._text_buffer.add_message(body, date,
                     nickname=remote_nick,
                     nick_color=get_theme().COLOR_REMOTE_USER,
@@ -2603,18 +2608,23 @@ class Core(object):
         if not tab:
             self.information(_("message received for a non-existing room: %s") % (room_from))
             return
-        if tab.get_user_by_name(nick_from) and\
-                tab.get_user_by_name(nick_from) in tab.ignores:
+        user = tab.get_user_by_name(nick_from)
+        if user and user in tab.ignores:
             return
         self.events.trigger('muc_msg', message, tab)
         body = xhtml.get_body_from_message_stanza(message)
         if body:
             date = date if delayed == True else None
             replaced_id = message['replace']['id']
+            replaced = False
             if replaced_id is not '':
-                if tab.modify_message(body, replaced_id, message['id'], date, nick_from):
-                    self.events.trigger('highlight', message, tab)
-            elif tab.add_message(body, date, nick_from, history=True if date else False, identifier=message['id']):
+                try:
+                    if tab.modify_message(body, replaced_id, message['id'], date, nick_from, user):
+                        self.events.trigger('highlight', message, tab)
+                    replaced = True
+                except CorrectionError:
+                    pass
+            if not replaced and tab.add_message(body, date, nick_from, history=True if date else False, identifier=message['id']):
                 self.events.trigger('highlight', message, tab)
             if tab is self.current_tab():
                 tab.text_win.refresh()
@@ -2653,26 +2663,31 @@ class Core(object):
         if not body or not tab:
             return
         replaced_id = message['replace']['id']
+        replaced = False
         if replaced_id is not '':
-            tab.modify_message(body, replaced_id, message['id'])
-        else:
+            user = self.get_tab_by_name(room_from, tabs.MucTab).get_user_by_name(nick_from),
+            try:
+                tab.modify_message(body, replaced_id, message['id'], user=user)
+                replaced = True
+            except CorrectionError:
+                pass
+        if not replaced:
             tab.add_message(body, time=None, nickname=nick_from,
                             forced_user=self.get_tab_by_name(room_from, tabs.MucTab).get_user_by_name(nick_from),
                             identifier=message['id'])
-        conversation = self.get_tab_by_name(jid.full, tabs.PrivateTab)
-        if conversation and conversation.remote_wants_chatstates is None:
+        if tab.remote_wants_chatstates is None:
             if message['chat_state']:
-                conversation.remote_wants_chatstates = True
+                tab.remote_wants_chatstates = True
             else:
-                conversation.remote_wants_chatstates = False
+                tab.remote_wants_chatstates = False
         if 'private' in config.get('beep_on', 'highlight private').split():
             if config.get_by_tabname('disable_beep', 'false', jid.full, False).lower() != 'true':
                 curses.beep()
         logger.log_message(jid.full.replace('/', '\\'), nick_from, body)
-        if conversation is self.current_tab():
+        if tab is self.current_tab():
             self.refresh_window()
         else:
-            conversation.state = 'private'
+            tab.state = 'private'
             self.refresh_tab_win()
 
     ### Chatstates ###
diff --git a/src/tabs.py b/src/tabs.py
index b81bce8b..db840911 100644
--- a/src/tabs.py
+++ b/src/tabs.py
@@ -482,6 +482,23 @@ class ChatTab(Tab):
         if time is None and self.joined:        # don't log the history messages
             logger.log_message(self.name, nickname, txt)
 
+    def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, identifier=None):
+        self._text_buffer.add_message(txt, time=time,
+                nickname=nickname,
+                nick_color=nick_color,
+                history=None,
+                user=forced_user,
+                identifier=identifier)
+
+    def modify_message(self, txt, old_id, new_id, user=None):
+        self.log_message(txt, time, self.name)
+        message = self._text_buffer.modify_message(txt, old_id, new_id, time=time, user=user)
+        if message:
+            self.text_win.modify_message(old_id, message)
+            self.core.refresh_window()
+            return True
+        return False
+
     def last_words_completion(self):
         """
         Complete the input with words recently said
@@ -1733,14 +1750,15 @@ class MucTab(ChatTab):
         self._text_buffer.add_message(txt, time, nickname, nick_color, history, user, highlight=highlight, identifier=identifier)
         return highlight
 
-    def modify_message(self, txt, old_id, new_id, time=None, nickname=None):
+    def modify_message(self, txt, old_id, new_id, time=None, nickname=None, user=None):
         self.log_message(txt, time, nickname)
         highlight = self.do_highlight(txt, time, nickname)
-        message = self._text_buffer.modify_message(txt, old_id, new_id, highlight=highlight, time=time)
+        message = self._text_buffer.modify_message(txt, old_id, new_id, highlight=highlight, time=time, user=user)
         if message:
             self.text_win.modify_message(old_id, message)
             self.core.refresh_window()
             return highlight
+        return False
 
     def matching_names(self):
         return [safeJID(self.get_name()).user]
@@ -1826,6 +1844,7 @@ class PrivateTab(ChatTab):
         else:
             self.add_message(msg['body'],
                     nickname=self.core.own_nick or self.own_nick,
+                    forced_user=self.parent_muc.get_user_by_name(self.own_nick),
                     nick_color=get_theme().COLOR_OWN_NICK,
                     identifier=msg['id'])
         if msg['body'].find('\x19') != -1:
@@ -2013,21 +2032,6 @@ class PrivateTab(ChatTab):
         if reason:
             self.add_message(txt=reason)
 
-    def modify_message(self, txt, old_id, new_id):
-        self.log_message(txt, time, self.name)
-        message = self._text_buffer.modify_message(txt, old_id, new_id, time=time)
-        if message:
-            self.text_win.modify_message(old_id, message)
-            self.core.refresh_window()
-
-    def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, identifier=None):
-        self._text_buffer.add_message(txt, time=time,
-                nickname=nickname,
-                nick_color=nick_color,
-                history=None,
-                user=forced_user,
-                identifier=identifier)
-
     def matching_names(self):
         return [safeJID(self.get_name()).resource]
 
@@ -3175,21 +3179,6 @@ class ConversationTab(ChatTab):
         if config.get_by_tabname('send_chat_states', 'true', self.general_jid, True) == 'true':
             self.send_chat_state('gone')
 
-    def modify_message(self, txt, old_id, new_id):
-        self.log_message(txt, time, self.name)
-        message = self._text_buffer.modify_message(txt, old_id, new_id, time=time)
-        if message:
-            self.text_win.modify_message(old_id, message)
-            self.core.refresh_window()
-
-    def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, identifier=None):
-        self._text_buffer.add_message(txt, time=time,
-                nickname=nickname,
-                nick_color=nick_color,
-                history=None,
-                user=forced_user,
-                identifier=identifier)
-
     def matching_names(self):
         contact = roster[self.get_name()]
         res = [contact.bare_jid if contact else safeJID(self.get_name()).bare]
diff --git a/src/text_buffer.py b/src/text_buffer.py
index 2aa80670..0b9fc319 100644
--- a/src/text_buffer.py
+++ b/src/text_buffer.py
@@ -21,6 +21,8 @@ from theming import get_theme
 message_fields = 'txt nick_color time str_time nickname user identifier highlight me old_message revisions'
 Message = collections.namedtuple('Message', message_fields)
 
+class CorrectionError(Exception): pass
+
 def other_elems(self):
     acc = ['Message(']
     fields = message_fields.split()
@@ -103,16 +105,20 @@ class TextBuffer(object):
                 window.scroll_up(nb)
         return ret_val or 1
 
-    def modify_message(self, txt, old_id, new_id, highlight=False, time=None):
+    def modify_message(self, txt, old_id, new_id, highlight=False, time=None, user=None):
         for i in range(len(self.messages) -1, -1, -1):
             msg = self.messages[i]
             if msg.identifier == old_id:
+                if msg.user and msg.user is not user:
+                    raise CorrectionError("wrong user")
+                elif len(msg.str_time) > 8: # ugly
+                    raise CorrectionError("delayed message")
                 message = self.make_message(txt, time if time else msg.time, msg.nickname, msg.nick_color, None, msg.user, new_id, highlight=highlight, old_message=msg, revisions=msg.revisions + 1)
                 self.messages[i] = message
                 log.debug('Replacing message %s with %s.', old_id, new_id)
                 return message
         log.debug('Message %s not found in text_buffer, abort replacement.', old_id)
-        return
+        raise CorrectionError("nothing to replace")
 
     def del_window(self, win):
         self.windows.remove(win)
-- 
cgit v1.2.3