gajim-plural/src/common/socks5.py

1166 lines
34 KiB
Python
Raw Normal View History

# -*- coding:utf-8 -*-
## src/common/socks5.py
2005-07-30 12:58:46 +02:00
##
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2005-2007 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
2005-07-30 12:58:46 +02:00
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
2005-07-30 12:58:46 +02:00
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
2005-07-30 12:58:46 +02:00
##
## Gajim is distributed in the hope that it will be useful,
2005-07-30 12:58:46 +02:00
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2005-07-30 12:58:46 +02:00
## 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/>.
##
2005-07-30 12:58:46 +02:00
import socket
import struct
import hashlib
2008-12-07 14:23:27 +01:00
import os
2005-07-30 12:58:46 +02:00
from errno import EWOULDBLOCK
from errno import ENOBUFS
from errno import EINTR
from errno import EISCONN
2008-12-02 16:10:31 +01:00
from errno import EINPROGRESS
from xmpp.idlequeue import IdleObject
MAX_BUFF_LEN = 65536
# after foo seconds without activity label transfer as 'stalled'
STALLED_TIMEOUT = 10
# after foo seconds of waiting to connect, disconnect from
# streamhost and try next one
2006-02-12 03:25:21 +01:00
CONNECT_TIMEOUT = 30
# nothing received for the last foo seconds - stop transfer
# if it is 0, then transfer will wait forever
READ_TIMEOUT = 180
# nothing sent for the last foo seconds - stop transfer
# if it is 0, then transfer will wait forever
SEND_TIMEOUT = 180
2005-07-30 12:58:46 +02:00
class SocksQueue:
"""
Queue for all file requests objects
"""
def __init__(self, idlequeue, complete_transfer_cb=None,
progress_transfer_cb=None, error_cb=None):
2005-07-30 12:58:46 +02:00
self.connected = 0
self.readers = {}
self.files_props = {}
2005-08-03 16:04:54 +02:00
self.senders = {}
2005-08-10 17:59:55 +02:00
self.idx = 1
2005-08-03 16:04:54 +02:00
self.listener = None
self.sha_handlers = {}
# handle all io events in the global idle queue, instead of processing
# each foo seconds
self.idlequeue = idlequeue
2005-07-30 16:13:45 +02:00
self.complete_transfer_cb = complete_transfer_cb
self.progress_transfer_cb = progress_transfer_cb
self.error_cb = error_cb
self.on_success = None
self.on_failure = None
2008-11-24 18:00:20 +01:00
def start_listener(self, port, sha_str, sha_handler, sid):
"""
Start waiting for incomming connections on (host, port) and do a socks5
authentication using sid for generated SHA
"""
2005-08-03 16:04:54 +02:00
self.sha_handlers[sha_str] = (sha_handler, sid)
if self.listener is None:
self.listener = Socks5Listener(self.idlequeue, port)
self.listener.queue = self
2005-08-03 16:04:54 +02:00
self.listener.bind()
2008-08-27 12:09:38 +02:00
if self.listener.started is False:
self.listener = None
# We cannot bind port, call error callback and fail
self.error_cb(_('Unable to bind to port %s.') % port,
_('Maybe you have another running instance of Gajim. File '
'Transfer will be cancelled.'))
return None
2005-08-03 16:04:54 +02:00
self.connected += 1
return self.listener
2008-11-24 18:00:20 +01:00
2005-08-07 20:43:53 +02:00
def send_success_reply(self, file_props, streamhost):
if 'streamhost-used' in file_props and \
2008-11-24 18:00:20 +01:00
file_props['streamhost-used'] is True:
if 'proxyhosts' in file_props:
for proxy in file_props['proxyhosts']:
if proxy == streamhost:
self.on_success(streamhost)
return 2
return 0
if 'streamhosts' in file_props:
2005-08-09 13:38:11 +02:00
for host in file_props['streamhosts']:
2005-08-10 17:59:55 +02:00
if streamhost['state'] == 1:
return 0
2005-08-07 20:43:53 +02:00
streamhost['state'] = 1
self.on_success(streamhost)
2005-08-10 17:59:55 +02:00
return 1
return 0
2008-11-24 18:00:20 +01:00
def connect_to_hosts(self, account, sid, on_success=None, on_failure=None):
self.on_success = on_success
self.on_failure = on_failure
file_props = self.files_props[account][sid]
file_props['failure_cb'] = on_failure
2008-11-24 18:00:20 +01:00
# add streamhosts to the queue
for streamhost in file_props['streamhosts']:
receiver = Socks5Receiver(self.idlequeue, streamhost, sid, file_props)
self.add_receiver(account, receiver)
streamhost['idx'] = receiver.queue_idx
2008-11-24 18:00:20 +01:00
def _socket_connected(self, streamhost, file_props):
"""
Called when there is a host connected to one of the senders's
streamhosts. Stop othere attempts for connections
"""
for host in file_props['streamhosts']:
if host != streamhost and 'idx' in host:
2005-08-09 13:38:11 +02:00
if host['state'] == 1:
# remove current
2005-08-09 13:38:11 +02:00
self.remove_receiver(streamhost['idx'])
return
# set state -2, meaning that this streamhost is stopped,
# but it may be connectected later
2008-11-24 18:00:20 +01:00
if host['state'] >= 0:
self.remove_receiver(host['idx'])
host['idx'] = -1
host['state'] = -2
2008-11-24 18:00:20 +01:00
def reconnect_receiver(self, receiver, streamhost):
"""
Check the state of all streamhosts and if all has failed, then emit
connection failure cb. If there are some which are still not connected
try to establish connection to one of them
"""
self.idlequeue.remove_timeout(receiver.fd)
self.idlequeue.unplug_idle(receiver.fd)
file_props = receiver.file_props
streamhost['state'] = -1
# boolean, indicates that there are hosts, which are not tested yet
unused_hosts = False
for host in file_props['streamhosts']:
if 'idx' in host:
if host['state'] >= 0:
return
elif host['state'] == -2:
unused_hosts = True
if unused_hosts:
for host in file_props['streamhosts']:
if host['state'] == -2:
host['state'] = 0
2008-11-24 18:00:20 +01:00
receiver = Socks5Receiver(self.idlequeue, host, host['sid'],
file_props)
self.add_receiver(receiver.account, receiver)
host['idx'] = receiver.queue_idx
# we still have chances to connect
return
if 'received-len' not in file_props or file_props['received-len'] == 0:
# there are no other streamhosts and transfer hasn't started
self._connection_refused(streamhost, file_props, receiver.queue_idx)
else:
# transfer stopped, it is most likely stopped from sender
receiver.disconnect()
file_props['error'] = -1
self.process_result(-1, receiver)
2008-11-24 18:00:20 +01:00
def _connection_refused(self, streamhost, file_props, idx):
"""
Called when we loose connection during transfer
"""
if file_props is None:
return
streamhost['state'] = -1
self.remove_receiver(idx, False)
if 'streamhosts' in file_props:
for host in file_props['streamhosts']:
if host['state'] != -1:
return
# failure_cb exists - this means that it has never been called
if 'failure_cb' in file_props and file_props['failure_cb']:
2008-11-24 18:00:20 +01:00
file_props['failure_cb'](streamhost['initiator'], streamhost['id'],
2005-08-15 00:29:16 +02:00
file_props['sid'], code = 404)
del(file_props['failure_cb'])
2008-11-24 18:00:20 +01:00
def add_receiver(self, account, sock5_receiver):
"""
Add new file request
"""
2005-07-30 12:58:46 +02:00
self.readers[self.idx] = sock5_receiver
sock5_receiver.queue_idx = self.idx
sock5_receiver.queue = self
sock5_receiver.account = account
2005-07-30 12:58:46 +02:00
self.idx += 1
result = sock5_receiver.connect()
2005-08-11 22:26:17 +02:00
self.connected += 1
if result is not None:
result = sock5_receiver.main()
self.process_result(result, sock5_receiver)
return 1
return None
2008-11-24 18:00:20 +01:00
def get_file_from_sender(self, file_props, account):
if file_props is None:
return
2008-11-24 18:00:20 +01:00
if 'hash' in file_props and file_props['hash'] in self.senders:
sender = self.senders[file_props['hash']]
sender.account = account
result = self.get_file_contents(0)
self.process_result(result, sender)
2008-11-24 18:00:20 +01:00
def result_sha(self, sha_str, idx):
if sha_str in self.sha_handlers:
props = self.sha_handlers[sha_str]
props[0](props[1], idx)
2008-11-24 18:00:20 +01:00
2005-08-10 17:59:55 +02:00
def activate_proxy(self, idx):
if idx not in self.readers:
2005-08-10 17:59:55 +02:00
return
reader = self.readers[idx]
if reader.file_props['type'] != 's':
return
if reader.state != 5:
return
reader.state = 6
if reader.connected:
reader.file_props['error'] = 0
reader.file_props['disconnect_cb'] = reader.disconnect
reader.file_props['started'] = True
reader.file_props['completed'] = False
reader.file_props['paused'] = False
reader.file_props['stalled'] = False
2005-09-09 00:12:14 +02:00
reader.file_props['elapsed-time'] = 0
reader.file_props['last-time'] = self.idlequeue.current_time()
2005-08-10 17:59:55 +02:00
reader.file_props['received-len'] = 0
reader.pauses = 0
# start sending file to proxy
self.idlequeue.set_read_timeout(reader.fd, STALLED_TIMEOUT)
self.idlequeue.plug_idle(reader, True, False)
2005-08-10 17:59:55 +02:00
result = reader.write_next()
self.process_result(result, reader)
2008-11-24 18:00:20 +01:00
def send_file(self, file_props, account):
2008-11-24 18:00:20 +01:00
if 'hash' in file_props and file_props['hash'] in self.senders:
sender = self.senders[file_props['hash']]
2005-08-09 13:38:11 +02:00
file_props['streamhost-used'] = True
sender.account = account
if file_props['type'] == 's':
2008-11-24 18:00:20 +01:00
sender.file_props = file_props
result = sender.send_file()
self.process_result(result, sender)
else:
2005-09-09 00:12:14 +02:00
file_props['elapsed-time'] = 0
file_props['last-time'] = self.idlequeue.current_time()
file_props['received-len'] = 0
sender.file_props = file_props
2008-11-24 18:00:20 +01:00
def add_file_props(self, account, file_props):
"""
File_prop to the dict of current file_props. It is identified by account
name and sid
"""
2008-11-24 18:00:20 +01:00
if file_props is None or ('sid' in file_props) is False:
2005-07-30 12:58:46 +02:00
return
_id = file_props['sid']
if account not in self.files_props:
self.files_props[account] = {}
self.files_props[account][_id] = file_props
2008-11-24 18:00:20 +01:00
2005-08-10 17:59:55 +02:00
def remove_file_props(self, account, sid):
if account in self.files_props:
2005-08-10 17:59:55 +02:00
fl_props = self.files_props[account]
if sid in fl_props:
2005-08-10 17:59:55 +02:00
del(fl_props[sid])
2008-11-24 18:00:20 +01:00
2005-08-10 17:59:55 +02:00
if len(self.files_props) == 0:
self.connected = 0
2008-11-24 18:00:20 +01:00
def get_file_props(self, account, sid):
"""
Get fil_prop by account name and session id
"""
if account in self.files_props:
fl_props = self.files_props[account]
if sid in fl_props:
2005-08-03 18:53:29 +02:00
return fl_props[sid]
2005-07-30 12:58:46 +02:00
return None
2008-11-24 18:00:20 +01:00
def on_connection_accepted(self, sock):
2008-11-24 18:00:20 +01:00
sock_hash = sock.__hash__()
if sock_hash not in self.senders:
2008-11-24 18:00:20 +01:00
self.senders[sock_hash] = Socks5Sender(self.idlequeue, sock_hash, self,
sock[0], sock[1][0], sock[1][1])
self.connected += 1
2008-11-24 18:00:20 +01:00
2005-08-03 16:04:54 +02:00
def process_result(self, result, actor):
"""
Take appropriate actions upon the result:
[ 0, - 1 ] complete/end transfer
[ > 0 ] send progress message
[ None ] do nothing
"""
if result is None:
return
2005-11-21 11:34:10 +01:00
if result in (0, -1) and self.complete_transfer_cb is not None:
account = actor.account
if account is None and 'tt_account' in actor.file_props:
2006-02-12 03:25:21 +01:00
account = actor.file_props['tt_account']
2005-11-21 11:34:10 +01:00
self.complete_transfer_cb(account, actor.file_props)
2005-08-03 16:04:54 +02:00
elif self.progress_transfer_cb is not None:
2005-11-21 11:34:10 +01:00
self.progress_transfer_cb(actor.account, actor.file_props)
2008-11-24 18:00:20 +01:00
def remove_receiver(self, idx, do_disconnect=True):
"""
Remove reciver from the list and decrease the number of active
connections with 1
"""
2005-07-30 12:58:46 +02:00
if idx != -1:
if idx in self.readers:
reader = self.readers[idx]
self.idlequeue.unplug_idle(reader.fd)
self.idlequeue.remove_timeout(reader.fd)
2005-08-10 17:59:55 +02:00
if do_disconnect:
reader.disconnect()
2005-08-10 17:59:55 +02:00
else:
if reader.streamhost is not None:
2008-11-24 18:00:20 +01:00
reader.streamhost['state'] = -1
2005-08-10 17:59:55 +02:00
del(self.readers[idx])
2008-11-24 18:00:20 +01:00
def remove_sender(self, idx, do_disconnect=True):
"""
Remove sender from the list of senders and decrease the number of active
connections with 1
"""
2005-08-03 16:04:54 +02:00
if idx != -1:
if idx in self.senders:
2005-08-10 17:59:55 +02:00
if do_disconnect:
self.senders[idx].disconnect()
return
2005-08-10 17:59:55 +02:00
else:
del(self.senders[idx])
if self.connected > 0:
self.connected -= 1
if len(self.senders) == 0 and self.listener is not None:
self.listener.disconnect()
self.listener = None
self.connected -= 1
2008-11-24 18:00:20 +01:00
2005-07-30 12:58:46 +02:00
class Socks5:
def __init__(self, idlequeue, host, port, initiator, target, sid):
2005-08-03 16:04:54 +02:00
if host is not None:
try:
self.host = host
2008-11-24 18:00:20 +01:00
self.ais = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM)
except socket.gaierror:
self.ais = None
self.idlequeue = idlequeue
self.fd = -1
2005-07-30 12:58:46 +02:00
self.port = port
self.initiator = initiator
self.target = target
self.sid = sid
self._sock = None
self.account = None
self.state = 0 # not connected
self.pauses = 0
self.size = 0
self.remaining_buff = ''
self.file = None
2008-11-24 18:00:20 +01:00
def open_file_for_reading(self):
if self.file is None:
2005-09-10 13:56:25 +02:00
try:
self.file = open(self.file_props['file-name'],'rb')
if 'offset' in self.file_props and self.file_props['offset']:
Merged revisions 5017-5020,5022-5029 via svnmerge from svn://svn.gajim.org/gajim/trunk ........ r5017 | asterix | 2006-01-06 01:55:51 -0700 (Fri, 06 Jan 2006) | 2 lines use escape for pango markup ........ r5018 | asterix | 2006-01-06 02:21:39 -0700 (Fri, 06 Jan 2006) | 2 lines missing new contacts function ........ r5019 | asterix | 2006-01-06 11:03:07 -0700 (Fri, 06 Jan 2006) | 2 lines handle the click on toggle_gpg_encryption menuitem ........ r5020 | asterix | 2006-01-06 11:14:14 -0700 (Fri, 06 Jan 2006) | 2 lines use the saved size even if a chat window is already opened ........ r5022 | asterix | 2006-01-07 03:43:47 -0700 (Sat, 07 Jan 2006) | 2 lines we can now resume filetransfert ........ r5023 | asterix | 2006-01-07 03:56:31 -0700 (Sat, 07 Jan 2006) | 2 lines [Knuckles] Google E-Mail Notification ........ r5024 | asterix | 2006-01-07 04:02:16 -0700 (Sat, 07 Jan 2006) | 2 lines better string ........ r5025 | asterix | 2006-01-07 04:14:32 -0700 (Sat, 07 Jan 2006) | 2 lines fix a TB ........ r5026 | asterix | 2006-01-07 05:36:55 -0700 (Sat, 07 Jan 2006) | 2 lines we can now drag a file on a contact in the roster to send him a file ........ r5027 | asterix | 2006-01-07 06:26:28 -0700 (Sat, 07 Jan 2006) | 2 lines contact.groups is always a list, even if emtpy ........ r5028 | asterix | 2006-01-07 06:54:30 -0700 (Sat, 07 Jan 2006) | 2 lines make all buttons insensitive on a category row in disco ........ r5029 | asterix | 2006-01-07 07:19:25 -0700 (Sat, 07 Jan 2006) | 2 lines auto open groupchat configuration window when we create a new room ........
2006-01-07 18:25:35 +01:00
self.size = self.file_props['offset']
self.file.seek(self.size)
Merged revisions 5017-5020,5022-5029 via svnmerge from svn://svn.gajim.org/gajim/trunk ........ r5017 | asterix | 2006-01-06 01:55:51 -0700 (Fri, 06 Jan 2006) | 2 lines use escape for pango markup ........ r5018 | asterix | 2006-01-06 02:21:39 -0700 (Fri, 06 Jan 2006) | 2 lines missing new contacts function ........ r5019 | asterix | 2006-01-06 11:03:07 -0700 (Fri, 06 Jan 2006) | 2 lines handle the click on toggle_gpg_encryption menuitem ........ r5020 | asterix | 2006-01-06 11:14:14 -0700 (Fri, 06 Jan 2006) | 2 lines use the saved size even if a chat window is already opened ........ r5022 | asterix | 2006-01-07 03:43:47 -0700 (Sat, 07 Jan 2006) | 2 lines we can now resume filetransfert ........ r5023 | asterix | 2006-01-07 03:56:31 -0700 (Sat, 07 Jan 2006) | 2 lines [Knuckles] Google E-Mail Notification ........ r5024 | asterix | 2006-01-07 04:02:16 -0700 (Sat, 07 Jan 2006) | 2 lines better string ........ r5025 | asterix | 2006-01-07 04:14:32 -0700 (Sat, 07 Jan 2006) | 2 lines fix a TB ........ r5026 | asterix | 2006-01-07 05:36:55 -0700 (Sat, 07 Jan 2006) | 2 lines we can now drag a file on a contact in the roster to send him a file ........ r5027 | asterix | 2006-01-07 06:26:28 -0700 (Sat, 07 Jan 2006) | 2 lines contact.groups is always a list, even if emtpy ........ r5028 | asterix | 2006-01-07 06:54:30 -0700 (Sat, 07 Jan 2006) | 2 lines make all buttons insensitive on a category row in disco ........ r5029 | asterix | 2006-01-07 07:19:25 -0700 (Sat, 07 Jan 2006) | 2 lines auto open groupchat configuration window when we create a new room ........
2006-01-07 18:25:35 +01:00
self.file_props['received-len'] = self.size
2005-09-10 13:56:25 +02:00
except IOError, e:
self.close_file()
raise IOError, e
2008-11-24 18:00:20 +01:00
def close_file(self):
if self.file:
if not self.file.closed:
try:
self.file.close()
except Exception:
pass
self.file = None
2008-11-24 18:00:20 +01:00
def get_fd(self):
"""
Test if file is already open and return its fd, or just open the file and
return the fd
"""
if 'fd' in self.file_props:
fd = self.file_props['fd']
else:
Merged revisions 5017-5020,5022-5029 via svnmerge from svn://svn.gajim.org/gajim/trunk ........ r5017 | asterix | 2006-01-06 01:55:51 -0700 (Fri, 06 Jan 2006) | 2 lines use escape for pango markup ........ r5018 | asterix | 2006-01-06 02:21:39 -0700 (Fri, 06 Jan 2006) | 2 lines missing new contacts function ........ r5019 | asterix | 2006-01-06 11:03:07 -0700 (Fri, 06 Jan 2006) | 2 lines handle the click on toggle_gpg_encryption menuitem ........ r5020 | asterix | 2006-01-06 11:14:14 -0700 (Fri, 06 Jan 2006) | 2 lines use the saved size even if a chat window is already opened ........ r5022 | asterix | 2006-01-07 03:43:47 -0700 (Sat, 07 Jan 2006) | 2 lines we can now resume filetransfert ........ r5023 | asterix | 2006-01-07 03:56:31 -0700 (Sat, 07 Jan 2006) | 2 lines [Knuckles] Google E-Mail Notification ........ r5024 | asterix | 2006-01-07 04:02:16 -0700 (Sat, 07 Jan 2006) | 2 lines better string ........ r5025 | asterix | 2006-01-07 04:14:32 -0700 (Sat, 07 Jan 2006) | 2 lines fix a TB ........ r5026 | asterix | 2006-01-07 05:36:55 -0700 (Sat, 07 Jan 2006) | 2 lines we can now drag a file on a contact in the roster to send him a file ........ r5027 | asterix | 2006-01-07 06:26:28 -0700 (Sat, 07 Jan 2006) | 2 lines contact.groups is always a list, even if emtpy ........ r5028 | asterix | 2006-01-07 06:54:30 -0700 (Sat, 07 Jan 2006) | 2 lines make all buttons insensitive on a category row in disco ........ r5029 | asterix | 2006-01-07 07:19:25 -0700 (Sat, 07 Jan 2006) | 2 lines auto open groupchat configuration window when we create a new room ........
2006-01-07 18:25:35 +01:00
offset = 0
opt = 'wb'
if 'offset' in self.file_props and self.file_props['offset']:
Merged revisions 5017-5020,5022-5029 via svnmerge from svn://svn.gajim.org/gajim/trunk ........ r5017 | asterix | 2006-01-06 01:55:51 -0700 (Fri, 06 Jan 2006) | 2 lines use escape for pango markup ........ r5018 | asterix | 2006-01-06 02:21:39 -0700 (Fri, 06 Jan 2006) | 2 lines missing new contacts function ........ r5019 | asterix | 2006-01-06 11:03:07 -0700 (Fri, 06 Jan 2006) | 2 lines handle the click on toggle_gpg_encryption menuitem ........ r5020 | asterix | 2006-01-06 11:14:14 -0700 (Fri, 06 Jan 2006) | 2 lines use the saved size even if a chat window is already opened ........ r5022 | asterix | 2006-01-07 03:43:47 -0700 (Sat, 07 Jan 2006) | 2 lines we can now resume filetransfert ........ r5023 | asterix | 2006-01-07 03:56:31 -0700 (Sat, 07 Jan 2006) | 2 lines [Knuckles] Google E-Mail Notification ........ r5024 | asterix | 2006-01-07 04:02:16 -0700 (Sat, 07 Jan 2006) | 2 lines better string ........ r5025 | asterix | 2006-01-07 04:14:32 -0700 (Sat, 07 Jan 2006) | 2 lines fix a TB ........ r5026 | asterix | 2006-01-07 05:36:55 -0700 (Sat, 07 Jan 2006) | 2 lines we can now drag a file on a contact in the roster to send him a file ........ r5027 | asterix | 2006-01-07 06:26:28 -0700 (Sat, 07 Jan 2006) | 2 lines contact.groups is always a list, even if emtpy ........ r5028 | asterix | 2006-01-07 06:54:30 -0700 (Sat, 07 Jan 2006) | 2 lines make all buttons insensitive on a category row in disco ........ r5029 | asterix | 2006-01-07 07:19:25 -0700 (Sat, 07 Jan 2006) | 2 lines auto open groupchat configuration window when we create a new room ........
2006-01-07 18:25:35 +01:00
offset = self.file_props['offset']
opt = 'ab'
fd = open(self.file_props['file-name'], opt)
self.file_props['fd'] = fd
2005-09-09 00:12:14 +02:00
self.file_props['elapsed-time'] = 0
self.file_props['last-time'] = self.idlequeue.current_time()
Merged revisions 5017-5020,5022-5029 via svnmerge from svn://svn.gajim.org/gajim/trunk ........ r5017 | asterix | 2006-01-06 01:55:51 -0700 (Fri, 06 Jan 2006) | 2 lines use escape for pango markup ........ r5018 | asterix | 2006-01-06 02:21:39 -0700 (Fri, 06 Jan 2006) | 2 lines missing new contacts function ........ r5019 | asterix | 2006-01-06 11:03:07 -0700 (Fri, 06 Jan 2006) | 2 lines handle the click on toggle_gpg_encryption menuitem ........ r5020 | asterix | 2006-01-06 11:14:14 -0700 (Fri, 06 Jan 2006) | 2 lines use the saved size even if a chat window is already opened ........ r5022 | asterix | 2006-01-07 03:43:47 -0700 (Sat, 07 Jan 2006) | 2 lines we can now resume filetransfert ........ r5023 | asterix | 2006-01-07 03:56:31 -0700 (Sat, 07 Jan 2006) | 2 lines [Knuckles] Google E-Mail Notification ........ r5024 | asterix | 2006-01-07 04:02:16 -0700 (Sat, 07 Jan 2006) | 2 lines better string ........ r5025 | asterix | 2006-01-07 04:14:32 -0700 (Sat, 07 Jan 2006) | 2 lines fix a TB ........ r5026 | asterix | 2006-01-07 05:36:55 -0700 (Sat, 07 Jan 2006) | 2 lines we can now drag a file on a contact in the roster to send him a file ........ r5027 | asterix | 2006-01-07 06:26:28 -0700 (Sat, 07 Jan 2006) | 2 lines contact.groups is always a list, even if emtpy ........ r5028 | asterix | 2006-01-07 06:54:30 -0700 (Sat, 07 Jan 2006) | 2 lines make all buttons insensitive on a category row in disco ........ r5029 | asterix | 2006-01-07 07:19:25 -0700 (Sat, 07 Jan 2006) | 2 lines auto open groupchat configuration window when we create a new room ........
2006-01-07 18:25:35 +01:00
self.file_props['received-len'] = offset
return fd
2008-11-24 18:00:20 +01:00
def rem_fd(self, fd):
if 'fd' in self.file_props:
del(self.file_props['fd'])
try:
fd.close()
except Exception:
pass
2008-11-24 18:00:20 +01:00
2005-07-30 12:58:46 +02:00
def receive(self):
"""
Read small chunks of data. Call owner's disconnected() method if
appropriate
"""
received = ''
2008-11-24 18:00:20 +01:00
try:
add = self._recv(64)
2008-12-02 16:53:23 +01:00
except Exception:
2008-11-24 18:00:20 +01:00
add = ''
received += add
if len(add) == 0:
self.disconnect()
return add
2008-11-24 18:00:20 +01:00
2005-07-30 12:58:46 +02:00
def send_raw(self,raw_data):
"""
Write raw outgoing data
"""
2005-07-30 12:58:46 +02:00
try:
2008-12-02 16:53:23 +01:00
self._send(raw_data)
except Exception:
2005-07-30 12:58:46 +02:00
self.disconnect()
return len(raw_data)
2008-11-24 18:00:20 +01:00
def write_next(self):
if self.remaining_buff != '':
buff = self.remaining_buff
self.remaining_buff = ''
else:
2005-09-10 13:56:25 +02:00
try:
self.open_file_for_reading()
except IOError, e:
self.state = 8 # end connection
self.disconnect()
self.file_props['error'] = -7 # unable to read from file
return -1
buff = self.file.read(MAX_BUFF_LEN)
if len(buff) > 0:
lenn = 0
try:
lenn = self._send(buff)
except Exception, e:
if e.args[0] not in (EINTR, ENOBUFS, EWOULDBLOCK):
# peer stopped reading
self.state = 8 # end connection
self.disconnect()
self.file_props['error'] = -1
return -1
self.size += lenn
current_time = self.idlequeue.current_time()
2005-09-09 00:12:14 +02:00
self.file_props['elapsed-time'] += current_time - \
self.file_props['last-time']
self.file_props['last-time'] = current_time
self.file_props['received-len'] = self.size
if self.size >= int(self.file_props['size']):
self.state = 8 # end connection
self.file_props['error'] = 0
self.disconnect()
return -1
if lenn != len(buff):
self.remaining_buff = buff[lenn:]
else:
self.remaining_buff = ''
self.state = 7 # continue to write in the socket
if lenn == 0:
return None
self.file_props['stalled'] = False
return lenn
else:
self.state = 8 # end connection
self.disconnect()
return -1
2008-11-24 18:00:20 +01:00
def get_file_contents(self, timeout):
"""
Read file contents from socket and write them to file
"""
2008-11-24 18:00:20 +01:00
if self.file_props is None or ('file-name' in self.file_props) is False:
self.file_props['error'] = -2
return None
fd = None
if self.remaining_buff != '':
try:
fd = self.get_fd()
except IOError, e:
self.disconnect(False)
self.file_props['error'] = -6 # file system error
return 0
fd.write(self.remaining_buff)
lenn = len(self.remaining_buff)
current_time = self.idlequeue.current_time()
2005-09-09 00:12:14 +02:00
self.file_props['elapsed-time'] += current_time - \
self.file_props['last-time']
self.file_props['last-time'] = current_time
self.file_props['received-len'] += lenn
self.remaining_buff = ''
if self.file_props['received-len'] == int(self.file_props['size']):
self.rem_fd(fd)
self.disconnect()
self.file_props['error'] = 0
self.file_props['completed'] = True
return 0
else:
try:
fd = self.get_fd()
except IOError, e:
self.disconnect(False)
self.file_props['error'] = -6 # file system error
return 0
2008-11-24 18:00:20 +01:00
try:
buff = self._recv(MAX_BUFF_LEN)
2008-12-02 16:53:23 +01:00
except Exception:
buff = ''
current_time = self.idlequeue.current_time()
self.file_props['elapsed-time'] += current_time - \
self.file_props['last-time']
self.file_props['last-time'] = current_time
self.file_props['received-len'] += len(buff)
2006-02-12 03:25:21 +01:00
if len(buff) == 0:
# Transfer stopped somehow:
# reset, paused or network error
self.rem_fd(fd)
self.disconnect(False)
self.file_props['error'] = -1
return 0
try:
fd.write(buff)
except IOError, e:
self.rem_fd(fd)
self.disconnect(False)
self.file_props['error'] = -6 # file system error
return 0
if self.file_props['received-len'] >= int(self.file_props['size']):
# transfer completed
self.rem_fd(fd)
self.disconnect()
self.file_props['error'] = 0
self.file_props['completed'] = True
return 0
# return number of read bytes. It can be used in progressbar
if fd is not None:
self.file_props['stalled'] = False
if fd is None and self.file_props['stalled'] is False:
return None
if 'received-len' in self.file_props:
if self.file_props['received-len'] != 0:
return self.file_props['received-len']
return None
2008-11-24 18:00:20 +01:00
def disconnect(self):
"""
Close open descriptors and remover socket descr. from idleque
"""
# be sure that we don't leave open file
self.close_file()
2006-02-12 03:25:21 +01:00
self.idlequeue.remove_timeout(self.fd)
self.idlequeue.unplug_idle(self.fd)
try:
2006-02-12 03:25:21 +01:00
self._sock.shutdown(socket.SHUT_RDWR)
self._sock.close()
except Exception:
# socket is already closed
pass
self.connected = False
self.fd = -1
self.state = -1
2008-11-24 18:00:20 +01:00
2005-07-30 12:58:46 +02:00
def _get_auth_buff(self):
"""
Message, that we support 1 one auth mechanism: the 'no auth' mechanism
"""
2005-07-30 12:58:46 +02:00
return struct.pack('!BBB', 0x05, 0x01, 0x00)
2008-11-24 18:00:20 +01:00
2005-08-03 16:04:54 +02:00
def _parse_auth_buff(self, buff):
"""
Parse the initial message and create a list of auth mechanisms
"""
2005-08-03 16:04:54 +02:00
auth_mechanisms = []
try:
2008-12-02 16:53:23 +01:00
num_auth = struct.unpack('!xB', buff[:2])[0]
for i in xrange(num_auth):
mechanism, = struct.unpack('!B', buff[1 + i])
auth_mechanisms.append(mechanism)
except Exception:
return None
2005-08-03 16:04:54 +02:00
return auth_mechanisms
2008-11-24 18:00:20 +01:00
2005-08-03 16:04:54 +02:00
def _get_auth_response(self):
"""
Socks version(5), number of extra auth methods (we send 0x00 - no auth)
"""
2005-08-03 16:04:54 +02:00
return struct.pack('!BB', 0x05, 0x00)
2008-11-24 18:00:20 +01:00
2005-07-30 12:58:46 +02:00
def _get_connect_buff(self):
2005-07-30 16:13:45 +02:00
''' Connect request by domain name '''
2008-11-24 18:00:20 +01:00
buff = struct.pack('!BBBBB%dsBB' % len(self.host),
0x05, 0x01, 0x00, 0x03, len(self.host), self.host,
2005-07-30 12:58:46 +02:00
self.port >> 8, self.port & 0xff)
return buff
2008-11-24 18:00:20 +01:00
2005-08-03 16:04:54 +02:00
def _get_request_buff(self, msg, command = 0x01):
"""
Connect request by domain name, sid sha, instead of domain name (jep
0096)
"""
2008-11-24 18:00:20 +01:00
buff = struct.pack('!BBBBB%dsBB' % len(msg),
2005-08-03 16:04:54 +02:00
0x05, command, 0x00, 0x03, len(msg), msg, 0, 0)
2005-07-30 12:58:46 +02:00
return buff
2008-11-24 18:00:20 +01:00
2005-08-03 16:04:54 +02:00
def _parse_request_buff(self, buff):
try: # don't trust on what comes from the outside
2008-12-02 16:53:23 +01:00
req_type, host_type, = struct.unpack('!xBxB', buff[:4])
if host_type == 0x01:
host_arr = struct.unpack('!iiii', buff[4:8])
2008-10-11 11:49:30 +02:00
host, = '.'.join(str(s) for s in host_arr)
host_len = len(host)
elif host_type == 0x03:
host_len, = struct.unpack('!B' , buff[4])
host, = struct.unpack('!%ds' % host_len, buff[5:5 + host_len])
portlen = len(buff[host_len + 5:])
2008-11-24 18:00:20 +01:00
if portlen == 1:
port, = struct.unpack('!B', buff[host_len + 5])
2008-11-24 18:00:20 +01:00
elif portlen == 2:
port, = struct.unpack('!H', buff[host_len + 5:])
# file data, comes with auth message (Gaim bug)
2008-11-24 18:00:20 +01:00
else:
port, = struct.unpack('!H', buff[host_len + 5: host_len + 7])
self.remaining_buff = buff[host_len + 7:]
except Exception:
return (None, None, None)
2005-08-03 16:04:54 +02:00
return (req_type, host, port)
2008-11-24 18:00:20 +01:00
2005-07-30 12:58:46 +02:00
def read_connect(self):
"""
Connect response: version, auth method
"""
2005-07-30 12:58:46 +02:00
buff = self._recv()
try:
version, method = struct.unpack('!BB', buff)
except Exception:
version, method = None, None
2005-07-30 12:58:46 +02:00
if version != 0x05 or method == 0xff:
self.disconnect()
2008-11-24 18:00:20 +01:00
2006-02-12 03:25:21 +01:00
def continue_paused_transfer(self):
if self.state < 5:
return
if self.file_props['type'] == 'r':
self.idlequeue.plug_idle(self, False, True)
else:
self.idlequeue.plug_idle(self, True, False)
2008-11-24 18:00:20 +01:00
2005-07-30 12:58:46 +02:00
def _get_sha1_auth(self):
"""
Get sha of sid + Initiator jid + Target jid
"""
if 'is_a_proxy' in self.file_props:
2005-08-06 23:40:01 +02:00
del(self.file_props['is_a_proxy'])
return hashlib.sha1('%s%s%s' % (self.sid,
self.file_props['proxy_sender'],
self.file_props['proxy_receiver'])).hexdigest()
return hashlib.sha1('%s%s%s' % (self.sid, self.initiator, self.target)).\
2008-11-24 18:00:20 +01:00
hexdigest()
class Socks5Sender(Socks5, IdleObject):
"""
Class for sending file to socket over socks5
"""
2008-11-24 18:00:20 +01:00
def __init__(self, idlequeue, sock_hash, parent, _sock, host=None,
port=None):
2005-08-03 16:04:54 +02:00
self.queue_idx = sock_hash
self.queue = parent
Socks5.__init__(self, idlequeue, host, port, None, None, None)
2005-08-03 16:04:54 +02:00
self._sock = _sock
self._sock.setblocking(False)
self.fd = _sock.fileno()
2005-08-03 16:04:54 +02:00
self._recv = _sock.recv
self._send = _sock.send
self.connected = True
self.state = 1 # waiting for first bytes
self.file_props = None
# start waiting for data
self.idlequeue.plug_idle(self, False, True)
2008-11-24 18:00:20 +01:00
def read_timeout(self):
self.idlequeue.remove_timeout(self.fd)
2006-02-12 03:25:21 +01:00
if self.state > 5:
# no activity for foo seconds
if self.file_props['stalled'] == False:
self.file_props['stalled'] = True
self.queue.process_result(-1, self)
if SEND_TIMEOUT > 0:
self.idlequeue.set_read_timeout(self.fd, SEND_TIMEOUT)
else:
2008-11-24 18:00:20 +01:00
# stop transfer, there is no error code for this
2006-02-12 03:25:21 +01:00
self.pollend()
2008-11-24 18:00:20 +01:00
def pollout(self):
if not self.connected:
self.disconnect()
return
self.idlequeue.remove_timeout(self.fd)
if self.state == 2: # send reply with desired auth type
self.send_raw(self._get_auth_response())
elif self.state == 4: # send positive response to the 'connect'
self.send_raw(self._get_request_buff(self.sha_msg, 0x00))
elif self.state == 7:
if self.file_props['paused']:
2006-02-12 03:25:21 +01:00
self.file_props['continue_cb'] = self.continue_paused_transfer
self.idlequeue.plug_idle(self, False, False)
2008-11-24 18:00:20 +01:00
return
result = self.write_next()
self.queue.process_result(result, self)
if result is None or result <= 0:
self.disconnect()
2006-02-12 03:25:21 +01:00
return
self.idlequeue.set_read_timeout(self.fd, STALLED_TIMEOUT)
elif self.state == 8:
self.disconnect()
return
else:
self.disconnect()
if self.state < 5:
self.state += 1
# unplug and plug this time for reading
self.idlequeue.plug_idle(self, False, True)
2008-11-24 18:00:20 +01:00
def pollend(self):
self.state = 8 # end connection
self.disconnect()
2006-02-12 03:25:21 +01:00
self.file_props['error'] = -1
self.queue.process_result(-1, self)
2008-11-24 18:00:20 +01:00
def pollin(self):
if self.connected:
if self.state < 5:
result = self.main()
if self.state == 4:
self.queue.result_sha(self.sha_msg, self.queue_idx)
if result == -1:
self.disconnect()
2008-11-24 18:00:20 +01:00
elif self.state == 5:
2008-11-24 18:00:20 +01:00
if self.file_props is not None and self.file_props['type'] == 'r':
result = self.get_file_contents(0)
self.queue.process_result(result, self)
else:
self.disconnect()
2008-11-24 18:00:20 +01:00
def send_file(self):
"""
Start sending the file over verified connection
"""
if self.file_props['started']:
return
self.file_props['error'] = 0
self.file_props['disconnect_cb'] = self.disconnect
self.file_props['started'] = True
self.file_props['completed'] = False
self.file_props['paused'] = False
2006-02-12 03:25:21 +01:00
self.file_props['continue_cb'] = self.continue_paused_transfer
self.file_props['stalled'] = False
self.file_props['connected'] = True
2005-09-09 00:12:14 +02:00
self.file_props['elapsed-time'] = 0
self.file_props['last-time'] = self.idlequeue.current_time()
self.file_props['received-len'] = 0
2005-08-03 16:04:54 +02:00
self.pauses = 0
self.state = 7
# plug for writing
self.idlequeue.plug_idle(self, True, False)
return self.write_next() # initial for nl byte
2008-11-24 18:00:20 +01:00
def main(self):
"""
Initial requests for verifying the connection
"""
if self.state == 1: # initial read
2005-08-03 16:04:54 +02:00
buff = self.receive()
if not self.connected:
return -1
mechs = self._parse_auth_buff(buff)
if mechs is None:
return -1 # invalid auth methods received
elif self.state == 3: # get next request
2005-08-03 16:04:54 +02:00
buff = self.receive()
2008-12-02 16:53:23 +01:00
req_type, self.sha_msg = self._parse_request_buff(buff)[:2]
if req_type != 0x01:
return -1 # request is not of type 'connect'
self.state += 1 # go to the next step
# unplug & plug for writing
self.idlequeue.plug_idle(self, True, False)
2005-08-03 16:04:54 +02:00
return None
2008-11-24 18:00:20 +01:00
def disconnect(self, cb=True):
"""
Close the socket
"""
2005-08-03 16:04:54 +02:00
# close connection and remove us from the queue
Socks5.disconnect(self)
if self.file_props is not None:
self.file_props['connected'] = False
self.file_props['disconnect_cb'] = None
2005-08-03 16:04:54 +02:00
if self.queue is not None:
2005-08-10 17:59:55 +02:00
self.queue.remove_sender(self.queue_idx, False)
class Socks5Listener(IdleObject):
def __init__(self, idlequeue, port):
"""
Handle all incomming connections on (0.0.0.0, port)
This class implements IdleObject, but we will expect
only pollin events though
"""
self.port = port
self.ais = socket.getaddrinfo(None, port, socket.AF_UNSPEC,
2008-11-24 18:00:20 +01:00
socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_PASSIVE)
self.ais.sort(reverse=True) # Try IPv6 first
2008-11-24 18:00:20 +01:00
self.queue_idx = -1
self.idlequeue = idlequeue
self.queue = None
2005-08-03 16:04:54 +02:00
self.started = False
self._sock = None
self.fd = -1
2008-11-24 18:00:20 +01:00
2005-08-03 16:04:54 +02:00
def bind(self):
for ai in self.ais:
# try the different possibilities (ipv6, ipv4, etc.)
try:
self._serv = socket.socket(*ai[:3])
except socket.error, e:
if e.errno == EAFNOSUPPORT:
self.ai = None
continue
raise
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# Under windows Vista, we need that to listen on ipv6 AND ipv4
# Doesn't work under windows XP
if os.name == 'nt':
ver = os.sys.getwindowsversion()
if (ver[3], ver[0], ver[1]) == (2, 6, 0):
# 27 is socket.IPV6_V6ONLY under windows, but not defined ...
self._serv.setsockopt(socket.IPPROTO_IPV6, 27, 1)
# will fail when port as busy, or we don't have rights to bind
try:
self._serv.bind(ai[4])
self.ai = ai
break
except Exception:
self.ai = None
continue
if not self.ai:
# unable to bind, show error dialog
return None
self._serv.listen(socket.SOMAXCONN)
self._serv.setblocking(False)
self.fd = self._serv.fileno()
self.idlequeue.plug_idle(self, False, True)
2005-08-03 16:04:54 +02:00
self.started = True
2008-11-24 18:00:20 +01:00
def pollend(self):
"""
Called when we stop listening on (host, port)
"""
self.disconnect()
2008-11-24 18:00:20 +01:00
def pollin(self):
"""
Accept a new incomming connection and notify queue
"""
sock = self.accept_conn()
self.queue.on_connection_accepted(sock)
2008-11-24 18:00:20 +01:00
def disconnect(self):
"""
Free all resources, we are not listening anymore
"""
self.idlequeue.remove_timeout(self.fd)
self.idlequeue.unplug_idle(self.fd)
self.fd = -1
self.state = -1
self.started = False
try:
self._serv.close()
except Exception:
pass
2008-11-24 18:00:20 +01:00
2005-08-03 16:04:54 +02:00
def accept_conn(self):
"""
Accept a new incomming connection
"""
2005-08-03 16:04:54 +02:00
_sock = self._serv.accept()
_sock[0].setblocking(False)
2005-08-03 16:04:54 +02:00
return _sock
2008-11-24 18:00:20 +01:00
class Socks5Receiver(Socks5, IdleObject):
def __init__(self, idlequeue, streamhost, sid, file_props = None):
2005-07-30 12:58:46 +02:00
self.queue_idx = -1
self.streamhost = streamhost
2005-07-30 12:58:46 +02:00
self.queue = None
self.file_props = file_props
self.connect_timeout = 0
self.connected = False
self.pauses = 0
if not self.file_props:
self.file_props = {}
self.file_props['disconnect_cb'] = self.disconnect
self.file_props['error'] = 0
self.file_props['started'] = True
self.file_props['completed'] = False
self.file_props['paused'] = False
2006-02-12 03:25:21 +01:00
self.file_props['continue_cb'] = self.continue_paused_transfer
self.file_props['stalled'] = False
2008-11-24 18:00:20 +01:00
Socks5.__init__(self, idlequeue, streamhost['host'],
int(streamhost['port']), streamhost['initiator'], streamhost['target'],
sid)
def read_timeout(self):
self.idlequeue.remove_timeout(self.fd)
if self.state > 5:
# no activity for foo seconds
2006-02-12 03:25:21 +01:00
if self.file_props['stalled'] == False:
self.file_props['stalled'] = True
if 'received-len' not in self.file_props:
2006-02-12 03:25:21 +01:00
self.file_props['received-len'] = 0
self.queue.process_result(-1, self)
if READ_TIMEOUT > 0:
self.idlequeue.set_read_timeout(self.fd, READ_TIMEOUT)
else:
2008-11-24 18:00:20 +01:00
# stop transfer, there is no error code for this
2006-02-12 03:25:21 +01:00
self.pollend()
else:
2006-02-12 03:25:21 +01:00
self.queue.reconnect_receiver(self, self.streamhost)
2008-11-24 18:00:20 +01:00
def connect(self):
"""
Create the socket and plug it to the idlequeue
"""
if self.ais is None:
2008-05-10 17:40:27 +02:00
return None
for ai in self.ais:
try:
2008-11-24 18:00:20 +01:00
self._sock = socket.socket(*ai[:3])
# this will not block the GUI
self._sock.setblocking(False)
2008-11-24 18:00:20 +01:00
self._server = ai[4]
break
2008-12-02 16:10:31 +01:00
except socket.error, e:
if not isinstance(e, basestring) and e[0] == EINPROGRESS:
break
2008-12-02 16:10:31 +01:00
# for all other errors, we try other addresses
continue
self.fd = self._sock.fileno()
self.state = 0 # about to be connected
self.idlequeue.plug_idle(self, True, False)
self.do_connect()
self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
return None
2008-11-24 18:00:20 +01:00
def _is_connected(self):
if self.state < 5:
return False
return True
2008-11-24 18:00:20 +01:00
def pollout(self):
self.idlequeue.remove_timeout(self.fd)
if self.state == 0:
self.do_connect()
return
elif self.state == 1: # send initially: version and auth types
self.send_raw(self._get_auth_buff())
elif self.state == 3: # send 'connect' request
self.send_raw(self._get_request_buff(self._get_sha1_auth()))
elif self.file_props['type'] != 'r':
if self.file_props['paused']:
2006-02-12 03:25:21 +01:00
self.idlequeue.plug_idle(self, False, False)
return
result = self.write_next()
self.queue.process_result(result, self)
return
self.state += 1
# unplug and plug for reading
self.idlequeue.plug_idle(self, False, True)
2006-02-12 03:25:21 +01:00
self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
2008-11-24 18:00:20 +01:00
def pollend(self):
if self.state >= 5:
2006-02-12 03:25:21 +01:00
# error during transfer
self.disconnect()
2006-02-12 03:25:21 +01:00
self.file_props['error'] = -1
self.queue.process_result(-1, self)
else:
self.queue.reconnect_receiver(self, self.streamhost)
2008-11-24 18:00:20 +01:00
def pollin(self):
self.idlequeue.remove_timeout(self.fd)
if self.connected:
if self.file_props['paused']:
2006-02-12 03:25:21 +01:00
self.idlequeue.plug_idle(self, False, False)
return
if self.state < 5:
2006-02-12 03:25:21 +01:00
self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
result = self.main(0)
self.queue.process_result(result, self)
elif self.state == 5: # wait for proxy reply
pass
elif self.file_props['type'] == 'r':
2006-02-12 03:25:21 +01:00
self.idlequeue.set_read_timeout(self.fd, STALLED_TIMEOUT)
result = self.get_file_contents(0)
self.queue.process_result(result, self)
else:
self.disconnect()
2008-11-24 18:00:20 +01:00
def do_connect(self):
try:
self._sock.connect(self._server)
self._sock.setblocking(False)
self._send=self._sock.send
self._recv=self._sock.recv
except Exception, ee:
2008-12-02 16:53:23 +01:00
errnum = ee[0]
self.connect_timeout += 1
if errnum == 111 or self.connect_timeout > 1000:
2008-11-24 18:00:20 +01:00
self.queue._connection_refused(self.streamhost,
self.file_props, self.queue_idx)
2005-08-25 22:31:58 +02:00
return None
# win32 needs this
elif errnum not in (10056, EISCONN) or self.state != 0:
2005-08-25 22:31:58 +02:00
return None
else: # socket is already connected
self._sock.setblocking(False)
self._send=self._sock.send
self._recv=self._sock.recv
self.buff = ''
self.connected = True
self.file_props['connected'] = True
2005-08-10 17:59:55 +02:00
self.file_props['disconnect_cb'] = self.disconnect
self.state = 1 # connected
2008-11-24 18:00:20 +01:00
# stop all others connections to sender's streamhosts
self.queue._socket_connected(self.streamhost, self.file_props)
self.idlequeue.plug_idle(self, True, False)
return 1 # we are connected
2008-11-24 18:00:20 +01:00
def main(self, timeout=0):
"""
Begin negotiation. on success 'address' != 0
"""
2005-08-10 17:59:55 +02:00
result = 1
buff = self.receive()
if buff == '':
# end connection
self.pollend()
return
2008-11-24 18:00:20 +01:00
if self.state == 2: # read auth response
if buff is None or len(buff) != 2:
return None
version, method = struct.unpack('!BB', buff[:2])
if version != 0x05 or method == 0xff:
self.disconnect()
elif self.state == 4: # get approve of our request
if buff is None:
return None
sub_buff = buff[:4]
if len(sub_buff) < 4:
return None
2008-12-02 16:53:23 +01:00
version, address_type = struct.unpack('!BxxB', buff[:4])
addrlen = 0
if address_type == 0x03:
addrlen = ord(buff[4])
address = struct.unpack('!%ds' % addrlen, buff[5:addrlen + 5])
portlen = len(buff[addrlen + 5:])
2008-11-24 18:00:20 +01:00
if portlen == 1:
port, = struct.unpack('!B', buff[addrlen + 5])
elif portlen == 2:
port, = struct.unpack('!H', buff[addrlen + 5:])
else: # Gaim bug :)
port, = struct.unpack('!H', buff[addrlen + 5:addrlen + 7])
self.remaining_buff = buff[addrlen + 7:]
self.state = 5 # for senders: init file_props and send '\n'
if self.queue.on_success:
2008-11-24 18:00:20 +01:00
result = self.queue.send_success_reply(self.file_props,
2005-08-10 17:59:55 +02:00
self.streamhost)
if result == 0:
2005-08-07 20:43:53 +02:00
self.state = 8
self.disconnect()
2008-11-24 18:00:20 +01:00
# for senders: init file_props
if result == 1 and self.state == 5:
if self.file_props['type'] == 's':
self.file_props['error'] = 0
self.file_props['disconnect_cb'] = self.disconnect
self.file_props['started'] = True
self.file_props['completed'] = False
self.file_props['paused'] = False
self.file_props['stalled'] = False
2005-09-09 00:12:14 +02:00
self.file_props['elapsed-time'] = 0
self.file_props['last-time'] = self.idlequeue.current_time()
self.file_props['received-len'] = 0
self.pauses = 0
# start sending file contents to socket
self.idlequeue.set_read_timeout(self.fd, STALLED_TIMEOUT)
self.idlequeue.plug_idle(self, True, False)
else:
# receiving file contents from socket
self.idlequeue.plug_idle(self, False, True)
2006-02-12 03:25:21 +01:00
self.file_props['continue_cb'] = self.continue_paused_transfer
# we have set up the connection, next - retrieve file
2008-11-24 18:00:20 +01:00
self.state = 6
2005-08-10 17:59:55 +02:00
if self.state < 5:
self.idlequeue.plug_idle(self, True, False)
self.state += 1
return None
2008-11-24 18:00:20 +01:00
def disconnect(self, cb=True):
"""
Close the socket. Remove self from queue if cb is True
"""
2008-11-24 18:00:20 +01:00
# close connection
Socks5.disconnect(self)
if cb is True:
self.file_props['disconnect_cb'] = None
2005-07-30 12:58:46 +02:00
if self.queue is not None:
2005-08-10 17:59:55 +02:00
self.queue.remove_receiver(self.queue_idx, False)
# vim: se ts=3: