diff --git a/src/common/config.py b/src/common/config.py index 4df5634ef..2ea157d31 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -380,7 +380,7 @@ class Config: 'http_auth': [opt_str, 'ask'], # yes, no, ask 'dont_ack_subscription': [opt_bool, False, _('Jabberd2 workaround')], # proxy65 for FT - 'file_transfer_proxies': [opt_str, 'proxy.eu.jabber.org, proxy.jabber.ru, proxy.jabbim.cz'], + 'file_transfer_proxies': [opt_str, ''], 'use_ft_proxies': [opt_bool, False, _('If checked, Gajim will use your IP and proxies defined in file_transfer_proxies option for file transfer.'), True], 'test_ft_proxies_on_startup': [opt_bool, False, _('If True, Gajim will test file transfer proxies on startup to be sure it works. Openfire\'s proxies are known to fail this test even if they work.')], 'msgwin-x-position': [opt_int, -1], # Default is to let the wm decide diff --git a/src/common/events.py b/src/common/events.py index 9322679fd..3896cc69b 100644 --- a/src/common/events.py +++ b/src/common/events.py @@ -163,7 +163,7 @@ class FileStoppedEvent(FileRequestEvent): type_ = 'file-stopped' class FileHashErrorEvent(FileRequestEvent): - type_ = 'file-hash-rror' + type_ = 'file-hash-error' class JingleIncomingEvent(Event): type_ = 'jingle-incoming' diff --git a/src/common/jingle_ftstates.py b/src/common/jingle_ftstates.py index 3d5d6b9dc..5c305f78a 100644 --- a/src/common/jingle_ftstates.py +++ b/src/common/jingle_ftstates.py @@ -146,7 +146,7 @@ class StateTransfering(JingleFileTransferStates): def _start_ibb_transfer(self, con): self.jft.file_props.transport_sid = self.jft.transport.sid - fp = open(self.jft.file_props.file_name, 'r') + fp = open(self.jft.file_props.file_name, 'rb') con.OpenStream(self.jft.file_props.sid, self.jft.session.peerjid, fp, blocksize=4096) diff --git a/src/common/protocol/bytestream.py b/src/common/protocol/bytestream.py index a3d0069dc..28528663c 100644 --- a/src/common/protocol/bytestream.py +++ b/src/common/protocol/bytestream.py @@ -251,7 +251,7 @@ class ConnectionBytestream: if field.getValue() == nbxmpp.NS_IBB: sid = file_props.sid file_props.transport_sid = sid - fp = open(file_props.file_name, 'r') + fp = open(file_props.file_name, 'rb') self.OpenStream(sid, file_props.receiver, fp) raise nbxmpp.NodeProcessed @@ -759,7 +759,6 @@ class ConnectionIBBytestream(ConnectionBytestream): def __init__(self): ConnectionBytestream.__init__(self) self._streams = {} - self.last_sent_ibb_id = None def IBBIqHandler(self, conn, stanza): """ @@ -767,12 +766,22 @@ class ConnectionIBBytestream(ConnectionBytestream): """ typ = stanza.getType() log.debug('IBBIqHandler called typ->%s' % typ) - if typ == 'set' and stanza.getTag('open', namespace=nbxmpp.NS_IBB): + if typ == 'set' and stanza.getTag('open'): self.StreamOpenHandler(conn, stanza) - elif typ == 'set' and stanza.getTag('close', namespace=nbxmpp.NS_IBB): + elif typ == 'set' and stanza.getTag('close'): self.StreamCloseHandler(conn, stanza) - elif typ == 'result': - self.SendHandler() + elif typ == 'set' and stanza.getTag('data'): + sid = stanza.getTagAttr('data', 'sid') + file_props = FilesProp.getFilePropByTransportSid(self.name, sid) + if not file_props: + conn.send(nbxmpp.Error(stanza, nbxmpp.ERR_ITEM_NOT_FOUND)) + elif file_props.connected and self.IBBMessageHandler(conn, + stanza): + reply = stanza.buildReply('result') + reply.delChild('data') + conn.send(reply) + elif not file_props.connected: + log.debug('Received IQ for closed filetransfer, IQ dropped') elif typ == 'error': gajim.socks5queue.error_cb() else: @@ -815,18 +824,23 @@ class ConnectionIBBytestream(ConnectionBytestream): file_props.disconnect_cb = None file_props.continue_cb = None file_props.syn_id = stanza.getID() - file_props.fp = open(file_props.file_name, 'w') + file_props.fp = open(file_props.file_name, 'wb') conn.send(rep) def CloseIBBStream(self, file_props): file_props.connected = False file_props.fp.close() file_props.stopped = True - self.connection.send(nbxmpp.Protocol('iq', - file_props.direction[1:], 'set', + to = file_props.receiver + if file_props.direction == '<': + to = file_props.sender + self.connection.send( + nbxmpp.Protocol('iq', to, 'set', payload=[nbxmpp.Node(nbxmpp.NS_IBB + ' close', - {'sid':file_props.sid})])) - if file_props.session_type == 'jingle': + {'sid':file_props.transport_sid})])) + if file_props.completed: + gajim.socks5queue.complete_transfer_cb(self.name, file_props) + elif file_props.session_type == 'jingle': peerjid = \ file_props.receiver if file_props.type_ == 's' else file_props.sender session = self.get_jingle_session(peerjid, file_props.sid, 'file') @@ -844,7 +858,7 @@ class ConnectionIBBytestream(ConnectionBytestream): base64 encoding that increases size of data by 1/3. """ file_props = FilesProp.getFilePropBySid(sid) - file_props.direction = '|>' + to + file_props.direction = '>' file_props.block_size = blocksize file_props.fp = fp file_props.seq = 0 @@ -863,53 +877,41 @@ class ConnectionIBBytestream(ConnectionBytestream): file_props.syn_id = syn.getID() return file_props - def SendHandler(self): + def SendHandler(self, file_props): """ Send next portion of data if it is time to do it. Used internally. """ log.debug('SendHandler called') - for file_props in FilesProp.getAllFileProp(): - if not file_props.direction: - # it's socks5 bytestream - continue - sid = file_props.sid - if file_props.direction[:2] == '|>': - # We waitthat other part accept stream - continue - if file_props.direction[0] == '>': - if file_props.paused: - continue - if not file_props.connected: - #TODO: Reply with out of order error - continue - chunk = file_props.fp.read(file_props.block_size) - if chunk: - datanode = nbxmpp.Node(nbxmpp.NS_IBB + ' data', { - 'sid': file_props.transport_sid, - 'seq': file_props.seq}, base64.b64encode(chunk.encode( - 'utf-8')).decode('utf-8')) - file_props.seq += 1 - file_props.started = True - if file_props.seq == 65536: - file_props.seq = 0 - self.last_sent_ibb_id = self.connection.send( - nbxmpp.Protocol(name='iq', to=file_props.direction[1:], - typ='set', payload=[datanode])) - current_time = time.time() - file_props.elapsed_time += current_time - file_props.last_time - file_props.last_time = current_time - file_props.received_len += len(chunk) - gajim.socks5queue.progress_transfer_cb(self.name, - file_props) - else: - # notify the other side about stream closing - # notify the local user about sucessfull send - # delete the local stream - self.connection.send(nbxmpp.Protocol('iq', - file_props.direction[1:], 'set', - payload=[nbxmpp.Node(nbxmpp.NS_IBB + ' close', - {'sid': file_props.transport_sid})])) - file_props.completed = True + if file_props.completed: + self.CloseIBBStream(file_props) + if file_props.paused: + return + if not file_props.connected: + #TODO: Reply with out of order error + return + chunk = file_props.fp.read(file_props.block_size) + if chunk: + datanode = nbxmpp.Node(nbxmpp.NS_IBB + ' data', { + 'sid': file_props.transport_sid, + 'seq': file_props.seq}, + base64.b64encode(chunk).decode('ascii')) + file_props.seq += 1 + file_props.started = True + if file_props.seq == 65536: + file_props.seq = 0 + file_props.syn_id = self.connection.send( + nbxmpp.Protocol(name='iq', to=file_props.receiver, + typ='set', payload=[datanode])) + current_time = time.time() + file_props.elapsed_time += current_time - file_props.last_time + file_props.last_time = current_time + file_props.received_len += len(chunk) + if file_props.size == file_props.received_len: + file_props.completed = True + gajim.socks5queue.progress_transfer_cb(self.name, + file_props) + else: + log.debug('Nothing to read, but file not completed') def IBBMessageHandler(self, conn, stanza): """ @@ -978,6 +980,7 @@ class ConnectionIBBytestream(ConnectionBytestream): else: conn.send(nbxmpp.Error(stanza, nbxmpp.ERR_ITEM_NOT_FOUND)) + def IBBAllIqHandler(self, conn, stanza): """ Handle remote side reply about if it agree or not to receive our @@ -999,25 +1002,9 @@ class ConnectionIBBytestream(ConnectionBytestream): else: conn.Event('IBB', 'ERROR ON SEND', file_props) elif stanza.getType() == 'result': - if file_props.direction[0] == '|': - file_props.direction = file_props.direction[1:] - self.SendHandler() - else: - conn.send(nbxmpp.Error(stanza, - nbxmpp.ERR_UNEXPECTED_REQUEST)) + self.SendHandler(file_props) break - else: - if stanza.getTag('data'): - sid = stanza.getTagAttr('data', 'sid') - file_props = FilesProp.getFilePropByTransportSid(self.name, sid) - if file_props.connected and self.IBBMessageHandler(conn, - stanza): - reply = stanza.buildReply('result') - reply.delChild('data') - conn.send(reply) - raise nbxmpp.NodeProcessed - elif syn_id == self.last_sent_ibb_id: - self.SendHandler() + class ConnectionSocks5BytestreamZeroconf(ConnectionSocks5Bytestream): diff --git a/src/dialogs.py b/src/dialogs.py index 2058455f2..549bb05e2 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -1271,6 +1271,11 @@ class AddNewContactWindow: def _nec_gateway_prompt_received(self, obj): if self.adding_jid: jid, transport, type_ = self.adding_jid + if obj.stanza.getError(): + ErrorDialog(_('Error while adding transport contact'), + _('This error occured while adding a contact for transport ' + '%s:\n\n%s') % (transport, obj.stanza.getErrorMsg())) + return if obj.prompt_jid: self._add_jid(obj.prompt_jid, type_) else: diff --git a/src/filetransfers_window.py b/src/filetransfers_window.py index 7ad4fc23e..b008c1c33 100644 --- a/src/filetransfers_window.py +++ b/src/filetransfers_window.py @@ -70,8 +70,7 @@ class FileTransfersWindow: 'notify_ft_complete_checkbox') shall_notify = gajim.config.get('notify_on_file_complete') - self.notify_ft_checkbox.set_active(shall_notify - ) + self.notify_ft_checkbox.set_active(shall_notify) self.model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, str, str, int, int, str) self.tree.set_model(self.model) @@ -841,7 +840,7 @@ class FileTransfersWindow: if not is_row_selected: # no selection, disable the buttons self.set_all_insensitive() - elif not is_stopped: + elif not is_stopped and file_props.continue_cb: if is_transfer_active(file_props): # file transfer is active self.toggle_pause_continue(True) diff --git a/src/gui_interface.py b/src/gui_interface.py index d1589062c..7a5ba2e0a 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -988,11 +988,6 @@ class Interface: session = gajim.connections[account].get_jingle_session(jid=None, sid=file_props.sid) ft_win = self.instances['file_transfers'] - if not file_props.hash_: - # We disn't get the hash, sender probably don't support that - jid = file_props.sender - self.popup_ft_result(account, jid, file_props) - ft_win.set_status(file_props, 'ok') h = Hashes() try: file_ = open(file_props.file_name, 'rb') @@ -1027,14 +1022,26 @@ class Interface: if file_props.stalled or file_props.paused: return - if file_props.type_ == 'r' and file_props.hash_: # we receive a file + if file_props.type_ == 'r': # we receive a file gajim.socks5queue.remove_receiver(file_props.sid, True, True) - # we compare hashes if file_props.session_type == 'jingle': - # Compare hashes in a new thread - self.hashThread = Thread(target=self.__compare_hashes, - args=(account, file_props)) - self.hashThread.start() + if file_props.hash_ and file_props.error == 0: + # We compare hashes in a new thread + self.hashThread = Thread(target=self.__compare_hashes, + args=(account, file_props)) + self.hashThread.start() + else: + # We disn't get the hash, sender probably don't support that + jid = file_props.sender + self.popup_ft_result(account, jid, file_props) + if file_props.error == 0: + ft.set_status(file_props, 'ok') + session = gajim.connections[account].get_jingle_session(jid=None, + sid=file_props.sid) + # End jingle session + # TODO: only if there are no other parallel downloads in this session + if session: + session.end_session() else: # we send a file jid = file_props.receiver gajim.socks5queue.remove_sender(file_props.sid, True, True)