From df17f8751d2bef4fd35ffc22e3e3708e14f58db6 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger 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 647b6f96b..8e0b80a36 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 501da4df8..bc96b43cd 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 6349ff3da..3401343ab 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 1bd218a53..ce3af0a09 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)