Commit a2bbe5f7 authored by Philipp Hörist's avatar Philipp Hörist

HTTPUpload: Refactor code

- Refactor File class to a more generic FileTransfer class

- FileTransfer class holds the transfer state and notifies other objects via signals
parent 77ed800a
......@@ -829,3 +829,36 @@ GIO_TLS_ERRORS = {
Gio.TlsCertificateFlags.EXPIRED: _('The certificate has expired'),
}
# pylint: enable=line-too-long
class FTState(Enum):
PREPARING = 'prepare'
ENCRYPTING = 'encrypt'
STARTED = 'started'
IN_PROGRESS = 'progress'
FINISHED = 'finished'
ERROR = 'error'
@property
def is_preparing(self):
return self == FTState.PREPARING
@property
def is_encrypting(self):
return self == FTState.ENCRYPTING
@property
def is_started(self):
return self == FTState.STARTED
@property
def is_in_progress(self):
return self == FTState.IN_PROGRESS
@property
def is_finished(self):
return self == FTState.FINISHED
@property
def is_error(self):
return self == FTState.ERROR
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from gajim.common.helpers import Observable
from gajim.common.const import FTState
class FileTransfer(Observable):
def __init__(self, account):
Observable.__init__(self)
self._account = account
self._seen = 0
self.size = 0
self._state = None
self._error_text = ''
@property
def account(self):
return self._account
@property
def seen(self):
return self._seen
@property
def is_complete(self):
if self.size == 0:
return False
return self._seen >= self.size
def set_preparing(self):
self._state = FTState.PREPARING
self.notify('state-changed', FTState.PREPARING)
def set_encrypting(self):
self._state = FTState.ENCRYPTING
self.notify('state-changed', FTState.ENCRYPTING)
def set_started(self):
self._state = FTState.STARTED
self.notify('state-changed', FTState.STARTED)
def set_error(self, text=''):
self._error_text = text
self._state = FTState.ERROR
self.notify('state-changed', FTState.ERROR)
self.disconnect_signals()
def set_in_progress(self):
self._state = FTState.IN_PROGRESS
self.notify('state-changed', FTState.IN_PROGRESS)
def set_finished(self):
self._state = FTState.FINISHED
self.notify('state-changed', FTState.FINISHED)
self.disconnect_signals()
def update_progress(self):
self.notify('progress')
......@@ -44,7 +44,9 @@ import logging
import json
import shutil
import collections
from collections import defaultdict
import random
import weakref
import string
from string import Template
import urllib
......@@ -1705,3 +1707,34 @@ def get_tls_error_phrase(tls_error):
if phrase is None:
return GIO_TLS_ERRORS.get(Gio.TlsCertificateFlags.GENERIC_ERROR)
return phrase
class Observable:
def __init__(self, log_=None):
self._log = log_
self._callbacks = defaultdict(list)
def disconnect_signals(self):
self._callbacks = defaultdict(list)
def disconnect(self, object_):
for signal_name, handlers in self._callbacks.items():
for handler in list(handlers):
func = handler()
if func is None or func.__self__ == object_:
self._callbacks[signal_name].remove(handler)
def connect(self, signal_name, func):
weak_func = weakref.WeakMethod(func)
self._callbacks[signal_name].append(weak_func)
def notify(self, signal_name, *args, **kwargs):
if self._log is not None:
self._log.info('Signal: %s', signal_name)
callbacks = self._callbacks.get(signal_name, [])
for func in list(callbacks):
if func() is None:
self._callbacks[signal_name].remove(func)
continue
func()(self, signal_name, *args, **kwargs)
This diff is collapsed.
......@@ -19,7 +19,6 @@ from gi.repository import Gtk
from gi.repository import GLib
from gajim.common import app
from gajim.common import ged
from gajim.common.i18n import _
from gajim.gtk.util import get_builder
......@@ -27,7 +26,7 @@ from gajim.gtk.util import EventHelper
class HTTPUploadProgressWindow(Gtk.ApplicationWindow, EventHelper):
def __init__(self, file):
def __init__(self, transfer):
Gtk.ApplicationWindow.__init__(self)
EventHelper.__init__(self)
self.set_name('HTTPUploadProgressWindow')
......@@ -37,8 +36,10 @@ class HTTPUploadProgressWindow(Gtk.ApplicationWindow, EventHelper):
self.set_title(_('File Transfer'))
self._destroyed = False
self._con = app.connections[file.account]
self._file = file
self._con = app.connections[transfer.account]
self._transfer = transfer
self._transfer.connect('state-changed', self._on_transfer_state_change)
self._transfer.connect('progress', self._on_transfer_progress)
if app.config.get('use_kib_mib'):
self._units = GLib.FormatSizeFlags.IEC_UNITS
......@@ -48,7 +49,7 @@ class HTTPUploadProgressWindow(Gtk.ApplicationWindow, EventHelper):
self._start_time = time.time()
self._ui = get_builder('httpupload_progress_dialog.ui')
self._ui.file_name_label.set_text(os.path.basename(file.path))
self._ui.file_name_label.set_text(os.path.basename(transfer.path))
self.add(self._ui.box)
self._pulse = GLib.timeout_add(100, self._pulse_progressbar)
......@@ -57,23 +58,17 @@ class HTTPUploadProgressWindow(Gtk.ApplicationWindow, EventHelper):
self.connect('destroy', self._on_destroy)
self._ui.connect_signals(self)
self.register_events([
('httpupload-progress', ged.CORE, self._on_httpupload_progress),
])
def _on_httpupload_progress(self, obj):
if self._file != obj.file:
return
if obj.status == 'request':
def _on_transfer_state_change(self, _transfer, _signal_name, state):
if state.is_preparing:
self._ui.label.set_text(_('Requesting HTTP File Upload Slot…'))
elif obj.status == 'close':
elif state.is_finished or state.is_error:
self.destroy()
elif obj.status == 'upload':
elif state.is_started:
self._ui.label.set_text(_('Uploading via HTTP File Upload…'))
elif obj.status == 'update':
self._update_progress(obj.seen, obj.total)
elif obj.status == 'encrypt':
elif state.is_encrypting:
self._ui.label.set_text(_('Encrypting file…'))
def _pulse_progressbar(self):
......@@ -84,13 +79,14 @@ class HTTPUploadProgressWindow(Gtk.ApplicationWindow, EventHelper):
self.destroy()
def _on_destroy(self, *args):
self._con.get_module('HTTPUpload').cancel_upload(self._file)
self._con.get_module('HTTPUpload').cancel_upload(self._transfer)
self._destroyed = True
self._file = None
self._transfer.disconnect(self)
self._transfer = None
if self._pulse is not None:
GLib.source_remove(self._pulse)
def _update_progress(self, seen, total):
def _on_transfer_progress(self, transfer, _signal_name):
if self._destroyed:
return
if self._pulse is not None:
......@@ -98,17 +94,17 @@ class HTTPUploadProgressWindow(Gtk.ApplicationWindow, EventHelper):
self._pulse = None
time_now = time.time()
bytes_sec = round(seen / (time_now - self._start_time), 1)
bytes_sec = round(transfer.seen / (time_now - self._start_time), 1)
size_progress = GLib.format_size_full(seen, self._units)
size_total = GLib.format_size_full(total, self._units)
size_progress = GLib.format_size_full(transfer.seen, self._units)
size_total = GLib.format_size_full(transfer.size, self._units)
speed = '%s/s' % GLib.format_size_full(bytes_sec, self._units)
if bytes_sec == 0:
eta = '∞'
else:
eta = self._format_eta(
round((total - seen) / bytes_sec))
round((transfer.size - transfer.seen) / bytes_sec))
self._ui.progress_label.set_text(
_('%(progress)s of %(total)s') % {
......@@ -117,7 +113,7 @@ class HTTPUploadProgressWindow(Gtk.ApplicationWindow, EventHelper):
self._ui.speed_label.set_text(speed)
self._ui.eta_label.set_text(eta)
self._ui.progressbar.set_fraction(float(seen) / total)
self._ui.progressbar.set_fraction(float(transfer.seen) / transfer.size)
@staticmethod
def _format_eta(time_):
......
......@@ -79,7 +79,6 @@ from gajim.common.nec import NetworkEvent
from gajim.common.i18n import _
from gajim.common.connection_handlers_events import OurShowEvent
from gajim.common.modules.httpupload import HTTPUploadProgressEvent
from gajim.common.connection import Connection
from gajim.common.file_props import FilesProp
......@@ -858,8 +857,8 @@ class Interface:
location.enable()
@staticmethod
def show_httpupload_progress(file):
HTTPUploadProgressWindow(file)
def show_httpupload_progress(transfer):
HTTPUploadProgressWindow(transfer)
def send_httpupload(self, chat_control):
accept_cb = partial(self.on_file_dialog_ok, chat_control)
......@@ -877,16 +876,13 @@ class Interface:
chat_control.contact,
chat_control.is_groupchat)
def encrypt_file(self, file, account, callback):
app.nec.push_incoming_event(HTTPUploadProgressEvent(
None, status='encrypt', file=file))
encryption = file.encryption
plugin = app.plugin_manager.encryption_plugins[encryption]
def encrypt_file(self, transfer, account, callback):
transfer.set_encrypting()
plugin = app.plugin_manager.encryption_plugins[transfer.encryption]
if hasattr(plugin, 'encrypt_file'):
plugin.encrypt_file(file, account, callback)
plugin.encrypt_file(transfer, account, callback)
else:
app.nec.push_incoming_event(HTTPUploadProgressEvent(
None, status='close', file=file))
transfer.set_error()
self.raise_dialog('httpupload-encryption-not-available')
@staticmethod
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment