Commit bbafe882 authored by Daniel Brötzmann's avatar Daniel Brötzmann
Browse files

Jingle file transfers: Typing

parent 02e29ce8
Pipeline #9049 failed with stages
in 3 minutes and 39 seconds
......@@ -12,6 +12,8 @@
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import typing
from typing import Any
from typing import Union
......@@ -29,11 +31,13 @@
from nbxmpp.const import InviteType
from gajim.common import app
from gajim.common.file_props import FileProp
if typing.TYPE_CHECKING:
from gajim.common.helpers import AdditionalDataDict
from gajim.common.const import KindConstant
from gajim.common.client import Client
from gajim.common.jingle_session import JingleSession
@dataclass
......@@ -167,14 +171,14 @@ class AdHocCommandActionResponse(ApplicationEvent):
@dataclass
class FileProgress(ApplicationEvent):
name: str = field(init=False, default='file-progress')
file_props: Any
file_props: FileProp
@dataclass
class FileCompleted(ApplicationEvent):
name: str = field(init=False, default='file-completed')
account: str
file_props: Any
file_props: FileProp
jid: str
......@@ -182,7 +186,7 @@ class FileCompleted(ApplicationEvent):
class FileError(ApplicationEvent):
name: str = field(init=False, default='file-error')
account: str
file_props: Any
file_props: FileProp
jid: str
......@@ -190,7 +194,7 @@ class FileError(ApplicationEvent):
class FileHashError(ApplicationEvent):
name: str = field(init=False, default='file-hash-error')
account: str
file_props: Any
file_props: FileProp
jid: str
......@@ -198,7 +202,7 @@ class FileHashError(ApplicationEvent):
class FileRequestSent(ApplicationEvent):
name: str = field(init=False, default='file-request-sent')
account: str
file_props: Any
file_props: FileProp
jid: str
......@@ -206,7 +210,7 @@ class FileRequestSent(ApplicationEvent):
class FileRequestError(ApplicationEvent):
name: str = field(init=False, default='file-request-error')
conn: 'Client'
file_props: Any
file_props: FileProp
jid: str
error_msg: str = ''
......@@ -215,8 +219,9 @@ class FileRequestError(ApplicationEvent):
class FileSendError(ApplicationEvent):
name: str = field(init=False, default='file-send-error')
account: str
file_props: Any
file_props: FileProp
jid: str
error_msg: str = ''
@dataclass
......@@ -484,7 +489,7 @@ class JingleEvent(ApplicationEvent):
jid: str
sid: str
resource: str
jingle_session: Any
jingle_session: JingleSession
@dataclass
......@@ -651,7 +656,7 @@ class FileRequestReceivedEvent(ApplicationEvent):
fjid: str = field(init=False)
account: str = field(init=False)
jid: str = field(init=False)
file_props: Any = field(init=False)
file_props: FileProp = field(init=False)
def __post_init__(self):
from gajim.common.jingle_transport import JingleTransportSocks5
......
......@@ -112,7 +112,7 @@ def __init__(self, account: str, sid: str) -> None:
# Do not instantiate this class directly!
# Call FilesProp.getNeFileProp instead
self.streamhosts = []
self.transfered_size: list[int] = []
self.transfered_size: list[tuple[float, int]] = []
self.started: bool = False
self.completed: bool = False
self.paused: bool = False
......
......@@ -66,7 +66,8 @@ def _iq_error_received(self, _con, _stanza, properties):
app.ged.raise_event(
FileSendError(account=self._account,
jid=str(properties.jid),
file_props=file_props))
file_props=file_props,
error_msg=''))
self._con.get_module('Bytestream').disconnect_transfer(
file_props)
raise nbxmpp.NodeProcessed
......
......@@ -29,6 +29,7 @@
from typing import Dict
from typing import List
from typing import Optional
from typing import Union
import os
import logging
......@@ -54,7 +55,10 @@
from gajim.common.helpers import get_file_path_from_dnd_dropped_uri
from gajim.common.i18n import _
from gajim.common.ged import EventHelper
from gajim.common.events import ApplicationEvent
from gajim.common.events import JingleRequestReceived
from gajim.common.events import FileRequestReceivedEvent
from gajim.common.events import FileRequestSent
from gajim.common.events import Notification
from gajim.common.helpers import AdditionalDataDict
from gajim.common.helpers import get_retraction_text
......@@ -293,7 +297,7 @@ def _connect_contact_signals(self) -> None:
def _get_action(self, name: str) -> Gio.SimpleAction:
return app.window.lookup_action(f'{name}{self.control_id}')
def process_event(self, event):
def process_event(self, event: ApplicationEvent) -> None:
if event.account != self.account:
return
......@@ -311,6 +315,7 @@ def process_event(self, event):
if event.name in jingle_av_events:
self._process_jingle_av_event(event)
return
if event.name in ('file-request-received', 'file-request-sent'):
self.add_jingle_file_transfer(event=event)
return
......@@ -1173,7 +1178,12 @@ def add_info_message(self, text: str) -> None:
def add_file_transfer(self, transfer: HTTPFileTransfer) -> None:
self.conversation_view.add_file_transfer(transfer)
def add_jingle_file_transfer(self, event):
def add_jingle_file_transfer(self,
event: Union[
FileRequestReceivedEvent,
FileRequestSent,
None]
) -> None:
if self._allow_add_message():
self.conversation_view.add_jingle_file_transfer(event)
......
......@@ -12,11 +12,17 @@
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import Union
from typing import Optional
import logging
import os
import time
from datetime import datetime
from gi.repository import GdkPixbuf
from gi.repository import GLib
from gi.repository import Gtk
......@@ -24,27 +30,44 @@
from gajim.common import ged
from gajim.common.const import AvatarSize
from gajim.common.const import KindConstant
from gajim.common.events import FileRequestReceivedEvent
from gajim.common.events import FileRequestSent
from gajim.common.events import FileCompleted
from gajim.common.events import FileProgress
from gajim.common.events import FileError
from gajim.common.events import FileHashError
from gajim.common.events import FileSendError
from gajim.common.events import FileRequestError
from gajim.common.events import JingleErrorReceived
from gajim.common.events import JingleFtCancelledReceived
from gajim.common.file_props import FileProp
from gajim.common.file_props import FilesProp
from gajim.common.helpers import open_file
from gajim.common.i18n import _
from gajim.common.modules.contacts import BareContact
from gajim.common.storage.archive import ConversationRow
from .base import BaseRow
from ...builder import get_builder
from ...util import format_eta
TransferEventT = Union[FileRequestReceivedEvent, FileRequestSent]
log = logging.getLogger('gajim.gui.conversation.rows.file_transfer_jingle')
class FileTransferJingleRow(BaseRow):
def __init__(self, account, contact, event=None, db_message=None):
def __init__(self,
account: str,
contact: BareContact,
event: Optional[TransferEventT] = None,
db_message: Optional[ConversationRow] = None
) -> None:
BaseRow.__init__(self, account)
self.type = 'file-transfer'
is_from_db = bool(db_message is not None)
if is_from_db:
if db_message is not None:
timestamp = db_message.time
else:
timestamp = time.time()
......@@ -53,12 +76,14 @@ def __init__(self, account, contact, event=None, db_message=None):
self._contact = contact
if is_from_db:
if db_message is not None:
sid = db_message.additional_data.get_value('gajim', 'sid')
assert sid is not None
self._file_props = FilesProp.getFilePropBySid(sid)
if self._file_props is None:
log.debug('File props not found for SID: %s', sid)
log.debug('File prop not found for SID: %s', sid)
else:
assert event is not None
self._file_props = event.file_props
self._start_time = 0
......@@ -72,7 +97,7 @@ def __init__(self, account, contact, event=None, db_message=None):
avatar_placeholder.set_valign(Gtk.Align.START)
self.grid.attach(avatar_placeholder, 0, 0, 1, 1)
if is_from_db:
if db_message is not None:
if db_message.kind == KindConstant.FILE_TRANSFER_INCOMING:
contact = self._contact
is_self = True
......@@ -81,7 +106,7 @@ def __init__(self, account, contact, event=None, db_message=None):
str(self._client.get_own_jid().bare))
is_self = False
else:
if event.name == 'file-request-sent':
if isinstance(event, FileRequestSent):
contact = self._client.get_module('Contacts').get_contact(
str(self._client.get_own_jid().bare))
is_self = False
......@@ -91,6 +116,7 @@ def __init__(self, account, contact, event=None, db_message=None):
scale = self.get_scale_factor()
avatar = contact.get_avatar(AvatarSize.ROSTER, scale, add_show=False)
assert not isinstance(avatar, GdkPixbuf.Pixbuf)
avatar_image = Gtk.Image.new_from_surface(avatar)
avatar_placeholder.add(avatar_image)
......@@ -112,9 +138,10 @@ def __init__(self, account, contact, event=None, db_message=None):
self.show_all()
if is_from_db:
if db_message is not None:
self._reconstruct_transfer()
else:
assert event is not None
self._display_transfer_info(event.name)
if self._file_props is None:
......@@ -170,7 +197,7 @@ def _show_file_infos(self) -> None:
self._ui.file_size.hide()
return
file_name = GLib.markup_escape_text(self._file_props.name)
file_name = GLib.markup_escape_text(str(self._file_props.name))
if self._file_props.mime_type:
file_name = f'{file_name} ({self._file_props.mime_type})'
self._ui.file_name.set_text(file_name)
......@@ -186,8 +213,8 @@ def _show_file_infos(self) -> None:
self._ui.file_size.set_text(
GLib.format_size_full(self._file_props.size, self._units))
def process_event(self, event):
if event.name == 'jingle-error-received':
def process_event(self, event: TransferEventT) -> None:
if isinstance(event, JingleErrorReceived):
if event.sid != self._file_props.sid:
return
self._ui.action_stack.set_visible_child_name('error')
......@@ -195,7 +222,7 @@ def process_event(self, event):
self._ui.error_label.set_text(event.reason)
return
if event.name == 'jingle-ft-cancelled-received':
if isinstance(event, JingleFtCancelledReceived):
if event.sid != self._file_props.sid:
return
self._ui.action_stack.set_visible_child_name('error')
......@@ -209,14 +236,15 @@ def process_event(self, event):
if event.file_props.sid != self._file_props.sid:
return
if event.name == 'file-completed':
if isinstance(event, FileCompleted):
self._show_completed()
elif event.name == 'file-error':
elif isinstance(event, FileError):
self._show_error(event.file_props)
elif event.name == 'file-hash-error':
elif isinstance(event, FileHashError):
self._ui.action_stack.set_visible_child_name('hash-error')
self._ui.transfer_action.set_text(_('File Verification Failed'))
elif event.name in ('file-request-error', 'file-send-error'):
elif (isinstance(event, FileRequestError) or
isinstance(event, FileSendError)):
self._ui.action_stack.set_visible_child_name('error')
self._ui.transfer_action.set_text(_('File Transfer Cancelled'))
error_text = _('Connection with %s could not be '
......@@ -225,7 +253,7 @@ def process_event(self, event):
error_text = f'{error_text} ({event.error_msg})'
self._ui.error_label.set_text(error_text)
elif event.name == 'file-progress':
elif isinstance(event, FileProgress):
self._update_progress(event.file_props)
def _update_progress(self, file_props: FileProp) -> None:
......
......@@ -36,6 +36,8 @@
from gajim.common import app
from gajim.common.client import Client
from gajim.common.events import JingleRequestReceived
from gajim.common.events import FileRequestReceivedEvent
from gajim.common.events import FileRequestSent
from gajim.common.helpers import AdditionalDataDict
from gajim.common.helpers import to_user_string
from gajim.common.helpers import get_start_of_day
......@@ -231,7 +233,14 @@ def add_file_transfer(self, transfer: HTTPFileTransfer) -> None:
transfer_row = FileTransferRow(self._account, transfer)
self._insert_message(transfer_row)
def add_jingle_file_transfer(self, event=None, db_message=None):
def add_jingle_file_transfer(self,
event: Union[
FileRequestReceivedEvent,
FileRequestSent,
None] = None,
db_message: Optional[ConversationRow] = None
) -> None:
assert isinstance(self._contact, BareContact)
jingle_transfer_row = FileTransferJingleRow(
self._account, self._contact, event=event, db_message=db_message)
self._insert_message(jingle_transfer_row)
......
......@@ -30,6 +30,8 @@
from gi.repository import GLib
from gi.repository import Pango
from nbxmpp.protocol import JID
from gajim.common import app
from gajim.common import ged
from gajim.common import helpers
......@@ -38,12 +40,14 @@
from gajim.common.const import KindConstant
from gajim.common.i18n import _
from gajim.common.file_props import FilesProp
from gajim.common.file_props import FileProp
from gajim.common.helpers import open_file
from gajim.common.helpers import file_is_locked
from gajim.common.helpers import AdditionalDataDict
from gajim.common.modules.bytestream import is_transfer_active
from gajim.common.modules.bytestream import is_transfer_paused
from gajim.common.modules.bytestream import is_transfer_stopped
from gajim.common.modules.contacts import BareContact
from .dialogs import DialogButton
from .dialogs import ConfirmationDialog
......@@ -343,7 +347,11 @@ def show_stopped(self, jid, file_props, error_msg=''):
ErrorDialog(_('File transfer stopped'), sectext)
self._ui.transfers_list.get_selection().unselect_all()
def show_hash_error(self, jid, file_props, account):
def show_hash_error(self,
jid: JID,
file_props: FileProp,
account: str
) -> None:
def _on_yes():
# Delete old file
os.remove(file_props.file_name)
......@@ -436,7 +444,11 @@ def _start_receive(self, file_path, account, contact, file_props):
client = app.get_client(account)
client.get_module('Bytestream').send_file_approval(file_props)
def on_file_request_accepted(self, account, contact, file_props):
def on_file_request_accepted(self,
account: str,
contact: BareContact,
file_props: FileProp
) -> None:
def _on_accepted(account, contact, file_props, file_path):
if os.path.exists(file_path):
app.settings.set('last_save_dir', os.path.dirname(file_path))
......@@ -887,10 +899,10 @@ def _on_cancel_button_clicked(self, widget):
file_props = FilesProp.getFilePropByType(sid[0], sid[1:])
self.cancel_transfer(file_props)
def cancel_transfer(self, file_props):
def cancel_transfer(self, file_props: FileProp) -> None:
# TODO: does not cancel transfer somehow
account = file_props.tt_account
if account not in app.connections:
if account is None or account not in app.connections:
return
client = app.get_client(account)
# Check if we are in a IBB transfer
......
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