From 2af1af2011220d38d4ef13e2583134af8df6b12b Mon Sep 17 00:00:00 2001 From: Jefry Lagrange Date: Thu, 14 Jun 2012 12:27:23 -0400 Subject: [PATCH] file_props refactoring --- src/chat_control.py | 18 +- src/common/connection_handlers_events.py | 99 ++--- src/common/file_props.py | 153 ++++++++ src/common/jingle.py | 2 +- src/common/jingle_content.py | 16 +- src/common/jingle_ft.py | 46 +-- src/common/jingle_ftstates.py | 43 +-- src/common/jingle_transport.py | 18 +- src/common/protocol/bytestream.py | 448 +++++++++++------------ src/common/proxy65_manager.py | 15 +- src/common/socks5.py | 391 +++++++++----------- src/filetransfers_window.py | 273 +++++++------- src/gui_interface.py | 109 +++--- src/tooltips.py | 49 ++- 14 files changed, 876 insertions(+), 804 deletions(-) create mode 100644 src/common/file_props.py diff --git a/src/chat_control.py b/src/chat_control.py index d2201eb91..243a87891 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -3203,11 +3203,11 @@ class ChatControl(ChatControlBase): """ Show an InfoBar on top of control """ - markup = '%s: %s' % (_('File transfer'), file_props['name']) - if 'desc' in file_props and file_props['desc']: - markup += ' (%s)' % file_props['desc'] + markup = '%s: %s' % (_('File transfer'), file_props.name) + if file_props.desc: + markup += ' (%s)' % file_props.desc markup += '\n%s: %s' % (_('Size'), helpers.convert_bytes( - file_props['size'])) + file_props.size)) b1 = gtk.Button(_('_Accept')) b1.connect('clicked', self._on_accept_file_request, file_props) b2 = gtk.Button(stock=gtk.STOCK_CANCEL) @@ -3216,9 +3216,7 @@ class ChatControl(ChatControlBase): gtk.MESSAGE_QUESTION) def _on_open_ft_folder(self, widget, file_props): - if 'file-name' not in file_props: - return - path = os.path.split(file_props['file-name'])[0] + path = os.path.split(file_props.file_name)[0] if os.path.exists(path) and os.path.isdir(path): helpers.launch_file_manager(path) ev = self._get_file_props_event(file_props, 'file-completed') @@ -3232,9 +3230,9 @@ class ChatControl(ChatControlBase): def _got_file_completed(self, file_props): markup = '%s: %s' % (_('File transfer completed'), - file_props['name']) - if 'desc' in file_props and file_props['desc']: - markup += ' (%s)' % file_props['desc'] + file_props.name) + if file_props.desc: + markup += ' (%s)' % file_props.desc b1 = gtk.Button(_('_Open Containing Folder')) b1.connect('clicked', self._on_open_ft_folder, file_props) b2 = gtk.Button(stock=gtk.STOCK_OK) diff --git a/src/common/connection_handlers_events.py b/src/common/connection_handlers_events.py index 4706d473d..4a8fc1bde 100644 --- a/src/common/connection_handlers_events.py +++ b/src/common/connection_handlers_events.py @@ -37,6 +37,7 @@ from common.logger import LOG_DB_PATH from common.pep import SUPPORTED_PERSONAL_USER_EVENTS from common.xmpp.protocol import NS_CHATSTATES from common.jingle_transport import JingleTransportSocks5 +from common.file_props import FilesProp import gtkgui_helpers @@ -1940,16 +1941,44 @@ class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.get_id() self.fjid = self.conn._ft_get_from(self.stanza) self.jid = gajim.get_jid_without_resource(self.fjid) - self.file_props = {'type': 'r'} - self.file_props['sender'] = self.fjid - self.file_props['request-id'] = self.id_ if self.jingle_content: - self.file_props['session-type'] = 'jingle' - self.file_props['stream-methods'] = xmpp.NS_BYTESTREAM + self.FT_content.use_security = bool(self.jingle_content.getTag( + 'security')) + if not self.FT_content.transport: + self.FT_content.transport = JingleTransportSocks5() + self.FT_content.transport.set_our_jid( + self.FT_content.session.ourjid) + self.FT_content.transport.set_connection( + self.FT_content.session.connection) + sid = self.FT_content.transport.sid + self.file_props = FilesProp.getNewFileProp(self.conn.name, sid) + self.file_props.session_sid = unicode( + self.stanza.getTag('jingle').getAttr('sid') + ) + self.FT_content.file_props = self.file_props + self.FT_content.transport.set_file_props(self.file_props) + if self.file_props.streamhosts: + self.file_props.streamhosts.extend( + self.FT_content.transport.remote_candidates) + else: + self.file_props.streamhosts = \ + self.FT_content.transport.remote_candidates + for host in self.file_props.streamhosts: + host['initiator'] = self.FT_content.session.initiator + host['target'] = self.FT_content.session.responder + else: + si = self.stanza.getTag('si') + self.file_props = FilesProp.getNewFileProp(self.conn.name, + unicode(si.getAttr('id')) + ) + self.file_props.sender = self.fjid + self.file_props.request_id = self.id_ + if self.jingle_content: + self.file_props.session_type = 'jingle' + self.file_props.stream_methods = xmpp.NS_BYTESTREAM file_tag = self.jingle_content.getTag('description').getTag( 'offer').getTag('file') else: - si = self.stanza.getTag('si') profile = si.getAttr('profile') if profile != xmpp.NS_FILE: self.conn.send_file_rejection(self.file_props, code='400', @@ -1965,7 +1994,7 @@ class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): for f in self.dataform.iter_fields(): if f.var == 'stream-method' and f.type == 'list-single': values = [o[1] for o in f.options] - self.file_props['stream-methods'] = ' '.join(values) + self.file_props.stream_methods = ' '.join(values) if xmpp.NS_BYTESTREAM in values or xmpp.NS_IBB in values: break else: @@ -1975,54 +2004,30 @@ class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): file_tag = si.getTag('file') for child in file_tag.getChildren(): name = child.getName() - if name in ('name', 'size', 'hash', 'date'): - val = child.getData() - if val is None: - continue - self.file_props[name] = val - # Delete this, it shouldn't be necesary after file_props gets - # refactored. - if name == 'hash': - self.file_props['algo'] = child.getAttr('algo') + val = child.getData() + if val is None: + continue + if name == 'name': + self.file_props.name = val + if name == 'size': + self.file_props.size = val + if name == 'hash': + self.file_props.algo = child.getAttr('algo') + self.file_props.hash_ = val + if name == 'date': + self.file_props.date = val file_desc_tag = file_tag.getTag('desc') if file_desc_tag is not None: - self.file_props['desc'] = file_desc_tag.getData() + self.file_props.desc = file_desc_tag.getData() if not self.jingle_content: mime_type = si.getAttr('mime-type') if mime_type is not None: - self.file_props['mime-type'] = mime_type + self.file_props.mime_type = mime_type - self.file_props['receiver'] = self.conn._ft_get_our_jid() - self.file_props['transfered_size'] = [] - if self.jingle_content: - self.FT_content.use_security = bool(self.jingle_content.getTag( - 'security')) - self.file_props['session-sid'] = unicode(self.stanza.getTag( - 'jingle').getAttr('sid')) + self.file_props.receiver = self.conn._ft_get_our_jid() + self.file_props.transfered_size = [] - self.FT_content.file_props = self.file_props - if not self.FT_content.transport: - self.FT_content.transport = JingleTransportSocks5() - self.FT_content.transport.set_our_jid( - self.FT_content.session.ourjid) - self.FT_content.transport.set_connection( - self.FT_content.session.connection) - self.file_props['sid'] = self.FT_content.transport.sid - self.FT_content.session.connection.files_props[ - self.file_props['sid']] = self.file_props - self.FT_content.transport.set_file_props(self.file_props) - if self.file_props.has_key('streamhosts'): - self.file_props['streamhosts'].extend( - self.FT_content.transport.remote_candidates) - else: - self.file_props['streamhosts'] = \ - self.FT_content.transport.remote_candidates - for host in self.file_props['streamhosts']: - host['initiator'] = self.FT_content.session.initiator - host['target'] = self.FT_content.session.responder - else: - self.file_props['sid'] = unicode(si.getAttr('id')) return True class FileRequestErrorEvent(nec.NetworkIncomingEvent): diff --git a/src/common/file_props.py b/src/common/file_props.py new file mode 100644 index 000000000..dcdc04862 --- /dev/null +++ b/src/common/file_props.py @@ -0,0 +1,153 @@ +""" +This module is in charge of taking care of all the infomation related to +individual files. Files are identified by the account name and its sid. + + +>>> print FilesProp.getFileProp('jabberid', '10') +None +>>> fp = FilesProp() +Traceback (most recent call last): + ... +Exception: this class should not be instatiated +>>> print FilesProp.getAllFileProp() +[] +>>> fp = FilesProp.getNewFileProp('jabberid', '10') +>>> fp2 = FilesProp.getFileProp('jabberid', '10') +>>> fp == fp2 +True +""" + +class FilesProp: + _files_props = {} + + def __init__(self): + raise Exception('this class should not be instatiated') + + @classmethod + def getNewFileProp(cls, account, sid): + fp = FileProp(account, sid) + cls.setFileProp(fp, account, sid) + return fp + + @classmethod + def getFileProp(cls, account, sid): + if (account, sid) in cls._files_props.keys(): + return cls._files_props[account, sid] + + @classmethod + def getFilePropByAccount(cls, account): + # Returns a list of file_props in one account + file_props = [] + for account, sid in cls._files_props: + if account == account: + file_props.append(cls._files_props[account, sid]) + return file_props + + @classmethod + def getFilePropByType(cls, type_, sid): + # This method should be deleted. Getting fileprop by type and sid is not + # unique enough. More than one fileprop might have the same type and sid + files_prop = cls.getAllFileProp() + for fp in files_prop: + if fp.type_ == type_ and fp.sid == sid: + return fp + + @classmethod + def getFilePropBySid(cls, sid): + # This method should be deleted. It is kept to make things compatible + # This method should be replaced and instead get the file_props by + # account and sid + files_prop = cls.getAllFileProp() + for fp in files_prop: + if fp.sid == sid: + return fp + + @classmethod + def getAllFileProp(cls): + return cls._files_props.values() + + @classmethod + def setFileProp(cls, fp, account, sid): + cls._files_props[account, sid] = fp + + @classmethod + def deleteFileProp(cls, file_prop): + files_props = cls._files_props + a = s = None + for account, sid in files_props: + fp = files_props[account, sid] + if fp is file_prop: + a = account + s = sid + if a != None and s != None: + del files_props[a, s] + + +class FileProp(object): + + def __init__(self, account, sid): + # Do not instatiate this class directly. Call FilesProp.getNeFileProp + # instead + self.streamhosts = [] + self.transfered_size = [] + self.started = False + self.completed = False + self.paused = False + self.stalled = False + self.connected = False + self.stopped = False + self.is_a_proxy = False + self.proxyhost = None + self.proxy_sender = None + self.proxy_receiver = None + self.streamhost_used = None + # method callback called in case of transfer failure + self.failure_cb = None + # method callback called when disconnecting + self.disconnect_cb = None + self.continue_cb = None + self.sha_str = None + # transfer type: 's' for sending and 'r' for receiving + self.type_ = None + self.error = None + self.elapsed_time = None + self.last_time = None + self.received_len = None + # full file path + self.file_name = None + self.name = None + self.file_desc = None + self.offset = None + self.sender = None + self.receiver = None + self.tt_account = None + self.size = None + self._sid = sid + self.account = account + self.mime_type = None + self.algo = None + self.direction = None + self.syn_id = None + self.seq = None + self.hash_ = None + self.session_sid = None + self.fd = None + self.startexmpp = None + self.session_type = None + + def getsid(self): + # Getter of the property sid + return self._sid + + def setsid(self, value): + # The sid value will change + # we need to change the in _files_props key as well + del FilesProp._files_props[self.account, self._sid] + self._sid = value + FilesProp._files_props[self.account, self._sid] = self + + sid = property(getsid, setsid) + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/src/common/jingle.py b/src/common/jingle.py index c3bfc494f..54eb0f7dc 100644 --- a/src/common/jingle.py +++ b/src/common/jingle.py @@ -152,7 +152,7 @@ class ConnectionJingle(object): # this is a file transfer jingle.session_type_FT = True self._sessions[jingle.sid] = jingle - file_props['sid'] = jingle.sid + file_props.sid = jingle.sid if contact.supports(xmpp.NS_JINGLE_BYTESTREAM): transport = JingleTransportSocks5() elif contact.supports(xmpp.NS_JINGLE_IBB): diff --git a/src/common/jingle_content.py b/src/common/jingle_content.py index b31993ea6..b1351454c 100644 --- a/src/common/jingle_content.py +++ b/src/common/jingle_content.py @@ -172,25 +172,25 @@ class JingleContent(object): simode = xmpp.simplexml.Node(tag='offer') file_tag = simode.setTag('file', namespace=xmpp.NS_FILE) - if 'name' in self.file_props: + if self.file_props.name: node = xmpp.simplexml.Node(tag='name') - node.addData(self.file_props['name']) + node.addData(self.file_props.name) file_tag.addChild(node=node) - if 'size' in self.file_props: + if self.file_props.size: node = xmpp.simplexml.Node(tag='size') - node.addData(self.file_props['size']) + node.addData(self.file_props.size) file_tag.addChild(node=node) - if 'hash' in self.file_props: + if self.file_props.hash_: # TODO: use xep-300 for this bit pass # if the file is less than 10 mb, then it is small # lets calculate it right away - if int(self.file_props['size']) < 10000000: + if int(self.file_props.size) < 10000000: h = self._calcHash() file_tag.addChild(node=h) desc = file_tag.setTag('desc') - if 'desc' in self.file_props: - desc.setData(self.file_props['desc']) + if self.file_props.desc: + desc.setData(self.file_props.desc) description_node.addChild(node=simode) diff --git a/src/common/jingle_ft.py b/src/common/jingle_ft.py index 37944f3c0..5cc4c93f7 100644 --- a/src/common/jingle_ft.py +++ b/src/common/jingle_ft.py @@ -76,11 +76,11 @@ class JingleFileTransfer(JingleContent): self.weinitiate = True if self.file_props is not None: - self.file_props['sender'] = session.ourjid - self.file_props['receiver'] = session.peerjid - self.file_props['session-type'] = 'jingle' - self.file_props['session-sid'] = session.sid - self.file_props['transfered_size'] = [] + self.file_props.sender = session.ourjid + self.file_props.receiver = session.peerjid + self.file_props.session_type = 'jingle' + self.file_props.session_sid = session.sid + self.file_props.transfered_size = [] log.info("FT request: %s" % file_props) @@ -92,7 +92,7 @@ class JingleFileTransfer(JingleContent): log.info('ourjid: %s' % session.ourjid) if self.file_props is not None: - self.file_props['sid'] = self.transport.sid + self.file_props.sid = self.transport.sid self.session = session self.media = 'file' @@ -119,13 +119,12 @@ class JingleFileTransfer(JingleContent): FT_content=self)) self._listen_host() # Delete this after file_props refactoring this shouldn't be necesary - self.session.file_hash = self.file_props['hash'] - self.session.hash_algo = self.file_props['algo'] - + self.session.file_hash = self.file_props.hash_ + self.session.hash_algo = self.file_props.algo def __on_session_initiate_sent(self, stanza, content, error, action): # Calculate file_hash in a new thread # if we haven't sent the hash already. - if 'hash' not in self.file_props: + if self.file_props.hash_ is None: self.hashThread = threading.Thread(target=self.__send_hash) self.hashThread.start() @@ -143,7 +142,7 @@ class JingleFileTransfer(JingleContent): if self.session.hash_algo == None: return try: - file_ = open(self.file_props['file-name'], 'r') + file_ = open(self.file_props.file_name, 'r') except: # can't open file return @@ -154,7 +153,7 @@ class JingleFileTransfer(JingleContent): if not hash_: # Hash alogrithm not supported return - self.file_props['hash'] = hash_ + self.file_props.hash_ = hash_ h.addHash(hash_, self.session.hash_algo) return h @@ -175,26 +174,21 @@ class JingleFileTransfer(JingleContent): self.__state_changed(STATE_TRANSFERING) raise xmpp.NodeProcessed - self.file_props['streamhosts'] = self.transport.remote_candidates - for host in self.file_props['streamhosts']: + self.file_props.streamhosts = self.transport.remote_candidates + for host in self.file_props.streamhosts: host['initiator'] = self.session.initiator host['target'] = self.session.responder - host['sid'] = self.file_props['sid'] + host['sid'] = self.file_props.sid response = stanza.buildReply('result') response.delChild(response.getQuery()) con.connection.send(response) - - if not gajim.socks5queue.get_file_props( - self.session.connection.name, self.file_props['sid']): - gajim.socks5queue.add_file_props(self.session.connection.name, - self.file_props) fingerprint = None if self.use_security: fingerprint = 'client' if self.transport.type == TransportType.SOCKS5: gajim.socks5queue.connect_to_hosts(self.session.connection.name, - self.file_props['sid'], self.on_connect, + self.file_props.sid, self.on_connect, self._on_connect_error, fingerprint=fingerprint, receiving=False) return @@ -310,15 +304,15 @@ class JingleFileTransfer(JingleContent): def _store_socks5_sid(self, sid, hash_id): # callback from socsk5queue.start_listener - self.file_props['hash'] = hash_id + self.file_props.hash_ = hash_id def _listen_host(self): - receiver = self.file_props['receiver'] - sender = self.file_props['sender'] - sha_str = helpers.get_auth_sha(self.file_props['sid'], sender, + receiver = self.file_props.receiver + sender = self.file_props.sender + sha_str = helpers.get_auth_sha(self.file_props.sid, sender, receiver) - self.file_props['sha_str'] = sha_str + self.file_props.sha_str = sha_str port = gajim.config.get('file_transfers_port') diff --git a/src/common/jingle_ftstates.py b/src/common/jingle_ftstates.py index 271f53927..2afba98fe 100644 --- a/src/common/jingle_ftstates.py +++ b/src/common/jingle_ftstates.py @@ -14,6 +14,7 @@ import gajim import xmpp from jingle_transport import * +from common.socks5 import Socks5ReceiverClient, Socks5SenderClient class JingleFileTransferStates: @@ -37,26 +38,18 @@ class StateInitialized(JingleFileTransferStates): ''' def action(self, args=None): - self.jft._listen_host() if self.jft.weinitiate: # update connection's fileprops - self.jft.session.connection.files_props[self.jft.file_props['sid']] = \ - self.jft.file_props + self.jft._listen_host() # Listen on configured port for file transfer else: - # Add file_props to the queue - if not gajim.socks5queue.get_file_props( - self.jft.session.connection.name, self.jft.file_props['sid']): - gajim.socks5queue.add_file_props( - self.jft.session.connection.name, - self.jft.file_props) fingerprint = None if self.jft.use_security: fingerprint = 'client' # Connect to the candidate host, on success call on_connect method gajim.socks5queue.connect_to_hosts( self.jft.session.connection.name, - self.jft.file_props['sid'], self.jft.on_connect, + self.jft.file_props.sid, self.jft.on_connect, self.jft._on_connect_error, fingerprint=fingerprint) @@ -156,9 +149,7 @@ class StateTransfering(JingleFileTransferStates): ''' def __start_IBB_transfer(self, con): - con.files_props[self.jft.file_props['sid']] = \ - self.jft.file_props - fp = open(self.jft.file_props['file-name'], 'r') + fp = open(self.jft.file_props.file_name, 'r') con.OpenStream( self.jft.transport.sid, self.jft.session.peerjid, fp, blocksize=4096) @@ -174,14 +165,14 @@ class StateTransfering(JingleFileTransferStates): streamhost_used = self.jft.nominated_cand['peer-cand'] if streamhost_used['type'] == 'proxy': - self.jft.file_props['is_a_proxy'] = True + self.jft.file_props.is_a_proxy = True # This needs to be changed when requesting if self.jft.weinitiate: - self.jft.file_props['proxy_sender'] = streamhost_used['initiator'] - self.jft.file_props['proxy_receiver'] = streamhost_used['target'] + self.jft.file_props.proxy_sender = streamhost_used['initiator'] + self.jft.file_props.proxy_receiver = streamhost_used['target'] else: - self.jft.file_props['proxy_sender'] = streamhost_used['target'] - self.jft.file_props['proxy_receiver'] = streamhost_used['initiator'] + self.jft.file_props.proxy_sender = streamhost_used['target'] + self.jft.file_props.proxy_receiver = streamhost_used['initiator'] # This needs to be changed when requesting if not self.jft.weinitiate and streamhost_used['type'] == 'proxy': @@ -200,12 +191,12 @@ class StateTransfering(JingleFileTransferStates): return if streamhost_used['type'] == 'proxy': - self.jft.file_props['streamhost-used'] = True - streamhost_used['sid'] = self.jft.file_props['sid'] - self.jft.file_props['streamhosts'] = [] - self.jft.file_props['streamhosts'].append(streamhost_used) - self.jft.file_props['proxyhosts'] = [] - self.jft.file_props['proxyhosts'].append(streamhost_used) + self.jft.file_props.streamhost_used = True + streamhost_used['sid'] = self.jft.file_props.sid + self.jft.file_props.streamhosts = [] + self.jft.file_props.streamhosts.append(streamhost_used) + self.jft.file_props.proxyhosts = [] + self.jft.file_props.proxyhosts.append(streamhost_used) # This needs to be changed when requesting if self.jft.weinitiate: @@ -218,7 +209,7 @@ class StateTransfering(JingleFileTransferStates): connected=False, file_props=self.jft.file_props) else: sockobj = Socks5ReceiverClient(gajim.idlequeue, streamhost_used, - sid=self.jft.file_props['sid'], + sid=self.jft.file_props.sid, file_props=self.jft.file_props, fingerprint=None) sockobj.proxy = True sockobj.streamhost = streamhost_used @@ -228,7 +219,7 @@ class StateTransfering(JingleFileTransferStates): # If we offered the nominated candidate used, we activate # the proxy if not self.jft.isOurCandUsed(): - gajim.socks5queue.on_success[self.jft.file_props['sid']] = \ + gajim.socks5queue.on_success[self.jft.file_props.sid] = \ self.jft.transport._on_proxy_auth_ok # TODO: add on failure else: diff --git a/src/common/jingle_transport.py b/src/common/jingle_transport.py index ff736f718..543150d11 100644 --- a/src/common/jingle_transport.py +++ b/src/common/jingle_transport.py @@ -190,8 +190,8 @@ class JingleTransportSocks5(JingleTransport): c['type'] = 'direct' c['jid'] = self.ourjid c['priority'] = (2**16) * type_preference - c['initiator'] = self.file_props['sender'] - c['target'] = self.file_props['receiver'] + c['initiator'] = self.file_props.sender + c['target'] = self.file_props.receiver local_ip_cand.append(c) self._add_candidates(local_ip_cand) @@ -213,8 +213,8 @@ class JingleTransportSocks5(JingleTransport): c['type'] = 'direct' c['jid'] = self.ourjid c['priority'] = (2**16) * type_preference - c['initiator'] = self.file_props['sender'] - c['target'] = self.file_props['receiver'] + c['initiator'] = self.file_props.sender + c['target'] = self.file_props.receiver additional_ip_cand.append(c) self._add_candidates(additional_ip_cand) @@ -228,7 +228,7 @@ class JingleTransportSocks5(JingleTransport): proxyhosts = socks5conn._get_file_transfer_proxies_from_config(self.file_props) if proxyhosts: - self.file_props['proxyhosts'] = proxyhosts + self.file_props.proxyhosts = proxyhosts for proxyhost in proxyhosts: c = {'host': proxyhost['host']} @@ -237,15 +237,15 @@ class JingleTransportSocks5(JingleTransport): c['type'] = 'proxy' c['jid'] = proxyhost['jid'] c['priority'] = (2**16) * type_preference - c['initiator'] = self.file_props['sender'] - c['target'] = self.file_props['receiver'] + c['initiator'] = self.file_props.sender + c['target'] = self.file_props.receiver proxy_cand.append(c) self._add_candidates(proxy_cand) def get_content(self): sesn = self.connection.get_jingle_session(self.ourjid, - self.file_props['session-sid']) + self.file_props.session_sid) for content in sesn.contents.values(): if content.transport == self: return content @@ -256,7 +256,7 @@ class JingleTransportSocks5(JingleTransport): if not self.connection: return sesn = self.connection.get_jingle_session(self.ourjid, - self.file_props['session-sid']) + self.file_props.session_sid) if sesn is None: return diff --git a/src/common/protocol/bytestream.py b/src/common/protocol/bytestream.py index b370ec266..644c85454 100644 --- a/src/common/protocol/bytestream.py +++ b/src/common/protocol/bytestream.py @@ -32,6 +32,7 @@ import socket import base64 import gobject import time +import pdb from common import xmpp from common import gajim @@ -39,40 +40,42 @@ from common import helpers from common import dataforms from common import ged from common import jingle_xtls - +from common.file_props import FilesProp from common.socks5 import Socks5Receiver import logging log = logging.getLogger('gajim.c.p.bytestream') def is_transfer_paused(file_props): - if 'stopped' in file_props and file_props['stopped']: + if file_props.stopped: return False - if 'completed' in file_props and file_props['completed']: + if file_props.completed: return False - if 'disconnect_cb' not in file_props: + if file_props.disconnect_cb: return False - return file_props['paused'] + return file_props.paused def is_transfer_active(file_props): - if 'stopped' in file_props and file_props['stopped']: + if file_props.stopped: return False - if 'completed' in file_props and file_props['completed']: + if file_props.completed: return False - if 'started' not in file_props or not file_props['started']: + if not file_props.started: return False - if 'paused' not in file_props: + if file_props.paused: return True - return not file_props['paused'] + return not file_props.paused def is_transfer_stopped(file_props): - if 'error' in file_props and file_props['error'] != 0: + if not file_props: return True - if 'completed' in file_props and file_props['completed']: + if file_props.error != 0: return True - if 'connected' in file_props and file_props['connected'] == False: + if file_props.completed: return True - if 'stopped' not in file_props or not file_props['stopped']: + if not file_props.connected: + return True + if not file_props.stopped: return False return True @@ -80,7 +83,6 @@ def is_transfer_stopped(file_props): class ConnectionBytestream: def __init__(self): - self.files_props = {} gajim.ged.register_event_handler('file-request-received', ged.GUI1, self._nec_file_request_received) @@ -94,7 +96,7 @@ class ConnectionBytestream: return our_jid + '/' + resource def _ft_get_receiver_jid(self, file_props): - return file_props['receiver'].jid + '/' + file_props['receiver'].resource + return file_props.receiver.jid + '/' + file_props.receiver.resource def _ft_get_from(self, iq_obj): return helpers.get_full_jid_from_iq(iq_obj) @@ -108,20 +110,19 @@ class ConnectionBytestream: """ if not self.connection or self.connected < 2: return - file_props['sender'] = self._ft_get_our_jid() + file_props.sender = self._ft_get_our_jid() fjid = self._ft_get_receiver_jid(file_props) iq = xmpp.Iq(to=fjid, typ='set') - iq.setID(file_props['sid']) - self.files_props[file_props['sid']] = file_props + iq.setID(file_props.sid) si = iq.setTag('si', namespace=xmpp.NS_SI) si.setAttr('profile', xmpp.NS_FILE) - si.setAttr('id', file_props['sid']) + si.setAttr('id', file_props.sid) file_tag = si.setTag('file', namespace=xmpp.NS_FILE) - file_tag.setAttr('name', file_props['name']) - file_tag.setAttr('size', file_props['size']) + file_tag.setAttr('name', file_props.name) + file_tag.setAttr('size', file_props.size) desc = file_tag.setTag('desc') - if 'desc' in file_props: - desc.setData(file_props['desc']) + if file_props.desc: + desc.setData(file_props.desc) file_tag.setTag('range') feature = si.setTag('feature', namespace=xmpp.NS_FEATURE) _feature = xmpp.DataForm(typ='form') @@ -142,24 +143,22 @@ class ConnectionBytestream: # file transfer initiated by a jingle session log.info("send_file_approval: jingle session accept") - if file_props.get('session-type') == 'jingle': - session = self.get_jingle_session(file_props['sender'], - file_props['session-sid']) + if file_props.session_type == 'jingle': + session = self.get_jingle_session(file_props.sender, + file_props.session_sid) if not session: return content = None for c in session.contents.values(): - if c.transport.sid == file_props['sid']: + if c.transport.sid == file_props.sid: content = c break if not content: return - gajim.socks5queue.add_file_props(self.name, file_props) - if not session.accepted: if session.get_content('file', content.name).use_security: id_ = jingle_xtls.send_cert_request(self, - file_props['sender']) + file_props.sender) jingle_xtls.key_exchange_pend(id_, content) return session.approve_session() @@ -167,19 +166,19 @@ class ConnectionBytestream: session.approve_content('file', content.name) return - iq = xmpp.Iq(to=unicode(file_props['sender']), typ='result') - iq.setAttr('id', file_props['request-id']) + iq = xmpp.Iq(to=unicode(file_props.sender), typ='result') + iq.setAttr('id', file_props.request_id) si = iq.setTag('si', namespace=xmpp.NS_SI) - if 'offset' in file_props and file_props['offset']: + if file_props.offset: file_tag = si.setTag('file', namespace=xmpp.NS_FILE) range_tag = file_tag.setTag('range') - range_tag.setAttr('offset', file_props['offset']) + range_tag.setAttr('offset', file_props.offset) feature = si.setTag('feature', namespace=xmpp.NS_FEATURE) _feature = xmpp.DataForm(typ='submit') feature.addChild(node=_feature) field = _feature.setField('stream-method') field.delAttr('type') - if xmpp.NS_BYTESTREAM in file_props['stream-methods']: + if xmpp.NS_BYTESTREAM in file_props.stream_methods: field.setValue(xmpp.NS_BYTESTREAM) else: field.setValue(xmpp.NS_IBB) @@ -195,12 +194,12 @@ class ConnectionBytestream: # user response to ConfirmationDialog may come after we've disconneted if not self.connection or self.connected < 2: return - if file_props['session-type'] == 'jingle': - jingle = self._sessions[file_props['session-sid']] + if file_props.session_type == 'jingle': + jingle = self._sessions[file_props.session_sid] jingle.cancel_session() return - iq = xmpp.Iq(to=unicode(file_props['sender']), typ='error') - iq.setAttr('id', file_props['request-id']) + iq = xmpp.Iq(to=unicode(file_props.sender), typ='error') + iq.setAttr('id', file_props.request_id) if code == '400' and typ in ('stream', 'profile'): name = 'bad-request' text = '' @@ -217,13 +216,13 @@ class ConnectionBytestream: self.connection.send(iq) def _siResultCB(self, con, iq_obj): - file_props = self.files_props.get(iq_obj.getAttr('id')) + file_props = FilesProp.getFileProp(con.name, iq_obj.getAttr('id')) if not file_props: return - if 'request-id' in file_props: + if file_props.request_id: # we have already sent streamhosts info return - file_props['receiver'] = self._ft_get_from(iq_obj) + file_props.receiver = self._ft_get_from(iq_obj) si = iq_obj.getTag('si') file_tag = si.getTag('file') range_tag = None @@ -232,10 +231,10 @@ class ConnectionBytestream: if range_tag: offset = range_tag.getAttr('offset') if offset: - file_props['offset'] = int(offset) + file_props.offset = int(offset) length = range_tag.getAttr('length') if length: - file_props['length'] = int(length) + file_props.length = int(length) feature = si.setTag('feature') if feature.getNamespace() != xmpp.NS_FEATURE: return @@ -246,9 +245,9 @@ class ConnectionBytestream: self._send_socks5_info(file_props) raise xmpp.NodeProcessed if field.getValue() == xmpp.NS_IBB: - sid = file_props['sid'] - fp = open(file_props['file-name'], 'r') - self.OpenStream(sid, file_props['receiver'], fp) + sid = file_props.sid + fp = open(file_props.file_name, 'r') + self.OpenStream(sid, file_props.receiver, fp) raise xmpp.NodeProcessed def _siSetCB(self, con, iq_obj): @@ -258,20 +257,18 @@ class ConnectionBytestream: raise xmpp.NodeProcessed def _nec_file_request_received(self, obj): - if obj.conn.name != self.name: - return - gajim.socks5queue.add_file_props(self.name, obj.file_props) + pass def _siErrorCB(self, con, iq_obj): si = iq_obj.getTag('si') profile = si.getAttr('profile') if profile != xmpp.NS_FILE: return - file_props = self.files_props.get(iq_obj.getAttr('id')) + file_props = FilesProp.getFileProp(con.name, iq_obj.getAttr('id')) if not file_props: return jid = self._ft_get_from(iq_obj) - file_props['error'] = -3 + file_props.error = -3 from common.connection_handlers_events import FileRequestErrorEvent gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=self, jid=jid, file_props=file_props, error_msg='')) @@ -299,59 +296,54 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): """ Stop all active transfer to or from the given contact """ - for file_props in self.files_props.values(): + for file_props in FilesProp.getAllFileProp(): if is_transfer_stopped(file_props): continue - receiver_jid = unicode(file_props['receiver']) + receiver_jid = unicode(file_props.receiver) if contact.get_full_jid() == receiver_jid: - file_props['error'] = -5 + file_props.error = -5 self.remove_transfer(file_props) from common.connection_handlers_events import \ FileRequestErrorEvent gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=self, jid=contact.jid, file_props=file_props, error_msg='')) - sender_jid = unicode(file_props['sender']) + sender_jid = unicode(file_props.sender) if contact.get_full_jid() == sender_jid: - file_props['error'] = -3 + file_props.error = -3 self.remove_transfer(file_props) def remove_all_transfers(self): """ Stop and remove all active connections from the socks5 pool """ - for file_props in self.files_props.values(): + for file_props in FilesProp.getAllFileProp(): self.remove_transfer(file_props, remove_from_list=False) - self.files_props = {} def remove_transfer(self, file_props, remove_from_list=True): if file_props is None: return self.disconnect_transfer(file_props) - sid = file_props['sid'] + sid = file_props.sid gajim.socks5queue.remove_file_props(self.name, sid) - if remove_from_list: - if 'sid' in self.files_props: - del(self.files_props['sid']) def disconnect_transfer(self, file_props): if file_props is None: return - if 'hash' in file_props: - gajim.socks5queue.remove_sender(file_props['hash']) + if file_props.hash_: + gajim.socks5queue.remove_sender(file_props.hash_) - if 'streamhosts' in file_props: - for host in file_props['streamhosts']: + if file_props.streamhosts: + for host in file_props.streamhosts: if 'idx' in host and host['idx'] > 0: gajim.socks5queue.remove_receiver(host['idx']) gajim.socks5queue.remove_sender(host['idx']) - if 'direction' in file_props: + if file_props.direction: # it's a IBB - sid = file_props['sid'] - if sid in self.files_props: - del self.files_props[sid] + FilesProp.deleteFileProp(file_props) + del(file_props) def _send_socks5_info(self, file_props): """ @@ -359,28 +351,28 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): """ if not self.connection or self.connected < 2: return - receiver = file_props['receiver'] - sender = file_props['sender'] + receiver = file_props.receiver + sender = file_props.sender - sha_str = helpers.get_auth_sha(file_props['sid'], sender, receiver) - file_props['sha_str'] = sha_str + sha_str = helpers.get_auth_sha(file_props.sid, sender, receiver) + file_props.sha_str = sha_str port = gajim.config.get('file_transfers_port') listener = gajim.socks5queue.start_listener(port, sha_str, self._result_socks5_sid, file_props) if not listener: - file_props['error'] = -5 + file_props.error = -5 from common.connection_handlers_events import FileRequestErrorEvent gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=self, jid=unicode(receiver), file_props=file_props, error_msg='')) - self._connect_error(unicode(receiver), file_props['sid'], - file_props['sid'], code=406) + self._connect_error(unicode(receiver), file_props.sid, + file_props.sid, code=406) else: iq = xmpp.Iq(to=unicode(receiver), typ='set') - file_props['request-id'] = 'id_' + file_props['sid'] - iq.setID(file_props['request-id']) + file_props.request_id = 'id_' + file_props.sid + iq.setID(file_props.request_id) query = iq.setTag('query', namespace=xmpp.NS_BYTESTREAM) - query.setAttr('sid', file_props['sid']) + query.setAttr('sid', file_props.sid) self._add_addiditional_streamhosts_to_query(query, file_props) self._add_local_ips_as_streamhosts_to_query(query, file_props) @@ -406,7 +398,7 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): if not addr[4][0] in my_ips and not addr[4][0].startswith('127'): my_ips.append(addr[4][0]) - sender = file_props['sender'] + sender = file_props.sender port = gajim.config.get('file_transfers_port') self._add_streamhosts_to_query(query, sender, port, my_ips) except socket.gaierror: @@ -416,7 +408,7 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): sec_txt=_('Invalid local address? :-O'))) def _add_addiditional_streamhosts_to_query(self, query, file_props): - sender = file_props['sender'] + sender = file_props.sender port = gajim.config.get('file_transfers_port') ft_add_hosts_to_send = gajim.config.get('ft_add_hosts_to_send') additional_hosts = [] @@ -469,12 +461,12 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): log.debug('Got GUPnP-IGD answer: external: %s:%s, internal: %s:%s', ext_ip, ext_port, local_ip, local_port) if local_port != gajim.config.get('file_transfers_port'): - sender = file_props['sender'] - receiver = file_props['receiver'] - sha_str = helpers.get_auth_sha(file_props['sid'], sender, + sender = file_props.sender + receiver = file_props.receiver + sha_str = helpers.get_auth_sha(file_props.sid, sender, receiver) listener = gajim.socks5queue.start_listener(local_port, sha_str, - self._result_socks5_sid, file_props['sid']) + self._result_socks5_sid, file_props.sid) if listener: self._add_streamhosts_to_query(query, sender, ext_port, [ext_ip]) @@ -507,9 +499,9 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): def _add_proxy_streamhosts_to_query(self, query, file_props): proxyhosts = self._get_file_transfer_proxies_from_config(file_props) if proxyhosts: - file_props['proxy_receiver'] = unicode(file_props['receiver']) - file_props['proxy_sender'] = unicode(file_props['sender']) - file_props['proxyhosts'] = proxyhosts + file_props.proxy_receiver = unicode(file_props.receiver) + file_props.proxy_sender = unicode(file_props.sender) + file_props.proxyhosts = proxyhosts for proxyhost in proxyhosts: self._add_streamhosts_to_query(query, proxyhost['jid'], @@ -536,9 +528,9 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): continue host_dict = { 'state': 0, - 'target': unicode(file_props['receiver']), - 'id': file_props['sid'], - 'sid': file_props['sid'], + 'target': unicode(file_props.receiver), + 'id': file_props.sid, + 'sid': file_props.sid, 'initiator': proxy, 'host': host, 'port': unicode(_port), @@ -553,10 +545,8 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): """ Store the result of SHA message from auth """ - if sid not in self.files_props: - return - file_props = self.files_props[sid] - file_props['hash'] = hash_id + file_props = FilesProp.getFilePropBySid(sid) + file_props.hash_ = hash_id return def _connect_error(self, to, _id, sid, code=404): @@ -579,10 +569,10 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): err.setData(msg) self.connection.send(iq) if code == 404: - file_props = gajim.socks5queue.get_file_props(self.name, sid) + file_props = FilesProp.getFileProp(self.name, sid) if file_props is not None: self.disconnect_transfer(file_props) - file_props['error'] = -3 + file_props.error = -3 from common.connection_handlers_events import \ FileRequestErrorEvent gajim.nec.push_incoming_event(FileRequestErrorEvent(None, @@ -594,14 +584,14 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): """ if not self.connection or self.connected < 2: return - file_props = self.files_props[proxy['sid']] + file_props = FilesProp.getFileProp(self.connection, proxy['sid']) iq = xmpp.Iq(to=proxy['initiator'], typ='set') auth_id = "au_" + proxy['sid'] iq.setID(auth_id) query = iq.setTag('query', namespace=xmpp.NS_BYTESTREAM) query.setAttr('sid', proxy['sid']) activate = query.setTag('activate') - activate.setData(file_props['proxy_receiver']) + activate.setData(file_props.proxy_receiver) iq.setID(auth_id) self.connection.send(iq) @@ -613,10 +603,10 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): gajim.proxy65_manager.error_cb(frm, query) jid = helpers.get_jid_from_iq(iq_obj) id_ = id_[3:] - if id_ not in self.files_props: + file_props = FilesProp.getFilePropBySid(id_) + if not file_props: return - file_props = self.files_props[id_] - file_props['error'] = -4 + file_props.error = -4 from common.connection_handlers_events import FileRequestErrorEvent gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=self, jid=jid, file_props=file_props, error_msg='')) @@ -627,7 +617,7 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): id_ = unicode(iq_obj.getAttr('id')) query = iq_obj.getTag('query') sid = unicode(query.getAttr('sid')) - file_props = gajim.socks5queue.get_file_props(self.name, sid) + file_props = FilesProp.getFileProp(self.name, sid) streamhosts = [] for item in query.getChildren(): if item.getName() == 'streamhost': @@ -647,28 +637,24 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): if 'port' not in host_dict: continue streamhosts.append(host_dict) - if file_props is None: - if sid in self.files_props: - file_props = self.files_props[sid] - file_props['fast'] = streamhosts - if file_props['type'] == 's': # FIXME: remove fast xmlns - # only psi do this - if 'streamhosts' in file_props: - file_props['streamhosts'].extend(streamhosts) - else: - file_props['streamhosts'] = streamhosts - if not gajim.socks5queue.get_file_props(self.name, sid): - gajim.socks5queue.add_file_props(self.name, file_props) - gajim.socks5queue.connect_to_hosts(self.name, sid, - self.send_success_connect_reply, None) - raise xmpp.NodeProcessed - - if file_props is None: + file_props = FilesProp.getFilePropBySid(sid) + if file_props is not None: + file_props.fast = streamhosts + if file_props.type_ == 's': # FIXME: remove fast xmlns + # only psi do this + if file_props.streamhosts: + file_props.streamhosts.extend(streamhosts) + else: + file_props.streamhosts = streamhosts + gajim.socks5queue.connect_to_hosts(self.name, sid, + self.send_success_connect_reply, None) + raise xmpp.NodeProcessed + else: log.warn('Gajim got streamhosts for unknown transfer. Ignoring it.') raise xmpp.NodeProcessed - file_props['streamhosts'] = streamhosts - if file_props['type'] == 'r': + file_props.streamhosts = streamhosts + if file_props.type_ == 'r': gajim.socks5queue.connect_to_hosts(self.name, sid, self.send_success_connect_reply, self._connect_error) raise xmpp.NodeProcessed @@ -681,13 +667,12 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): return frm = self._ft_get_from(iq_obj) id_ = real_id[3:] - if id_ in self.files_props: - file_props = self.files_props[id_] - if file_props['streamhost-used']: - for host in file_props['proxyhosts']: - if host['initiator'] == frm and 'idx' in host: - gajim.socks5queue.activate_proxy(host['idx']) - raise xmpp.NodeProcessed + file_props = FilesProp.getFilePropBySid(id_) + if file_props.streamhost_used: + for host in file_props.proxyhosts: + if host['initiator'] == frm and 'idx' in host: + gajim.socks5queue.activate_proxy(host['idx']) + raise xmpp.NodeProcessed def _bytestreamResultCB(self, con, iq_obj): frm = self._ft_get_from(iq_obj) @@ -700,67 +685,62 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): except Exception: # this bytestream result is not what we need pass id_ = real_id[3:] - if id_ in self.files_props: - file_props = self.files_props[id_] - else: + file_props = FilesProp.getFileProp(con.name, id_) + if file_props is None: raise xmpp.NodeProcessed if streamhost is None: # proxy approves the activate query if real_id.startswith('au_'): - if 'streamhost-used' not in file_props or \ - file_props['streamhost-used'] is False: + if file_props.streamhost_used is False: raise xmpp.NodeProcessed - if 'proxyhosts' not in file_props: + if not file_props.proxyhosts: raise xmpp.NodeProcessed - for host in file_props['proxyhosts']: + for host in file_props.proxyhosts: if host['initiator'] == frm and \ - unicode(query.getAttr('sid')) == file_props['sid']: + unicode(query.getAttr('sid')) == file_props.sid: gajim.socks5queue.activate_proxy(host['idx']) break raise xmpp.NodeProcessed jid = self._ft_get_streamhost_jid_attr(streamhost) - if 'streamhost-used' in file_props and \ - file_props['streamhost-used'] is True: + if file_props.streamhost_used is True: raise xmpp.NodeProcessed if real_id.startswith('au_'): - if 'stopped' in file_props and file_props['stopped']: + if file_props.stopped: self.remove_transfer(file_props) else: gajim.socks5queue.send_file(file_props, self.name) raise xmpp.NodeProcessed proxy = None - if 'proxyhosts' in file_props: - for proxyhost in file_props['proxyhosts']: + if file_props.proxyhosts: + for proxyhost in file_props.proxyhosts: if proxyhost['jid'] == jid: proxy = proxyhost - if 'stopped' in file_props and file_props['stopped']: + if file_props.stopped: self.remove_transfer(file_props) raise xmpp.NodeProcessed if proxy is not None: - file_props['streamhost-used'] = True - if 'streamhosts' not in file_props: - file_props['streamhosts'] = [] - file_props['streamhosts'].append(proxy) - file_props['is_a_proxy'] = True + file_props.streamhost_used = True + file_props.streamhosts.append(proxy) + file_props.is_a_proxy = True receiver = Socks5Receiver(gajim.idlequeue, proxy, - file_props['sid'], file_props) + file_props.sid, file_props) gajim.socks5queue.add_receiver(self.name, receiver) proxy['idx'] = receiver.queue_idx gajim.socks5queue.on_success = self._proxy_auth_ok raise xmpp.NodeProcessed else: - if 'stopped' in file_props and file_props['stopped']: + if file_props.stopped: self.remove_transfer(file_props) else: gajim.socks5queue.send_file(file_props, self.name, 'client') - if 'fast' in file_props: - fasts = file_props['fast'] + if file_props.fast: + fasts = file_props.fast if len(fasts) > 0: - self._connect_error(frm, fasts[0]['id'], file_props['sid'], + self._connect_error(frm, fasts[0]['id'], file_props.sid, code=406) raise xmpp.NodeProcessed @@ -800,33 +780,33 @@ class ConnectionIBBytestream(ConnectionBytestream): blocksize = stanza.getTagAttr('open', 'block-size') log.debug('StreamOpenHandler called sid->%s blocksize->%s' % (sid, blocksize)) + file_props = FilesProp.getFileProp(self.name, sid) try: blocksize = int(blocksize) except: err = xmpp.ERR_BAD_REQUEST if not sid or not blocksize: err = xmpp.ERR_BAD_REQUEST - elif not gajim.socks5queue.get_file_props(self.name, sid): + elif not file_props: err = xmpp.ERR_UNEXPECTED_REQUEST if err: rep = xmpp.Error(stanza, err) else: - file_props = gajim.socks5queue.get_file_props(self.name, sid) log.debug("Opening stream: id %s, block-size %s" % (sid, blocksize)) rep = xmpp.Protocol('iq', stanza.getFrom(), 'result', stanza.getTo(), {'id': stanza.getID()}) - file_props['block-size'] = blocksize - file_props['seq'] = 0 - file_props['received-len'] = 0 - file_props['last-time'] = time.time() - file_props['error'] = 0 - file_props['paused'] = False - file_props['connected'] = True - file_props['completed'] = False - 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.block_size = blocksize + file_props.seq = 0 + file_props.received_len = 0 + file_props.last_time = time.time() + file_props.error = 0 + file_props.paused = False + file_props.connected = True + file_props.completed = False + 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') conn.send(rep) def OpenStream(self, sid, to, fp, blocksize=4096): @@ -837,62 +817,59 @@ class ConnectionIBBytestream(ConnectionBytestream): Take into account that recommended stanza size is 4k and IBB uses base64 encoding that increases size of data by 1/3. """ - if sid not in self.files_props.keys(): - return if not xmpp.JID(to).getResource(): return - self.files_props[sid]['direction'] = '|>' + to - self.files_props[sid]['block-size'] = blocksize - self.files_props[sid]['fp'] = fp - self.files_props[sid]['seq'] = 0 - self.files_props[sid]['error'] = 0 - self.files_props[sid]['paused'] = False - self.files_props[sid]['received-len'] = 0 - self.files_props[sid]['last-time'] = time.time() - self.files_props[sid]['connected'] = True - self.files_props[sid]['completed'] = False - self.files_props[sid]['disconnect_cb'] = None - self.files_props[sid]['continue_cb'] = None + file_props = FilesProp.getFilePropBySid(sid) + file_props.direction = '|>' + to + file_props.block_size = blocksize + file_props.fp = fp + file_props.seq = 0 + file_props.error = 0 + file_props.paused = False + file_props.received_len = 0 + file_props.last_time = time.time() + file_props.connected = True + file_props.completed = False + file_props.disconnect_cb = None + file_props.continue_cb = None syn = xmpp.Protocol('iq', to, 'set', payload=[xmpp.Node(xmpp.NS_IBB + \ ' open', {'sid': sid, 'block-size': blocksize, 'stanza': 'iq'})]) self.connection.send(syn) - self.files_props[sid]['syn_id'] = syn.getID() - return self.files_props[sid] + file_props.syn_id = syn.getID() + return file_props def SendHandler(self): """ Send next portion of data if it is time to do it. Used internally. """ log.debug('SendHandler called') - if not self.files_props: - return - for file_props in self.files_props.values(): - if 'direction' not in file_props: + #pdb.set_trace() + 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] == '|>': + sid = file_props.sid + if file_props.direction[:2] == '|>': # We waitthat other part accept stream continue - if file_props['direction'][0] == '>': - if 'paused' in file_props and file_props['paused']: + if file_props.direction[0] == '>': + if file_props.paused: continue - chunk = file_props['fp'].read(file_props['block-size']) + chunk = file_props.fp.read(file_props.block_size) if chunk: datanode = xmpp.Node(xmpp.NS_IBB + ' data', {'sid': sid, - 'seq': file_props['seq']}, base64.encodestring(chunk)) - file_props['seq'] += 1 - file_props['started'] = True - if file_props['seq'] == 65536: - file_props['seq'] = 0 + 'seq': file_props.seq}, base64.encodestring(chunk)) + 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(xmpp.Protocol( - name='iq', to=file_props['direction'][1:], typ='set', + 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) + 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: @@ -900,11 +877,12 @@ class ConnectionIBBytestream(ConnectionBytestream): # notify the local user about sucessfull send # delete the local stream self.connection.send(xmpp.Protocol('iq', - file_props['direction'][1:], 'set', + file_props.direction[1:], 'set', payload=[xmpp.Node(xmpp.NS_IBB + ' close', {'sid':sid})])) - file_props['completed'] = True - del self.files_props[sid] + file_props.completed = True + FilesProp.deleteFileProp(file_props) + del(file_props) def IBBMessageHandler(self, conn, stanza): """ @@ -922,28 +900,27 @@ class ConnectionIBBytestream(ConnectionBytestream): seq = '' data = '' err = None - if not gajim.socks5queue.get_file_props(self.name, sid): + file_props = FilesProp.getFileProp(self.name, sid) + if file_props is None: err = xmpp.ERR_ITEM_NOT_FOUND else: - file_props = gajim.socks5queue.get_file_props(self.name, sid) if not data: err = xmpp.ERR_BAD_REQUEST - elif seq <> file_props['seq']: + elif seq <> file_props.seq: err = xmpp.ERR_UNEXPECTED_REQUEST else: log.debug('Successfull receive sid->%s %s+%s bytes' % (sid, - file_props['fp'].tell(), len(data))) - file_props['seq'] += 1 - file_props['started'] = True - file_props['fp'].write(data) + file_props.fp.tell(), len(data))) + file_props.seq += 1 + file_props.started = True + file_props.fp.write(data) 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(data) + file_props.elapsed_time += current_time - file_props.last_time + file_props.last_time = current_time + file_props.received_len += len(data) gajim.socks5queue.progress_transfer_cb(self.name, file_props) - if file_props['received-len'] >= file_props['size']: - file_props['completed'] = True + if file_props.received_len >= file_props.size: + file_props.completed = True if err: log.debug('Error on receive: %s' % err) conn.send(xmpp.Error(xmpp.Iq(to=stanza.getFrom(), @@ -960,19 +937,16 @@ class ConnectionIBBytestream(ConnectionBytestream): sid = stanza.getTagAttr('close', 'sid') log.debug('StreamCloseHandler called sid->%s' % sid) # look in sending files - if sid in self.files_props.keys(): + file_props = FilesProp.getFileProp(self.name, sid) + if file_props: reply = stanza.buildReply('result') reply.delChild('close') conn.send(reply) - gajim.socks5queue.complete_transfer_cb(self.name, self.files_props[sid]) - del self.files_props[sid] - # look in receiving files - elif gajim.socks5queue.get_file_props(self.name, sid): - file_props = gajim.socks5queue.get_file_props(self.name, sid) + # look in receiving files reply = stanza.buildReply('result') reply.delChild('close') conn.send(reply) - file_props['fp'].close() + file_props.fp.close() gajim.socks5queue.complete_transfer_cb(self.name, file_props) gajim.socks5queue.remove_file_props(self.name, sid) else: @@ -987,21 +961,21 @@ class ConnectionIBBytestream(ConnectionBytestream): """ syn_id = stanza.getID() log.debug('IBBAllIqHandler called syn_id->%s' % syn_id) - for sid in self.files_props.keys(): - file_props = self.files_props[sid] - if not 'direction' in file_props: + for file_props in FilesProp.getAllFileProp(): + if not file_props.direction: # It's socks5 bytestream continue - if file_props['syn_id'] == syn_id: + if file_props.syn_id == syn_id: if stanza.getType() == 'error': - if file_props['direction'][0] == '<': + if file_props.direction[0] == '<': conn.Event('IBB', 'ERROR ON RECEIVE', file_props) else: conn.Event('IBB', 'ERROR ON SEND', file_props) - del self.files_props[sid] + FilesProp.deleteFileProp(file_props) + del(file_props) elif stanza.getType() == 'result': - if file_props['direction'][0] == '|': - file_props['direction'] = file_props['direction'][1:] + if file_props.direction[0] == '|': + file_props.direction = file_props.direction[1:] self.SendHandler() else: conn.send(xmpp.Error(stanza, @@ -1026,7 +1000,7 @@ class ConnectionSocks5BytestreamZeroconf(ConnectionSocks5Bytestream): return gajim.get_jid_from_account(self.name) def _ft_get_receiver_jid(self, file_props): - return file_props['receiver'].jid + return file_props.receiver.jid def _ft_get_streamhost_jid_attr(self, streamhost): return streamhost.getAttr('jid') diff --git a/src/common/proxy65_manager.py b/src/common/proxy65_manager.py index 879458aea..ded92010f 100644 --- a/src/common/proxy65_manager.py +++ b/src/common/proxy65_manager.py @@ -31,6 +31,7 @@ from common import gajim from common import helpers from socks5 import Socks5 from common.xmpp.idlequeue import IdleObject +from common.file_props import FilesProp S_INITIAL = 0 S_STARTED = 1 @@ -248,9 +249,10 @@ class HostTester(Socks5, IdleObject): self.on_success = on_success self.on_failure = on_failure self._sock = None - self.file_props = {'is_a_proxy': True, - 'proxy_sender': sender_jid, - 'proxy_receiver': 'test@gajim.org/test2'} + self.file_props = FilesProp.getNewFileProp(jid, sid) + self.file_props.is_a_proxy = True + self.file_props.proxy_sender = sender_jid + self.file_props.proxy_receiver = 'test@gajim.org/test2' Socks5.__init__(self, gajim.idlequeue, host, port, None, None, None) self.sid = sid @@ -367,9 +369,10 @@ class ReceiverTester(Socks5, IdleObject): self.on_success = on_success self.on_failure = on_failure self._sock = None - self.file_props = {'is_a_proxy': True, - 'proxy_sender': sender_jid, - 'proxy_receiver': 'test@gajim.org/test2'} + self.file_props = FilesProp.getNewFileProp(jid, sid) + self.file_props.is_a_proxy = True + self.file_props.proxy_sender = sender_jid + self.file_props.proxy_receiver = 'test@gajim.org/test2' Socks5.__init__(self, gajim.idlequeue, host, port, None, None, None) self.sid = sid diff --git a/src/common/socks5.py b/src/common/socks5.py index 534fcedbc..e022a9f3d 100644 --- a/src/common/socks5.py +++ b/src/common/socks5.py @@ -34,7 +34,7 @@ from errno import EISCONN from errno import EINPROGRESS from errno import EAFNOSUPPORT from xmpp.idlequeue import IdleObject - +from file_props import FilesProp import jingle_xtls if jingle_xtls.PYOPENSSL_PRESENT: @@ -69,7 +69,6 @@ class SocksQueue: progress_transfer_cb=None, error_cb=None): self.connected = 0 self.readers = {} - self.files_props = {} self.senders = {} self.idx = 1 self.listener = None @@ -83,58 +82,54 @@ class SocksQueue: self.on_success = {} # {id: cb} self.on_failure = {} # {id: cb} - def start_listener(self, port, sha_str, sha_handler, fp, fingerprint=None, + def start_listener(self, port, sha_str, sha_handler, file_props, fingerprint=None, type='sender'): """ Start waiting for incomming connections on (host, port) and do a socks5 authentication using sid for generated SHA """ - sid = fp['sid'] + sid = file_props.sid self.type = type # It says whether we are sending or receiving self.sha_handlers[sha_str] = (sha_handler, sid) if self.listener is None or self.listener.connections == []: - self.listener = Socks5Listener(self.idlequeue, port, fp, + self.listener = Socks5Listener(self.idlequeue, port, file_props, fingerprint=fingerprint) self.listener.queue = self self.listener.bind() else: # There is already a listener, we update the file's information # on the new connection. - self.listener.file_props = fp + self.listener.file_props = file_props self.connected += 1 return self.listener def send_success_reply(self, file_props, streamhost): - if 'streamhost-used' in file_props and \ - file_props['streamhost-used'] is True: - if 'proxyhosts' in file_props: - for proxy in file_props['proxyhosts']: - if proxy['host'] == streamhost['host']: - self.on_success[file_props['sid']](proxy) - return 1 + if file_props.streamhost_used == True: + for proxy in file_props.proxyhosts: + if proxy['host'] == streamhost['host']: + self.on_success[file_props.sid](proxy) + return 1 return 0 - if 'streamhosts' in file_props: - for host in file_props['streamhosts']: - if streamhost['state'] == 1: - return 0 - streamhost['state'] = 1 - self.on_success[file_props['sid']](streamhost) - return 1 - return 0 + for host in file_props.streamhosts: + if streamhost['state'] == 1: + return 0 + streamhost['state'] = 1 + self.on_success[file_props.sid](streamhost) + return 1 def connect_to_hosts(self, account, sid, on_success=None, on_failure=None, fingerprint=None, receiving=True): self.on_success[sid] = on_success self.on_failure[sid] = on_failure - file_props = self.files_props[account][sid] - file_props['failure_cb'] = on_failure + file_props = FilesProp.getFileProp(account, sid) + file_props.failure_cb = on_failure - if not file_props['streamhosts']: - on_failure(file_props['sid']) + if not file_props.streamhosts: + on_failure(file_props.sid) # add streamhosts to the queue - for streamhost in file_props['streamhosts']: + for streamhost in file_props.streamhosts: if 'type' in streamhost and streamhost['type'] == 'proxy': fp = None else: @@ -145,16 +140,16 @@ class SocksQueue: file_props, fingerprint=fp) self.add_sockobj(account, socks5obj) else: - if 'sha_str' in file_props: - idx = file_props['sha_str'] + if file_props.sha_str: + idx = file_props.sha_str else: idx = self.idx self.idx = self.idx + 1 self.type = 'sender' if 'type' in streamhost and streamhost['type'] == 'proxy': - file_props['is_a_proxy'] = True - file_props['proxy_sender'] = streamhost['target'] - file_props['proxy_receiver'] = streamhost['initiator'] + file_props.is_a_proxy = True + file_props.proxy_sender = streamhost['target'] + file_props.proxy_receiver = streamhost['initiator'] socks5obj = Socks5SenderClient(self.idlequeue, idx, self, _sock=None,host=str(streamhost['host']), port=int(streamhost['port']),fingerprint=fp, @@ -169,7 +164,7 @@ class SocksQueue: Called when there is a host connected to one of the senders's streamhosts. Stop other attempts for connections """ - for host in file_props['streamhosts']: + for host in file_props.streamhosts: if host != streamhost and 'idx' in host: if host['state'] == 1: # remove current @@ -200,14 +195,14 @@ class SocksQueue: streamhost['state'] = -1 # boolean, indicates that there are hosts, which are not tested yet unused_hosts = False - for host in file_props['streamhosts']: + for host in file_props.streamhosts: if 'idx' in host: if host['state'] >= 0: return elif host['state'] == -2: unused_hosts = True if unused_hosts: - for host in file_props['streamhosts']: + for host in file_props.streamhosts: if host['state'] == -2: host['state'] = 0 # FIXME: make the sender reconnect also @@ -217,13 +212,13 @@ class SocksQueue: host['idx'] = client.queue_idx # we still have chances to connect return - if 'received-len' not in file_props or file_props['received-len'] == 0: + if file_props.received_len == 0: # there are no other streamhosts and transfer hasn't started self._connection_refused(streamhost, file_props, client.queue_idx) else: # transfer stopped, it is most likely stopped from sender client.disconnect() - file_props['error'] = -1 + file_props.error = -1 self.process_result(-1, client) def _connection_refused(self, streamhost, file_props, idx): @@ -235,15 +230,14 @@ class SocksQueue: streamhost['state'] = -1 # FIXME: should only the receiver be remove? what if we are sending? self.remove_receiver(idx, False) - if 'streamhosts' in file_props: - for host in file_props['streamhosts']: - if host['state'] != -1: - return + for host in file_props.streamhosts: + if host['state'] != -1: + return self.readers = {} # failure_cb exists - this means that it has never been called - if 'failure_cb' in file_props and file_props['failure_cb']: - file_props['failure_cb'](file_props['sid']) - del(file_props['failure_cb']) + if file_props.failure_cb: + file_props.failure_cb(file_props.sid) + file_props.failure_cb = None def add_sockobj(self, account, sockobj, type='receiver'): """ @@ -266,11 +260,11 @@ class SocksQueue: return 1 return None - def _add(self, sockobj, sockobjects, fp, hash): + def _add(self, sockobj, sockobjects, file_props, hash_): ''' Adds the sockobj to the current list of sockobjects ''' - keys = (fp['sid'], fp['name'], hash) + keys = (file_props.sid, file_props.name, hash_) sockobjects[keys] = sockobj def result_sha(self, sha_str, idx): @@ -284,21 +278,21 @@ class SocksQueue: for key in self.readers.keys(): if idx in key: reader = self.readers[key] - if reader.file_props['type'] != 's': + if reader.file_props.type_ != 's': return if reader.state != 5: return reader.state = 6 if reader.connected: - reader.file_props['error'] = 0 - reader.file_props['disconnect_cb'] = reader.disconnect - reader.file_props['started'] = True - reader.file_props['completed'] = False - reader.file_props['paused'] = False - reader.file_props['stalled'] = False - reader.file_props['elapsed-time'] = 0 - reader.file_props['last-time'] = self.idlequeue.current_time() - reader.file_props['received-len'] = 0 + reader.file_props.error = 0 + reader.file_props.disconnect_cb = reader.disconnect + reader.file_props.started = True + reader.file_props.completed = False + reader.file_props.paused = False + reader.file_props.stalled = False + reader.file_props.elapsed_time = 0 + reader.file_props.last_time = self.idlequeue.current_time() + reader.file_props.received_len = 0 reader.pauses = 0 # start sending file to proxy self.idlequeue.set_read_timeout(reader.fd, STALLED_TIMEOUT) @@ -314,53 +308,34 @@ class SocksQueue: # Maybe it is my machine. Without this there is a KeyError # traceback. return - if file_props['name'] in key and file_props['sid'] in key \ + if file_props.name in key and file_props.sid in key \ and self.senders[key].mode == mode: log.info("socks5: sending file") sender = self.senders[key] - file_props['streamhost-used'] = True + file_props.streamhost_used = True sender.account = account sender.file_props = file_props result = sender.send_file() self.process_result(result, sender) - def add_file_props(self, account, file_props): - """ - File_prop to the dict of current file_props. It is identified by account - name and sid - """ - if file_props is None or ('sid' in file_props) is False: - return - _id = file_props['sid'] - if account not in self.files_props: - self.files_props[account] = {} - self.files_props[account][_id] = file_props - def remove_file_props(self, account, sid): - if account in self.files_props: - fl_props = self.files_props[account] - if sid in fl_props: - if sid in self.on_success: - del self.on_success[sid] - if sid in self.on_failure: - del self.on_failure[sid] - del(fl_props[sid]) + fp = FilesProp.getFileProp(account, sid) + if not fp: + log.warning('trying to remove a file props that doesnt exist ' + + 'from account ' + str(account) + ' and sid ' + str(sid)) + return + if sid in self.on_success: + del self.on_success[fp.sid] + if sid in self.on_failure: + del self.on_failure[fp.sid] - if len(self.files_props) == 0: + FilesProp.deleteFileProp(fp) + + if len(FilesProp.getFilePropByAccount(account)) == 0: self.connected = 0 - def get_file_props(self, account, sid): - """ - Get fil_prop by account name and session id - """ - if account in self.files_props: - fl_props = self.files_props[account] - if sid in fl_props: - return fl_props[sid] - return None - def isHashInSockObjs(self, sockobjs, hash): ''' It tells wether there is a particular hash in sockobjs or not @@ -413,8 +388,8 @@ class SocksQueue: return if result in (0, -1) and self.complete_transfer_cb is not None: account = actor.account - if account is None and 'tt_account' in actor.file_props: - account = actor.file_props['tt_account'] + if account is None and actor.file_props.tt_account: + account = actor.file_props.tt_account self.complete_transfer_cb(account, actor.file_props) elif self.progress_transfer_cb is not None: self.progress_transfer_cb(actor.account, actor.file_props) @@ -553,9 +528,9 @@ class Socks5: self._recv=self._sock.recv self.buff = '' self.connected = True - self.file_props['connected'] = True - self.file_props['disconnect_cb'] = self.disconnect - self.file_props['paused'] = False + self.file_props.connected = True + self.file_props.disconnect_cb = self.disconnect + self.file_props.paused = False self.state = 1 # connected # stop all others connections to sender's streamhosts @@ -567,11 +542,11 @@ class Socks5: self.idlequeue.remove_timeout(self.fd) if self.state > 5: # no activity for foo seconds - if self.file_props['stalled'] == False: - self.file_props['stalled'] = True + if self.file_props.stalled == False: + self.file_props.stalled = True self.queue.process_result(-1, self) - if 'received-len' not in self.file_props: - self.file_props['received-len'] = 0 + if not self.file_props.received_len: + self.file_props.received_len = 0 if SEND_TIMEOUT > 0: self.idlequeue.set_read_timeout(self.fd, SEND_TIMEOUT) else: @@ -585,11 +560,11 @@ class Socks5: def open_file_for_reading(self): if self.file is None: try: - self.file = open(self.file_props['file-name'], 'rb') - if 'offset' in self.file_props and self.file_props['offset']: - self.size = self.file_props['offset'] + self.file = open(self.file_props.file_name, 'rb') + if self.file_props.offset: + self.size = self.file_props.offset self.file.seek(self.size) - self.file_props['received-len'] = self.size + self.file_props.received_len = self.size except IOError, e: self.close_file() raise IOError, e @@ -608,24 +583,24 @@ class Socks5: Test if file is already open and return its fd, or just open the file and return the fd """ - if 'fd' in self.file_props: - fd = self.file_props['fd'] + if self.file_props.fd: + fd = self.file_props.fd else: offset = 0 opt = 'wb' - if 'offset' in self.file_props and self.file_props['offset']: - offset = self.file_props['offset'] + if self.file_props.offset: + offset = self.file_props.offset opt = 'ab' - fd = open(self.file_props['file-name'], opt) - self.file_props['fd'] = fd - self.file_props['elapsed-time'] = 0 - self.file_props['last-time'] = self.idlequeue.current_time() - self.file_props['received-len'] = offset + fd = open(self.file_props.file_name, opt) + self.file_props.fd = fd + self.file_props.elapsed_time = 0 + self.file_props.last_time = self.idlequeue.current_time() + self.file_props.received_len = offset return fd def rem_fd(self, fd): - if 'fd' in self.file_props: - del(self.file_props['fd']) + if self.file_props.fd: + self.file_props.fd = None try: fd.close() except Exception: @@ -674,7 +649,7 @@ class Socks5: except IOError, e: self.state = 8 # end connection self.disconnect() - self.file_props['error'] = -7 # unable to read from file + self.file_props.error = -7 # unable to read from file return -1 buff = self.file.read(MAX_BUFF_LEN) if len(buff) > 0: @@ -690,17 +665,17 @@ class Socks5: # peer stopped reading self.state = 8 # end connection self.disconnect() - self.file_props['error'] = -1 + self.file_props.error = -1 return -1 self.size += lenn current_time = self.idlequeue.current_time() - self.file_props['elapsed-time'] += current_time - \ - self.file_props['last-time'] - self.file_props['last-time'] = current_time - self.file_props['received-len'] = self.size - if self.size >= int(self.file_props['size']): + self.file_props.elapsed_time += current_time - \ + self.file_props.last_time + self.file_props.last_time = current_time + self.file_props.received_len = self.size + if self.size >= int(self.file_props.size): self.state = 8 # end connection - self.file_props['error'] = 0 + self.file_props.error = 0 self.disconnect() return -1 if lenn != len(buff): @@ -710,7 +685,7 @@ class Socks5: self.state = 7 # continue to write in the socket if lenn == 0: return None - self.file_props['stalled'] = False + self.file_props.stalled = False return lenn else: self.state = 8 # end connection @@ -722,8 +697,8 @@ class Socks5: Read file contents from socket and write them to file """ - if self.file_props is None or ('file-name' in self.file_props) is False: - self.file_props['error'] = -2 + if self.file_props is None or not self.file_props.file_name: + self.file_props.error = -2 return None fd = None if self.remaining_buff != '': @@ -731,28 +706,28 @@ class Socks5: fd = self.get_fd() except IOError, e: self.disconnect(False) - self.file_props['error'] = -6 # file system error + self.file_props.error = -6 # file system error return 0 fd.write(self.remaining_buff) lenn = len(self.remaining_buff) current_time = self.idlequeue.current_time() - self.file_props['elapsed-time'] += current_time - \ - self.file_props['last-time'] - self.file_props['last-time'] = current_time - self.file_props['received-len'] += lenn + self.file_props.elapsed_time += current_time - \ + self.file_props.last_time + self.file_props.last_time = current_time + self.file_props.received_len += lenn self.remaining_buff = '' - if self.file_props['received-len'] == int(self.file_props['size']): + if self.file_props.received_len == int(self.file_props.size): self.rem_fd(fd) self.disconnect() - self.file_props['error'] = 0 - self.file_props['completed'] = True + self.file_props.error = 0 + self.file_props.completed = True return 0 else: try: fd = self.get_fd() except IOError, e: self.disconnect(False) - self.file_props['error'] = -6 # file system error + self.file_props.error = -6 # file system error return 0 try: buff = self._recv(MAX_BUFF_LEN) @@ -763,39 +738,39 @@ class Socks5: except Exception: buff = '' current_time = self.idlequeue.current_time() - self.file_props['elapsed-time'] += current_time - \ - self.file_props['last-time'] - self.file_props['last-time'] = current_time - self.file_props['received-len'] += len(buff) + self.file_props.elapsed_time += current_time - \ + self.file_props.last_time + self.file_props.last_time = current_time + self.file_props.received_len += len(buff) if len(buff) == 0: # Transfer stopped somehow: # reset, paused or network error self.rem_fd(fd) self.disconnect() - self.file_props['error'] = -1 + self.file_props.error = -1 return 0 try: fd.write(buff) except IOError, e: self.rem_fd(fd) self.disconnect() - self.file_props['error'] = -6 # file system error + self.file_props.error = -6 # file system error return 0 - if self.file_props['received-len'] >= int(self.file_props['size']): + if self.file_props.received_len >= int(self.file_props.size): # transfer completed self.rem_fd(fd) self.disconnect() - self.file_props['error'] = 0 - self.file_props['completed'] = True + self.file_props.error = 0 + self.file_props.completed = True return 0 # return number of read bytes. It can be used in progressbar if fd is not None: - self.file_props['stalled'] = False - if fd is None and self.file_props['stalled'] is False: + self.file_props.stalled = False + if fd is None and self.file_props.stalled is False: return None - if 'received-len' in self.file_props: - if self.file_props['received-len'] != 0: - return self.file_props['received-len'] + if self.file_props.received_len: + if self.file_props.received_len != 0: + return self.file_props.received_len return None def disconnect(self): @@ -910,7 +885,7 @@ class Socks5: def continue_paused_transfer(self): if self.state < 5: return - if self.file_props['type'] == 'r': + if self.file_props.type_ == 'r': self.idlequeue.plug_idle(self, False, True) else: self.idlequeue.plug_idle(self, True, False) @@ -920,11 +895,11 @@ class Socks5: Get sha of sid + Initiator jid + Target jid """ - if 'is_a_proxy' in self.file_props: - del(self.file_props['is_a_proxy']) + if self.file_props.is_a_proxy: + self.file_props.is_a_proxy = None # Is this necesary? return hashlib.sha1('%s%s%s' % (self.sid, - self.file_props['proxy_sender'], - self.file_props['proxy_receiver'])).hexdigest() + self.file_props.proxy_sender, + self.file_props.proxy_receiver)).hexdigest() return hashlib.sha1('%s%s%s' % (self.sid, self.initiator, self.target)).\ hexdigest() @@ -961,17 +936,17 @@ class Socks5Sender(IdleObject): self.state = 1 # waiting for first bytes self.connect_timeout = 0 - self.file_props['error'] = 0 - self.file_props['disconnect_cb'] = self.disconnect - self.file_props['started'] = True - self.file_props['completed'] = False - self.file_props['paused'] = False - self.file_props['continue_cb'] = self.continue_paused_transfer - self.file_props['stalled'] = False - self.file_props['connected'] = True - self.file_props['elapsed-time'] = 0 - self.file_props['last-time'] = self.idlequeue.current_time() - self.file_props['received-len'] = 0 + self.file_props.error = 0 + self.file_props.disconnect_cb = self.disconnect + self.file_props.started = True + self.file_props.completed = False + self.file_props.paused = False + self.file_props.continue_cb = self.continue_paused_transfer + self.file_props.stalled = False + self.file_props.connected = True + self.file_props.elapsed_time = 0 + self.file_props.last_time = self.idlequeue.current_time() + self.file_props.received_len = 0 self.type = 'sender' def start_transfer(self): @@ -1018,8 +993,8 @@ class Socks5Sender(IdleObject): # close connection and remove us from the queue Socks5.disconnect(self) if self.file_props is not None: - self.file_props['connected'] = False - self.file_props['disconnect_cb'] = None + self.file_props.connected = False + self.file_props.disconnect_cb = None if self.queue is not None: self.queue.remove_sender(self.queue_idx, False) @@ -1039,33 +1014,33 @@ class Socks5Receiver(IdleObject): self.connected = False self.pauses = 0 self.file_props = file_props - self.file_props['disconnect_cb'] = self.disconnect - self.file_props['error'] = 0 - self.file_props['started'] = True - self.file_props['completed'] = False - self.file_props['paused'] = False - self.file_props['continue_cb'] = self.continue_paused_transfer - self.file_props['stalled'] = False - self.file_props['received-len'] = 0 + self.file_props.disconnect_cb = self.disconnect + self.file_props.error = 0 + self.file_props.started = True + self.file_props.completed = False + self.file_props.paused = False + self.file_props.continue_cb = self.continue_paused_transfer + self.file_props.stalled = False + self.file_props.received_len = 0 def receive_file(self): """ Start receiving the file over verified connection """ - if self.file_props['started']: + if self.file_props.started: return - self.file_props['error'] = 0 - self.file_props['disconnect_cb'] = self.disconnect - self.file_props['started'] = True - self.file_props['completed'] = False - self.file_props['paused'] = False - self.file_props['continue_cb'] = self.continue_paused_transfer - self.file_props['stalled'] = False - self.file_props['connected'] = True - self.file_props['elapsed-time'] = 0 - self.file_props['last-time'] = self.idlequeue.current_time() - self.file_props['received-len'] = 0 + self.file_props.error = 0 + self.file_props.disconnect_cb = self.disconnect + self.file_props.started = True + self.file_props.completed = False + self.file_props.paused = False + self.file_props.continue_cb = self.continue_paused_transfer + self.file_props.stalled = False + self.file_props.connected = True + self.file_props.elapsed_time = 0 + self.file_props.last_time = self.idlequeue.current_time() + self.file_props.received_len = 0 self.pauses = 0 self.state = 7 # plug for reading @@ -1096,7 +1071,7 @@ class Socks5Receiver(IdleObject): # close connection Socks5.disconnect(self) if cb is True: - self.file_props['disconnect_cb'] = None + self.file_props.disconnect_cb = None if self.queue is not None: self.queue.remove_receiver(self.queue_idx, False) @@ -1152,8 +1127,8 @@ class Socks5Server(Socks5): return elif self.state == 7: - if self.file_props['paused']: - self.file_props['continue_cb'] = self.continue_paused_transfer + if self.file_props.paused: + self.file_props.continue_cb = self.continue_paused_transfer self.idlequeue.plug_idle(self, False, False) return self.idlequeue.set_read_timeout(self.fd, STALLED_TIMEOUT) @@ -1169,7 +1144,7 @@ class Socks5Server(Socks5): def pollend(self): self.state = 8 # end connection self.disconnect() - self.file_props['error'] = -1 + self.file_props.error = -1 self.queue.process_result(-1, self) def pollout(self): @@ -1182,8 +1157,8 @@ class Socks5Server(Socks5): elif self.state == 4: # send positive response to the 'connect' self.send_raw(self._get_request_buff(self.sha_msg, 0x00)) elif self.state == 7: - if self.file_props['paused']: - self.file_props['continue_cb'] = self.continue_paused_transfer + if self.file_props.paused: + self.file_props.continue_cb = self.continue_paused_transfer self.idlequeue.plug_idle(self, False, False) return result = self.start_transfer() # send @@ -1262,16 +1237,16 @@ class Socks5Client(Socks5): # for senders: init file_props if result == 1 and self.state == 5: - if self.file_props['type'] == 's': - self.file_props['error'] = 0 - self.file_props['disconnect_cb'] = self.disconnect - self.file_props['started'] = True - self.file_props['completed'] = False - self.file_props['paused'] = False - self.file_props['stalled'] = False - self.file_props['elapsed-time'] = 0 - self.file_props['last-time'] = self.idlequeue.current_time() - self.file_props['received-len'] = 0 + if self.file_props.type_ == 's': + self.file_props.error = 0 + self.file_props.disconnect_cb = self.disconnect + self.file_props.started = True + self.file_props.completed = False + self.file_props.paused = False + self.file_props.stalled = False + self.file_props.elapsed_time = 0 + self.file_props.last_time = self.idlequeue.current_time() + self.file_props.received_len = 0 self.pauses = 0 # start sending file contents to socket #self.idlequeue.set_read_timeout(self.fd, STALLED_TIMEOUT) @@ -1281,7 +1256,7 @@ class Socks5Client(Socks5): # receiving file contents from socket self.idlequeue.plug_idle(self, False, True) - self.file_props['continue_cb'] = self.continue_paused_transfer + self.file_props.continue_cb = self.continue_paused_transfer # we have set up the connection, next - retrieve file self.state = 6 if self.state < 5: @@ -1294,7 +1269,7 @@ class Socks5Client(Socks5): self.idlequeue.remove_timeout(self.fd) if self.connected: try: - if self.file_props['paused']: + if self.file_props.paused: self.idlequeue.plug_idle(self, False, False) return if self.state < 5: @@ -1303,7 +1278,7 @@ class Socks5Client(Socks5): self.queue.process_result(result, self) elif self.state == 5: # wait for proxy reply pass - elif self.file_props['type'] == 'r': + elif self.file_props.type_ == 'r': self.idlequeue.set_read_timeout(self.fd, STALLED_TIMEOUT) result = self.start_transfer() # receive self.queue.process_result(result, self) @@ -1324,8 +1299,8 @@ class Socks5Client(Socks5): self.send_raw(self._get_auth_buff()) elif self.state == 3: # send 'connect' request self.send_raw(self._get_request_buff(self._get_sha1_auth())) - elif self.file_props['type'] != 'r': - if self.file_props['paused']: + elif self.file_props.type_ != 'r': + if self.file_props.paused: self.idlequeue.plug_idle(self, False, False) return result = self.start_transfer() # send @@ -1344,7 +1319,7 @@ class Socks5Client(Socks5): if self.state >= 5: # error during transfer self.disconnect() - self.file_props['error'] = -1 + self.file_props.error = -1 self.queue.process_result(-1, self) else: self.queue.reconnect_client(self, self.streamhost) @@ -1357,7 +1332,7 @@ class Socks5SenderClient(Socks5Client, Socks5Sender): port=None, fingerprint = None, connected=True, file_props={}): Socks5Client.__init__(self, idlequeue, host, port, None, None, - file_props['sid']) + file_props.sid) Socks5Sender.__init__(self,idlequeue, sock_hash, parent,_sock, host, port, fingerprint , connected, file_props) @@ -1372,7 +1347,7 @@ class Socks5SenderServer(Socks5Server, Socks5Sender): port=None, fingerprint = None, connected=True, file_props={}): Socks5Server.__init__(self, idlequeue, host, port, None, None, - file_props['sid']) + file_props.sid) Socks5Sender.__init__(self,idlequeue, sock_hash, parent, _sock, host, port, fingerprint , connected, file_props) diff --git a/src/filetransfers_window.py b/src/filetransfers_window.py index ac4d3eb5b..9b1bf0119 100644 --- a/src/filetransfers_window.py +++ b/src/filetransfers_window.py @@ -33,6 +33,7 @@ import dialogs from common import gajim from common import helpers +from common.file_props import FilesProp from common.protocol.bytestream import (is_transfer_active, is_transfer_paused, is_transfer_stopped) from common.xmpp.protocol import NS_JINGLE_FILE_TRANSFER @@ -147,22 +148,20 @@ class FileTransfersWindow: Find all transfers with peer 'jid' that belong to 'account' """ active_transfers = [[], []] # ['senders', 'receivers'] - - # 'account' is the sender - for file_props in self.files_props['s'].values(): - if file_props['tt_account'] == account: - receiver_jid = unicode(file_props['receiver']).split('/')[0] - if jid == receiver_jid: - if not is_transfer_stopped(file_props): - active_transfers[0].append(file_props) - - # 'account' is the recipient - for file_props in self.files_props['r'].values(): - if file_props['tt_account'] == account: - sender_jid = unicode(file_props['sender']).split('/')[0] - if jid == sender_jid: - if not is_transfer_stopped(file_props): - active_transfers[1].append(file_props) + allfp = FilesProp.getAllFileProp() + for file_props in allfp: + if file_props.type_ == 's' and file_props.tt_account == account: + # 'account' is the sender + receiver_jid = unicode(file_props.receiver).split('/')[0] + if jid == receiver_jid and not is_transfer_stopped(file_props): + active_transfers[0].append(file_props) + elif file_props.type_ == 'r' and file_props.tt_account == account: + # 'account' is the recipient + sender_jid = unicode(file_props.sender).split('/')[0] + if jid == sender_jid and not is_transfer_stopped(file_props): + active_transfers[1].append(file_props) + else: + raise Exception('file_props has no type') return active_transfers def show_completed(self, jid, file_props): @@ -171,46 +170,46 @@ class FileTransfersWindow: """ def on_open(widget, file_props): dialog.destroy() - if 'file-name' not in file_props: + if not file_props.file_name: return - path = os.path.split(file_props['file-name'])[0] + path = os.path.split(file_props.file_name)[0] if os.path.exists(path) and os.path.isdir(path): helpers.launch_file_manager(path) self.tree.get_selection().unselect_all() - if file_props['type'] == 'r': + if file_props.type_ == 'r': # file path is used below in 'Save in' - (file_path, file_name) = os.path.split(file_props['file-name']) + (file_path, file_name) = os.path.split(file_props.file_name) else: - file_name = file_props['name'] + file_name = file_props.name sectext = '\t' + _('Filename: %s') % gobject.markup_escape_text( file_name) sectext += '\n\t' + _('Size: %s') % \ - helpers.convert_bytes(file_props['size']) - if file_props['type'] == 'r': - jid = unicode(file_props['sender']).split('/')[0] + helpers.convert_bytes(file_props.size) + if file_props.type_ == 'r': + jid = unicode(file_props.sender).split('/')[0] sender_name = gajim.contacts.get_first_contact_from_jid( - file_props['tt_account'], jid).get_shown_name() + file_props.tt_account, jid).get_shown_name() sender = sender_name else: #You is a reply of who sent a file sender = _('You') sectext += '\n\t' + _('Sender: %s') % sender sectext += '\n\t' + _('Recipient: ') - if file_props['type'] == 's': - jid = unicode(file_props['receiver']).split('/')[0] + if file_props.type_ == 's': + jid = unicode(file_props.receiver).split('/')[0] receiver_name = gajim.contacts.get_first_contact_from_jid( - file_props['tt_account'], jid).get_shown_name() + file_props.tt_account, jid).get_shown_name() recipient = receiver_name else: #You is a reply of who received a file recipient = _('You') sectext += recipient - if file_props['type'] == 'r': + if file_props.type_ == 'r': sectext += '\n\t' + _('Saved in: %s') % file_path dialog = dialogs.HigDialog(None, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE, _('File transfer completed'), sectext) - if file_props['type'] == 'r': + if file_props.type_ == 'r': button = gtk.Button(_('_Open Containing Folder')) button.connect('clicked', on_open, file_props) dialog.action_area.pack_start(button) @@ -236,10 +235,10 @@ class FileTransfersWindow: self.tree.get_selection().unselect_all() def show_stopped(self, jid, file_props, error_msg=''): - if file_props['type'] == 'r': - file_name = os.path.basename(file_props['file-name']) + if file_props.type_ == 'r': + file_name = os.path.basename(file_props.file_name) else: - file_name = file_props['name'] + file_name = file_props.name sectext = '\t' + _('Filename: %s') % gobject.markup_escape_text( file_name) sectext += '\n\t' + _('Recipient: %s') % jid @@ -254,13 +253,13 @@ class FileTransfersWindow: sid = gajim.connections[account].start_file_transfer(jid, file_props, True) - file_props['sid'] = sid + file_props.sid = sid - if file_props['type'] == 'r': - file_name = os.path.basename(file_props['file-name']) + if file_props.type_ == 'r': + file_name = os.path.basename(file_props.file_name) else: - file_name = file_props['name'] + 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?') % \ @@ -339,10 +338,8 @@ class FileTransfersWindow: return False if contact.supports(NS_JINGLE_FILE_TRANSFER): log.info("contact %s supports jingle file transfer"%(contact.get_full_jid())) - # this call has the side effect of setting file_props['sid'] to the jingle sid, but for the sake of clarity - # make it explicit here - sid = gajim.connections[account].start_file_transfer(contact.get_full_jid(), file_props) - file_props['sid'] = sid + gajim.connections[account].start_file_transfer(contact.get_full_jid(), + file_props) self.add_transfer(account, contact, file_props) else: log.info("contact does not support jingle file transfer") @@ -354,7 +351,8 @@ class FileTransfersWindow: file_dir = os.path.dirname(file_path) if file_dir: gajim.config.set('last_save_dir', file_dir) - file_props['file-name'] = file_path + file_props.file_name = file_path + file_props.type_ = 'r' self.add_transfer(account, contact, file_props) gajim.connections[account].send_file_approval(file_props) @@ -375,14 +373,14 @@ class FileTransfersWindow: return stat = os.stat(file_path) dl_size = stat.st_size - file_size = file_props['size'] + file_size = file_props.size dl_finished = dl_size >= file_size def on_response(response): if response < 0: return elif response == 100: - file_props['offset'] = dl_size + file_props.offset = dl_size dialog2.destroy() self._start_receive(file_path, account, contact, file_props) @@ -419,7 +417,7 @@ class FileTransfersWindow: on_response_ok=(on_ok, account, contact, file_props), on_response_cancel=(on_cancel, account, contact, file_props)) - dialog2.set_current_name(file_props['name']) + dialog2.set_current_name(file_props.name) dialog2.connect('delete-event', lambda widget, event: on_cancel(widget, account, contact, file_props)) @@ -428,17 +426,17 @@ class FileTransfersWindow: Show dialog asking for comfirmation and store location of new file requested by a contact """ - if file_props is None or 'name' not in file_props: + if not file_props or not file_props.name: return sec_text = '\t' + _('File: %s') % gobject.markup_escape_text( - file_props['name']) - if 'size' in file_props: + file_props.name) + if file_props.size: sec_text += '\n\t' + _('Size: %s') % \ - helpers.convert_bytes(file_props['size']) - if 'mime-type' in file_props: - sec_text += '\n\t' + _('Type: %s') % file_props['mime-type'] - if 'desc' in file_props: - sec_text += '\n\t' + _('Description: %s') % file_props['desc'] + helpers.convert_bytes(file_props.size) + if file_props.mime_type: + sec_text += '\n\t' + _('Type: %s') % file_props.mime_type + if file_props.desc: + sec_text += '\n\t' + _('Description: %s') % file_props.desc prim_text = _('%s wants to send you a file:') % contact.jid dialog = None @@ -459,22 +457,21 @@ class FileTransfersWindow: return self.images.setdefault(ident, self.window.render_icon(self.icons[ident], gtk.ICON_SIZE_MENU)) - def set_status(self, typ, sid, status): + def set_status(self,file_props, status): """ Change the status of a transfer to state 'status' """ - iter_ = self.get_iter_by_sid(typ, sid) + iter_ = self.get_iter_by_sid(file_props.type_, file_props.sid) if iter_ is None: return - sid = self.model[iter_][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] + self.model[iter_][C_SID].decode('utf-8') if status == 'stop': - file_props['stopped'] = True + file_props.stopped = True elif status == 'ok': - file_props['completed'] = True + file_props.completed = True text = self._format_percent(100) - received_size = int(file_props['received-len']) - full_size = int(file_props['size']) + 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) @@ -482,8 +479,8 @@ class FileTransfersWindow: 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']) + 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) @@ -496,8 +493,8 @@ class FileTransfersWindow: 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']) + 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) @@ -535,14 +532,14 @@ class FileTransfersWindow: return _('%(hours)02.d:%(minutes)02.d:%(seconds)02.d') % times def _get_eta_and_speed(self, full_size, transfered_size, file_props): - if len(file_props['transfered_size']) == 0: + if len(file_props.transfered_size) == 0: return 0., 0. - elif len(file_props['transfered_size']) == 1: - speed = round(float(transfered_size) / file_props['elapsed-time']) + elif len(file_props.transfered_size) == 1: + speed = round(float(transfered_size) / file_props.elapsed_time) else: # first and last are (time, transfered_size) - first = file_props['transfered_size'][0] - last = file_props['transfered_size'][-1] + first = file_props.transfered_size[0] + last = file_props.transfered_size[-1] transfered = last[1] - first[1] tim = last[0] - first[0] if tim == 0: @@ -556,16 +553,16 @@ class FileTransfersWindow: def _remove_transfer(self, iter_, sid, file_props): self.model.remove(iter_) - if 'tt_account' in file_props: + if file_props.tt_account: # file transfer is set - account = file_props['tt_account'] + account = file_props.tt_account if account in gajim.connections: # there is a connection to the account gajim.connections[account].remove_transfer(file_props) - if file_props['type'] == 'r': # we receive a file - other = file_props['sender'] + if file_props.type_ == 'r': # we receive a file + other = file_props.sender else: # we send a file - other = file_props['receiver'] + other = file_props.receiver if isinstance(other, unicode): jid = gajim.get_jid_without_resource(other) else: # It's a Contact instance @@ -573,21 +570,19 @@ class FileTransfersWindow: for ev_type in ('file-error', 'file-completed', 'file-request-error', 'file-send-error', 'file-stopped'): for event in gajim.events.get_events(account, jid, [ev_type]): - if event.parameters['sid'] == file_props['sid']: + if event.parameters['sid'] == file_props.sid: gajim.events.remove_events(account, jid, event) gajim.interface.roster.draw_contact(jid, account) gajim.interface.roster.show_title() - del(self.files_props[sid[0]][sid[1:]]) + FilesProp.deleteFileProp(files_props) del(file_props) def set_progress(self, typ, sid, transfered_size, iter_=None): """ Change the progress of a transfer with new transfered size """ - if sid not in self.files_props[typ]: - return - file_props = self.files_props[typ][sid] - full_size = int(file_props['size']) + file_props = FilesProp.getFilePropByType(typ, sid) + full_size = int(file_props.size) if full_size == 0: percent = 0 else: @@ -607,14 +602,14 @@ class FileTransfersWindow: # Kb/s # remaining time - if 'offset' in file_props and file_props['offset']: - transfered_size -= file_props['offset'] - full_size -= file_props['offset'] + if file_props.offset: + transfered_size -= file_props.offset + full_size -= file_props.offset - if file_props['elapsed-time'] > 0: - file_props['transfered_size'].append((file_props['last-time'], transfered_size)) - if len(file_props['transfered_size']) > 6: - file_props['transfered_size'].pop(0) + if file_props.elapsed_time > 0: + file_props.transfered_size.append((file_props.last_time, transfered_size)) + if len(file_props.transfered_size) > 6: + file_props.transfered_size.pop(0) eta, speed = self._get_eta_and_speed(full_size, transfered_size, file_props) @@ -630,24 +625,24 @@ class FileTransfersWindow: self.model.set(iter_, C_TIME, text) # try to guess what should be the status image - if file_props['type'] == 'r': + if file_props.type_ == 'r': status = 'download' else: status = 'upload' - if 'paused' in file_props and file_props['paused'] == True: + if file_props.paused == True: status = 'pause' - elif 'stalled' in file_props and file_props['stalled'] == True: + elif file_props.stalled == True: status = 'waiting' - if 'connected' in file_props and file_props['connected'] == False: + if file_props.connected == False: status = 'stop' self.model.set(iter_, 0, self.get_icon(status)) if transfered_size == full_size: # If we are receiver and this is a jingle session - if file_props['type'] == 'r' and 'session-sid' in file_props: + if file_props.type_ == 'r' and file_props.session_sid: # Show that we are computing the hash - self.set_status(typ, sid, 'computing') + self.set_status(file_props, 'computing') else: - self.set_status(typ, sid, 'ok') + self.set_status(file_props, 'ok') elif just_began: path = self.model.get_path(iter_) self.select_func(path) @@ -668,8 +663,6 @@ class FileTransfersWindow: """ Create new file_props dict and set initial file transfer properties in it """ - file_props = {'file-name' : file_path, 'name' : file_name, - 'type' : 's', 'desc' : file_desc} if os.path.isfile(file_path): stat = os.stat(file_path) else: @@ -679,16 +672,17 @@ class FileTransfersWindow: dialogs.ErrorDialog(_('Invalid File'), _('It is not possible to send empty files')) return None - file_props['elapsed-time'] = 0 - file_props['size'] = unicode(stat[6]) - file_props['sid'] = helpers.get_random_string_16() - file_props['completed'] = False - file_props['started'] = False - file_props['sender'] = account - file_props['receiver'] = contact - file_props['tt_account'] = account - # keep the last time: transfered_size to compute transfer speed - file_props['transfered_size'] = [] + file_props = FilesProp.getNewFileProp(account, + sid=helpers.get_random_string_16()) + file_props.file_name = file_path + file_props.name = file_name + file_props.type_ = 's' + file_props.desc = file_desc + file_props.elapsed_time = 0 + file_props.size = unicode(stat[6]) + file_props.sender = account + file_props.receiver = contact + file_props.tt_account = account return file_props def add_transfer(self, account, contact, file_props): @@ -698,32 +692,31 @@ class FileTransfersWindow: self.on_transfers_list_leave_notify_event(None) if file_props is None: return - file_props['elapsed-time'] = 0 - self.files_props[file_props['type']][file_props['sid']] = file_props + file_props.elapsed_time = 0 iter_ = self.model.prepend() text_labels = '' + _('Name: ') + '\n' - if file_props['type'] == 'r': + if file_props.type_ == 'r': text_labels += '' + _('Sender: ') + '' else: text_labels += '' + _('Recipient: ') + '' - if file_props['type'] == 'r': - file_name = os.path.split(file_props['file-name'])[1] + if file_props.type_ == 'r': + file_name = os.path.split(file_props.file_name)[1] else: - file_name = file_props['name'] + 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_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: + file_props.type_ + file_props.sid) + self.set_progress(file_props.type_, file_props.sid, 0, iter_) + if file_props.started is False: status = 'waiting' - elif file_props['type'] == 'r': + elif file_props.type_ == 'r': status = 'download' else: status = 'upload' - file_props['tt_account'] = account - self.set_status(file_props['type'], file_props['sid'], status) + file_props.tt_account = account + self.set_status(file_props, status) self.set_cleanup_sensitivity() self.window.show_all() @@ -743,7 +736,7 @@ class FileTransfersWindow: self.tooltip.hide_tooltip() return sid = self.model[iter_][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) if file_props is not None: if self.tooltip.timeout == 0 or self.tooltip.id != props[0]: self.tooltip.id = row @@ -798,7 +791,7 @@ class FileTransfersWindow: return current_iter = self.model.get_iter(path) sid = self.model[current_iter][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) self.remove_menuitem.set_sensitive(is_row_selected) self.open_folder_menuitem.set_sensitive(is_row_selected) is_stopped = False @@ -856,7 +849,7 @@ class FileTransfersWindow: while i >= 0: iter_ = self.model.get_iter((i)) sid = self.model[iter_][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) if is_transfer_stopped(file_props): self._remove_transfer(iter_, sid, file_props) i -= 1 @@ -891,20 +884,20 @@ class FileTransfersWindow: return s_iter = selected[1] sid = self.model[s_iter][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) if is_transfer_paused(file_props): - file_props['last-time'] = time.time() - file_props['paused'] = False + file_props.last_time = time.time() + file_props.paused = False types = {'r' : 'download', 's' : 'upload'} - self.set_status(file_props['type'], file_props['sid'], types[sid[0]]) + self.set_status(file_props, types[sid[0]]) self.toggle_pause_continue(True) - if file_props['continue_cb']: - file_props['continue_cb']() + if file_props.continue_cb: + file_props.continue_cb() elif is_transfer_active(file_props): - file_props['paused'] = True - self.set_status(file_props['type'], file_props['sid'], 'pause') + file_props.paused = True + self.set_status(file_props, 'pause') # reset that to compute speed only when we resume - file_props['transfered_size'] = [] + file_props.transfered_size = [] self.toggle_pause_continue(False) def on_cancel_button_clicked(self, widget): @@ -913,14 +906,12 @@ class FileTransfersWindow: return s_iter = selected[1] sid = self.model[s_iter][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] - if 'tt_account' not in file_props: - return - account = file_props['tt_account'] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) + account = file_props.tt_account if account not in gajim.connections: return gajim.connections[account].disconnect_transfer(file_props) - self.set_status(file_props['type'], file_props['sid'], 'stop') + self.set_status(file_props, 'stop') def show_tooltip(self, widget): if self.height_diff == 0: @@ -934,7 +925,7 @@ class FileTransfersWindow: if props and self.tooltip.id == props[0]: iter_ = self.model.get_iter(props[0]) sid = self.model[iter_][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) # bounding rectangle of coordinates for the cell within the treeview rect = self.tree.get_cell_area(props[0], props[1]) # position of the treeview on the screen @@ -1022,10 +1013,10 @@ class FileTransfersWindow: return s_iter = selected[1] sid = self.model[s_iter][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] - if 'file-name' not in file_props: + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) + if not file_props.file_name: return - path = os.path.split(file_props['file-name'])[0] + path = os.path.split(file_props.file_name)[0] if os.path.exists(path) and os.path.isdir(path): helpers.launch_file_manager(path) @@ -1044,7 +1035,7 @@ class FileTransfersWindow: return s_iter = selected[1] sid = self.model[s_iter][C_SID].decode('utf-8') - file_props = self.files_props[sid[0]][sid[1:]] + file_props = FilesProp.getFilePropByType(sid[0], sid[1:]) self._remove_transfer(s_iter, sid, file_props) self.set_all_insensitive() diff --git a/src/gui_interface.py b/src/gui_interface.py index 587dd8206..5ef69587c 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -84,6 +84,7 @@ from common.connection_handlers_events import OurShowEvent, \ FileRequestErrorEvent, InformationEvent from common.connection import Connection from common import jingle +from common.file_props import FilesProp import roster_window import profile_window @@ -168,12 +169,12 @@ class Interface: sid = obj.id_ if len(obj.id_) > 3 and obj.id_[2] == '_': sid = obj.id_[3:] - if sid in ft.files_props['s']: - file_props = ft.files_props['s'][sid] + file_props = FilesProp.getFileProp(obj.conn.name, sid) + if file_props : if unicode(obj.errcode) == '400': - file_props['error'] = -3 + file_props.error = -3 else: - file_props['error'] = -4 + file_props.error = -4 gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=obj.conn, jid=obj.jid, file_props=file_props, error_msg=obj.errmsg)) @@ -183,12 +184,11 @@ class Interface: sid = obj.id_ if len(obj.id_) > 3 and obj.id_[2] == '_': sid = obj.id_[3:] - if sid in obj.conn.files_props: - file_props = obj.conn.files_props[sid] - self.handle_event_file_send_error(obj.conn.name, (obj.fjid, - file_props)) - obj.conn.disconnect_transfer(file_props) - return + file_props = FilesProp.getFileProp(obj.conn.name, sid) + self.handle_event_file_send_error(obj.conn.name, (obj.fjid, + file_props)) + obj.conn.disconnect_transfer(file_props) + return ctrl = self.msg_win_mgr.get_control(obj.fjid, obj.conn.name) if ctrl and ctrl.type_id == message_control.TYPE_GC: @@ -802,7 +802,7 @@ class Interface: jid = array[0] file_props = array[1] ft = self.instances['file_transfers'] - ft.set_status(file_props['type'], file_props['sid'], 'stop') + ft.set_status(file_props, 'stop') if helpers.allow_popup_window(account): ft.show_send_error(file_props) @@ -814,7 +814,7 @@ class Interface: path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48) event_type = _('File Transfer Error') notify.popup(event_type, jid, account, 'file-send-error', path, - event_type, file_props['name']) + event_type, file_props.name) def handle_event_gmail_notify(self, obj): jid = obj.jid @@ -856,8 +856,8 @@ class Interface: def handle_event_file_request_error(self, obj): # ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg)) ft = self.instances['file_transfers'] - ft.set_status(obj.file_props['type'], obj.file_props['sid'], 'stop') - errno = obj.file_props['error'] + ft.set_status(obj.file_props, 'stop') + errno = obj.file_props.error if helpers.allow_popup_window(obj.conn.name): if errno in (-4, -5): @@ -878,7 +878,7 @@ class Interface: path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48) event_type = _('File Transfer Error') notify.popup(event_type, obj.jid, obj.conn.name, msg_type, path, - title=event_type, text=obj.file_props['name']) + title=event_type, text=obj.file_props.name) def handle_event_file_request(self, obj): account = obj.conn.name @@ -916,92 +916,90 @@ class Interface: if time.time() - self.last_ftwindow_update > 0.5: # update ft window every 500ms self.last_ftwindow_update = time.time() - self.instances['file_transfers'].set_progress(file_props['type'], - file_props['sid'], file_props['received-len']) + self.instances['file_transfers'].set_progress(file_props.type_, + file_props.sid, file_props.received_len) def __compare_hashes(self, account, file_props): session = gajim.connections[account].get_jingle_session(jid=None, - sid=file_props['session-sid']) + 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']) + jid = unicode(file_props.sender) self.popup_ft_result(account, jid, file_props) - ft_win.set_status(file_props['type'], file_props['sid'], 'ok') + ft_win.set_status(file_props, 'ok') h = Hashes() try: - file_ = open(file_props['file-name'], 'r') + file_ = open(file_props.file_name, 'r') except: return hash_ = h.calculateHash(session.hash_algo, file_) file_.close() # If the hash we received and the hash of the file are the same, # then the file is not corrupt - jid = unicode(file_props['sender']) + 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') + ft_win.set_status(file_props, 'ok') else: # wrong hash, we need to get the file again! - file_props['error'] = -10 + file_props.error = -10 self.popup_ft_result(account, jid, file_props) - ft_win.set_status(file_props['type'], file_props['sid'], - 'hash_error') + ft_win.set_status(file_props, 'hash_error') # End jingle session if session: session.end_session() def handle_event_file_rcv_completed(self, account, file_props): ft = self.instances['file_transfers'] - if file_props['error'] == 0: - ft.set_progress(file_props['type'], file_props['sid'], - file_props['received-len']) + if file_props.error == 0: + ft.set_progress(file_props.type_, file_props.sid, + file_props.received_len) else: - ft.set_status(file_props['type'], file_props['sid'], 'stop') - if 'stalled' in file_props and file_props['stalled'] or \ - 'paused' in file_props and file_props['paused']: + ft.set_status(file_props, 'stop') + if file_props.stalled or file_props.paused: return - if file_props['type'] == 'r': # we receive a file + if file_props.type_ == 'r': # we receive a file # If we have a jingle session id, it is a jingle transfer # we compare hashes - if 'session-sid' in file_props: + if file_props.session_sid: # Compare hashes in a new thread self.hashThread = Thread(target=self.__compare_hashes, args=(account, file_props)) self.hashThread.start() - gajim.socks5queue.remove_receiver(file_props['sid'], True, True) + gajim.socks5queue.remove_receiver(file_props.sid, True, True) else: # we send a file - jid = unicode(file_props['receiver']) - gajim.socks5queue.remove_sender(file_props['sid'], True, True) + 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 file_props.error == 0: if gajim.config.get('notify_on_file_complete'): ft.show_completed(jid, file_props) - elif file_props['error'] == -1: + elif file_props.error == -1: ft.show_stopped(jid, file_props, error_msg=_('Remote contact stopped transfer')) - elif file_props['error'] == -6: + elif file_props.error == -6: ft.show_stopped(jid, file_props, error_msg=_('Error opening file')) - elif file_props['error'] == -10: + elif file_props.error == -10: ft.show_hash_error(jid, file_props, account) return msg_type = '' event_type = '' - if file_props['error'] == 0 and gajim.config.get( + if file_props.error == 0 and gajim.config.get( 'notify_on_file_complete'): msg_type = 'file-completed' event_type = _('File Transfer Completed') - elif file_props['error'] in (-1, -6): + elif file_props.error in (-1, -6): msg_type = 'file-stopped' event_type = _('File Transfer Stopped') - elif file_props['error'] == -10: + elif file_props.error == -10: msg_type = 'file-hash-error' event_type = _('File Transfer Failed') @@ -1017,12 +1015,12 @@ class Interface: self.add_event(account, jid, msg_type, file_props) if file_props is not None: - if file_props['type'] == 'r': + if file_props.type_ == 'r': # get the name of the sender, as it is in the roster - sender = unicode(file_props['sender']).split('/')[0] + sender = unicode(file_props.sender).split('/')[0] name = gajim.contacts.get_first_contact_from_jid(account, sender).get_shown_name() - filename = os.path.basename(file_props['file-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} @@ -1036,14 +1034,14 @@ class Interface: 'failed.') % {'filename': filename, 'name': name} img_name = 'gajim-ft_stopped' else: - receiver = file_props['receiver'] + receiver = file_props.receiver if hasattr(receiver, 'jid'): receiver = receiver.jid 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() - filename = os.path.basename(file_props['file-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} @@ -1164,17 +1162,10 @@ class Interface: def handle_event_jingleft_cancel(self, obj): ft = self.instances['file_transfers'] file_props = None - # get the file_props of our session - for sid in obj.conn.files_props: - fp = obj.conn.files_props[sid] - if fp['session-sid'] == obj.sid: - file_props = fp - break - - ft.set_status(file_props['type'], file_props['sid'], 'stop') - file_props['error'] = -4 # is it the right error code? - + file_props = FilesProp.getFileProp(obj.conn.name, sid) + ft.set_status(file_props, 'stop') + file_props.error = -4 # is it the right error code? ft.show_stopped(obj.jid, file_props, 'Peer cancelled ' + 'the transfer') diff --git a/src/tooltips.py b/src/tooltips.py index db5064ad3..47836ae99 100644 --- a/src/tooltips.py +++ b/src/tooltips.py @@ -730,23 +730,23 @@ class FileTransfersTooltip(BaseTooltip): current_row = 1 self.create_window() properties = [] - name = file_props['name'] - if file_props['type'] == 'r': - file_name = os.path.split(file_props['file-name'])[1] + name = file_props.name + if file_props.type_ == 'r': + file_name = os.path.split(file_props.file_name)[1] else: - file_name = file_props['name'] + file_name = file_props.name properties.append((_('Name: '), gobject.markup_escape_text(file_name))) - if file_props['type'] == 'r': + if file_props.type_ == 'r': type_ = _('Download') actor = _('Sender: ') - sender = unicode(file_props['sender']).split('/')[0] + sender = unicode(file_props.sender).split('/')[0] name = gajim.contacts.get_first_contact_from_jid( - file_props['tt_account'], sender).get_shown_name() + file_props.tt_account, sender).get_shown_name() else: type_ = _('Upload') actor = _('Recipient: ') - receiver = file_props['receiver'] + receiver = file_props.receiver if hasattr(receiver, 'name'): name = receiver.get_shown_name() else: @@ -754,26 +754,24 @@ class FileTransfersTooltip(BaseTooltip): properties.append((_('Type: '), type_)) properties.append((actor, gobject.markup_escape_text(name))) - transfered_len = file_props.get('received-len', 0) + transfered_len = file_props.received_len + if not transfered_len: + transfered_len = 0 properties.append((_('Transferred: '), helpers.convert_bytes(transfered_len))) status = '' - if 'started' not in file_props or not file_props['started']: + if file_props.started: status = _('Not started') - elif 'connected' in file_props: - if 'stopped' in file_props and \ - file_props['stopped'] == True: - status = _('Stopped') - elif file_props['completed']: + if file_props.stopped == True: + status = _('Stopped') + elif file_props.completed: + status = _('Completed') + elif file_props.connected == False: + if file_props.completed: status = _('Completed') - elif file_props['connected'] == False: - if file_props['completed']: - status = _('Completed') else: - if 'paused' in file_props and \ - file_props['paused'] == True: + if file_props.paused == True: status = _('?transfer status:Paused') - elif 'stalled' in file_props and \ - file_props['stalled'] == True: + elif file_props.stalled == True: #stalled is not paused. it is like 'frozen' it stopped alone status = _('Stalled') else: @@ -781,10 +779,9 @@ class FileTransfersTooltip(BaseTooltip): else: status = _('Not started') properties.append((_('Status: '), status)) - if 'desc' in file_props: - file_desc = file_props['desc'] - properties.append((_('Description: '), gobject.markup_escape_text( - file_desc))) + file_desc = file_props.desc + properties.append((_('Description: '), gobject.markup_escape_text( + file_desc))) while properties: property_ = properties.pop(0) current_row += 1