Merge local changes (not again...)
This commit is contained in:
commit
b7aa05a684
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import base64
|
import base64
|
||||||
import socket
|
|
||||||
import sys
|
import sys
|
||||||
import operator
|
import operator
|
||||||
import hashlib
|
import hashlib
|
||||||
|
@ -41,7 +40,6 @@ from time import (altzone, daylight, gmtime, localtime, mktime, strftime,
|
||||||
from calendar import timegm
|
from calendar import timegm
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import socks5
|
|
||||||
import common.xmpp
|
import common.xmpp
|
||||||
|
|
||||||
from common import helpers
|
from common import helpers
|
||||||
|
@ -51,6 +49,7 @@ from common.commands import ConnectionCommands
|
||||||
from common.pubsub import ConnectionPubSub
|
from common.pubsub import ConnectionPubSub
|
||||||
from common.pep import ConnectionPEP
|
from common.pep import ConnectionPEP
|
||||||
from common.protocol.caps import ConnectionCaps
|
from common.protocol.caps import ConnectionCaps
|
||||||
|
from common.protocol.bytestream import ConnectionBytestream
|
||||||
import common.caps_cache as capscache
|
import common.caps_cache as capscache
|
||||||
if gajim.HAVE_FARSIGHT:
|
if gajim.HAVE_FARSIGHT:
|
||||||
from common.jingle import ConnectionJingle
|
from common.jingle import ConnectionJingle
|
||||||
|
@ -84,613 +83,6 @@ except Exception:
|
||||||
log.debug(_('Unable to load idle module'))
|
log.debug(_('Unable to load idle module'))
|
||||||
HAS_IDLE = False
|
HAS_IDLE = False
|
||||||
|
|
||||||
class ConnectionBytestream:
|
|
||||||
def __init__(self):
|
|
||||||
self.files_props = {}
|
|
||||||
self.awaiting_xmpp_ping_id = None
|
|
||||||
|
|
||||||
def is_transfer_stopped(self, file_props):
|
|
||||||
if 'error' in file_props and file_props['error'] != 0:
|
|
||||||
return True
|
|
||||||
if 'completed' in file_props and file_props['completed']:
|
|
||||||
return True
|
|
||||||
if 'connected' in file_props and file_props['connected'] == False:
|
|
||||||
return True
|
|
||||||
if 'stopped' not in file_props or not file_props['stopped']:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def send_success_connect_reply(self, streamhost):
|
|
||||||
"""
|
|
||||||
Send reply to the initiator of FT that we made a connection
|
|
||||||
"""
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
if streamhost is None:
|
|
||||||
return None
|
|
||||||
iq = common.xmpp.Iq(to = streamhost['initiator'], typ = 'result',
|
|
||||||
frm = streamhost['target'])
|
|
||||||
iq.setAttr('id', streamhost['id'])
|
|
||||||
query = iq.setTag('query')
|
|
||||||
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
|
||||||
stream_tag = query.setTag('streamhost-used')
|
|
||||||
stream_tag.setAttr('jid', streamhost['jid'])
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def remove_transfers_for_contact(self, contact):
|
|
||||||
"""
|
|
||||||
Stop all active transfer for contact
|
|
||||||
"""
|
|
||||||
for file_props in self.files_props.values():
|
|
||||||
if self.is_transfer_stopped(file_props):
|
|
||||||
continue
|
|
||||||
receiver_jid = unicode(file_props['receiver'])
|
|
||||||
if contact.get_full_jid() == receiver_jid:
|
|
||||||
file_props['error'] = -5
|
|
||||||
self.remove_transfer(file_props)
|
|
||||||
self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props, ''))
|
|
||||||
sender_jid = unicode(file_props['sender'])
|
|
||||||
if contact.get_full_jid() == sender_jid:
|
|
||||||
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():
|
|
||||||
self.remove_transfer(file_props, remove_from_list = False)
|
|
||||||
del(self.files_props)
|
|
||||||
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']
|
|
||||||
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 'streamhosts' in file_props:
|
|
||||||
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'])
|
|
||||||
|
|
||||||
def send_socks5_info(self, file_props, fast = True, receiver = None, sender
|
|
||||||
= None):
|
|
||||||
"""
|
|
||||||
Send iq for the present streamhosts and proxies
|
|
||||||
"""
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
if not isinstance(self.peerhost, tuple):
|
|
||||||
return
|
|
||||||
port = gajim.config.get('file_transfers_port')
|
|
||||||
ft_add_hosts_to_send = gajim.config.get('ft_add_hosts_to_send')
|
|
||||||
cfg_proxies = gajim.config.get_per('accounts', self.name,
|
|
||||||
'file_transfer_proxies')
|
|
||||||
if receiver is None:
|
|
||||||
receiver = file_props['receiver']
|
|
||||||
if sender is None:
|
|
||||||
sender = file_props['sender']
|
|
||||||
proxyhosts = []
|
|
||||||
if fast and cfg_proxies:
|
|
||||||
proxies = [e.strip() for e in cfg_proxies.split(',')]
|
|
||||||
default = gajim.proxy65_manager.get_default_for_name(self.name)
|
|
||||||
if default:
|
|
||||||
# add/move default proxy at top of the others
|
|
||||||
if proxies.__contains__(default):
|
|
||||||
proxies.remove(default)
|
|
||||||
proxies.insert(0, default)
|
|
||||||
|
|
||||||
for proxy in proxies:
|
|
||||||
(host, _port, jid) = gajim.proxy65_manager.get_proxy(proxy, self.name)
|
|
||||||
if host is None:
|
|
||||||
continue
|
|
||||||
host_dict={
|
|
||||||
'state': 0,
|
|
||||||
'target': unicode(receiver),
|
|
||||||
'id': file_props['sid'],
|
|
||||||
'sid': file_props['sid'],
|
|
||||||
'initiator': proxy,
|
|
||||||
'host': host,
|
|
||||||
'port': unicode(_port),
|
|
||||||
'jid': jid
|
|
||||||
}
|
|
||||||
proxyhosts.append(host_dict)
|
|
||||||
sha_str = helpers.get_auth_sha(file_props['sid'], sender,
|
|
||||||
receiver)
|
|
||||||
file_props['sha_str'] = sha_str
|
|
||||||
ft_add_hosts = []
|
|
||||||
if ft_add_hosts_to_send:
|
|
||||||
ft_add_hosts_to_send = [e.strip() for e in ft_add_hosts_to_send.split(',')]
|
|
||||||
for ft_host in ft_add_hosts_to_send:
|
|
||||||
ft_add_hosts.append(ft_host)
|
|
||||||
listener = gajim.socks5queue.start_listener(port,
|
|
||||||
sha_str, self._result_socks5_sid, file_props['sid'])
|
|
||||||
if listener is None:
|
|
||||||
file_props['error'] = -5
|
|
||||||
self.dispatch('FILE_REQUEST_ERROR', (unicode(receiver), file_props,
|
|
||||||
''))
|
|
||||||
self._connect_error(unicode(receiver), file_props['sid'],
|
|
||||||
file_props['sid'], code = 406)
|
|
||||||
return
|
|
||||||
|
|
||||||
iq = common.xmpp.Protocol(name = 'iq', to = unicode(receiver),
|
|
||||||
typ = 'set')
|
|
||||||
file_props['request-id'] = 'id_' + file_props['sid']
|
|
||||||
iq.setID(file_props['request-id'])
|
|
||||||
query = iq.setTag('query')
|
|
||||||
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
|
||||||
query.setAttr('mode', 'plain')
|
|
||||||
query.setAttr('sid', file_props['sid'])
|
|
||||||
for ft_host in ft_add_hosts:
|
|
||||||
# The streamhost, if set
|
|
||||||
ostreamhost = common.xmpp.Node(tag = 'streamhost')
|
|
||||||
query.addChild(node = ostreamhost)
|
|
||||||
ostreamhost.setAttr('port', unicode(port))
|
|
||||||
ostreamhost.setAttr('host', ft_host)
|
|
||||||
ostreamhost.setAttr('jid', sender)
|
|
||||||
try:
|
|
||||||
# The ip we're connected to server with
|
|
||||||
my_ips = [self.peerhost[0]]
|
|
||||||
# all IPs from local DNS
|
|
||||||
for addr in socket.getaddrinfo(socket.gethostname(), None):
|
|
||||||
if not addr[4][0] in my_ips and not addr[4][0].startswith('127'):
|
|
||||||
my_ips.append(addr[4][0])
|
|
||||||
for ip in my_ips:
|
|
||||||
streamhost = common.xmpp.Node(tag = 'streamhost')
|
|
||||||
query.addChild(node = streamhost)
|
|
||||||
streamhost.setAttr('port', unicode(port))
|
|
||||||
streamhost.setAttr('host', ip)
|
|
||||||
streamhost.setAttr('jid', sender)
|
|
||||||
except socket.gaierror:
|
|
||||||
self.dispatch('ERROR', (_('Wrong host'),
|
|
||||||
_('Invalid local address? :-O')))
|
|
||||||
|
|
||||||
if fast and proxyhosts != [] and gajim.config.get_per('accounts',
|
|
||||||
self.name, 'use_ft_proxies'):
|
|
||||||
file_props['proxy_receiver'] = unicode(receiver)
|
|
||||||
file_props['proxy_sender'] = unicode(sender)
|
|
||||||
file_props['proxyhosts'] = proxyhosts
|
|
||||||
for proxyhost in proxyhosts:
|
|
||||||
streamhost = common.xmpp.Node(tag = 'streamhost')
|
|
||||||
query.addChild(node=streamhost)
|
|
||||||
streamhost.setAttr('port', proxyhost['port'])
|
|
||||||
streamhost.setAttr('host', proxyhost['host'])
|
|
||||||
streamhost.setAttr('jid', proxyhost['jid'])
|
|
||||||
|
|
||||||
# don't add the proxy child tag for streamhosts, which are proxies
|
|
||||||
# proxy = streamhost.setTag('proxy')
|
|
||||||
# proxy.setNamespace(common.xmpp.NS_STREAM)
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def send_file_rejection(self, file_props, code='403', typ=None):
|
|
||||||
"""
|
|
||||||
Inform sender that we refuse to download the file
|
|
||||||
|
|
||||||
typ is used when code = '400', in this case typ can be 'strean' for
|
|
||||||
invalid stream or 'profile' for invalid profile
|
|
||||||
"""
|
|
||||||
# user response to ConfirmationDialog may come after we've disconneted
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
iq = common.xmpp.Protocol(name = '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 = ''
|
|
||||||
else:
|
|
||||||
name = 'forbidden'
|
|
||||||
text = 'Offer Declined'
|
|
||||||
err = common.xmpp.ErrorNode(code=code, typ='cancel', name=name, text=text)
|
|
||||||
if code == '400' and typ in ('stream', 'profile'):
|
|
||||||
if typ == 'stream':
|
|
||||||
err.setTag('no-valid-streams', namespace=common.xmpp.NS_SI)
|
|
||||||
else:
|
|
||||||
err.setTag('bad-profile', namespace=common.xmpp.NS_SI)
|
|
||||||
iq.addChild(node=err)
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def send_file_approval(self, file_props):
|
|
||||||
"""
|
|
||||||
Send iq, confirming that we want to download the file
|
|
||||||
"""
|
|
||||||
# user response to ConfirmationDialog may come after we've disconneted
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
iq = common.xmpp.Protocol(name = 'iq',
|
|
||||||
to = unicode(file_props['sender']), typ = 'result')
|
|
||||||
iq.setAttr('id', file_props['request-id'])
|
|
||||||
si = iq.setTag('si')
|
|
||||||
si.setNamespace(common.xmpp.NS_SI)
|
|
||||||
if 'offset' in file_props and file_props['offset']:
|
|
||||||
file_tag = si.setTag('file')
|
|
||||||
file_tag.setNamespace(common.xmpp.NS_FILE)
|
|
||||||
range_tag = file_tag.setTag('range')
|
|
||||||
range_tag.setAttr('offset', file_props['offset'])
|
|
||||||
feature = si.setTag('feature')
|
|
||||||
feature.setNamespace(common.xmpp.NS_FEATURE)
|
|
||||||
_feature = common.xmpp.DataForm(typ='submit')
|
|
||||||
feature.addChild(node=_feature)
|
|
||||||
field = _feature.setField('stream-method')
|
|
||||||
field.delAttr('type')
|
|
||||||
field.setValue(common.xmpp.NS_BYTESTREAM)
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def _ft_get_our_jid(self):
|
|
||||||
our_jid = gajim.get_jid_from_account(self.name)
|
|
||||||
resource = self.server_resource
|
|
||||||
return our_jid + '/' + resource
|
|
||||||
|
|
||||||
def _ft_get_receiver_jid(self, file_props):
|
|
||||||
return file_props['receiver'].jid + '/' + file_props['receiver'].resource
|
|
||||||
|
|
||||||
def send_file_request(self, file_props):
|
|
||||||
"""
|
|
||||||
Send iq for new FT request
|
|
||||||
"""
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
file_props['sender'] = self._ft_get_our_jid()
|
|
||||||
fjid = self._ft_get_receiver_jid(file_props)
|
|
||||||
iq = common.xmpp.Protocol(name = 'iq', to = fjid,
|
|
||||||
typ = 'set')
|
|
||||||
iq.setID(file_props['sid'])
|
|
||||||
self.files_props[file_props['sid']] = file_props
|
|
||||||
si = iq.setTag('si')
|
|
||||||
si.setNamespace(common.xmpp.NS_SI)
|
|
||||||
si.setAttr('profile', common.xmpp.NS_FILE)
|
|
||||||
si.setAttr('id', file_props['sid'])
|
|
||||||
file_tag = si.setTag('file')
|
|
||||||
file_tag.setNamespace(common.xmpp.NS_FILE)
|
|
||||||
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'])
|
|
||||||
file_tag.setTag('range')
|
|
||||||
feature = si.setTag('feature')
|
|
||||||
feature.setNamespace(common.xmpp.NS_FEATURE)
|
|
||||||
_feature = common.xmpp.DataForm(typ='form')
|
|
||||||
feature.addChild(node=_feature)
|
|
||||||
field = _feature.setField('stream-method')
|
|
||||||
field.setAttr('type', 'list-single')
|
|
||||||
field.addOption(common.xmpp.NS_BYTESTREAM)
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def _result_socks5_sid(self, sid, hash_id):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
return
|
|
||||||
|
|
||||||
def _connect_error(self, to, _id, sid, code=404):
|
|
||||||
"""
|
|
||||||
Called when there is an error establishing BS connection, or when
|
|
||||||
connection is rejected
|
|
||||||
"""
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
msg_dict = {
|
|
||||||
404: 'Could not connect to given hosts',
|
|
||||||
405: 'Cancel',
|
|
||||||
406: 'Not acceptable',
|
|
||||||
}
|
|
||||||
msg = msg_dict[code]
|
|
||||||
iq = None
|
|
||||||
iq = common.xmpp.Protocol(name = 'iq', to = to,
|
|
||||||
typ = 'error')
|
|
||||||
iq.setAttr('id', _id)
|
|
||||||
err = iq.setTag('error')
|
|
||||||
err.setAttr('code', unicode(code))
|
|
||||||
err.setData(msg)
|
|
||||||
self.connection.send(iq)
|
|
||||||
if code == 404:
|
|
||||||
file_props = gajim.socks5queue.get_file_props(self.name, sid)
|
|
||||||
if file_props is not None:
|
|
||||||
self.disconnect_transfer(file_props)
|
|
||||||
file_props['error'] = -3
|
|
||||||
self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
|
|
||||||
|
|
||||||
def _proxy_auth_ok(self, proxy):
|
|
||||||
"""
|
|
||||||
Called after authentication to proxy server
|
|
||||||
"""
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
file_props = self.files_props[proxy['sid']]
|
|
||||||
iq = common.xmpp.Protocol(name = 'iq', to = proxy['initiator'],
|
|
||||||
typ = 'set')
|
|
||||||
auth_id = "au_" + proxy['sid']
|
|
||||||
iq.setID(auth_id)
|
|
||||||
query = iq.setTag('query')
|
|
||||||
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
|
||||||
query.setAttr('sid', proxy['sid'])
|
|
||||||
activate = query.setTag('activate')
|
|
||||||
activate.setData(file_props['proxy_receiver'])
|
|
||||||
iq.setID(auth_id)
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
# register xmpppy handlers for bytestream and FT stanzas
|
|
||||||
def _bytestreamErrorCB(self, con, iq_obj):
|
|
||||||
log.debug('_bytestreamErrorCB')
|
|
||||||
id_ = unicode(iq_obj.getAttr('id'))
|
|
||||||
frm = helpers.get_full_jid_from_iq(iq_obj)
|
|
||||||
query = iq_obj.getTag('query')
|
|
||||||
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:
|
|
||||||
return
|
|
||||||
file_props = self.files_props[id_]
|
|
||||||
file_props['error'] = -4
|
|
||||||
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
def _ft_get_from(self, iq_obj):
|
|
||||||
return helpers.get_full_jid_from_iq(iq_obj)
|
|
||||||
|
|
||||||
def _bytestreamSetCB(self, con, iq_obj):
|
|
||||||
log.debug('_bytestreamSetCB')
|
|
||||||
target = unicode(iq_obj.getAttr('to'))
|
|
||||||
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)
|
|
||||||
streamhosts=[]
|
|
||||||
for item in query.getChildren():
|
|
||||||
if item.getName() == 'streamhost':
|
|
||||||
host_dict={
|
|
||||||
'state': 0,
|
|
||||||
'target': target,
|
|
||||||
'id': id_,
|
|
||||||
'sid': sid,
|
|
||||||
'initiator': self._ft_get_from(iq_obj)
|
|
||||||
}
|
|
||||||
for attr in item.getAttrs():
|
|
||||||
host_dict[attr] = item.getAttr(attr)
|
|
||||||
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 common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
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 common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
def _ResultCB(self, con, iq_obj):
|
|
||||||
log.debug('_ResultCB')
|
|
||||||
# if we want to respect xep-0065 we have to check for proxy
|
|
||||||
# activation result in any result iq
|
|
||||||
real_id = unicode(iq_obj.getAttr('id'))
|
|
||||||
if real_id == self.awaiting_xmpp_ping_id:
|
|
||||||
self.awaiting_xmpp_ping_id = None
|
|
||||||
return
|
|
||||||
if not real_id.startswith('au_'):
|
|
||||||
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 common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
def _ft_get_streamhost_jid_attr(self, streamhost):
|
|
||||||
return helpers.parse_jid(streamhost.getAttr('jid'))
|
|
||||||
|
|
||||||
def _bytestreamResultCB(self, con, iq_obj):
|
|
||||||
log.debug('_bytestreamResultCB')
|
|
||||||
frm = self._ft_get_from(iq_obj)
|
|
||||||
real_id = unicode(iq_obj.getAttr('id'))
|
|
||||||
query = iq_obj.getTag('query')
|
|
||||||
gajim.proxy65_manager.resolve_result(frm, query)
|
|
||||||
|
|
||||||
try:
|
|
||||||
streamhost = query.getTag('streamhost-used')
|
|
||||||
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:
|
|
||||||
raise common.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:
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
if 'proxyhosts' not in file_props:
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
for host in file_props['proxyhosts']:
|
|
||||||
if host['initiator'] == frm and \
|
|
||||||
unicode(query.getAttr('sid')) == file_props['sid']:
|
|
||||||
gajim.socks5queue.activate_proxy(host['idx'])
|
|
||||||
break
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
jid = self._ft_get_streamhost_jid_attr(streamhost)
|
|
||||||
if 'streamhost-used' in file_props and \
|
|
||||||
file_props['streamhost-used'] is True:
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
if real_id.startswith('au_'):
|
|
||||||
if 'stopped' in file and file_props['stopped']:
|
|
||||||
self.remove_transfer(file_props)
|
|
||||||
else:
|
|
||||||
gajim.socks5queue.send_file(file_props, self.name)
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
proxy = None
|
|
||||||
if 'proxyhosts' in file_props:
|
|
||||||
for proxyhost in file_props['proxyhosts']:
|
|
||||||
if proxyhost['jid'] == jid:
|
|
||||||
proxy = proxyhost
|
|
||||||
|
|
||||||
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
|
|
||||||
receiver = socks5.Socks5Receiver(gajim.idlequeue, proxy,
|
|
||||||
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 common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
else:
|
|
||||||
if 'stopped' in file_props and file_props['stopped']:
|
|
||||||
self.remove_transfer(file_props)
|
|
||||||
else:
|
|
||||||
gajim.socks5queue.send_file(file_props, self.name)
|
|
||||||
if 'fast' in file_props:
|
|
||||||
fasts = file_props['fast']
|
|
||||||
if len(fasts) > 0:
|
|
||||||
self._connect_error(frm, fasts[0]['id'], file_props['sid'],
|
|
||||||
code = 406)
|
|
||||||
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
def _siResultCB(self, con, iq_obj):
|
|
||||||
log.debug('_siResultCB')
|
|
||||||
id_ = iq_obj.getAttr('id')
|
|
||||||
if id_ not in self.files_props:
|
|
||||||
# no such jid
|
|
||||||
return
|
|
||||||
file_props = self.files_props[id_]
|
|
||||||
if file_props is None:
|
|
||||||
# file properties for jid is none
|
|
||||||
return
|
|
||||||
if 'request-id' in file_props:
|
|
||||||
# we have already sent streamhosts info
|
|
||||||
return
|
|
||||||
file_props['receiver'] = self._ft_get_from(iq_obj)
|
|
||||||
si = iq_obj.getTag('si')
|
|
||||||
file_tag = si.getTag('file')
|
|
||||||
range_tag = None
|
|
||||||
if file_tag:
|
|
||||||
range_tag = file_tag.getTag('range')
|
|
||||||
if range_tag:
|
|
||||||
offset = range_tag.getAttr('offset')
|
|
||||||
if offset:
|
|
||||||
file_props['offset'] = int(offset)
|
|
||||||
length = range_tag.getAttr('length')
|
|
||||||
if length:
|
|
||||||
file_props['length'] = int(length)
|
|
||||||
feature = si.setTag('feature')
|
|
||||||
if feature.getNamespace() != common.xmpp.NS_FEATURE:
|
|
||||||
return
|
|
||||||
form_tag = feature.getTag('x')
|
|
||||||
form = common.xmpp.DataForm(node=form_tag)
|
|
||||||
field = form.getField('stream-method')
|
|
||||||
if field.getValue() != common.xmpp.NS_BYTESTREAM:
|
|
||||||
return
|
|
||||||
self.send_socks5_info(file_props, fast = True)
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
def _siSetCB(self, con, iq_obj):
|
|
||||||
log.debug('_siSetCB')
|
|
||||||
jid = self._ft_get_from(iq_obj)
|
|
||||||
file_props = {'type': 'r'}
|
|
||||||
file_props['sender'] = jid
|
|
||||||
file_props['request-id'] = unicode(iq_obj.getAttr('id'))
|
|
||||||
si = iq_obj.getTag('si')
|
|
||||||
profile = si.getAttr('profile')
|
|
||||||
mime_type = si.getAttr('mime-type')
|
|
||||||
if profile != common.xmpp.NS_FILE:
|
|
||||||
self.send_file_rejection(file_props, code='400', typ='profile')
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
feature_tag = si.getTag('feature', namespace=common.xmpp.NS_FEATURE)
|
|
||||||
if not feature_tag:
|
|
||||||
return
|
|
||||||
form_tag = feature_tag.getTag('x', namespace=common.xmpp.NS_DATA)
|
|
||||||
if not form_tag:
|
|
||||||
return
|
|
||||||
form = common.dataforms.ExtendForm(node=form_tag)
|
|
||||||
for f in form.iter_fields():
|
|
||||||
if f.var == 'stream-method' and f.type == 'list-single':
|
|
||||||
values = [o[1] for o in f.options]
|
|
||||||
if common.xmpp.NS_BYTESTREAM in values:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.send_file_rejection(file_props, code='400', typ='stream')
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
file_tag = si.getTag('file')
|
|
||||||
for attribute in file_tag.getAttrs():
|
|
||||||
if attribute in ('name', 'size', 'hash', 'date'):
|
|
||||||
val = file_tag.getAttr(attribute)
|
|
||||||
if val is None:
|
|
||||||
continue
|
|
||||||
file_props[attribute] = val
|
|
||||||
file_desc_tag = file_tag.getTag('desc')
|
|
||||||
if file_desc_tag is not None:
|
|
||||||
file_props['desc'] = file_desc_tag.getData()
|
|
||||||
|
|
||||||
if mime_type is not None:
|
|
||||||
file_props['mime-type'] = mime_type
|
|
||||||
our_jid = gajim.get_jid_from_account(self.name)
|
|
||||||
resource = self.server_resource
|
|
||||||
file_props['receiver'] = self._ft_get_our_jid()
|
|
||||||
file_props['sid'] = unicode(si.getAttr('id'))
|
|
||||||
file_props['transfered_size'] = []
|
|
||||||
gajim.socks5queue.add_file_props(self.name, file_props)
|
|
||||||
self.dispatch('FILE_REQUEST', (jid, file_props))
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
def _siErrorCB(self, con, iq_obj):
|
|
||||||
log.debug('_siErrorCB')
|
|
||||||
si = iq_obj.getTag('si')
|
|
||||||
profile = si.getAttr('profile')
|
|
||||||
if profile != common.xmpp.NS_FILE:
|
|
||||||
return
|
|
||||||
id_ = iq_obj.getAttr('id')
|
|
||||||
if id_ not in self.files_props:
|
|
||||||
# no such jid
|
|
||||||
return
|
|
||||||
file_props = self.files_props[id_]
|
|
||||||
if file_props is None:
|
|
||||||
# file properties for jid is none
|
|
||||||
return
|
|
||||||
jid = self._ft_get_from(iq_obj)
|
|
||||||
file_props['error'] = -3
|
|
||||||
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
|
||||||
raise common.xmpp.NodeProcessed
|
|
||||||
|
|
||||||
class ConnectionDisco:
|
class ConnectionDisco:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,648 @@
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
## src/common/connection_handlers.py
|
||||||
|
##
|
||||||
|
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||||
|
## Junglecow J <junglecow AT gmail.com>
|
||||||
|
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
|
||||||
|
## Travis Shirk <travis AT pobox.com>
|
||||||
|
## Nikos Kouremenos <kourem AT gmail.com>
|
||||||
|
## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
|
||||||
|
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
||||||
|
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
|
||||||
|
## Jean-Marie Traissard <jim AT lapin.org>
|
||||||
|
## Stephan Erb <steve-e AT h3c.de>
|
||||||
|
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||||
|
##
|
||||||
|
## This file is part of Gajim.
|
||||||
|
##
|
||||||
|
## Gajim is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 3 only.
|
||||||
|
##
|
||||||
|
## Gajim is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from common import xmpp
|
||||||
|
from common import gajim
|
||||||
|
from common import helpers
|
||||||
|
from common import dataforms
|
||||||
|
|
||||||
|
|
||||||
|
def is_transfer_paused(file_props):
|
||||||
|
if 'stopped' in file_props and file_props['stopped']:
|
||||||
|
return False
|
||||||
|
if 'completed' in file_props and file_props['completed']:
|
||||||
|
return False
|
||||||
|
if 'disconnect_cb' not in file_props:
|
||||||
|
return False
|
||||||
|
return file_props['paused']
|
||||||
|
|
||||||
|
def is_transfer_active(file_props):
|
||||||
|
if 'stopped' in file_props and file_props['stopped']:
|
||||||
|
return False
|
||||||
|
if 'completed' in file_props and file_props['completed']:
|
||||||
|
return False
|
||||||
|
if 'started' not in file_props or not file_props['started']:
|
||||||
|
return False
|
||||||
|
if 'paused' not in file_props:
|
||||||
|
return True
|
||||||
|
return not file_props['paused']
|
||||||
|
|
||||||
|
def is_transfer_stopped(file_props):
|
||||||
|
if 'error' in file_props and file_props['error'] != 0:
|
||||||
|
return True
|
||||||
|
if 'completed' in file_props and file_props['completed']:
|
||||||
|
return True
|
||||||
|
if 'connected' in file_props and file_props['connected'] == False:
|
||||||
|
return True
|
||||||
|
if 'stopped' not in file_props or not file_props['stopped']:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionBytestream:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.files_props = {}
|
||||||
|
|
||||||
|
def send_success_connect_reply(self, streamhost):
|
||||||
|
"""
|
||||||
|
Send reply to the initiator of FT that we made a connection
|
||||||
|
"""
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
if streamhost is None:
|
||||||
|
return None
|
||||||
|
iq = xmpp.Iq(to=streamhost['initiator'], typ='result',
|
||||||
|
frm=streamhost['target'])
|
||||||
|
iq.setAttr('id', streamhost['id'])
|
||||||
|
query = iq.setTag('query', namespace=xmpp.NS_BYTESTREAM)
|
||||||
|
stream_tag = query.setTag('streamhost-used')
|
||||||
|
stream_tag.setAttr('jid', streamhost['jid'])
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def stop_all_active_file_transfers(self, contact):
|
||||||
|
"""
|
||||||
|
Stop all active transfer to or from the given contact
|
||||||
|
"""
|
||||||
|
for file_props in self.files_props.values():
|
||||||
|
if is_transfer_stopped(file_props):
|
||||||
|
continue
|
||||||
|
receiver_jid = unicode(file_props['receiver'])
|
||||||
|
if contact.get_full_jid() == receiver_jid:
|
||||||
|
file_props['error'] = -5
|
||||||
|
self.remove_transfer(file_props)
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props, ''))
|
||||||
|
sender_jid = unicode(file_props['sender'])
|
||||||
|
if contact.get_full_jid() == sender_jid:
|
||||||
|
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():
|
||||||
|
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']
|
||||||
|
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 'streamhosts' in file_props:
|
||||||
|
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'])
|
||||||
|
|
||||||
|
def _send_socks5_info(self, file_props):
|
||||||
|
"""
|
||||||
|
Send iq for the present streamhosts and proxies
|
||||||
|
"""
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
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
|
||||||
|
|
||||||
|
port = gajim.config.get('file_transfers_port')
|
||||||
|
listener = gajim.socks5queue.start_listener(port, sha_str,
|
||||||
|
self._result_socks5_sid, file_props['sid'])
|
||||||
|
if not listener:
|
||||||
|
file_props['error'] = -5
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (unicode(receiver), file_props, ''))
|
||||||
|
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'])
|
||||||
|
query = iq.setTag('query', namespace=xmpp.NS_BYTESTREAM)
|
||||||
|
query.setAttr('mode', 'plain')
|
||||||
|
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)
|
||||||
|
self._add_proxy_streamhosts_to_query(query, file_props)
|
||||||
|
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def _add_streamhosts_to_query(self, query, sender, port, hosts):
|
||||||
|
for host in hosts:
|
||||||
|
streamhost = xmpp.Node(tag='streamhost')
|
||||||
|
query.addChild(node=streamhost)
|
||||||
|
streamhost.setAttr('port', unicode(port))
|
||||||
|
streamhost.setAttr('host', host)
|
||||||
|
streamhost.setAttr('jid', sender)
|
||||||
|
|
||||||
|
def _add_local_ips_as_streamhosts_to_query(self, query, file_props):
|
||||||
|
try:
|
||||||
|
my_ips = [self.peerhost[0]] # The ip we're connected to server with
|
||||||
|
# all IPs from local DNS
|
||||||
|
for addr in socket.getaddrinfo(socket.gethostname(), None):
|
||||||
|
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']
|
||||||
|
port = gajim.config.get('file_transfers_port')
|
||||||
|
self._add_streamhosts_to_query(query, sender, port, my_ips)
|
||||||
|
except socket.gaierror:
|
||||||
|
self.dispatch('ERROR', (_('Wrong host'),
|
||||||
|
_('Invalid local address? :-O')))
|
||||||
|
|
||||||
|
def _add_addiditional_streamhosts_to_query(self, query, file_props):
|
||||||
|
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 = []
|
||||||
|
if ft_add_hosts_to_send:
|
||||||
|
additional_hosts = [e.strip() for e in ft_add_hosts_to_send.split(',')]
|
||||||
|
else:
|
||||||
|
additional_hosts = []
|
||||||
|
self._add_streamhosts_to_query(query, sender, port, additional_hosts)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
for proxyhost in proxyhosts:
|
||||||
|
self._add_streamhosts_to_query(query, proxyhost['jid'],
|
||||||
|
proxyhost['port'], [proxyhost['host']])
|
||||||
|
|
||||||
|
def _get_file_transfer_proxies_from_config(self, file_props):
|
||||||
|
configured_proxies = gajim.config.get_per('accounts', self.name,
|
||||||
|
'file_transfer_proxies')
|
||||||
|
shall_use_proxies = gajim.config.get_per('accounts', self.name,
|
||||||
|
'use_ft_proxies')
|
||||||
|
if shall_use_proxies and configured_proxies:
|
||||||
|
proxyhost_dicts = []
|
||||||
|
proxies = [item.strip() for item in configured_proxies.split(',')]
|
||||||
|
default_proxy = gajim.proxy65_manager.get_default_for_name(self.name)
|
||||||
|
if default_proxy:
|
||||||
|
# add/move default proxy at top of the others
|
||||||
|
if default_proxy in proxies:
|
||||||
|
proxies.remove(default_proxy)
|
||||||
|
proxies.insert(0, default_proxy)
|
||||||
|
|
||||||
|
for proxy in proxies:
|
||||||
|
(host, _port, jid) = gajim.proxy65_manager.get_proxy(proxy, self.name)
|
||||||
|
if not host:
|
||||||
|
continue
|
||||||
|
host_dict = {
|
||||||
|
'state': 0,
|
||||||
|
'target': unicode(file_props['receiver']),
|
||||||
|
'id': file_props['sid'],
|
||||||
|
'sid': file_props['sid'],
|
||||||
|
'initiator': proxy,
|
||||||
|
'host': host,
|
||||||
|
'port': unicode(_port),
|
||||||
|
'jid': jid
|
||||||
|
}
|
||||||
|
proxyhost_dicts.append(host_dict)
|
||||||
|
return proxyhost_dicts
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def send_file_rejection(self, file_props, code='403', typ=None):
|
||||||
|
"""
|
||||||
|
Inform sender that we refuse to download the file
|
||||||
|
|
||||||
|
typ is used when code = '400', in this case typ can be 'strean' for
|
||||||
|
invalid stream or 'profile' for invalid profile
|
||||||
|
"""
|
||||||
|
# user response to ConfirmationDialog may come after we've disconneted
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
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 = ''
|
||||||
|
else:
|
||||||
|
name = 'forbidden'
|
||||||
|
text = 'Offer Declined'
|
||||||
|
err = xmpp.ErrorNode(code=code, typ='cancel', name=name, text=text)
|
||||||
|
if code == '400' and typ in ('stream', 'profile'):
|
||||||
|
if typ == 'stream':
|
||||||
|
err.setTag('no-valid-streams', namespace=xmpp.NS_SI)
|
||||||
|
else:
|
||||||
|
err.setTag('bad-profile', namespace=xmpp.NS_SI)
|
||||||
|
iq.addChild(node=err)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def send_file_approval(self, file_props):
|
||||||
|
"""
|
||||||
|
Send iq, confirming that we want to download the file
|
||||||
|
"""
|
||||||
|
# user response to ConfirmationDialog may come after we've disconneted
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
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']:
|
||||||
|
file_tag = si.setTag('file', namespace=xmpp.NS_FILE)
|
||||||
|
range_tag = file_tag.setTag('range')
|
||||||
|
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')
|
||||||
|
field.setValue(xmpp.NS_BYTESTREAM)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def _ft_get_our_jid(self):
|
||||||
|
our_jid = gajim.get_jid_from_account(self.name)
|
||||||
|
resource = self.server_resource
|
||||||
|
return our_jid + '/' + resource
|
||||||
|
|
||||||
|
def _ft_get_receiver_jid(self, file_props):
|
||||||
|
return file_props['receiver'].jid + '/' + file_props['receiver'].resource
|
||||||
|
|
||||||
|
def send_file_request(self, file_props):
|
||||||
|
"""
|
||||||
|
Send iq for new FT request
|
||||||
|
"""
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
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
|
||||||
|
si = iq.setTag('si', namespace=xmpp.NS_SI)
|
||||||
|
si.setAttr('profile', xmpp.NS_FILE)
|
||||||
|
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'])
|
||||||
|
desc = file_tag.setTag('desc')
|
||||||
|
if 'desc' in file_props:
|
||||||
|
desc.setData(file_props['desc'])
|
||||||
|
file_tag.setTag('range')
|
||||||
|
feature = si.setTag('feature', namespace=xmpp.NS_FEATURE)
|
||||||
|
_feature = xmpp.DataForm(typ='form')
|
||||||
|
feature.addChild(node=_feature)
|
||||||
|
field = _feature.setField('stream-method')
|
||||||
|
field.setAttr('type', 'list-single')
|
||||||
|
field.addOption(xmpp.NS_BYTESTREAM)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def _result_socks5_sid(self, sid, hash_id):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
return
|
||||||
|
|
||||||
|
def _connect_error(self, to, _id, sid, code=404):
|
||||||
|
"""
|
||||||
|
Called when there is an error establishing BS connection, or when
|
||||||
|
connection is rejected
|
||||||
|
"""
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
msg_dict = {
|
||||||
|
404: 'Could not connect to given hosts',
|
||||||
|
405: 'Cancel',
|
||||||
|
406: 'Not acceptable',
|
||||||
|
}
|
||||||
|
msg = msg_dict[code]
|
||||||
|
iq = xmpp.Iq(to=to, typ='error')
|
||||||
|
iq.setAttr('id', _id)
|
||||||
|
err = iq.setTag('error')
|
||||||
|
err.setAttr('code', unicode(code))
|
||||||
|
err.setData(msg)
|
||||||
|
self.connection.send(iq)
|
||||||
|
if code == 404:
|
||||||
|
file_props = gajim.socks5queue.get_file_props(self.name, sid)
|
||||||
|
if file_props is not None:
|
||||||
|
self.disconnect_transfer(file_props)
|
||||||
|
file_props['error'] = -3
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
|
||||||
|
|
||||||
|
def _proxy_auth_ok(self, proxy):
|
||||||
|
"""
|
||||||
|
Called after authentication to proxy server
|
||||||
|
"""
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
file_props = self.files_props[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'])
|
||||||
|
iq.setID(auth_id)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
# register xmpppy handlers for bytestream and FT stanzas
|
||||||
|
def _bytestreamErrorCB(self, con, iq_obj):
|
||||||
|
id_ = unicode(iq_obj.getAttr('id'))
|
||||||
|
frm = helpers.get_full_jid_from_iq(iq_obj)
|
||||||
|
query = iq_obj.getTag('query')
|
||||||
|
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:
|
||||||
|
return
|
||||||
|
file_props = self.files_props[id_]
|
||||||
|
file_props['error'] = -4
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _ft_get_from(self, iq_obj):
|
||||||
|
return helpers.get_full_jid_from_iq(iq_obj)
|
||||||
|
|
||||||
|
def _bytestreamSetCB(self, con, iq_obj):
|
||||||
|
target = unicode(iq_obj.getAttr('to'))
|
||||||
|
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)
|
||||||
|
streamhosts = []
|
||||||
|
for item in query.getChildren():
|
||||||
|
if item.getName() == 'streamhost':
|
||||||
|
host_dict = {
|
||||||
|
'state': 0,
|
||||||
|
'target': target,
|
||||||
|
'id': id_,
|
||||||
|
'sid': sid,
|
||||||
|
'initiator': self._ft_get_from(iq_obj)
|
||||||
|
}
|
||||||
|
for attr in item.getAttrs():
|
||||||
|
host_dict[attr] = item.getAttr(attr)
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def _ResultCB(self, con, iq_obj):
|
||||||
|
# if we want to respect xep-0065 we have to check for proxy
|
||||||
|
# activation result in any result iq
|
||||||
|
real_id = unicode(iq_obj.getAttr('id'))
|
||||||
|
if not real_id.startswith('au_'):
|
||||||
|
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
|
||||||
|
|
||||||
|
def _ft_get_streamhost_jid_attr(self, streamhost):
|
||||||
|
return helpers.parse_jid(streamhost.getAttr('jid'))
|
||||||
|
|
||||||
|
def _bytestreamResultCB(self, con, iq_obj):
|
||||||
|
frm = self._ft_get_from(iq_obj)
|
||||||
|
real_id = unicode(iq_obj.getAttr('id'))
|
||||||
|
query = iq_obj.getTag('query')
|
||||||
|
gajim.proxy65_manager.resolve_result(frm, query)
|
||||||
|
|
||||||
|
try:
|
||||||
|
streamhost = query.getTag('streamhost-used')
|
||||||
|
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:
|
||||||
|
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:
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
if 'proxyhosts' not in file_props:
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
for host in file_props['proxyhosts']:
|
||||||
|
if host['initiator'] == frm and \
|
||||||
|
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:
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
|
||||||
|
if real_id.startswith('au_'):
|
||||||
|
if 'stopped' in file and 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 proxyhost['jid'] == jid:
|
||||||
|
proxy = proxyhost
|
||||||
|
|
||||||
|
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
|
||||||
|
receiver = socks5.Socks5Receiver(gajim.idlequeue, proxy,
|
||||||
|
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']:
|
||||||
|
self.remove_transfer(file_props)
|
||||||
|
else:
|
||||||
|
gajim.socks5queue.send_file(file_props, self.name)
|
||||||
|
if 'fast' in file_props:
|
||||||
|
fasts = file_props['fast']
|
||||||
|
if len(fasts) > 0:
|
||||||
|
self._connect_error(frm, fasts[0]['id'], file_props['sid'],
|
||||||
|
code=406)
|
||||||
|
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _siResultCB(self, con, iq_obj):
|
||||||
|
file_props = self.files_props.get(iq_obj.getAttr('id'))
|
||||||
|
if not file_props:
|
||||||
|
return
|
||||||
|
if 'request-id' in file_props:
|
||||||
|
# we have already sent streamhosts info
|
||||||
|
return
|
||||||
|
file_props['receiver'] = self._ft_get_from(iq_obj)
|
||||||
|
si = iq_obj.getTag('si')
|
||||||
|
file_tag = si.getTag('file')
|
||||||
|
range_tag = None
|
||||||
|
if file_tag:
|
||||||
|
range_tag = file_tag.getTag('range')
|
||||||
|
if range_tag:
|
||||||
|
offset = range_tag.getAttr('offset')
|
||||||
|
if offset:
|
||||||
|
file_props['offset'] = int(offset)
|
||||||
|
length = range_tag.getAttr('length')
|
||||||
|
if length:
|
||||||
|
file_props['length'] = int(length)
|
||||||
|
feature = si.setTag('feature')
|
||||||
|
if feature.getNamespace() != xmpp.NS_FEATURE:
|
||||||
|
return
|
||||||
|
form_tag = feature.getTag('x')
|
||||||
|
form = xmpp.DataForm(node=form_tag)
|
||||||
|
field = form.getField('stream-method')
|
||||||
|
if field.getValue() != xmpp.NS_BYTESTREAM:
|
||||||
|
return
|
||||||
|
self._send_socks5_info(file_props)
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _siSetCB(self, con, iq_obj):
|
||||||
|
jid = self._ft_get_from(iq_obj)
|
||||||
|
file_props = {'type': 'r'}
|
||||||
|
file_props['sender'] = jid
|
||||||
|
file_props['request-id'] = unicode(iq_obj.getAttr('id'))
|
||||||
|
si = iq_obj.getTag('si')
|
||||||
|
profile = si.getAttr('profile')
|
||||||
|
mime_type = si.getAttr('mime-type')
|
||||||
|
if profile != xmpp.NS_FILE:
|
||||||
|
self.send_file_rejection(file_props, code='400', typ='profile')
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
feature_tag = si.getTag('feature', namespace=xmpp.NS_FEATURE)
|
||||||
|
if not feature_tag:
|
||||||
|
return
|
||||||
|
form_tag = feature_tag.getTag('x', namespace=xmpp.NS_DATA)
|
||||||
|
if not form_tag:
|
||||||
|
return
|
||||||
|
form = dataforms.ExtendForm(node=form_tag)
|
||||||
|
for f in form.iter_fields():
|
||||||
|
if f.var == 'stream-method' and f.type == 'list-single':
|
||||||
|
values = [o[1] for o in f.options]
|
||||||
|
if xmpp.NS_BYTESTREAM in values:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.send_file_rejection(file_props, code='400', typ='stream')
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
file_tag = si.getTag('file')
|
||||||
|
for attribute in file_tag.getAttrs():
|
||||||
|
if attribute in ('name', 'size', 'hash', 'date'):
|
||||||
|
val = file_tag.getAttr(attribute)
|
||||||
|
if val is None:
|
||||||
|
continue
|
||||||
|
file_props[attribute] = val
|
||||||
|
file_desc_tag = file_tag.getTag('desc')
|
||||||
|
if file_desc_tag is not None:
|
||||||
|
file_props['desc'] = file_desc_tag.getData()
|
||||||
|
|
||||||
|
if mime_type is not None:
|
||||||
|
file_props['mime-type'] = mime_type
|
||||||
|
file_props['receiver'] = self._ft_get_our_jid()
|
||||||
|
file_props['sid'] = unicode(si.getAttr('id'))
|
||||||
|
file_props['transfered_size'] = []
|
||||||
|
gajim.socks5queue.add_file_props(self.name, file_props)
|
||||||
|
self.dispatch('FILE_REQUEST', (jid, file_props))
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
|
||||||
|
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'))
|
||||||
|
if not file_props:
|
||||||
|
return
|
||||||
|
jid = self._ft_get_from(iq_obj)
|
||||||
|
file_props['error'] = -3
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
||||||
|
raise xmpp.NodeProcessed
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionBytestreamZeroconf(ConnectionBytestream):
|
||||||
|
|
||||||
|
def _ft_get_from(self, iq_obj):
|
||||||
|
return unicode(iq_obj.getFrom())
|
||||||
|
|
||||||
|
def _ft_get_our_jid(self):
|
||||||
|
return gajim.get_jid_from_account(self.name)
|
||||||
|
|
||||||
|
def _ft_get_receiver_jid(self, file_props):
|
||||||
|
return file_props['receiver'].jid
|
||||||
|
|
||||||
|
def _ft_get_streamhost_jid_attr(self, streamhost):
|
||||||
|
return streamhost.getAttr('jid')
|
||||||
|
|
||||||
|
# vim: se ts=3:
|
|
@ -28,7 +28,6 @@ import socket
|
||||||
|
|
||||||
from calendar import timegm
|
from calendar import timegm
|
||||||
|
|
||||||
from common import socks5
|
|
||||||
import common.xmpp
|
import common.xmpp
|
||||||
|
|
||||||
from common import helpers
|
from common import helpers
|
||||||
|
@ -36,6 +35,7 @@ from common import gajim
|
||||||
from common.zeroconf import zeroconf
|
from common.zeroconf import zeroconf
|
||||||
from common.commands import ConnectionCommands
|
from common.commands import ConnectionCommands
|
||||||
from common.pep import ConnectionPEP
|
from common.pep import ConnectionPEP
|
||||||
|
from common.protocol.bytestream import ConnectionBytestreamZeroconf
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
|
log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
|
||||||
|
@ -69,25 +69,12 @@ class ConnectionVcard(connection_handlers.ConnectionVcard):
|
||||||
def send_vcard(self, vcard):
|
def send_vcard(self, vcard):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ConnectionBytestream(connection_handlers.ConnectionBytestream):
|
|
||||||
def _ft_get_from(self, iq_obj):
|
|
||||||
return unicode(iq_obj.getFrom())
|
|
||||||
|
|
||||||
def _ft_get_our_jid(self):
|
class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestreamZeroconf,
|
||||||
return gajim.get_jid_from_account(self.name)
|
|
||||||
|
|
||||||
def _ft_get_receiver_jid(self, file_props):
|
|
||||||
return file_props['receiver'].jid
|
|
||||||
|
|
||||||
def _ft_get_streamhost_jid_attr(self, streamhost):
|
|
||||||
return streamhost.getAttr('jid')
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream,
|
|
||||||
ConnectionCommands, ConnectionPEP, connection_handlers.ConnectionHandlersBase):
|
ConnectionCommands, ConnectionPEP, connection_handlers.ConnectionHandlersBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
ConnectionVcard.__init__(self)
|
ConnectionVcard.__init__(self)
|
||||||
ConnectionBytestream.__init__(self)
|
ConnectionBytestreamZeroconf.__init__(self)
|
||||||
ConnectionCommands.__init__(self)
|
ConnectionCommands.__init__(self)
|
||||||
connection_handlers.ConnectionHandlersBase.__init__(self)
|
connection_handlers.ConnectionHandlersBase.__init__(self)
|
||||||
|
|
||||||
|
@ -186,21 +173,6 @@ ConnectionCommands, ConnectionPEP, connection_handlers.ConnectionHandlersBase):
|
||||||
# (there is no server)
|
# (there is no server)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def remove_transfers_for_contact(self, contact):
|
|
||||||
"""
|
|
||||||
Stop all active transfer for contact
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove_all_transfers(self):
|
|
||||||
"""
|
|
||||||
Stops and removes all active connections from the socks5 pool
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove_transfer(self, file_props, remove_from_list = True):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _DiscoverItemsGetCB(self, con, iq_obj):
|
def _DiscoverItemsGetCB(self, con, iq_obj):
|
||||||
log.debug('DiscoverItemsGetCB')
|
log.debug('DiscoverItemsGetCB')
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ import dialogs
|
||||||
|
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import helpers
|
from common import helpers
|
||||||
|
from common.protocol.bytestream import (is_transfer_active, is_transfer_paused,
|
||||||
|
is_transfer_stopped)
|
||||||
|
|
||||||
C_IMAGE = 0
|
C_IMAGE = 0
|
||||||
C_LABELS = 1
|
C_LABELS = 1
|
||||||
|
@ -55,18 +57,17 @@ class FileTransfersWindow:
|
||||||
self.cleanup_button = self.xml.get_widget('cleanup_button')
|
self.cleanup_button = self.xml.get_widget('cleanup_button')
|
||||||
self.notify_ft_checkbox = self.xml.get_widget(
|
self.notify_ft_checkbox = self.xml.get_widget(
|
||||||
'notify_ft_complete_checkbox')
|
'notify_ft_complete_checkbox')
|
||||||
notify = gajim.config.get('notify_on_file_complete')
|
|
||||||
if notify:
|
shall_notify = gajim.config.get('notify_on_file_complete')
|
||||||
self.notify_ft_checkbox.set_active(True)
|
self.notify_ft_checkbox.set_active(shall_notify
|
||||||
else:
|
)
|
||||||
self.notify_ft_checkbox.set_active(False)
|
|
||||||
self.model = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str, str, int, str)
|
self.model = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str, str, int, str)
|
||||||
self.tree.set_model(self.model)
|
self.tree.set_model(self.model)
|
||||||
col = gtk.TreeViewColumn()
|
col = gtk.TreeViewColumn()
|
||||||
|
|
||||||
render_pixbuf = gtk.CellRendererPixbuf()
|
render_pixbuf = gtk.CellRendererPixbuf()
|
||||||
|
|
||||||
col.pack_start(render_pixbuf, expand = True)
|
col.pack_start(render_pixbuf, expand=True)
|
||||||
render_pixbuf.set_property('xpad', 3)
|
render_pixbuf.set_property('xpad', 3)
|
||||||
render_pixbuf.set_property('ypad', 3)
|
render_pixbuf.set_property('ypad', 3)
|
||||||
render_pixbuf.set_property('yalign', .0)
|
render_pixbuf.set_property('yalign', .0)
|
||||||
|
@ -104,7 +105,7 @@ class FileTransfersWindow:
|
||||||
renderer = gtk.CellRendererProgress()
|
renderer = gtk.CellRendererProgress()
|
||||||
renderer.set_property('yalign', 0.5)
|
renderer.set_property('yalign', 0.5)
|
||||||
renderer.set_property('xalign', 0.5)
|
renderer.set_property('xalign', 0.5)
|
||||||
col.pack_start(renderer, expand = False)
|
col.pack_start(renderer, expand=False)
|
||||||
col.add_attribute(renderer, 'text' , C_PROGRESS)
|
col.add_attribute(renderer, 'text' , C_PROGRESS)
|
||||||
col.add_attribute(renderer, 'value' , C_PERCENT)
|
col.add_attribute(renderer, 'value' , C_PERCENT)
|
||||||
col.set_resizable(True)
|
col.set_resizable(True)
|
||||||
|
@ -137,14 +138,14 @@ class FileTransfersWindow:
|
||||||
"""
|
"""
|
||||||
Find all transfers with peer 'jid' that belong to 'account'
|
Find all transfers with peer 'jid' that belong to 'account'
|
||||||
"""
|
"""
|
||||||
active_transfers = [[],[]] # ['senders', 'receivers']
|
active_transfers = [[], []] # ['senders', 'receivers']
|
||||||
|
|
||||||
# 'account' is the sender
|
# 'account' is the sender
|
||||||
for file_props in self.files_props['s'].values():
|
for file_props in self.files_props['s'].values():
|
||||||
if file_props['tt_account'] == account:
|
if file_props['tt_account'] == account:
|
||||||
receiver_jid = unicode(file_props['receiver']).split('/')[0]
|
receiver_jid = unicode(file_props['receiver']).split('/')[0]
|
||||||
if jid == receiver_jid:
|
if jid == receiver_jid:
|
||||||
if not self.is_transfer_stopped(file_props):
|
if not is_transfer_stopped(file_props):
|
||||||
active_transfers[0].append(file_props)
|
active_transfers[0].append(file_props)
|
||||||
|
|
||||||
# 'account' is the recipient
|
# 'account' is the recipient
|
||||||
|
@ -152,7 +153,7 @@ class FileTransfersWindow:
|
||||||
if file_props['tt_account'] == account:
|
if file_props['tt_account'] == account:
|
||||||
sender_jid = unicode(file_props['sender']).split('/')[0]
|
sender_jid = unicode(file_props['sender']).split('/')[0]
|
||||||
if jid == sender_jid:
|
if jid == sender_jid:
|
||||||
if not self.is_transfer_stopped(file_props):
|
if not is_transfer_stopped(file_props):
|
||||||
active_transfers[1].append(file_props)
|
active_transfers[1].append(file_props)
|
||||||
return active_transfers
|
return active_transfers
|
||||||
|
|
||||||
|
@ -185,8 +186,8 @@ class FileTransfersWindow:
|
||||||
else:
|
else:
|
||||||
#You is a reply of who sent a file
|
#You is a reply of who sent a file
|
||||||
sender = _('You')
|
sender = _('You')
|
||||||
sectext += '\n\t' +_('Sender: %s') % sender
|
sectext += '\n\t' + _('Sender: %s') % sender
|
||||||
sectext += '\n\t' +_('Recipient: ')
|
sectext += '\n\t' + _('Recipient: ')
|
||||||
if file_props['type'] == 's':
|
if file_props['type'] == 's':
|
||||||
jid = unicode(file_props['receiver']).split('/')[0]
|
jid = unicode(file_props['receiver']).split('/')[0]
|
||||||
receiver_name = gajim.contacts.get_first_contact_from_jid(
|
receiver_name = gajim.contacts.get_first_contact_from_jid(
|
||||||
|
@ -197,7 +198,7 @@ class FileTransfersWindow:
|
||||||
recipient = _('You')
|
recipient = _('You')
|
||||||
sectext += recipient
|
sectext += recipient
|
||||||
if file_props['type'] == 'r':
|
if file_props['type'] == 'r':
|
||||||
sectext += '\n\t' +_('Saved in: %s') % file_path
|
sectext += '\n\t' + _('Saved in: %s') % file_path
|
||||||
dialog = dialogs.HigDialog(None, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE,
|
dialog = dialogs.HigDialog(None, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE,
|
||||||
_('File transfer completed'), sectext)
|
_('File transfer completed'), sectext)
|
||||||
if file_props['type'] == 'r':
|
if file_props['type'] == 'r':
|
||||||
|
@ -222,10 +223,10 @@ class FileTransfersWindow:
|
||||||
Show error dialog to the sender saying that transfer has been canceled
|
Show error dialog to the sender saying that transfer has been canceled
|
||||||
"""
|
"""
|
||||||
dialogs.InformationDialog(_('File transfer cancelled'),
|
dialogs.InformationDialog(_('File transfer cancelled'),
|
||||||
_('Connection with peer cannot be established.'))
|
_('Connection with peer cannot be established.'))
|
||||||
self.tree.get_selection().unselect_all()
|
self.tree.get_selection().unselect_all()
|
||||||
|
|
||||||
def show_stopped(self, jid, file_props, error_msg = ''):
|
def show_stopped(self, jid, file_props, error_msg=''):
|
||||||
if file_props['type'] == 'r':
|
if file_props['type'] == 'r':
|
||||||
file_name = os.path.basename(file_props['file-name'])
|
file_name = os.path.basename(file_props['file-name'])
|
||||||
else:
|
else:
|
||||||
|
@ -238,7 +239,6 @@ _('Connection with peer cannot be established.'))
|
||||||
self.tree.get_selection().unselect_all()
|
self.tree.get_selection().unselect_all()
|
||||||
|
|
||||||
def show_file_send_request(self, account, contact):
|
def show_file_send_request(self, account, contact):
|
||||||
|
|
||||||
desc_entry = gtk.Entry()
|
desc_entry = gtk.Entry()
|
||||||
|
|
||||||
def on_ok(widget):
|
def on_ok(widget):
|
||||||
|
@ -259,8 +259,8 @@ _('Connection with peer cannot be established.'))
|
||||||
gtk.RESPONSE_OK,
|
gtk.RESPONSE_OK,
|
||||||
True, # select multiple true as we can select many files to send
|
True, # select multiple true as we can select many files to send
|
||||||
gajim.config.get('last_send_dir'),
|
gajim.config.get('last_send_dir'),
|
||||||
on_response_ok = on_ok,
|
on_response_ok=on_ok,
|
||||||
on_response_cancel = lambda e:dialog.destroy()
|
on_response_cancel=lambda e:dialog.destroy()
|
||||||
)
|
)
|
||||||
|
|
||||||
btn = gtk.Button(_('_Send'))
|
btn = gtk.Button(_('_Send'))
|
||||||
|
@ -270,8 +270,8 @@ _('Connection with peer cannot be established.'))
|
||||||
dialog.set_default_response(gtk.RESPONSE_OK)
|
dialog.set_default_response(gtk.RESPONSE_OK)
|
||||||
|
|
||||||
desc_hbox = gtk.HBox(False, 5)
|
desc_hbox = gtk.HBox(False, 5)
|
||||||
desc_hbox.pack_start(gtk.Label(_('Description: ')),False,False,0)
|
desc_hbox.pack_start(gtk.Label(_('Description: ')), False, False, 0)
|
||||||
desc_hbox.pack_start(desc_entry,True,True,0)
|
desc_hbox.pack_start(desc_entry, True, True, 0)
|
||||||
|
|
||||||
dialog.vbox.pack_start(desc_hbox, False, False, 0)
|
dialog.vbox.pack_start(desc_hbox, False, False, 0)
|
||||||
|
|
||||||
|
@ -376,14 +376,14 @@ _('Connection with peer cannot be established.'))
|
||||||
gajim.connections[account].send_file_rejection(file_props)
|
gajim.connections[account].send_file_rejection(file_props)
|
||||||
|
|
||||||
dialog2 = dialogs.FileChooserDialog(
|
dialog2 = dialogs.FileChooserDialog(
|
||||||
title_text = _('Save File as...'),
|
title_text=_('Save File as...'),
|
||||||
action = gtk.FILE_CHOOSER_ACTION_SAVE,
|
action=gtk.FILE_CHOOSER_ACTION_SAVE,
|
||||||
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||||
gtk.STOCK_SAVE, gtk.RESPONSE_OK),
|
gtk.STOCK_SAVE, gtk.RESPONSE_OK),
|
||||||
default_response = gtk.RESPONSE_OK,
|
default_response=gtk.RESPONSE_OK,
|
||||||
current_folder = gajim.config.get('last_save_dir'),
|
current_folder=gajim.config.get('last_save_dir'),
|
||||||
on_response_ok = (on_ok, account, contact, file_props),
|
on_response_ok=(on_ok, account, contact, file_props),
|
||||||
on_response_cancel = (on_cancel, 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:
|
dialog2.connect('delete-event', lambda widget, event:
|
||||||
|
@ -393,8 +393,8 @@ _('Connection with peer cannot be established.'))
|
||||||
gajim.connections[account].send_file_rejection(file_props)
|
gajim.connections[account].send_file_rejection(file_props)
|
||||||
|
|
||||||
dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
|
dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
|
||||||
on_response_ok = (on_response_ok, account, contact, file_props),
|
on_response_ok=(on_response_ok, account, contact, file_props),
|
||||||
on_response_cancel = (on_response_cancel, account, file_props))
|
on_response_cancel=(on_response_cancel, account, file_props))
|
||||||
dialog.connect('delete-event', lambda widget, event:
|
dialog.connect('delete-event', lambda widget, event:
|
||||||
on_response_cancel(widget, account, file_props))
|
on_response_cancel(widget, account, file_props))
|
||||||
dialog.popup()
|
dialog.popup()
|
||||||
|
@ -446,7 +446,7 @@ _('Connection with peer cannot be established.'))
|
||||||
#Print remaining time in format 00:00:00
|
#Print remaining time in format 00:00:00
|
||||||
#You can change the places of (hours), (minutes), (seconds) -
|
#You can change the places of (hours), (minutes), (seconds) -
|
||||||
#they are not translatable.
|
#they are not translatable.
|
||||||
return _('%(hours)02.d:%(minutes)02.d:%(seconds)02.d') % times
|
return _('%(hours)02.d:%(minutes)02.d:%(seconds)02.d') % times
|
||||||
|
|
||||||
def _get_eta_and_speed(self, full_size, transfered_size, file_props):
|
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:
|
||||||
|
@ -494,7 +494,7 @@ _('Connection with peer cannot be established.'))
|
||||||
del(self.files_props[sid[0]][sid[1:]])
|
del(self.files_props[sid[0]][sid[1:]])
|
||||||
del(file_props)
|
del(file_props)
|
||||||
|
|
||||||
def set_progress(self, typ, sid, transfered_size, iter_ = None):
|
def set_progress(self, typ, sid, transfered_size, iter_=None):
|
||||||
"""
|
"""
|
||||||
Change the progress of a transfer with new transfered size
|
Change the progress of a transfer with new transfered size
|
||||||
"""
|
"""
|
||||||
|
@ -582,7 +582,7 @@ _('Connection with peer cannot be established.'))
|
||||||
if os.path.isfile(file_path):
|
if os.path.isfile(file_path):
|
||||||
stat = os.stat(file_path)
|
stat = os.stat(file_path)
|
||||||
else:
|
else:
|
||||||
dialogs.ErrorDialog(_('Invalid File'), _('File: ') + file_path)
|
dialogs.ErrorDialog(_('Invalid File'), _('File: ') + file_path)
|
||||||
return None
|
return None
|
||||||
if stat[6] == 0:
|
if stat[6] == 0:
|
||||||
dialogs.ErrorDialog(_('Invalid File'),
|
dialogs.ErrorDialog(_('Invalid File'),
|
||||||
|
@ -659,14 +659,13 @@ _('Connection with peer cannot be established.'))
|
||||||
self.tooltip.timeout = gobject.timeout_add(500,
|
self.tooltip.timeout = gobject.timeout_add(500,
|
||||||
self.show_tooltip, widget)
|
self.show_tooltip, widget)
|
||||||
|
|
||||||
def on_transfers_list_leave_notify_event(self, widget = None, event = None):
|
def on_transfers_list_leave_notify_event(self, widget=None, event=None):
|
||||||
if event is not None:
|
if event is not None:
|
||||||
self.height_diff = int(event.y)
|
self.height_diff = int(event.y)
|
||||||
elif self.height_diff is 0:
|
elif self.height_diff is 0:
|
||||||
return
|
return
|
||||||
pointer = self.tree.get_pointer()
|
pointer = self.tree.get_pointer()
|
||||||
props = self.tree.get_path_at_pos(pointer[0],
|
props = self.tree.get_path_at_pos(pointer[0], pointer[1] - self.height_diff)
|
||||||
pointer[1] - self.height_diff)
|
|
||||||
if self.tooltip.timeout > 0:
|
if self.tooltip.timeout > 0:
|
||||||
if not props or self.tooltip.id == props[0]:
|
if not props or self.tooltip.id == props[0]:
|
||||||
self.tooltip.hide_tooltip()
|
self.tooltip.hide_tooltip()
|
||||||
|
@ -675,37 +674,6 @@ _('Connection with peer cannot be established.'))
|
||||||
# try to open the containing folder
|
# try to open the containing folder
|
||||||
self.on_open_folder_menuitem_activate(widget)
|
self.on_open_folder_menuitem_activate(widget)
|
||||||
|
|
||||||
def is_transfer_paused(self, file_props):
|
|
||||||
if 'stopped' in file_props and file_props['stopped']:
|
|
||||||
return False
|
|
||||||
if 'completed' in file_props and file_props['completed']:
|
|
||||||
return False
|
|
||||||
if 'disconnect_cb' not in file_props:
|
|
||||||
return False
|
|
||||||
return file_props['paused']
|
|
||||||
|
|
||||||
def is_transfer_active(self, file_props):
|
|
||||||
if 'stopped' in file_props and file_props['stopped']:
|
|
||||||
return False
|
|
||||||
if 'completed' in file_props and file_props['completed']:
|
|
||||||
return False
|
|
||||||
if 'started' not in file_props or not file_props['started']:
|
|
||||||
return False
|
|
||||||
if 'paused' not in file_props:
|
|
||||||
return True
|
|
||||||
return not file_props['paused']
|
|
||||||
|
|
||||||
def is_transfer_stopped(self, file_props):
|
|
||||||
if 'error' in file_props and file_props['error'] != 0:
|
|
||||||
return True
|
|
||||||
if 'completed' in file_props and file_props['completed']:
|
|
||||||
return True
|
|
||||||
if 'connected' in file_props and file_props['connected'] == False:
|
|
||||||
return True
|
|
||||||
if 'stopped' not in file_props or not file_props['stopped']:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def set_cleanup_sensitivity(self):
|
def set_cleanup_sensitivity(self):
|
||||||
"""
|
"""
|
||||||
Check if there are transfer rows and set cleanup_button sensitive, or
|
Check if there are transfer rows and set cleanup_button sensitive, or
|
||||||
|
@ -743,7 +711,7 @@ _('Connection with peer cannot be established.'))
|
||||||
self.remove_menuitem.set_sensitive(is_row_selected)
|
self.remove_menuitem.set_sensitive(is_row_selected)
|
||||||
self.open_folder_menuitem.set_sensitive(is_row_selected)
|
self.open_folder_menuitem.set_sensitive(is_row_selected)
|
||||||
is_stopped = False
|
is_stopped = False
|
||||||
if self.is_transfer_stopped(file_props):
|
if is_transfer_stopped(file_props):
|
||||||
is_stopped = True
|
is_stopped = True
|
||||||
self.cancel_button.set_sensitive(not is_stopped)
|
self.cancel_button.set_sensitive(not is_stopped)
|
||||||
self.cancel_menuitem.set_sensitive(not is_stopped)
|
self.cancel_menuitem.set_sensitive(not is_stopped)
|
||||||
|
@ -751,11 +719,11 @@ _('Connection with peer cannot be established.'))
|
||||||
# no selection, disable the buttons
|
# no selection, disable the buttons
|
||||||
self.set_all_insensitive()
|
self.set_all_insensitive()
|
||||||
elif not is_stopped:
|
elif not is_stopped:
|
||||||
if self.is_transfer_active(file_props):
|
if is_transfer_active(file_props):
|
||||||
# file transfer is active
|
# file transfer is active
|
||||||
self.toggle_pause_continue(True)
|
self.toggle_pause_continue(True)
|
||||||
self.pause_button.set_sensitive(True)
|
self.pause_button.set_sensitive(True)
|
||||||
elif self.is_transfer_paused(file_props):
|
elif is_transfer_paused(file_props):
|
||||||
# file transfer is paused
|
# file transfer is paused
|
||||||
self.toggle_pause_continue(False)
|
self.toggle_pause_continue(False)
|
||||||
self.pause_button.set_sensitive(True)
|
self.pause_button.set_sensitive(True)
|
||||||
|
@ -798,7 +766,7 @@ _('Connection with peer cannot be established.'))
|
||||||
iter_ = self.model.get_iter((i))
|
iter_ = self.model.get_iter((i))
|
||||||
sid = self.model[iter_][C_SID].decode('utf-8')
|
sid = self.model[iter_][C_SID].decode('utf-8')
|
||||||
file_props = self.files_props[sid[0]][sid[1:]]
|
file_props = self.files_props[sid[0]][sid[1:]]
|
||||||
if self.is_transfer_stopped(file_props):
|
if is_transfer_stopped(file_props):
|
||||||
self._remove_transfer(iter_, sid, file_props)
|
self._remove_transfer(iter_, sid, file_props)
|
||||||
i -= 1
|
i -= 1
|
||||||
self.tree.get_selection().unselect_all()
|
self.tree.get_selection().unselect_all()
|
||||||
|
@ -840,7 +808,7 @@ _('Connection with peer cannot be established.'))
|
||||||
self.set_status(file_props['type'], file_props['sid'], types[sid[0]])
|
self.set_status(file_props['type'], file_props['sid'], types[sid[0]])
|
||||||
self.toggle_pause_continue(True)
|
self.toggle_pause_continue(True)
|
||||||
file_props['continue_cb']()
|
file_props['continue_cb']()
|
||||||
elif self.is_transfer_active(file_props):
|
elif is_transfer_active(file_props):
|
||||||
file_props['paused'] = True
|
file_props['paused'] = True
|
||||||
self.set_status(file_props['type'], file_props['sid'], 'pause')
|
self.set_status(file_props['type'], file_props['sid'], 'pause')
|
||||||
# reset that to compute speed only when we resume
|
# reset that to compute speed only when we resume
|
||||||
|
@ -876,7 +844,7 @@ _('Connection with peer cannot be established.'))
|
||||||
sid = self.model[iter_][C_SID].decode('utf-8')
|
sid = self.model[iter_][C_SID].decode('utf-8')
|
||||||
file_props = self.files_props[sid[0]][sid[1:]]
|
file_props = self.files_props[sid[0]][sid[1:]]
|
||||||
# bounding rectangle of coordinates for the cell within the treeview
|
# bounding rectangle of coordinates for the cell within the treeview
|
||||||
rect = self.tree.get_cell_area(props[0],props[1])
|
rect = self.tree.get_cell_area(props[0], props[1])
|
||||||
# position of the treeview on the screen
|
# position of the treeview on the screen
|
||||||
position = widget.window.get_origin()
|
position = widget.window.get_origin()
|
||||||
self.tooltip.show_tooltip(file_props , rect.height,
|
self.tooltip.show_tooltip(file_props , rect.height,
|
||||||
|
@ -898,10 +866,9 @@ _('Connection with peer cannot be established.'))
|
||||||
|
|
||||||
def show_context_menu(self, event, iter_):
|
def show_context_menu(self, event, iter_):
|
||||||
# change the sensitive propery of the buttons and menuitems
|
# change the sensitive propery of the buttons and menuitems
|
||||||
path = None
|
if iter_:
|
||||||
if iter_ is not None:
|
|
||||||
path = self.model.get_path(iter_)
|
path = self.model.get_path(iter_)
|
||||||
self.set_buttons_sensitive(path, True)
|
self.set_buttons_sensitive(path, True)
|
||||||
|
|
||||||
event_button = gtkgui_helpers.get_possible_button_event(event)
|
event_button = gtkgui_helpers.get_possible_button_event(event)
|
||||||
self.file_transfers_menu.show_all()
|
self.file_transfers_menu.show_all()
|
||||||
|
@ -950,16 +917,16 @@ _('Connection with peer cannot be established.'))
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.tree.get_selection().unselect_all()
|
self.tree.get_selection().unselect_all()
|
||||||
if event.button == 3: # Right click
|
if event.button == 3: # Right click
|
||||||
if path is not None:
|
if path:
|
||||||
self.tree.get_selection().select_path(path)
|
self.tree.get_selection().select_path(path)
|
||||||
iter_ = self.model.get_iter(path)
|
iter_ = self.model.get_iter(path)
|
||||||
self.show_context_menu(event, iter_)
|
self.show_context_menu(event, iter_)
|
||||||
if path is not None:
|
if path:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_open_folder_menuitem_activate(self, widget):
|
def on_open_folder_menuitem_activate(self, widget):
|
||||||
selected = self.tree.get_selection().get_selected()
|
selected = self.tree.get_selection().get_selected()
|
||||||
if selected is None or selected[1] is None:
|
if not selected or not selected[1]:
|
||||||
return
|
return
|
||||||
s_iter = selected[1]
|
s_iter = selected[1]
|
||||||
sid = self.model[s_iter][C_SID].decode('utf-8')
|
sid = self.model[s_iter][C_SID].decode('utf-8')
|
||||||
|
@ -981,7 +948,7 @@ _('Connection with peer cannot be established.'))
|
||||||
|
|
||||||
def on_remove_menuitem_activate(self, widget):
|
def on_remove_menuitem_activate(self, widget):
|
||||||
selected = self.tree.get_selection().get_selected()
|
selected = self.tree.get_selection().get_selected()
|
||||||
if selected is None or selected[1] is None:
|
if not selected or not selected[1]:
|
||||||
return
|
return
|
||||||
s_iter = selected[1]
|
s_iter = selected[1]
|
||||||
sid = self.model[s_iter][C_SID].decode('utf-8')
|
sid = self.model[s_iter][C_SID].decode('utf-8')
|
||||||
|
|
|
@ -440,7 +440,7 @@ class Interface:
|
||||||
|
|
||||||
# TODO: This causes problems when another
|
# TODO: This causes problems when another
|
||||||
# resource signs off!
|
# resource signs off!
|
||||||
conn.remove_transfers_for_contact(contact1)
|
conn.stop_all_active_file_transfers(contact1)
|
||||||
|
|
||||||
# disable encryption, since if any messages are
|
# disable encryption, since if any messages are
|
||||||
# lost they'll be not decryptable (note that
|
# lost they'll be not decryptable (note that
|
||||||
|
|
Loading…
Reference in New Issue