From df17f8751d2bef4fd35ffc22e3e3708e14f58db6 Mon Sep 17 00:00:00 2001
From: Yann Leboulanger <asterix@lagaule.org>
Date: Sat, 18 Feb 2012 21:06:08 +0100
Subject: [PATCH] handle GUI while checking file hash. Show show re-request
 dialog when hash is incorrect. TODO: re-request file to sender.

---
 src/dialogs.py              |  7 ++---
 src/filetransfers_window.py | 61 ++++++++++++++++++++++++++++++++++---
 src/gui_interface.py        | 52 ++++++++++++++++++++++++-------
 src/roster_window.py        |  3 ++
 4 files changed, 104 insertions(+), 19 deletions(-)

diff --git a/src/dialogs.py b/src/dialogs.py
index 647b6f96b0..8e0b80a368 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -1615,16 +1615,15 @@ class YesNoDialog(HigDialog):
     """
 
     def __init__(self, pritext, sectext='', checktext='', on_response_yes=None,
-    on_response_no=None):
+    on_response_no=None, type_=gtk.MESSAGE_QUESTION):
         self.user_response_yes = on_response_yes
         self.user_response_no = on_response_no
         if hasattr(gajim.interface, 'roster') and gajim.interface.roster:
             parent = gajim.interface.roster.window
         else:
             parent = None
-        HigDialog.__init__(self, parent, gtk.MESSAGE_QUESTION,
-            gtk.BUTTONS_YES_NO, pritext, sectext,
-            on_response_yes=self.on_response_yes,
+        HigDialog.__init__(self, parent, type_, gtk.BUTTONS_YES_NO, pritext,
+            sectext, on_response_yes=self.on_response_yes,
             on_response_no=self.on_response_no)
 
         if checktext:
diff --git a/src/filetransfers_window.py b/src/filetransfers_window.py
index 501da4df82..bc96b43cd0 100644
--- a/src/filetransfers_window.py
+++ b/src/filetransfers_window.py
@@ -46,7 +46,8 @@ C_FILE = 2
 C_TIME = 3
 C_PROGRESS = 4
 C_PERCENT = 5
-C_SID = 6
+C_PULSE = 6
+C_SID = 7
 
 
 class FileTransfersWindow:
@@ -65,7 +66,8 @@ class FileTransfersWindow:
         shall_notify = gajim.config.get('notify_on_file_complete')
         self.notify_ft_checkbox.set_active(shall_notify
                                                                                                 )
-        self.model = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str, str, int, str)
+        self.model = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str, str, int,
+            int, str)
         self.tree.set_model(self.model)
         col = gtk.TreeViewColumn()
 
@@ -112,6 +114,7 @@ class FileTransfersWindow:
         col.pack_start(renderer, expand=False)
         col.add_attribute(renderer, 'text', C_PROGRESS)
         col.add_attribute(renderer, 'value', C_PERCENT)
+        col.add_attribute(renderer, 'pulse', C_PULSE)
         col.set_resizable(True)
         col.set_expand(False)
         self.tree.append_column(col)
@@ -125,6 +128,8 @@ class FileTransfersWindow:
                 'pause': gtk.STOCK_MEDIA_PAUSE,
                 'continue': gtk.STOCK_MEDIA_PLAY,
                 'ok': gtk.STOCK_APPLY,
+                'computing': gtk.STOCK_EXECUTE,
+                'hash_error': gtk.STOCK_STOP,
         }
 
         self.tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
@@ -244,6 +249,21 @@ class FileTransfersWindow:
         dialogs.ErrorDialog(_('File transfer stopped'), sectext)
         self.tree.get_selection().unselect_all()
 
+    def show_hash_error(self, jid, file_props):
+        def on_yes(dummy):
+            # TODO: Request the file to the sender
+            pass
+
+        if file_props['type'] == 'r':
+            file_name = os.path.basename(file_props['file-name'])
+        else:
+            file_name = file_props['name']
+        dialogs.YesNoDialog(('File transfer error'),
+            _('The file %(file)s has been fully received, but it seems to be '
+            'wrongly received.\nDo you want to reload it?') % \
+            {'file': file_name}, on_response_yes=on_yes,
+            type_=gtk.MESSAGE_ERROR)
+
     def show_file_send_request(self, account, contact):
         win = gtk.ScrolledWindow()
         win.set_shadow_type(gtk.SHADOW_IN)
@@ -449,6 +469,36 @@ class FileTransfersWindow:
             file_props['stopped'] = True
         elif status == 'ok':
             file_props['completed'] = True
+            text = self._format_percent(100)
+            received_size = int(file_props['received-len'])
+            full_size = int(file_props['size'])
+            text += helpers.convert_bytes(received_size) + '/' + \
+                helpers.convert_bytes(full_size)
+            self.model.set(iter_, C_PROGRESS, text)
+            self.model.set(iter_, C_PULSE, gobject.constants.G_MAXINT)
+        elif status == 'computing':
+            self.model.set(iter_, C_PULSE, 1)
+            text = _('Checking file...') + '\n'
+            received_size = int(file_props['received-len'])
+            full_size = int(file_props['size'])
+            text += helpers.convert_bytes(received_size) + '/' + \
+                helpers.convert_bytes(full_size)
+            self.model.set(iter_, C_PROGRESS, text)
+            def pulse():
+                p = self.model.get(iter_, C_PULSE)[0]
+                if p == gobject.constants.G_MAXINT:
+                    return False
+                self.model.set(iter_, C_PULSE, p + 1)
+                return True
+            gobject.timeout_add(100, pulse)
+        elif status == 'hash_error':
+            text = _('File error') + '\n'
+            received_size = int(file_props['received-len'])
+            full_size = int(file_props['size'])
+            text += helpers.convert_bytes(received_size) + '/' + \
+                helpers.convert_bytes(full_size)
+            self.model.set(iter_, C_PROGRESS, text)
+            self.model.set(iter_, C_PULSE, gobject.constants.G_MAXINT)
         self.model.set(iter_, C_IMAGE, self.get_icon(status))
         path = self.model.get_path(iter_)
         self.select_func(path)
@@ -589,7 +639,10 @@ class FileTransfersWindow:
                 status = 'stop'
             self.model.set(iter_, 0, self.get_icon(status))
             if transfered_size == full_size:
-                self.set_status(typ, sid, 'ok')
+                if file_props['type'] == 'r':
+                    self.set_status(typ, sid, 'computing')
+                else:
+                    self.set_status(typ, sid, 'ok')
             elif just_began:
                 path = self.model.get_path(iter_)
                 self.select_func(path)
@@ -655,7 +708,7 @@ class FileTransfersWindow:
             file_name = file_props['name']
         text_props = gobject.markup_escape_text(file_name) + '\n'
         text_props += contact.get_shown_name()
-        self.model.set(iter_, 1, text_labels, 2, text_props, C_SID,
+        self.model.set(iter_, 1, text_labels, 2, text_props, C_PULSE, -1, C_SID,
                 file_props['type'] + file_props['sid'])
         self.set_progress(file_props['type'], file_props['sid'], 0, iter_)
         if 'started' in file_props and file_props['started'] is False:
diff --git a/src/gui_interface.py b/src/gui_interface.py
index 6349ff3da0..3401343ab5 100644
--- a/src/gui_interface.py
+++ b/src/gui_interface.py
@@ -913,6 +913,12 @@ class Interface:
     def __compare_hashes(self, account, file_props):
         session = gajim.connections[account].get_jingle_session(jid=None,
             sid=file_props['session-sid'])
+        ft_win = self.instances['file_transfers']
+        if not session.file_hash:
+            # We disn't get the hash, sender probably don't support that
+            jid = unicode(file_props['sender'])
+            self.popup_ft_result(account, jid, file_props)
+            ft_win.set_status(file_props['type'], file_props['sid'], 'ok')
         h = Hashes()
         try:
             file_ = open(file_props['file-name'], 'r')
@@ -922,8 +928,16 @@ class Interface:
         file_.close()
         # If the hash we received and the hash of the file are the same,
         # then the file is not corrupt
-        if session.file_hash == hash_:
-            print "they are te same"
+        jid = unicode(file_props['sender'])
+        if session.file_hash != hash_:
+            self.popup_ft_result(account, jid, file_props)
+            ft_win.set_status(file_props['type'], file_props['sid'], 'ok')
+        else:
+            # wrong hash, we need to get the file again!
+            file_props['error'] = -10
+            self.popup_ft_result(account, jid, file_props)
+            ft_win.set_status(file_props['type'], file_props['sid'],
+                'hash_error')
         # End jingle session
         if session:
             session.end_session()
@@ -949,7 +963,10 @@ class Interface:
         else: # we send a file
             jid = unicode(file_props['receiver'])
             gajim.socks5queue.remove_sender(file_props['sid'], True, True)
+            self.popup_ft_result(account, jid, file_props)
 
+    def popup_ft_result(self, account, jid, file_props):
+        ft = self.instances['file_transfers']
         if helpers.allow_popup_window(account):
             if file_props['error'] == 0:
                 if gajim.config.get('notify_on_file_complete'):
@@ -960,6 +977,8 @@ class Interface:
             elif file_props['error'] == -6:
                 ft.show_stopped(jid, file_props,
                     error_msg=_('Error opening file'))
+            elif file_props['error'] == -10:
+                ft.show_hash_error(jid, file_props)
             return
 
         msg_type = ''
@@ -971,6 +990,9 @@ class Interface:
         elif file_props['error'] in (-1, -6):
             msg_type = 'file-stopped'
             event_type = _('File Transfer Stopped')
+        elif file_props['error']  == -10:
+            msg_type = 'file-hash-error'
+            event_type = _('File Transfer Failed')
 
         if event_type == '':
             # FIXME: ugly workaround (this can happen Gajim sent, Gaim recvs)
@@ -988,16 +1010,20 @@ class Interface:
                 # get the name of the sender, as it is in the roster
                 sender = unicode(file_props['sender']).split('/')[0]
                 name = gajim.contacts.get_first_contact_from_jid(account,
-                        sender).get_shown_name()
+                    sender).get_shown_name()
                 filename = os.path.basename(file_props['file-name'])
                 if event_type == _('File Transfer Completed'):
                     txt = _('You successfully received %(filename)s from '
                         '%(name)s.') % {'filename': filename, 'name': name}
                     img_name = 'gajim-ft_done'
-                else: # ft stopped
+                elif event_type == _('File Transfer Stopped'):
                     txt = _('File transfer of %(filename)s from %(name)s '
                         'stopped.') % {'filename': filename, 'name': name}
                     img_name = 'gajim-ft_stopped'
+                else: # ft hash error
+                    txt = _('File transfer of %(filename)s from %(name)s '
+                        'failed.') % {'filename': filename, 'name': name}
+                    img_name = 'gajim-ft_stopped'
             else:
                 receiver = file_props['receiver']
                 if hasattr(receiver, 'jid'):
@@ -1005,24 +1031,28 @@ class Interface:
                 receiver = receiver.split('/')[0]
                 # get the name of the contact, as it is in the roster
                 name = gajim.contacts.get_first_contact_from_jid(account,
-                        receiver).get_shown_name()
+                    receiver).get_shown_name()
                 filename = os.path.basename(file_props['file-name'])
                 if event_type == _('File Transfer Completed'):
                     txt = _('You successfully sent %(filename)s to %(name)s.')\
-                            % {'filename': filename, 'name': name}
+                        % {'filename': filename, 'name': name}
                     img_name = 'gajim-ft_done'
-                else: # ft stopped
+                elif event_type == _('File Transfer Stopped'):
                     txt = _('File transfer of %(filename)s to %(name)s '
                         'stopped.') % {'filename': filename, 'name': name}
                     img_name = 'gajim-ft_stopped'
+                else: # ft hash error
+                    txt = _('File transfer of %(filename)s to %(name)s '
+                        'failed.') % {'filename': filename, 'name': name}
+                    img_name = 'gajim-ft_stopped'
             path = gtkgui_helpers.get_icon_path(img_name, 48)
         else:
             txt = ''
             path = ''
 
         if gajim.config.get('notify_on_file_complete') and \
-                (gajim.config.get('autopopupaway') or \
-                gajim.connections[account].connected in (2, 3)):
+        (gajim.config.get('autopopupaway') or \
+        gajim.connections[account].connected in (2, 3)):
             # we want to be notified and we are online/chat or we don't mind
             # bugged when away/na/busy
             notify.popup(event_type, jid, account, msg_type, path_to_image=path,
@@ -1514,7 +1544,7 @@ class Interface:
         no_queue = len(gajim.events.get_events(account, jid)) == 0
         # type_ can be gc-invitation file-send-error file-error
         #  file-request-error file-request file-completed file-stopped
-        #  jingle-incoming
+        #  file-hash-error jingle-incoming
         # event_type can be in advancedNotificationWindow.events_list
         event_types = {'file-request': 'ft_request',
             'file-completed': 'ft_finished'}
@@ -1643,7 +1673,7 @@ class Interface:
             w = ctrl.parent_win
         elif type_ in ('normal', 'file-request', 'file-request-error',
         'file-send-error', 'file-error', 'file-stopped', 'file-completed',
-        'jingle-incoming'):
+        'file-hash-error', 'jingle-incoming'):
             # Get the first single message event
             event = gajim.events.get_first_event(account, fjid, type_)
             if not event:
diff --git a/src/roster_window.py b/src/roster_window.py
index 1bd218a532..ce3af0a096 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -1917,6 +1917,9 @@ class RosterWindow:
             ft.show_stopped(jid, data, error_msg=msg_err)
             gajim.events.remove_events(account, jid, event)
             return True
+        elif event.type_ == 'file-hash-error':
+            ft.show_hash_error(jid, data)
+            gajim.events.remove_events(account, jid, event)
         elif event.type_ == 'file-completed':
             ft.show_completed(jid, data)
             gajim.events.remove_events(account, jid, event)
-- 
GitLab