Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gajim/chat_control_base.py8
-rw-r--r--gajim/common/const.py11
-rw-r--r--gajim/common/exceptions.py4
-rw-r--r--gajim/common/filetransfer.py26
-rw-r--r--gajim/common/helpers.py7
-rw-r--r--gajim/common/modules/httpupload.py118
-rw-r--r--gajim/dialog_messages.py31
-rw-r--r--gajim/gtk/filetransfer_progress.py13
-rw-r--r--gajim/gui_interface.py56
9 files changed, 148 insertions, 126 deletions
diff --git a/gajim/chat_control_base.py b/gajim/chat_control_base.py
index 51e9c92e8..cedd01c6b 100644
--- a/gajim/chat_control_base.py
+++ b/gajim/chat_control_base.py
@@ -814,14 +814,8 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper):
if method is None:
return
- con = app.connections[self.account]
-
if method == 'httpupload':
- con.get_module('HTTPUpload').check_file_before_transfer(
- path,
- self.encryption,
- self.contact,
- groupchat=self._type.is_groupchat)
+ app.interface.send_httpupload(self, path)
else:
ft = app.interface.instances['file_transfers']
diff --git a/gajim/common/const.py b/gajim/common/const.py
index 665fba11e..0f5087267 100644
--- a/gajim/common/const.py
+++ b/gajim/common/const.py
@@ -908,6 +908,7 @@ class FTState(Enum):
IN_PROGRESS = 'progress'
FINISHED = 'finished'
ERROR = 'error'
+ CANCELLED = 'cancelled'
@property
def is_preparing(self):
@@ -937,6 +938,16 @@ class FTState(Enum):
def is_error(self):
return self == FTState.ERROR
+ @property
+ def is_cancelled(self):
+ return self == FTState.CANCELLED
+
+ @property
+ def is_active(self):
+ return not (self.is_error or
+ self.is_cancelled or
+ self.is_finished)
+
SASL_ERRORS = {
'aborted': _('Authentication aborted'),
diff --git a/gajim/common/exceptions.py b/gajim/common/exceptions.py
index ab5450c56..5e6b06bf6 100644
--- a/gajim/common/exceptions.py
+++ b/gajim/common/exceptions.py
@@ -151,3 +151,7 @@ class StanzaMalformed(Exception):
class SendMessageError(Exception):
pass
+
+
+class FileError(Exception):
+ pass
diff --git a/gajim/common/filetransfer.py b/gajim/common/filetransfer.py
index b0085984c..f600fc9c6 100644
--- a/gajim/common/filetransfer.py
+++ b/gajim/common/filetransfer.py
@@ -21,23 +21,27 @@ class FileTransfer(Observable):
_state_descriptions = {} # type: Dict[FTState, str]
- def __init__(self, account, cancel_func=None):
+ def __init__(self, account):
Observable.__init__(self)
self._account = account
- self._cancel_func = cancel_func
self._seen = 0
self.size = 0
self._state = None
self._error_text = ''
+ self._error_domain = None
@property
def account(self):
return self._account
@property
+ def state(self):
+ return self._state
+
+ @property
def seen(self):
return self._seen
@@ -51,9 +55,13 @@ class FileTransfer(Observable):
def filename(self):
raise NotImplementedError
- def cancel(self):
- if self._cancel_func is not None:
- self._cancel_func(self)
+ @property
+ def error_text(self):
+ return self._error_text
+
+ @property
+ def error_domain(self):
+ return self._error_domain
def get_state_description(self):
return self._state_descriptions.get(self._state, '')
@@ -74,12 +82,18 @@ class FileTransfer(Observable):
self._state = FTState.STARTED
self.notify('state-changed', FTState.STARTED)
- def set_error(self, text=''):
+ def set_error(self, domain, text=''):
self._error_text = text
+ self._error_domain = domain
self._state = FTState.ERROR
self.notify('state-changed', FTState.ERROR)
self.disconnect_signals()
+ def set_cancelled(self):
+ self._state = FTState.CANCELLED
+ self.notify('state-changed', FTState.CANCELLED)
+ self.disconnect_signals()
+
def set_in_progress(self):
self._state = FTState.IN_PROGRESS
self.notify('state-changed', FTState.IN_PROGRESS)
diff --git a/gajim/common/helpers.py b/gajim/common/helpers.py
index bca56eb01..28d3fd8a0 100644
--- a/gajim/common/helpers.py
+++ b/gajim/common/helpers.py
@@ -45,6 +45,7 @@ import functools
from collections import defaultdict
import random
import weakref
+import inspect
import string
from string import Template
import urllib
@@ -1256,7 +1257,11 @@ class Observable:
self._callbacks[signal_name].remove(handler)
def connect(self, signal_name, func):
- weak_func = weakref.WeakMethod(func)
+ if inspect.ismethod(func):
+ weak_func = weakref.WeakMethod(func)
+ elif inspect.isfunction(func):
+ weak_func = weakref.ref(func)
+
self._callbacks[signal_name].append(weak_func)
def notify(self, signal_name, *args, **kwargs):
diff --git a/gajim/common/modules/httpupload.py b/gajim/common/modules/httpupload.py
index 0b2783061..0d29d3012 100644
--- a/gajim/common/modules/httpupload.py
+++ b/gajim/common/modules/httpupload.py
@@ -35,8 +35,7 @@ from gajim.common.helpers import get_user_proxy
from gajim.common.const import FTState
from gajim.common.filetransfer import FileTransfer
from gajim.common.modules.base import BaseModule
-from gajim.common.structs import OutgoingMessage
-from gajim.common.connection_handlers_events import InformationEvent
+from gajim.common.exceptions import FileError
class HTTPUpload(BaseModule):
@@ -87,10 +86,9 @@ class HTTPUpload(BaseModule):
for ctrl in app.interface.msg_win_mgr.get_controls(acct=self._account):
ctrl.update_actions()
- def check_file_before_transfer(self, path, encryption, contact,
- groupchat=False):
+ def make_transfer(self, path, encryption, contact, groupchat=False):
if not path or not os.path.exists(path):
- return
+ return None
invalid_file = False
stat = os.stat(path)
@@ -112,47 +110,41 @@ class HTTPUpload(BaseModule):
'maximum allowed file size is: %s') % size
if invalid_file:
- self._raise_information_event('open-file-error2', msg)
- return
+ raise FileError('file-error', msg)
mime = mimetypes.MimeTypes().guess_type(path)[0]
if not mime:
mime = 'application/octet-stream' # fallback mime type
self._log.info("Detected MIME type of file: %s", mime)
- try:
- transfer = HTTPFileTransfer(self._account,
- self._cancel_upload,
- path,
- contact,
- mime,
- encryption,
- groupchat)
- app.interface.show_httpupload_progress(transfer)
- except Exception as error:
- self._log.exception('Error while loading file')
- self._raise_information_event('open-file-error2', str(error))
- return
-
- if encryption is not None:
- app.interface.encrypt_file(transfer,
- self._account,
- self._request_slot)
- else:
- self._request_slot(transfer)
+ return HTTPFileTransfer(self._account,
+ path,
+ contact,
+ mime,
+ encryption,
+ groupchat)
- def _cancel_upload(self, transfer):
+ def cancel_transfer(self, transfer):
+ transfer.set_cancelled()
message = self._queued_messages.get(id(transfer))
if message is None:
return
+
self._session.cancel_message(message, Soup.Status.CANCELLED)
- @staticmethod
- def _raise_information_event(dialog_name, args=None):
- app.nec.push_incoming_event(InformationEvent(
- None, dialog_name=dialog_name, args=args))
+ def start_transfer(self, transfer):
+ if transfer.encryption is not None and not transfer.is_encrypted:
+ transfer.set_encrypting()
+ plugin = app.plugin_manager.encryption_plugins[transfer.encryption]
+ if hasattr(plugin, 'encrypt_file'):
+ plugin.encrypt_file(transfer,
+ self._account,
+ self.start_transfer)
+ else:
+ transfer.set_error('encryption-not-available')
+
+ return
- def _request_slot(self, transfer):
transfer.set_preparing()
self._log.info('Sending request for slot')
self._nbxmpp('HTTPUpload').request_slot(
@@ -172,8 +164,6 @@ class HTTPUpload(BaseModule):
HTTPUploadStanzaError,
MalformedStanzaError) as error:
- transfer.set_error()
-
if error.app_condition == 'file-too-large':
size_text = GLib.format_size_full(
error.get_max_file_size(),
@@ -181,20 +171,18 @@ class HTTPUpload(BaseModule):
error_text = _('File is too large, '
'maximum allowed file size is: %s' % size_text)
+ transfer.set_error('file-too-large', error_text)
+
else:
- error_text = str(error)
- self._log.warning(error)
+ transfer.set_error('misc', str(error))
- self._raise_information_event('request-upload-slot-error',
- error_text)
return
transfer.process_result(result)
if (urlparse(transfer.put_uri).scheme != 'https' or
urlparse(transfer.get_uri).scheme != 'https'):
- transfer.set_error()
- self._raise_information_event('unsecure-error')
+ transfer.set_error('unsecure')
return
self._log.info('Uploading file to %s', transfer.put_uri)
@@ -206,7 +194,7 @@ class HTTPUpload(BaseModule):
transfer.set_started()
message = Soup.Message.new('PUT', transfer.put_uri)
- message.connect('starting', self._check_certificate)
+ message.connect('starting', self._check_certificate, transfer)
# Set CAN_REBUILD so chunks get discarded after they have been
# written to the network
@@ -227,10 +215,11 @@ class HTTPUpload(BaseModule):
self._set_proxy_if_available()
self._session.queue_message(message, self._on_finish, transfer)
- def _check_certificate(self, message):
+ def _check_certificate(self, message, transfer):
https_used, tls_certificate, tls_errors = message.get_https_status()
if not https_used:
self._log.warning('HTTPS was not used for upload')
+ transfer.set_error('unsecure')
self._session.cancel_message(message, Soup.Status.CANCELLED)
return
@@ -241,12 +230,12 @@ class HTTPUpload(BaseModule):
for error in tls_errors:
phrase = get_tls_error_phrase(error)
self._log.warning('TLS verification failed: %s', phrase)
+
+ transfer.set_error('tls-verification-failed', phrase)
self._session.cancel_message(message, Soup.Status.CANCELLED)
- self._raise_information_event('httpupload-error', phrase)
def _on_finish(self, _session, message, transfer):
self._queued_messages.pop(id(transfer), None)
- transfer.set_finished()
if message.props.status_code == Soup.Status.CANCELLED:
self._log.info('Upload cancelled')
@@ -254,25 +243,14 @@ class HTTPUpload(BaseModule):
if message.props.status_code in (Soup.Status.OK, Soup.Status.CREATED):
self._log.info('Upload completed successfully')
- uri = transfer.get_transformed_uri()
+ transfer.set_finished()
- type_ = 'chat'
- if transfer.is_groupchat:
- type_ = 'groupchat'
-
- message = OutgoingMessage(account=self._account,
- contact=transfer.contact,
- message=uri,
- type_=type_,
- oob_url=uri)
-
- self._con.send_message(message)
else:
phrase = Soup.Status.get_phrase(message.props.status_code)
self._log.error('Got unexpected http upload response code: %s',
phrase)
- self._raise_information_event('httpupload-response-error', phrase)
+ transfer.set_error('http-response', phrase)
def _on_wrote_chunk(self, message, transfer):
transfer.update_progress()
@@ -303,15 +281,21 @@ class HTTPFileTransfer(FileTransfer):
FTState.STARTED: _('Uploading via HTTP File Upload…'),
}
+ _errors = {
+ 'unsecure': _('The server returned an insecure transport (HTTP).'),
+ 'encryption-not-available': _('There is no encryption method available '
+ 'for the chosen encryption.')
+ }
+
def __init__(self,
account,
- cancel_func,
path,
contact,
mime,
encryption,
groupchat):
- FileTransfer.__init__(self, account, cancel_func=cancel_func)
+
+ FileTransfer.__init__(self, account)
self._path = path
self._encryption = encryption
@@ -328,6 +312,8 @@ class HTTPFileTransfer(FileTransfer):
self._data = None
self._headers = {}
+ self._is_encrypted = False
+
@property
def mime(self):
return self._mime
@@ -352,6 +338,10 @@ class HTTPFileTransfer(FileTransfer):
def path(self):
return self._path
+ @property
+ def is_encrypted(self):
+ return self._is_encrypted
+
def get_transformed_uri(self):
if self._uri_transform_func is not None:
return self._uri_transform_func(self.get_uri)
@@ -364,9 +354,12 @@ class HTTPFileTransfer(FileTransfer):
def filename(self):
return os.path.basename(self._path)
- def set_error(self, text=''):
+ def set_error(self, domain, text=''):
+ if not text:
+ text = self._errors[domain]
+
self._close()
- super().set_error(text)
+ super().set_error(domain, text)
def set_finished(self):
self._close()
@@ -374,6 +367,7 @@ class HTTPFileTransfer(FileTransfer):
def set_encrypted_data(self, data):
self._data = data
+ self._is_encrypted = True
def _close(self):
if self._stream is not None:
diff --git a/gajim/dialog_messages.py b/gajim/dialog_messages.py
index bdcad6744..532b6854f 100644
--- a/gajim/dialog_messages.py
+++ b/gajim/dialog_messages.py
@@ -97,42 +97,11 @@ messages = {
_('%s\nLink-local messaging might not work properly.'),
ErrorDialog),
- 'request-upload-slot-error': Message(
- _('Could not request upload slot for HTTP File Upload'),
- '%s',
- ErrorDialog),
-
- 'open-file-error': Message(
- _('Could not Open File'),
- _('Exception raised while trying to open file (see log).'),
- ErrorDialog),
-
'open-file-error2': Message(
_('Could not Open File'),
'%s',
ErrorDialog),
- 'unsecure-error': Message(
- _('Not Secure'),
- _('The server returned an insecure transport (HTTP).'),
- ErrorDialog),
-
- 'httpupload-response-error': Message(
- _('Could not Upload File'),
- _('HTTP response code from server: %s'),
- ErrorDialog),
-
- 'httpupload-error': Message(
- _('Upload Error'),
- '%s',
- ErrorDialog),
-
- 'httpupload-encryption-not-available': Message(
- _('Encryption Error'),
- _('There is no encryption method available '
- 'for the chosen encryption.'),
- ErrorDialog),
-
}
diff --git a/gajim/gtk/filetransfer_progress.py b/gajim/gtk/filetransfer_progress.py
index 769782354..d6cf4c837 100644
--- a/gajim/gtk/filetransfer_progress.py
+++ b/gajim/gtk/filetransfer_progress.py
@@ -22,6 +22,7 @@ from gajim.common.i18n import _
from .util import get_builder
from .util import EventHelper
+from .dialogs import ErrorDialog
class FileTransferProgress(Gtk.ApplicationWindow, EventHelper):
@@ -57,7 +58,11 @@ class FileTransferProgress(Gtk.ApplicationWindow, EventHelper):
self._ui.connect_signals(self)
def _on_transfer_state_change(self, transfer, _signal_name, state):
- if state.is_finished or state.is_error:
+ if state.is_error:
+ ErrorDialog(_('Upload Failed'), transfer.error_text)
+ self.destroy()
+
+ if state.is_finished or state.is_cancelled:
self.destroy()
return
@@ -73,8 +78,10 @@ class FileTransferProgress(Gtk.ApplicationWindow, EventHelper):
self.destroy()
def _on_destroy(self, *args):
- self._transfer.cancel()
- self._transfer.disconnect(self)
+ if self._transfer.state.is_active:
+ client = app.get_client(self._transfer.account)
+ client.get_module('HTTPUpload').cancel_transfer(self._transfer)
+
self._transfer = None
self._destroyed = True
if self._pulse is not None:
diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py
index 818cdca61..09ad4f612 100644
--- a/gajim/gui_interface.py
+++ b/gajim/gui_interface.py
@@ -77,6 +77,7 @@ from gajim.common import logging_helpers
from gajim.common.helpers import ask_for_status_message
from gajim.common.helpers import get_group_chat_nick
from gajim.common.structs import MUCData
+from gajim.common.structs import OutgoingMessage
from gajim.common.nec import NetworkEvent
from gajim.common.i18n import _
from gajim.common.client import Client
@@ -84,9 +85,11 @@ from gajim.common.const import Display
from gajim.common.const import JingleState
from gajim.common.file_props import FilesProp
+from gajim.common.connection_handlers_events import InformationEvent
from gajim import roster_window
from gajim.common import ged
+from gajim.common.exceptions import FileError
from gajim.gui.avatar import AvatarStorage
from gajim.gui.notification import Notification
@@ -849,34 +852,55 @@ class Interface:
if ask_for_status_message(obj.conn.status, signin=True):
open_window('StatusChange', status=obj.conn.status)
- @staticmethod
- def show_httpupload_progress(transfer):
- FileTransferProgress(transfer)
+ def send_httpupload(self, chat_control, path=None):
+ if path is not None:
+ self._send_httpupload(chat_control, path)
+ return
- def send_httpupload(self, chat_control):
accept_cb = partial(self.on_file_dialog_ok, chat_control)
FileChooserDialog(accept_cb,
select_multiple=True,
transient_for=chat_control.parent_win.window)
- @staticmethod
- def on_file_dialog_ok(chat_control, paths):
- con = app.connections[chat_control.account]
+ def on_file_dialog_ok(self, chat_control, paths):
for path in paths:
- con.get_module('HTTPUpload').check_file_before_transfer(
+ self._send_httpupload(chat_control, path)
+
+ def _send_httpupload(self, chat_control, path):
+ con = app.connections[chat_control.account]
+ try:
+ transfer = con.get_module('HTTPUpload').make_transfer(
path,
chat_control.encryption,
chat_control.contact,
chat_control.is_groupchat)
+ except FileError as error:
+ app.nec.push_incoming_event(InformationEvent(
+ None, dialog_name='open-file-error2', args=error))
+ return
- 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(transfer, account, callback)
- else:
- transfer.set_error()
- self.raise_dialog('httpupload-encryption-not-available')
+ transfer.connect('state-changed',
+ self._on_http_upload_state_changed)
+ FileTransferProgress(transfer)
+ con.get_module('HTTPUpload').start_transfer(transfer)
+
+ @staticmethod
+ def _on_http_upload_state_changed(transfer, _signal_name, state):
+ if state.is_finished:
+ uri = transfer.get_transformed_uri()
+
+ type_ = 'chat'
+ if transfer.is_groupchat:
+ type_ = 'groupchat'
+
+ message = OutgoingMessage(account=transfer.account,
+ contact=transfer.contact,
+ message=uri,
+ type_=type_,
+ oob_url=uri)
+
+ client = app.get_client(transfer.account)
+ client.send_message(message)
@staticmethod
def handle_event_metacontacts(obj):