2009-12-10 21:52:32 +01:00
|
|
|
# -*- 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>
|
2012-01-11 22:20:34 +01:00
|
|
|
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
2009-12-10 21:52:32 +01:00
|
|
|
## 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
|
2010-08-16 14:33:50 +02:00
|
|
|
import base64
|
|
|
|
import gobject
|
|
|
|
import time
|
2012-06-14 18:27:23 +02:00
|
|
|
import pdb
|
2009-12-10 21:52:32 +01:00
|
|
|
|
2009-12-10 22:09:37 +01:00
|
|
|
from common import xmpp
|
2009-12-10 21:52:32 +01:00
|
|
|
from common import gajim
|
2009-12-10 22:09:37 +01:00
|
|
|
from common import helpers
|
|
|
|
from common import dataforms
|
2010-12-08 21:17:08 +01:00
|
|
|
from common import ged
|
2010-08-10 14:34:46 +02:00
|
|
|
from common import jingle_xtls
|
2012-06-14 18:27:23 +02:00
|
|
|
from common.file_props import FilesProp
|
2009-12-19 10:26:03 +01:00
|
|
|
from common.socks5 import Socks5Receiver
|
|
|
|
|
2010-06-14 14:41:24 +02:00
|
|
|
import logging
|
2010-08-16 14:33:50 +02:00
|
|
|
log = logging.getLogger('gajim.c.p.bytestream')
|
2009-12-10 21:52:32 +01:00
|
|
|
|
2009-12-10 23:53:39 +01:00
|
|
|
def is_transfer_paused(file_props):
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.stopped:
|
2010-02-08 15:08:40 +01:00
|
|
|
return False
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.completed:
|
2010-02-08 15:08:40 +01:00
|
|
|
return False
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.disconnect_cb:
|
2010-02-08 15:08:40 +01:00
|
|
|
return False
|
2012-06-14 18:27:23 +02:00
|
|
|
return file_props.paused
|
2009-12-10 23:53:39 +01:00
|
|
|
|
|
|
|
def is_transfer_active(file_props):
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.stopped:
|
2010-02-08 15:08:40 +01:00
|
|
|
return False
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.completed:
|
2010-02-08 15:08:40 +01:00
|
|
|
return False
|
2012-06-14 18:27:23 +02:00
|
|
|
if not file_props.started:
|
2010-02-08 15:08:40 +01:00
|
|
|
return False
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.paused:
|
2010-02-08 15:08:40 +01:00
|
|
|
return True
|
2012-06-14 18:27:23 +02:00
|
|
|
return not file_props.paused
|
2009-12-10 23:53:39 +01:00
|
|
|
|
|
|
|
def is_transfer_stopped(file_props):
|
2012-06-14 18:27:23 +02:00
|
|
|
if not file_props:
|
|
|
|
return True
|
|
|
|
if file_props.error != 0:
|
2010-02-08 15:08:40 +01:00
|
|
|
return True
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.completed:
|
2010-02-08 15:08:40 +01:00
|
|
|
return True
|
2012-06-14 18:27:23 +02:00
|
|
|
if not file_props.connected:
|
2010-02-08 15:08:40 +01:00
|
|
|
return True
|
2012-06-14 18:27:23 +02:00
|
|
|
if not file_props.stopped:
|
2010-02-08 15:08:40 +01:00
|
|
|
return False
|
|
|
|
return True
|
2009-12-10 23:53:39 +01:00
|
|
|
|
|
|
|
|
2009-12-10 21:52:32 +01:00
|
|
|
class ConnectionBytestream:
|
2009-12-10 22:09:37 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def __init__(self):
|
2010-12-08 21:17:08 +01:00
|
|
|
gajim.ged.register_event_handler('file-request-received', ged.GUI1,
|
|
|
|
self._nec_file_request_received)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2011-01-06 16:50:38 +01:00
|
|
|
def cleanup(self):
|
|
|
|
gajim.ged.remove_event_handler('file-request-received', ged.GUI1,
|
|
|
|
self._nec_file_request_received)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2010-02-22 16:29:01 +01:00
|
|
|
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):
|
2012-06-14 18:27:23 +02:00
|
|
|
return file_props.receiver.jid + '/' + file_props.receiver.resource
|
2010-02-22 16:29:01 +01:00
|
|
|
|
|
|
|
def _ft_get_from(self, iq_obj):
|
|
|
|
return helpers.get_full_jid_from_iq(iq_obj)
|
|
|
|
|
|
|
|
def _ft_get_streamhost_jid_attr(self, streamhost):
|
|
|
|
return helpers.parse_jid(streamhost.getAttr('jid'))
|
|
|
|
|
|
|
|
def send_file_request(self, file_props):
|
|
|
|
"""
|
|
|
|
Send iq for new FT request
|
|
|
|
"""
|
|
|
|
if not self.connection or self.connected < 2:
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.sender = self._ft_get_our_jid()
|
2010-02-22 16:29:01 +01:00
|
|
|
fjid = self._ft_get_receiver_jid(file_props)
|
|
|
|
iq = xmpp.Iq(to=fjid, typ='set')
|
2012-06-14 18:27:23 +02:00
|
|
|
iq.setID(file_props.sid)
|
2010-02-22 16:29:01 +01:00
|
|
|
si = iq.setTag('si', namespace=xmpp.NS_SI)
|
|
|
|
si.setAttr('profile', xmpp.NS_FILE)
|
2012-06-14 18:27:23 +02:00
|
|
|
si.setAttr('id', file_props.sid)
|
2010-02-22 16:29:01 +01:00
|
|
|
file_tag = si.setTag('file', namespace=xmpp.NS_FILE)
|
2012-06-14 18:27:23 +02:00
|
|
|
file_tag.setAttr('name', file_props.name)
|
|
|
|
file_tag.setAttr('size', file_props.size)
|
2010-02-22 16:29:01 +01:00
|
|
|
desc = file_tag.setTag('desc')
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.desc:
|
|
|
|
desc.setData(file_props.desc)
|
2010-02-22 16:29:01 +01:00
|
|
|
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')
|
2011-07-05 20:05:16 +02:00
|
|
|
field.addOption(xmpp.NS_BYTESTREAM)
|
2010-08-16 14:33:50 +02:00
|
|
|
field.addOption(xmpp.NS_IBB)
|
2010-02-22 16:29:01 +01:00
|
|
|
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
|
2010-06-14 14:41:24 +02:00
|
|
|
|
2010-08-16 17:32:23 +02:00
|
|
|
# file transfer initiated by a jingle session
|
2010-06-14 14:41:24 +02:00
|
|
|
log.info("send_file_approval: jingle session accept")
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.session_type == 'jingle':
|
|
|
|
session = self.get_jingle_session(file_props.sender,
|
|
|
|
file_props.session_sid)
|
2010-06-14 14:41:24 +02:00
|
|
|
if not session:
|
|
|
|
return
|
2010-08-26 11:09:35 +02:00
|
|
|
content = None
|
|
|
|
for c in session.contents.values():
|
2012-06-14 18:27:23 +02:00
|
|
|
if c.transport.sid == file_props.sid:
|
2010-08-26 11:09:35 +02:00
|
|
|
content = c
|
|
|
|
break
|
|
|
|
if not content:
|
|
|
|
return
|
2010-06-14 14:41:24 +02:00
|
|
|
if not session.accepted:
|
2010-08-26 11:09:35 +02:00
|
|
|
if session.get_content('file', content.name).use_security:
|
|
|
|
id_ = jingle_xtls.send_cert_request(self,
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.sender)
|
2010-08-26 11:09:35 +02:00
|
|
|
jingle_xtls.key_exchange_pend(id_, content)
|
2010-08-10 18:50:14 +02:00
|
|
|
return
|
2010-06-14 14:41:24 +02:00
|
|
|
session.approve_session()
|
2010-08-26 10:36:58 +02:00
|
|
|
|
2010-08-26 11:09:35 +02:00
|
|
|
session.approve_content('file', content.name)
|
2010-07-05 07:46:53 +02:00
|
|
|
return
|
2010-06-14 14:41:24 +02:00
|
|
|
|
2012-06-14 18:27:23 +02:00
|
|
|
iq = xmpp.Iq(to=unicode(file_props.sender), typ='result')
|
|
|
|
iq.setAttr('id', file_props.request_id)
|
2010-02-22 16:29:01 +01:00
|
|
|
si = iq.setTag('si', namespace=xmpp.NS_SI)
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.offset:
|
2010-02-22 16:29:01 +01:00
|
|
|
file_tag = si.setTag('file', namespace=xmpp.NS_FILE)
|
|
|
|
range_tag = file_tag.setTag('range')
|
2012-06-14 18:27:23 +02:00
|
|
|
range_tag.setAttr('offset', file_props.offset)
|
2010-02-22 16:29:01 +01:00
|
|
|
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')
|
2012-06-14 18:27:23 +02:00
|
|
|
if xmpp.NS_BYTESTREAM in file_props.stream_methods:
|
2010-08-16 14:33:50 +02:00
|
|
|
field.setValue(xmpp.NS_BYTESTREAM)
|
|
|
|
else:
|
|
|
|
field.setValue(xmpp.NS_IBB)
|
2010-02-22 16:29:01 +01:00
|
|
|
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
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.session_type == 'jingle':
|
|
|
|
jingle = self._sessions[file_props.session_sid]
|
2012-03-19 20:52:33 +01:00
|
|
|
jingle.cancel_session()
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
iq = xmpp.Iq(to=unicode(file_props.sender), typ='error')
|
|
|
|
iq.setAttr('id', file_props.request_id)
|
2010-02-22 16:29:01 +01:00
|
|
|
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 _siResultCB(self, con, iq_obj):
|
2012-06-14 18:43:47 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.name, iq_obj.getAttr('id'))
|
2010-02-22 16:29:01 +01:00
|
|
|
if not file_props:
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.request_id:
|
2010-02-22 16:29:01 +01:00
|
|
|
# we have already sent streamhosts info
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.receiver = self._ft_get_from(iq_obj)
|
2010-02-22 16:29:01 +01:00
|
|
|
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:
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.offset = int(offset)
|
2010-02-22 16:29:01 +01:00
|
|
|
length = range_tag.getAttr('length')
|
|
|
|
if length:
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.length = int(length)
|
2010-02-22 16:29:01 +01:00
|
|
|
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')
|
2010-08-16 14:33:50 +02:00
|
|
|
if field.getValue() == xmpp.NS_BYTESTREAM:
|
|
|
|
self._send_socks5_info(file_props)
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
if field.getValue() == xmpp.NS_IBB:
|
2012-06-14 18:27:23 +02:00
|
|
|
sid = file_props.sid
|
|
|
|
fp = open(file_props.file_name, 'r')
|
|
|
|
self.OpenStream(sid, file_props.receiver, fp)
|
2010-08-16 14:33:50 +02:00
|
|
|
raise xmpp.NodeProcessed
|
2010-02-22 16:29:01 +01:00
|
|
|
|
|
|
|
def _siSetCB(self, con, iq_obj):
|
2011-07-02 00:47:33 +02:00
|
|
|
from common.connection_handlers_events import FileRequestReceivedEvent
|
2010-12-08 21:17:08 +01:00
|
|
|
gajim.nec.push_incoming_event(FileRequestReceivedEvent(None, conn=self,
|
|
|
|
stanza=iq_obj))
|
2010-02-22 16:29:01 +01:00
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
2010-12-08 21:17:08 +01:00
|
|
|
def _nec_file_request_received(self, obj):
|
2012-06-14 18:27:23 +02:00
|
|
|
pass
|
2010-12-08 21:17:08 +01:00
|
|
|
|
2010-02-22 16:29:01 +01:00
|
|
|
def _siErrorCB(self, con, iq_obj):
|
|
|
|
si = iq_obj.getTag('si')
|
|
|
|
profile = si.getAttr('profile')
|
|
|
|
if profile != xmpp.NS_FILE:
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props = FilesProp.getFileProp(con.name, iq_obj.getAttr('id'))
|
2010-02-22 16:29:01 +01:00
|
|
|
if not file_props:
|
|
|
|
return
|
|
|
|
jid = self._ft_get_from(iq_obj)
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.error = -3
|
2011-07-02 00:47:33 +02:00
|
|
|
from common.connection_handlers_events import FileRequestErrorEvent
|
2010-12-08 21:17:08 +01:00
|
|
|
gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=self,
|
|
|
|
jid=jid, file_props=file_props, error_msg=''))
|
2010-02-22 16:29:01 +01:00
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
class ConnectionSocks5Bytestream(ConnectionBytestream):
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
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
|
|
|
|
"""
|
2012-06-14 18:27:23 +02:00
|
|
|
for file_props in FilesProp.getAllFileProp():
|
2010-02-08 15:08:40 +01:00
|
|
|
if is_transfer_stopped(file_props):
|
|
|
|
continue
|
2012-06-14 18:27:23 +02:00
|
|
|
receiver_jid = unicode(file_props.receiver)
|
2010-02-08 15:08:40 +01:00
|
|
|
if contact.get_full_jid() == receiver_jid:
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.error = -5
|
2010-02-08 15:08:40 +01:00
|
|
|
self.remove_transfer(file_props)
|
2011-07-02 00:47:33 +02:00
|
|
|
from common.connection_handlers_events import \
|
|
|
|
FileRequestErrorEvent
|
2010-12-08 21:17:08 +01:00
|
|
|
gajim.nec.push_incoming_event(FileRequestErrorEvent(None,
|
|
|
|
conn=self, jid=contact.jid, file_props=file_props,
|
|
|
|
error_msg=''))
|
2012-06-14 18:27:23 +02:00
|
|
|
sender_jid = unicode(file_props.sender)
|
2010-02-08 15:08:40 +01:00
|
|
|
if contact.get_full_jid() == sender_jid:
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.error = -3
|
2010-02-08 15:08:40 +01:00
|
|
|
self.remove_transfer(file_props)
|
|
|
|
|
|
|
|
def remove_all_transfers(self):
|
|
|
|
"""
|
|
|
|
Stop and remove all active connections from the socks5 pool
|
|
|
|
"""
|
2012-06-14 18:27:23 +02:00
|
|
|
for file_props in FilesProp.getAllFileProp():
|
2010-02-08 15:08:40 +01:00
|
|
|
self.remove_transfer(file_props, remove_from_list=False)
|
|
|
|
|
|
|
|
def remove_transfer(self, file_props, remove_from_list=True):
|
|
|
|
if file_props is None:
|
|
|
|
return
|
|
|
|
self.disconnect_transfer(file_props)
|
2012-06-14 18:27:23 +02:00
|
|
|
sid = file_props.sid
|
2010-02-08 15:08:40 +01:00
|
|
|
gajim.socks5queue.remove_file_props(self.name, sid)
|
|
|
|
|
|
|
|
|
|
|
|
def disconnect_transfer(self, file_props):
|
|
|
|
if file_props is None:
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.hash_:
|
|
|
|
gajim.socks5queue.remove_sender(file_props.hash_)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.streamhosts:
|
|
|
|
for host in file_props.streamhosts:
|
2010-02-08 15:08:40 +01:00
|
|
|
if 'idx' in host and host['idx'] > 0:
|
|
|
|
gajim.socks5queue.remove_receiver(host['idx'])
|
|
|
|
gajim.socks5queue.remove_sender(host['idx'])
|
2010-08-25 12:05:14 +02:00
|
|
|
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.direction:
|
2010-08-16 14:33:50 +02:00
|
|
|
# it's a IBB
|
2012-06-14 18:27:23 +02:00
|
|
|
FilesProp.deleteFileProp(file_props)
|
|
|
|
del(file_props)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
def _send_socks5_info(self, file_props):
|
|
|
|
"""
|
|
|
|
Send iq for the present streamhosts and proxies
|
|
|
|
"""
|
|
|
|
if not self.connection or self.connected < 2:
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
receiver = file_props.receiver
|
|
|
|
sender = file_props.sender
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2012-06-14 18:27:23 +02:00
|
|
|
sha_str = helpers.get_auth_sha(file_props.sid, sender, receiver)
|
|
|
|
file_props.sha_str = sha_str
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
port = gajim.config.get('file_transfers_port')
|
|
|
|
listener = gajim.socks5queue.start_listener(port, sha_str,
|
2011-10-29 06:09:45 +02:00
|
|
|
self._result_socks5_sid, file_props)
|
2010-02-08 15:08:40 +01:00
|
|
|
if not listener:
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.error = -5
|
2011-07-02 00:47:33 +02:00
|
|
|
from common.connection_handlers_events import FileRequestErrorEvent
|
2010-12-08 21:17:08 +01:00
|
|
|
gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=self,
|
|
|
|
jid=unicode(receiver), file_props=file_props, error_msg=''))
|
2012-06-14 18:27:23 +02:00
|
|
|
self._connect_error(unicode(receiver), file_props.sid,
|
|
|
|
file_props.sid, code=406)
|
2010-02-08 15:08:40 +01:00
|
|
|
else:
|
|
|
|
iq = xmpp.Iq(to=unicode(receiver), typ='set')
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.request_id = 'id_' + file_props.sid
|
|
|
|
iq.setID(file_props.request_id)
|
2010-02-08 15:08:40 +01:00
|
|
|
query = iq.setTag('query', namespace=xmpp.NS_BYTESTREAM)
|
2012-06-14 18:27:23 +02:00
|
|
|
query.setAttr('sid', file_props.sid)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
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)
|
2011-08-24 11:04:31 +02:00
|
|
|
self._add_upnp_igd_as_streamhost_to_query(query, file_props, iq)
|
|
|
|
# Upnp-igd is ascynchronous, so it will send the iq itself
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
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):
|
2011-11-06 21:12:27 +01:00
|
|
|
if not gajim.config.get_per('accounts', self.name, 'ft_send_local_ips'):
|
|
|
|
return
|
2010-02-08 15:08:40 +01:00
|
|
|
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])
|
|
|
|
|
2012-06-14 18:27:23 +02:00
|
|
|
sender = file_props.sender
|
2010-02-08 15:08:40 +01:00
|
|
|
port = gajim.config.get('file_transfers_port')
|
|
|
|
self._add_streamhosts_to_query(query, sender, port, my_ips)
|
|
|
|
except socket.gaierror:
|
2011-12-29 11:39:02 +01:00
|
|
|
from common.connection_handlers_events import InformationEvent
|
2011-11-22 19:25:15 +01:00
|
|
|
gajim.nec.push_incoming_event(InformationEvent(None, conn=self,
|
|
|
|
level='error', pri_txt=_('Wrong host'),
|
|
|
|
sec_txt=_('Invalid local address? :-O')))
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
def _add_addiditional_streamhosts_to_query(self, query, file_props):
|
2012-06-14 18:27:23 +02:00
|
|
|
sender = file_props.sender
|
2010-02-08 15:08:40 +01:00
|
|
|
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)
|
|
|
|
|
2011-08-24 11:04:31 +02:00
|
|
|
def _add_upnp_igd_as_streamhost_to_query(self, query, file_props, iq):
|
|
|
|
if not gajim.HAVE_UPNP_IGD:
|
|
|
|
self.connection.send(iq)
|
|
|
|
return
|
|
|
|
|
|
|
|
def ip_is_local(ip):
|
|
|
|
if '.' not in ip:
|
|
|
|
# it's an IPv6
|
|
|
|
return True
|
|
|
|
ip_s = ip.split('.')
|
|
|
|
ip_l = long(ip_s[0])<<24 | long(ip_s[1])<<16 | long(ip_s[2])<<8 | \
|
|
|
|
long(ip_s[3])
|
|
|
|
# 10/8
|
|
|
|
if ip_l & (255<<24) == 10<<24:
|
|
|
|
return True
|
|
|
|
# 172.16/12
|
|
|
|
if ip_l & (255<<24 | 240<<16) == (172<<24 | 16<<16):
|
|
|
|
return True
|
|
|
|
# 192.168
|
|
|
|
if ip_l & (255<<24 | 255<<16) == (192<<24 | 168<<16):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
my_ip = self.peerhost[0]
|
|
|
|
|
|
|
|
if not ip_is_local(my_ip):
|
|
|
|
self.connection.send(iq)
|
|
|
|
return
|
|
|
|
|
|
|
|
self.no_gupnp_reply_id = 0
|
|
|
|
|
|
|
|
def cleanup_gupnp():
|
|
|
|
if self.no_gupnp_reply_id:
|
|
|
|
gobject.source_remove(self.no_gupnp_reply_id)
|
|
|
|
self.no_gupnp_reply_id = 0
|
|
|
|
gajim.gupnp_igd.disconnect(self.ok_id)
|
|
|
|
gajim.gupnp_igd.disconnect(self.fail_id)
|
|
|
|
|
|
|
|
def ok(s, proto, ext_ip, re, ext_port, local_ip, local_port, desc):
|
|
|
|
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'):
|
2012-06-14 18:27:23 +02:00
|
|
|
sender = file_props.sender
|
|
|
|
receiver = file_props.receiver
|
|
|
|
sha_str = helpers.get_auth_sha(file_props.sid, sender,
|
2011-08-24 11:04:31 +02:00
|
|
|
receiver)
|
|
|
|
listener = gajim.socks5queue.start_listener(local_port, sha_str,
|
2012-06-14 18:27:23 +02:00
|
|
|
self._result_socks5_sid, file_props.sid)
|
2011-08-24 11:04:31 +02:00
|
|
|
if listener:
|
|
|
|
self._add_streamhosts_to_query(query, sender, ext_port,
|
|
|
|
[ext_ip])
|
|
|
|
self.connection.send(iq)
|
|
|
|
cleanup_gupnp()
|
|
|
|
|
|
|
|
def fail(s, error, proto, ext_ip, local_ip, local_port, desc):
|
|
|
|
log.debug('Got GUPnP-IGD error : %s', str(error))
|
|
|
|
self.connection.send(iq)
|
|
|
|
cleanup_gupnp()
|
|
|
|
|
|
|
|
def no_upnp_reply():
|
|
|
|
log.debug('Got not GUPnP-IGD answer')
|
|
|
|
# stop trying to use it
|
|
|
|
gajim.HAVE_UPNP_IGD = False
|
|
|
|
self.no_gupnp_reply_id = 0
|
|
|
|
self.connection.send(iq)
|
|
|
|
cleanup_gupnp()
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
self.ok_id = gajim.gupnp_igd.connect('mapped-external-port', ok)
|
|
|
|
self.fail_id = gajim.gupnp_igd.connect('error-mapping-port', fail)
|
|
|
|
|
|
|
|
port = gajim.config.get('file_transfers_port')
|
|
|
|
self.no_gupnp_reply_id = gobject.timeout_add_seconds(10, no_upnp_reply)
|
|
|
|
gajim.gupnp_igd.add_port('TCP', 0, my_ip, port, 3600,
|
|
|
|
'Gajim file transfer')
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def _add_proxy_streamhosts_to_query(self, query, file_props):
|
|
|
|
proxyhosts = self._get_file_transfer_proxies_from_config(file_props)
|
|
|
|
if proxyhosts:
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.proxy_receiver = unicode(file_props.receiver)
|
|
|
|
file_props.proxy_sender = unicode(file_props.sender)
|
|
|
|
file_props.proxyhosts = proxyhosts
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
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,
|
2012-06-14 18:27:23 +02:00
|
|
|
'target': unicode(file_props.receiver),
|
|
|
|
'id': file_props.sid,
|
|
|
|
'sid': file_props.sid,
|
2010-02-08 15:08:40 +01:00
|
|
|
'initiator': proxy,
|
|
|
|
'host': host,
|
|
|
|
'port': unicode(_port),
|
|
|
|
'jid': jid
|
|
|
|
}
|
|
|
|
proxyhost_dicts.append(host_dict)
|
|
|
|
return proxyhost_dicts
|
|
|
|
else:
|
|
|
|
return []
|
|
|
|
|
|
|
|
def _result_socks5_sid(self, sid, hash_id):
|
|
|
|
"""
|
|
|
|
Store the result of SHA message from auth
|
|
|
|
"""
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props = FilesProp.getFilePropBySid(sid)
|
|
|
|
file_props.hash_ = hash_id
|
2010-02-08 15:08:40 +01:00
|
|
|
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:
|
2012-06-14 18:34:07 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.name, sid)
|
2010-02-08 15:08:40 +01:00
|
|
|
if file_props is not None:
|
|
|
|
self.disconnect_transfer(file_props)
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.error = -3
|
2011-07-02 00:47:33 +02:00
|
|
|
from common.connection_handlers_events import \
|
|
|
|
FileRequestErrorEvent
|
2010-12-08 21:17:08 +01:00
|
|
|
gajim.nec.push_incoming_event(FileRequestErrorEvent(None,
|
|
|
|
conn=self, jid=to, file_props=file_props, error_msg=msg))
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
def _proxy_auth_ok(self, proxy):
|
|
|
|
"""
|
|
|
|
Called after authentication to proxy server
|
|
|
|
"""
|
|
|
|
if not self.connection or self.connected < 2:
|
|
|
|
return
|
2012-06-14 18:34:07 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.connection, proxy['sid'])
|
2010-02-08 15:08:40 +01:00
|
|
|
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')
|
2012-06-14 18:27:23 +02:00
|
|
|
activate.setData(file_props.proxy_receiver)
|
2010-02-08 15:08:40 +01:00
|
|
|
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:]
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props = FilesProp.getFilePropBySid(id_)
|
|
|
|
if not file_props:
|
2010-02-08 15:08:40 +01:00
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.error = -4
|
2011-07-02 00:47:33 +02:00
|
|
|
from common.connection_handlers_events import FileRequestErrorEvent
|
2010-12-08 21:17:08 +01:00
|
|
|
gajim.nec.push_incoming_event(FileRequestErrorEvent(None, conn=self,
|
|
|
|
jid=jid, file_props=file_props, error_msg=''))
|
2010-02-08 15:08:40 +01:00
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
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'))
|
2012-06-14 18:34:07 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.name, sid)
|
2010-02-08 15:08:40 +01:00
|
|
|
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)
|
2011-09-23 19:01:42 +02:00
|
|
|
if 'host' not in host_dict:
|
|
|
|
continue
|
|
|
|
if 'jid' not in host_dict:
|
|
|
|
continue
|
|
|
|
if 'port' not in host_dict:
|
|
|
|
continue
|
2010-02-08 15:08:40 +01:00
|
|
|
streamhosts.append(host_dict)
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props = FilesProp.getFilePropBySid(sid)
|
|
|
|
if file_props is not None:
|
|
|
|
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)
|
2012-06-20 23:16:18 +02:00
|
|
|
raise xmpp.NodeProcessed
|
2012-06-14 18:27:23 +02:00
|
|
|
else:
|
2011-10-23 13:48:00 +02:00
|
|
|
log.warn('Gajim got streamhosts for unknown transfer. Ignoring it.')
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.streamhosts = streamhosts
|
|
|
|
if file_props.type_ == 'r':
|
2010-02-08 15:08:40 +01:00
|
|
|
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:]
|
2012-06-14 18:27:23 +02:00
|
|
|
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
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
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:]
|
2012-06-20 23:16:18 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.name, id_)
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props is None:
|
2010-02-08 15:08:40 +01:00
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
if streamhost is None:
|
|
|
|
# proxy approves the activate query
|
|
|
|
if real_id.startswith('au_'):
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.streamhost_used is False:
|
2010-02-08 15:08:40 +01:00
|
|
|
raise xmpp.NodeProcessed
|
2012-06-14 18:27:23 +02:00
|
|
|
if not file_props.proxyhosts:
|
2010-02-08 15:08:40 +01:00
|
|
|
raise xmpp.NodeProcessed
|
2012-06-14 18:27:23 +02:00
|
|
|
for host in file_props.proxyhosts:
|
2010-02-08 15:08:40 +01:00
|
|
|
if host['initiator'] == frm and \
|
2012-06-14 18:27:23 +02:00
|
|
|
unicode(query.getAttr('sid')) == file_props.sid:
|
2010-02-08 15:08:40 +01:00
|
|
|
gajim.socks5queue.activate_proxy(host['idx'])
|
|
|
|
break
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
jid = self._ft_get_streamhost_jid_attr(streamhost)
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.streamhost_used is True:
|
2010-02-08 15:08:40 +01:00
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
if real_id.startswith('au_'):
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.stopped:
|
2010-02-08 15:08:40 +01:00
|
|
|
self.remove_transfer(file_props)
|
|
|
|
else:
|
|
|
|
gajim.socks5queue.send_file(file_props, self.name)
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
proxy = None
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.proxyhosts:
|
|
|
|
for proxyhost in file_props.proxyhosts:
|
2010-02-08 15:08:40 +01:00
|
|
|
if proxyhost['jid'] == jid:
|
|
|
|
proxy = proxyhost
|
|
|
|
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.stopped:
|
2010-07-06 22:11:43 +02:00
|
|
|
self.remove_transfer(file_props)
|
|
|
|
raise xmpp.NodeProcessed
|
2010-02-08 15:08:40 +01:00
|
|
|
if proxy is not None:
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.streamhost_used = True
|
|
|
|
file_props.streamhosts.append(proxy)
|
|
|
|
file_props.is_a_proxy = True
|
2010-02-08 15:08:40 +01:00
|
|
|
receiver = Socks5Receiver(gajim.idlequeue, proxy,
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.sid, file_props)
|
2010-02-08 15:08:40 +01:00
|
|
|
gajim.socks5queue.add_receiver(self.name, receiver)
|
|
|
|
proxy['idx'] = receiver.queue_idx
|
|
|
|
gajim.socks5queue.on_success = self._proxy_auth_ok
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
else:
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.stopped:
|
2010-02-08 15:08:40 +01:00
|
|
|
self.remove_transfer(file_props)
|
|
|
|
else:
|
2012-06-20 23:58:23 +02:00
|
|
|
gajim.socks5queue.send_file(file_props, self.name, 'server')
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
2010-08-16 14:33:50 +02:00
|
|
|
|
|
|
|
class ConnectionIBBytestream(ConnectionBytestream):
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
ConnectionBytestream.__init__(self)
|
|
|
|
self._streams = {}
|
2010-08-17 15:42:34 +02:00
|
|
|
self.last_sent_ibb_id = None
|
2010-08-16 14:33:50 +02:00
|
|
|
|
|
|
|
def IBBIqHandler(self, conn, stanza):
|
|
|
|
"""
|
|
|
|
Handles streams state change. Used internally.
|
|
|
|
"""
|
|
|
|
typ = stanza.getType()
|
|
|
|
log.debug('IBBIqHandler called typ->%s' % typ)
|
|
|
|
if typ == 'set' and stanza.getTag('open', namespace=xmpp.NS_IBB):
|
|
|
|
self.StreamOpenHandler(conn, stanza)
|
|
|
|
elif typ == 'set' and stanza.getTag('close', namespace=xmpp.NS_IBB):
|
|
|
|
self.StreamCloseHandler(conn, stanza)
|
|
|
|
elif typ == 'result':
|
2012-01-07 20:38:59 +01:00
|
|
|
self.SendHandler()
|
2010-08-16 14:33:50 +02:00
|
|
|
elif typ == 'error':
|
2012-01-08 02:49:07 +01:00
|
|
|
gajim.socks5queue.error_cb()
|
2010-08-16 14:33:50 +02:00
|
|
|
else:
|
|
|
|
conn.send(xmpp.Error(stanza, xmpp.ERR_BAD_REQUEST))
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
def StreamOpenHandler(self, conn, stanza):
|
|
|
|
"""
|
|
|
|
Handles opening of new incoming stream. Used internally.
|
|
|
|
"""
|
|
|
|
err = None
|
|
|
|
sid = stanza.getTagAttr('open', 'sid')
|
|
|
|
blocksize = stanza.getTagAttr('open', 'block-size')
|
|
|
|
log.debug('StreamOpenHandler called sid->%s blocksize->%s' % (sid,
|
|
|
|
blocksize))
|
2012-06-14 18:34:07 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.name, sid)
|
2010-08-16 14:33:50 +02:00
|
|
|
try:
|
|
|
|
blocksize = int(blocksize)
|
|
|
|
except:
|
|
|
|
err = xmpp.ERR_BAD_REQUEST
|
|
|
|
if not sid or not blocksize:
|
|
|
|
err = xmpp.ERR_BAD_REQUEST
|
2012-06-14 18:34:07 +02:00
|
|
|
elif not file_props:
|
2010-08-16 14:33:50 +02:00
|
|
|
err = xmpp.ERR_UNEXPECTED_REQUEST
|
|
|
|
if err:
|
|
|
|
rep = xmpp.Error(stanza, err)
|
|
|
|
else:
|
|
|
|
log.debug("Opening stream: id %s, block-size %s" % (sid, blocksize))
|
|
|
|
rep = xmpp.Protocol('iq', stanza.getFrom(), 'result',
|
|
|
|
stanza.getTo(), {'id': stanza.getID()})
|
2012-06-14 18:27:23 +02:00
|
|
|
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')
|
2010-08-16 14:33:50 +02:00
|
|
|
conn.send(rep)
|
|
|
|
|
2010-08-17 15:42:34 +02:00
|
|
|
def OpenStream(self, sid, to, fp, blocksize=4096):
|
2010-08-16 14:33:50 +02:00
|
|
|
"""
|
|
|
|
Start new stream. You should provide stream id 'sid', the endpoind jid
|
|
|
|
'to', the file object containing info for send 'fp'. Also the desired
|
|
|
|
blocksize can be specified.
|
|
|
|
Take into account that recommended stanza size is 4k and IBB uses
|
|
|
|
base64 encoding that increases size of data by 1/3.
|
|
|
|
"""
|
|
|
|
if not xmpp.JID(to).getResource():
|
|
|
|
return
|
2012-06-14 18:27:23 +02:00
|
|
|
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
|
2010-08-16 14:33:50 +02:00
|
|
|
syn = xmpp.Protocol('iq', to, 'set', payload=[xmpp.Node(xmpp.NS_IBB + \
|
2010-08-17 15:42:34 +02:00
|
|
|
' open', {'sid': sid, 'block-size': blocksize, 'stanza': 'iq'})])
|
2010-08-16 14:33:50 +02:00
|
|
|
self.connection.send(syn)
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.syn_id = syn.getID()
|
|
|
|
return file_props
|
2010-08-16 14:33:50 +02:00
|
|
|
|
|
|
|
def SendHandler(self):
|
|
|
|
"""
|
|
|
|
Send next portion of data if it is time to do it. Used internally.
|
|
|
|
"""
|
|
|
|
log.debug('SendHandler called')
|
2012-06-14 18:27:23 +02:00
|
|
|
#pdb.set_trace()
|
|
|
|
for file_props in FilesProp.getAllFileProp():
|
|
|
|
if not file_props.direction:
|
2010-08-16 14:33:50 +02:00
|
|
|
# it's socks5 bytestream
|
|
|
|
continue
|
2012-06-14 18:27:23 +02:00
|
|
|
sid = file_props.sid
|
|
|
|
if file_props.direction[:2] == '|>':
|
2010-08-16 14:33:50 +02:00
|
|
|
# We waitthat other part accept stream
|
|
|
|
continue
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.direction[0] == '>':
|
|
|
|
if file_props.paused:
|
2010-08-16 14:33:50 +02:00
|
|
|
continue
|
2012-06-14 18:27:23 +02:00
|
|
|
chunk = file_props.fp.read(file_props.block_size)
|
2010-08-16 14:33:50 +02:00
|
|
|
if chunk:
|
|
|
|
datanode = xmpp.Node(xmpp.NS_IBB + ' data', {'sid': sid,
|
2012-06-14 18:27:23 +02:00
|
|
|
'seq': file_props.seq}, base64.encodestring(chunk))
|
|
|
|
file_props.seq += 1
|
|
|
|
file_props.started = True
|
|
|
|
if file_props.seq == 65536:
|
|
|
|
file_props.seq = 0
|
2011-10-06 09:11:14 +02:00
|
|
|
self.last_sent_ibb_id = self.connection.send(xmpp.Protocol(
|
2012-06-14 18:27:23 +02:00
|
|
|
name='iq', to=file_props.direction[1:], typ='set',
|
2011-10-06 09:11:14 +02:00
|
|
|
payload=[datanode]))
|
2010-08-16 14:33:50 +02:00
|
|
|
current_time = time.time()
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.elapsed_time += current_time - file_props.last_time
|
|
|
|
file_props.last_time = current_time
|
|
|
|
file_props.received_len += len(chunk)
|
2010-08-16 14:33:50 +02:00
|
|
|
gajim.socks5queue.progress_transfer_cb(self.name,
|
|
|
|
file_props)
|
|
|
|
else:
|
|
|
|
# notify the other side about stream closing
|
|
|
|
# notify the local user about sucessfull send
|
|
|
|
# delete the local stream
|
|
|
|
self.connection.send(xmpp.Protocol('iq',
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.direction[1:], 'set',
|
2010-08-16 14:33:50 +02:00
|
|
|
payload=[xmpp.Node(xmpp.NS_IBB + ' close',
|
|
|
|
{'sid':sid})]))
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.completed = True
|
|
|
|
FilesProp.deleteFileProp(file_props)
|
|
|
|
del(file_props)
|
2010-08-25 12:05:14 +02:00
|
|
|
|
2010-08-16 14:33:50 +02:00
|
|
|
def IBBMessageHandler(self, conn, stanza):
|
|
|
|
"""
|
|
|
|
Receive next portion of incoming datastream and store it write
|
|
|
|
it to temporary file. Used internally.
|
|
|
|
"""
|
|
|
|
sid = stanza.getTagAttr('data', 'sid')
|
|
|
|
seq = stanza.getTagAttr('data', 'seq')
|
|
|
|
data = stanza.getTagData('data')
|
|
|
|
log.debug('ReceiveHandler called sid->%s seq->%s' % (sid, seq))
|
|
|
|
try:
|
|
|
|
seq = int(seq)
|
|
|
|
data = base64.decodestring(data)
|
|
|
|
except Exception:
|
|
|
|
seq = ''
|
|
|
|
data = ''
|
|
|
|
err = None
|
2012-06-14 18:34:07 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.name, sid)
|
|
|
|
if file_props is None:
|
2010-08-16 14:33:50 +02:00
|
|
|
err = xmpp.ERR_ITEM_NOT_FOUND
|
|
|
|
else:
|
|
|
|
if not data:
|
|
|
|
err = xmpp.ERR_BAD_REQUEST
|
2012-06-14 18:27:23 +02:00
|
|
|
elif seq <> file_props.seq:
|
2010-08-16 14:33:50 +02:00
|
|
|
err = xmpp.ERR_UNEXPECTED_REQUEST
|
|
|
|
else:
|
|
|
|
log.debug('Successfull receive sid->%s %s+%s bytes' % (sid,
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.fp.tell(), len(data)))
|
|
|
|
file_props.seq += 1
|
|
|
|
file_props.started = True
|
|
|
|
file_props.fp.write(data)
|
2010-08-16 14:33:50 +02:00
|
|
|
current_time = time.time()
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.elapsed_time += current_time - file_props.last_time
|
|
|
|
file_props.last_time = current_time
|
|
|
|
file_props.received_len += len(data)
|
2010-08-16 14:33:50 +02:00
|
|
|
gajim.socks5queue.progress_transfer_cb(self.name, file_props)
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.received_len >= file_props.size:
|
|
|
|
file_props.completed = True
|
2010-08-16 14:33:50 +02:00
|
|
|
if err:
|
|
|
|
log.debug('Error on receive: %s' % err)
|
|
|
|
conn.send(xmpp.Error(xmpp.Iq(to=stanza.getFrom(),
|
|
|
|
frm=stanza.getTo(),
|
|
|
|
payload=[xmpp.Node(xmpp.NS_IBB + ' close')]), err, reply=0))
|
2010-08-17 15:42:34 +02:00
|
|
|
else:
|
|
|
|
return True
|
2010-08-16 14:33:50 +02:00
|
|
|
|
|
|
|
def StreamCloseHandler(self, conn, stanza):
|
|
|
|
"""
|
|
|
|
Handle stream closure due to all data transmitted.
|
|
|
|
Raise xmpppy event specifying successfull data receive.
|
|
|
|
"""
|
|
|
|
sid = stanza.getTagAttr('close', 'sid')
|
|
|
|
log.debug('StreamCloseHandler called sid->%s' % sid)
|
|
|
|
# look in sending files
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props = FilesProp.getFileProp(self.name, sid)
|
|
|
|
if file_props:
|
2012-01-08 20:29:24 +01:00
|
|
|
reply = stanza.buildReply('result')
|
|
|
|
reply.delChild('close')
|
|
|
|
conn.send(reply)
|
2012-06-14 18:27:23 +02:00
|
|
|
# look in receiving files
|
2012-01-08 20:29:24 +01:00
|
|
|
reply = stanza.buildReply('result')
|
|
|
|
reply.delChild('close')
|
|
|
|
conn.send(reply)
|
2012-06-14 18:27:23 +02:00
|
|
|
file_props.fp.close()
|
2010-08-16 14:33:50 +02:00
|
|
|
gajim.socks5queue.complete_transfer_cb(self.name, file_props)
|
|
|
|
gajim.socks5queue.remove_file_props(self.name, sid)
|
|
|
|
else:
|
|
|
|
conn.send(xmpp.Error(stanza, xmpp.ERR_ITEM_NOT_FOUND))
|
|
|
|
|
2010-08-17 15:42:34 +02:00
|
|
|
def IBBAllIqHandler(self, conn, stanza):
|
2010-08-16 14:33:50 +02:00
|
|
|
"""
|
|
|
|
Handle remote side reply about if it agree or not to receive our
|
|
|
|
datastream.
|
|
|
|
Used internally. Raises xmpppy event specfiying if the data transfer
|
|
|
|
is agreed upon.
|
|
|
|
"""
|
|
|
|
syn_id = stanza.getID()
|
2010-08-17 15:42:34 +02:00
|
|
|
log.debug('IBBAllIqHandler called syn_id->%s' % syn_id)
|
2012-06-14 18:27:23 +02:00
|
|
|
for file_props in FilesProp.getAllFileProp():
|
|
|
|
if not file_props.direction:
|
2010-08-16 14:33:50 +02:00
|
|
|
# It's socks5 bytestream
|
|
|
|
continue
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.syn_id == syn_id:
|
2010-08-16 14:33:50 +02:00
|
|
|
if stanza.getType() == 'error':
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.direction[0] == '<':
|
2010-08-16 14:33:50 +02:00
|
|
|
conn.Event('IBB', 'ERROR ON RECEIVE', file_props)
|
|
|
|
else:
|
|
|
|
conn.Event('IBB', 'ERROR ON SEND', file_props)
|
2012-06-14 18:27:23 +02:00
|
|
|
FilesProp.deleteFileProp(file_props)
|
|
|
|
del(file_props)
|
2010-08-16 14:33:50 +02:00
|
|
|
elif stanza.getType() == 'result':
|
2012-06-14 18:27:23 +02:00
|
|
|
if file_props.direction[0] == '|':
|
|
|
|
file_props.direction = file_props.direction[1:]
|
2010-08-17 15:42:34 +02:00
|
|
|
self.SendHandler()
|
2010-08-16 14:33:50 +02:00
|
|
|
else:
|
|
|
|
conn.send(xmpp.Error(stanza,
|
|
|
|
xmpp.ERR_UNEXPECTED_REQUEST))
|
2010-08-17 15:42:34 +02:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
if stanza.getTag('data'):
|
|
|
|
if self.IBBMessageHandler(conn, stanza):
|
|
|
|
reply = stanza.buildReply('result')
|
2012-01-08 20:29:24 +01:00
|
|
|
reply.delChild('data')
|
2010-08-17 15:42:34 +02:00
|
|
|
conn.send(reply)
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
elif syn_id == self.last_sent_ibb_id:
|
|
|
|
self.SendHandler()
|
2010-08-16 14:33:50 +02:00
|
|
|
|
2010-02-22 16:29:01 +01:00
|
|
|
class ConnectionSocks5BytestreamZeroconf(ConnectionSocks5Bytestream):
|
2009-12-10 22:09:37 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def _ft_get_from(self, iq_obj):
|
|
|
|
return unicode(iq_obj.getFrom())
|
2009-12-10 21:52:32 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def _ft_get_our_jid(self):
|
|
|
|
return gajim.get_jid_from_account(self.name)
|
2009-12-10 21:52:32 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def _ft_get_receiver_jid(self, file_props):
|
2012-06-14 18:27:23 +02:00
|
|
|
return file_props.receiver.jid
|
2009-12-10 21:52:32 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def _ft_get_streamhost_jid_attr(self, streamhost):
|
|
|
|
return streamhost.getAttr('jid')
|