2006-09-19 14:46:09 +02:00
|
|
|
|
## common/zeroconf/client_zeroconf.py
|
2006-07-27 22:36:21 +02:00
|
|
|
|
##
|
|
|
|
|
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
|
2006-09-18 00:57:41 +02:00
|
|
|
|
## 2006 Dimitur Kirov <dkirov@gmail.com>
|
2006-07-27 22:36:21 +02:00
|
|
|
|
##
|
|
|
|
|
## This program 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 2 only.
|
|
|
|
|
##
|
|
|
|
|
## This program 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.
|
|
|
|
|
##
|
2006-09-18 00:57:41 +02:00
|
|
|
|
from common import gajim
|
2006-09-21 01:24:07 +02:00
|
|
|
|
import common.xmpp
|
2006-09-18 00:57:41 +02:00
|
|
|
|
from common.xmpp.idlequeue import IdleObject
|
2006-09-19 17:10:06 +02:00
|
|
|
|
from common.xmpp import dispatcher_nb, simplexml
|
2006-09-18 00:57:41 +02:00
|
|
|
|
from common.xmpp.client import *
|
|
|
|
|
from common.xmpp.simplexml import ustr
|
2006-10-02 22:45:49 +02:00
|
|
|
|
from common.zeroconf import zeroconf
|
|
|
|
|
|
2006-09-19 17:10:06 +02:00
|
|
|
|
from common.xmpp.protocol import *
|
2006-09-18 00:57:41 +02:00
|
|
|
|
import socket
|
|
|
|
|
import errno
|
2006-10-02 16:13:43 +02:00
|
|
|
|
import sys
|
2006-05-29 21:57:39 +02:00
|
|
|
|
|
2006-06-28 01:09:21 +02:00
|
|
|
|
from common.zeroconf import roster_zeroconf
|
2006-05-29 21:57:39 +02:00
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
MAX_BUFF_LEN = 65536
|
|
|
|
|
DATA_RECEIVED='DATA RECEIVED'
|
|
|
|
|
DATA_SENT='DATA SENT'
|
2006-09-19 17:10:06 +02:00
|
|
|
|
TYPE_SERVER, TYPE_CLIENT = range(2)
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
2006-09-20 13:01:47 +02:00
|
|
|
|
# wait XX sec to establish a connection
|
2006-11-06 22:30:39 +01:00
|
|
|
|
CONNECT_TIMEOUT_SECONDS = 10
|
2006-09-20 13:01:47 +02:00
|
|
|
|
|
|
|
|
|
# after XX sec with no activity, close the stream
|
2006-11-06 22:30:39 +01:00
|
|
|
|
ACTIVITY_TIMEOUT_SECONDS = 30
|
2006-09-20 13:01:47 +02:00
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
class ZeroconfListener(IdleObject):
|
2006-09-19 21:56:04 +02:00
|
|
|
|
def __init__(self, port, conn_holder):
|
2006-09-18 00:57:41 +02:00
|
|
|
|
''' handle all incomming connections on ('0.0.0.0', port)'''
|
|
|
|
|
self.port = port
|
|
|
|
|
self.queue_idx = -1
|
|
|
|
|
#~ self.queue = None
|
|
|
|
|
self.started = False
|
|
|
|
|
self._sock = None
|
|
|
|
|
self.fd = -1
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.caller = conn_holder.caller
|
|
|
|
|
self.conn_holder = conn_holder
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
|
|
|
|
def bind(self):
|
|
|
|
|
self._serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
|
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)
|
2006-09-29 01:38:55 +02:00
|
|
|
|
# will fail when port is busy, or we don't have rights to bind
|
2006-09-18 00:57:41 +02:00
|
|
|
|
try:
|
|
|
|
|
self._serv.bind(('0.0.0.0', self.port))
|
|
|
|
|
except Exception, e:
|
|
|
|
|
# unable to bind, show error dialog
|
|
|
|
|
return None
|
|
|
|
|
self._serv.listen(socket.SOMAXCONN)
|
|
|
|
|
self._serv.setblocking(False)
|
|
|
|
|
self.fd = self._serv.fileno()
|
|
|
|
|
gajim.idlequeue.plug_idle(self, False, True)
|
|
|
|
|
self.started = True
|
|
|
|
|
|
|
|
|
|
def pollend(self):
|
|
|
|
|
''' called when we stop listening on (host, port) '''
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.disconnect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
|
|
|
|
def pollin(self):
|
|
|
|
|
''' accept a new incomming connection and notify queue'''
|
|
|
|
|
sock = self.accept_conn()
|
2006-09-19 21:56:04 +02:00
|
|
|
|
P2PClient(sock[0], sock[1][0], sock[1][1], self.conn_holder)
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
|
|
|
|
def disconnect(self):
|
|
|
|
|
''' free all resources, we are not listening anymore '''
|
|
|
|
|
gajim.idlequeue.remove_timeout(self.fd)
|
|
|
|
|
gajim.idlequeue.unplug_idle(self.fd)
|
|
|
|
|
self.fd = -1
|
|
|
|
|
self.started = False
|
|
|
|
|
try:
|
|
|
|
|
self._serv.close()
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.conn_holder.kill_all_connections()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
|
|
|
|
def accept_conn(self):
|
2006-11-06 22:30:39 +01:00
|
|
|
|
''' accepts a new incoming connection '''
|
2006-09-18 00:57:41 +02:00
|
|
|
|
_sock = self._serv.accept()
|
|
|
|
|
_sock[0].setblocking(False)
|
|
|
|
|
return _sock
|
|
|
|
|
|
2006-09-19 14:46:09 +02:00
|
|
|
|
class P2PClient(IdleObject):
|
2006-11-06 22:30:39 +01:00
|
|
|
|
def __init__(self, _sock, host, port, conn_holder, stanzaqueue = [], to = None):
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self._owner = self
|
|
|
|
|
self.Namespace = 'jabber:client'
|
|
|
|
|
self.defaultNamespace = self.Namespace
|
2006-09-19 19:10:28 +02:00
|
|
|
|
self._component = 0
|
|
|
|
|
self._registered_name = None
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self._caller = conn_holder.caller
|
|
|
|
|
self.conn_holder = conn_holder
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.stanzaqueue = stanzaqueue
|
2006-09-19 22:49:34 +02:00
|
|
|
|
self.to = to
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.Server = host
|
|
|
|
|
self.DBG = 'client'
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.Connection = None
|
2006-09-20 13:01:47 +02:00
|
|
|
|
if gajim.verbose:
|
|
|
|
|
debug = ['always', 'nodebuilder']
|
|
|
|
|
else:
|
|
|
|
|
debug = []
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self._DEBUG = Debug.Debug(debug)
|
|
|
|
|
self.DEBUG = self._DEBUG.Show
|
|
|
|
|
self.debug_flags = self._DEBUG.debug_flags
|
|
|
|
|
self.debug_flags.append(self.DBG)
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.sock_hash = None
|
2006-09-19 20:18:03 +02:00
|
|
|
|
if _sock:
|
|
|
|
|
self.sock_type = TYPE_SERVER
|
|
|
|
|
else:
|
|
|
|
|
self.sock_type = TYPE_CLIENT
|
2007-07-27 20:19:09 +02:00
|
|
|
|
self.fd = -1
|
2006-11-06 22:30:39 +01:00
|
|
|
|
conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect, self)
|
2006-09-19 22:49:34 +02:00
|
|
|
|
self.sock_hash = conn._sock.__hash__
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.fd = conn.fd
|
|
|
|
|
self.conn_holder.add_connection(self, self.Server, port, self.to)
|
2006-11-08 00:25:24 +01:00
|
|
|
|
# count messages in queue
|
|
|
|
|
for val in self.stanzaqueue:
|
|
|
|
|
stanza, is_message = val
|
|
|
|
|
if is_message:
|
2007-07-27 20:19:09 +02:00
|
|
|
|
if self.fd == -1:
|
|
|
|
|
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
|
|
|
|
|
_('Connection to host could not be established'), None, None])
|
2006-11-08 00:25:24 +01:00
|
|
|
|
else:
|
2007-07-27 20:19:09 +02:00
|
|
|
|
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
|
|
|
|
|
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
|
|
|
|
|
else:
|
|
|
|
|
self.conn_holder.number_of_awaiting_messages[self.fd]=1
|
2006-09-19 22:49:34 +02:00
|
|
|
|
|
2006-11-06 22:30:39 +01:00
|
|
|
|
def add_stanza(self, stanza, is_message = False):
|
2006-09-19 22:49:34 +02:00
|
|
|
|
if self.Connection:
|
|
|
|
|
if self.Connection.state == -1:
|
|
|
|
|
return False
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.send(stanza, is_message)
|
2006-09-19 22:49:34 +02:00
|
|
|
|
else:
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.stanzaqueue.append((stanza, is_message))
|
|
|
|
|
|
|
|
|
|
if is_message:
|
|
|
|
|
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
|
|
|
|
|
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
|
|
|
|
|
else:
|
|
|
|
|
self.conn_holder.number_of_awaiting_messages[self.fd] = 1
|
2006-11-08 00:25:24 +01:00
|
|
|
|
|
2006-09-19 22:49:34 +02:00
|
|
|
|
return True
|
2006-09-19 17:10:06 +02:00
|
|
|
|
|
2006-11-06 22:30:39 +01:00
|
|
|
|
def on_message_sent(self, connection_id):
|
|
|
|
|
self.conn_holder.number_of_awaiting_messages[connection_id]-=1
|
|
|
|
|
|
2006-09-19 17:10:06 +02:00
|
|
|
|
def on_connect(self, conn):
|
|
|
|
|
self.Connection = conn
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.Connection.PlugIn(self)
|
|
|
|
|
dispatcher_nb.Dispatcher().PlugIn(self)
|
2006-09-21 01:24:07 +02:00
|
|
|
|
self._register_handlers()
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
2006-09-19 17:10:06 +02:00
|
|
|
|
def StreamInit(self):
|
|
|
|
|
''' Send an initial stream header. '''
|
|
|
|
|
self.Dispatcher.Stream = simplexml.NodeBuilder()
|
|
|
|
|
self.Dispatcher.Stream._dispatch_depth = 2
|
|
|
|
|
self.Dispatcher.Stream.dispatch = self.Dispatcher.dispatch
|
2006-09-19 20:18:03 +02:00
|
|
|
|
self.Dispatcher.Stream.stream_header_received = self._check_stream_start
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.debug_flags.append(simplexml.DBG_NODEBUILDER)
|
|
|
|
|
self.Dispatcher.Stream.DEBUG = self.DEBUG
|
|
|
|
|
self.Dispatcher.Stream.features = None
|
2006-09-19 20:18:03 +02:00
|
|
|
|
if self.sock_type == TYPE_CLIENT:
|
|
|
|
|
self.send_stream_header()
|
|
|
|
|
|
|
|
|
|
def send_stream_header(self):
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.Dispatcher._metastream = Node('stream:stream')
|
|
|
|
|
self.Dispatcher._metastream.setNamespace(self.Namespace)
|
2007-04-25 11:01:05 +02:00
|
|
|
|
self.Dispatcher._metastream.setAttr('version', '1.0')
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.Dispatcher._metastream.setAttr('xmlns:stream', NS_STREAMS)
|
2007-04-18 20:04:52 +02:00
|
|
|
|
self.Dispatcher._metastream.setAttr('from', self.conn_holder.zeroconf.name)
|
2007-04-25 11:01:05 +02:00
|
|
|
|
if self.to:
|
|
|
|
|
self.Dispatcher._metastream.setAttr('to', self.to)
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.Dispatcher.send("<?xml version='1.0'?>%s>" % str(self.Dispatcher._metastream)[:-2])
|
|
|
|
|
|
2006-09-19 20:18:03 +02:00
|
|
|
|
def _check_stream_start(self, ns, tag, attrs):
|
|
|
|
|
if ns<>NS_STREAMS or tag<>'stream':
|
2006-11-10 21:43:23 +01:00
|
|
|
|
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
|
|
|
|
|
_('Connection to host could not be established: Incorrect answer from server.'), None, None])
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.Connection.DEBUG('Incorrect stream start: (%s,%s).Terminating! ' % (tag, ns), 'error')
|
|
|
|
|
self.Connection.disconnect()
|
|
|
|
|
return
|
2006-09-19 20:18:03 +02:00
|
|
|
|
if self.sock_type == TYPE_SERVER:
|
2007-04-25 11:01:05 +02:00
|
|
|
|
if attrs.has_key('from'):
|
|
|
|
|
self.to = attrs['from']
|
2006-09-19 20:18:03 +02:00
|
|
|
|
self.send_stream_header()
|
2007-04-25 12:30:07 +02:00
|
|
|
|
if attrs.has_key('version') and attrs['version'] == '1.0':
|
|
|
|
|
# other part supports stream features
|
|
|
|
|
features = Node('stream:features')
|
|
|
|
|
self.Dispatcher.send(features)
|
|
|
|
|
while self.stanzaqueue:
|
|
|
|
|
stanza, is_message = self.stanzaqueue.pop(0)
|
|
|
|
|
self.send(stanza, is_message)
|
|
|
|
|
elif self.sock_type == TYPE_CLIENT:
|
2006-11-06 22:30:39 +01:00
|
|
|
|
while self.stanzaqueue:
|
|
|
|
|
stanza, is_message = self.stanzaqueue.pop(0)
|
|
|
|
|
self.send(stanza, is_message)
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
2006-09-19 20:18:03 +02:00
|
|
|
|
|
2006-09-19 21:56:04 +02:00
|
|
|
|
def on_disconnect(self):
|
|
|
|
|
if self.conn_holder:
|
2007-10-07 22:58:55 +02:00
|
|
|
|
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
|
|
|
|
|
if self.conn_holder.number_of_awaiting_messages[self.fd] > 0:
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
|
|
|
|
|
_('Connection to host could not be established'), None, None])
|
2007-10-07 22:58:55 +02:00
|
|
|
|
del self.conn_holder.number_of_awaiting_messages[self.fd]
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.conn_holder.remove_connection(self.sock_hash)
|
2006-09-19 14:46:09 +02:00
|
|
|
|
if self.__dict__.has_key('Dispatcher'):
|
|
|
|
|
self.Dispatcher.PlugOut()
|
|
|
|
|
if self.__dict__.has_key('P2PConnection'):
|
|
|
|
|
self.P2PConnection.PlugOut()
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.Connection = None
|
|
|
|
|
self._caller = None
|
|
|
|
|
self.conn_holder = None
|
|
|
|
|
|
|
|
|
|
def force_disconnect(self):
|
|
|
|
|
if self.Connection:
|
|
|
|
|
self.disconnect()
|
|
|
|
|
else:
|
|
|
|
|
self.on_disconnect()
|
2006-09-19 14:46:09 +02:00
|
|
|
|
|
|
|
|
|
def _on_receive_document_attrs(self, data):
|
|
|
|
|
if data:
|
|
|
|
|
self.Dispatcher.ProcessNonBlocking(data)
|
|
|
|
|
if not hasattr(self, 'Dispatcher') or \
|
|
|
|
|
self.Dispatcher.Stream._document_attrs is None:
|
|
|
|
|
return
|
|
|
|
|
self.onreceive(None)
|
|
|
|
|
if self.Dispatcher.Stream._document_attrs.has_key('version') and \
|
|
|
|
|
self.Dispatcher.Stream._document_attrs['version'] == '1.0':
|
|
|
|
|
#~ self.onreceive(self._on_receive_stream_features)
|
|
|
|
|
#XXX continue with TLS
|
|
|
|
|
return
|
|
|
|
|
self.onreceive(None)
|
|
|
|
|
return True
|
|
|
|
|
|
2006-09-21 01:24:07 +02:00
|
|
|
|
def _register_handlers(self):
|
|
|
|
|
self.RegisterHandler('message', lambda conn, data:self._caller._messageCB(self.Server, conn, data))
|
|
|
|
|
self.RegisterHandler('iq', self._caller._siSetCB, 'set',
|
|
|
|
|
common.xmpp.NS_SI)
|
|
|
|
|
self.RegisterHandler('iq', self._caller._siErrorCB, 'error',
|
|
|
|
|
common.xmpp.NS_SI)
|
|
|
|
|
self.RegisterHandler('iq', self._caller._siResultCB, 'result',
|
|
|
|
|
common.xmpp.NS_SI)
|
|
|
|
|
self.RegisterHandler('iq', self._caller._bytestreamSetCB, 'set',
|
|
|
|
|
common.xmpp.NS_BYTESTREAM)
|
|
|
|
|
self.RegisterHandler('iq', self._caller._bytestreamResultCB, 'result',
|
|
|
|
|
common.xmpp.NS_BYTESTREAM)
|
|
|
|
|
self.RegisterHandler('iq', self._caller._bytestreamErrorCB, 'error',
|
|
|
|
|
common.xmpp.NS_BYTESTREAM)
|
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
class P2PConnection(IdleObject, PlugIn):
|
2006-11-06 22:30:39 +01:00
|
|
|
|
def __init__(self, sock_hash, _sock, host = None, port = None, caller = None, on_connect = None, client = None):
|
2006-09-19 14:46:09 +02:00
|
|
|
|
IdleObject.__init__(self)
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self._owner = client
|
2006-09-18 00:57:41 +02:00
|
|
|
|
PlugIn.__init__(self)
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.DBG_LINE='socket'
|
2006-09-18 00:57:41 +02:00
|
|
|
|
self.sendqueue = []
|
|
|
|
|
self.sendbuff = None
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.buff_is_message = False
|
2006-09-18 00:57:41 +02:00
|
|
|
|
self._sock = _sock
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.sock_hash = None
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.host, self.port = host, port
|
|
|
|
|
self.on_connect = on_connect
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.client = client
|
2006-09-18 00:57:41 +02:00
|
|
|
|
self.writable = False
|
|
|
|
|
self.readable = False
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self._exported_methods=[self.send, self.disconnect, self.onreceive]
|
2006-09-18 00:57:41 +02:00
|
|
|
|
self.on_receive = None
|
2006-09-19 17:10:06 +02:00
|
|
|
|
if _sock:
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self._sock = _sock
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.state = 1
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self._sock.setblocking(False)
|
|
|
|
|
self.fd = self._sock.fileno()
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.on_connect(self)
|
|
|
|
|
else:
|
|
|
|
|
self.state = 0
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
|
self._sock.setblocking(False)
|
|
|
|
|
self.fd = self._sock.fileno()
|
|
|
|
|
gajim.idlequeue.plug_idle(self, True, False)
|
2006-09-20 13:01:47 +02:00
|
|
|
|
self.set_timeout(CONNECT_TIMEOUT_SECONDS)
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.do_connect()
|
2006-09-20 13:01:47 +02:00
|
|
|
|
|
|
|
|
|
def set_timeout(self, timeout):
|
|
|
|
|
gajim.idlequeue.remove_timeout(self.fd)
|
|
|
|
|
if self.state >= 0:
|
|
|
|
|
gajim.idlequeue.set_read_timeout(self.fd, timeout)
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
2006-09-19 14:46:09 +02:00
|
|
|
|
def plugin(self, owner):
|
|
|
|
|
self.onreceive(owner._on_receive_document_attrs)
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self._plug_idle()
|
2006-09-19 14:46:09 +02:00
|
|
|
|
return True
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
2006-09-19 14:46:09 +02:00
|
|
|
|
def plugout(self):
|
|
|
|
|
''' Disconnect from the remote server and unregister self.disconnected method from
|
|
|
|
|
the owner's dispatcher. '''
|
|
|
|
|
self.disconnect()
|
|
|
|
|
self._owner = None
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
def onreceive(self, recv_handler):
|
|
|
|
|
if not recv_handler:
|
|
|
|
|
if hasattr(self._owner, 'Dispatcher'):
|
|
|
|
|
self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
|
|
|
|
|
else:
|
|
|
|
|
self.on_receive = None
|
|
|
|
|
return
|
|
|
|
|
_tmp = self.on_receive
|
|
|
|
|
# make sure this cb is not overriden by recursive calls
|
|
|
|
|
if not recv_handler(None) and _tmp == self.on_receive:
|
|
|
|
|
self.on_receive = recv_handler
|
2006-11-06 22:30:39 +01:00
|
|
|
|
|
2007-03-20 08:13:25 +01:00
|
|
|
|
def send(self, packet, is_message = False, now = False):
|
|
|
|
|
'''Append stanza to the queue of messages to be send if now is
|
|
|
|
|
False, else send it instantly.
|
2006-09-18 00:57:41 +02:00
|
|
|
|
If supplied data is unicode string, encode it to utf-8.
|
|
|
|
|
'''
|
|
|
|
|
if self.state <= 0:
|
|
|
|
|
return
|
2006-11-06 22:30:39 +01:00
|
|
|
|
|
|
|
|
|
r = packet
|
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
if isinstance(r, unicode):
|
|
|
|
|
r = r.encode('utf-8')
|
2006-09-19 19:10:28 +02:00
|
|
|
|
elif not isinstance(r, str):
|
|
|
|
|
r = ustr(r).encode('utf-8')
|
2006-11-06 22:30:39 +01:00
|
|
|
|
|
2007-03-20 08:13:25 +01:00
|
|
|
|
if now:
|
|
|
|
|
self.sendqueue.insert(0, (r, is_message))
|
|
|
|
|
self._do_send()
|
|
|
|
|
else:
|
|
|
|
|
self.sendqueue.append((r, is_message))
|
2006-09-18 00:57:41 +02:00
|
|
|
|
self._plug_idle()
|
|
|
|
|
|
|
|
|
|
def read_timeout(self):
|
2006-11-10 21:43:23 +01:00
|
|
|
|
if self.client.conn_holder.number_of_awaiting_messages.has_key(self.fd) \
|
|
|
|
|
and self.client.conn_holder.number_of_awaiting_messages[self.fd] > 0:
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.client._caller.dispatch('MSGERROR',[unicode(self.client.to), -1, \
|
2006-11-10 21:43:23 +01:00
|
|
|
|
_('Connection to host could not be established: Timeout while sending data.'), None, None])
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.client.conn_holder.number_of_awaiting_messages[self.fd] = 0
|
2006-09-20 13:01:47 +02:00
|
|
|
|
self.pollend()
|
2006-11-06 22:30:39 +01:00
|
|
|
|
|
2006-09-19 17:10:06 +02:00
|
|
|
|
def do_connect(self):
|
2006-09-19 21:56:04 +02:00
|
|
|
|
errnum = 0
|
2006-09-19 17:10:06 +02:00
|
|
|
|
try:
|
|
|
|
|
self._sock.connect((self.host, self.port))
|
|
|
|
|
self._sock.setblocking(False)
|
|
|
|
|
except Exception, ee:
|
|
|
|
|
(errnum, errstr) = ee
|
2006-11-06 22:30:39 +01:00
|
|
|
|
if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):
|
2006-09-19 21:56:04 +02:00
|
|
|
|
return
|
|
|
|
|
# win32 needs this
|
|
|
|
|
elif errnum not in (0, 10056, errno.EISCONN) or self.state != 0:
|
|
|
|
|
self.disconnect()
|
|
|
|
|
return None
|
|
|
|
|
else: # socket is already connected
|
|
|
|
|
self._sock.setblocking(False)
|
2006-09-19 17:10:06 +02:00
|
|
|
|
self.state = 1 # connected
|
|
|
|
|
self.on_connect(self)
|
|
|
|
|
return 1 # we are connected
|
|
|
|
|
|
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
def pollout(self):
|
2006-09-19 17:10:06 +02:00
|
|
|
|
if self.state == 0:
|
|
|
|
|
self.do_connect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
return
|
|
|
|
|
gajim.idlequeue.remove_timeout(self.fd)
|
|
|
|
|
self._do_send()
|
|
|
|
|
|
|
|
|
|
def pollend(self):
|
|
|
|
|
self.state = -1
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.disconnect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
|
|
|
|
def pollin(self):
|
|
|
|
|
''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.'''
|
|
|
|
|
received = ''
|
|
|
|
|
errnum = 0
|
|
|
|
|
try:
|
|
|
|
|
# get as many bites, as possible, but not more than RECV_BUFSIZE
|
2006-09-19 17:10:06 +02:00
|
|
|
|
received = self._sock.recv(MAX_BUFF_LEN)
|
2006-09-18 00:57:41 +02:00
|
|
|
|
except Exception, e:
|
|
|
|
|
if len(e.args) > 0 and isinstance(e.args[0], int):
|
|
|
|
|
errnum = e[0]
|
|
|
|
|
# "received" will be empty anyhow
|
|
|
|
|
if errnum == socket.SSL_ERROR_WANT_READ:
|
|
|
|
|
pass
|
|
|
|
|
elif errnum in [errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN]:
|
|
|
|
|
self.pollend()
|
|
|
|
|
# don't proccess result, cas it will raise error
|
|
|
|
|
return
|
|
|
|
|
elif not received :
|
|
|
|
|
if errnum != socket.SSL_ERROR_EOF:
|
|
|
|
|
# 8 EOF occurred in violation of protocol
|
|
|
|
|
self.pollend()
|
|
|
|
|
if self.state >= 0:
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.disconnect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if self.state < 0:
|
|
|
|
|
return
|
|
|
|
|
if self.on_receive:
|
2006-09-20 23:49:11 +02:00
|
|
|
|
if self._owner.sock_type == TYPE_CLIENT:
|
|
|
|
|
self.set_timeout(ACTIVITY_TIMEOUT_SECONDS)
|
2006-09-19 19:58:29 +02:00
|
|
|
|
if received.strip():
|
|
|
|
|
self.DEBUG(received, 'got')
|
2006-09-18 00:57:41 +02:00
|
|
|
|
if hasattr(self._owner, 'Dispatcher'):
|
|
|
|
|
self._owner.Dispatcher.Event('', DATA_RECEIVED, received)
|
|
|
|
|
self.on_receive(received)
|
|
|
|
|
else:
|
|
|
|
|
# This should never happed, so we need the debug
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.DEBUG('Unhandled data received: %s' % received,'error')
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.disconnect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def onreceive(self, recv_handler):
|
|
|
|
|
if not recv_handler:
|
|
|
|
|
if hasattr(self._owner, 'Dispatcher'):
|
|
|
|
|
self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
|
|
|
|
|
else:
|
|
|
|
|
self.on_receive = None
|
|
|
|
|
return
|
|
|
|
|
_tmp = self.on_receive
|
|
|
|
|
# make sure this cb is not overriden by recursive calls
|
|
|
|
|
if not recv_handler(None) and _tmp == self.on_receive:
|
|
|
|
|
self.on_receive = recv_handler
|
|
|
|
|
|
2006-09-19 14:46:09 +02:00
|
|
|
|
def disconnect(self):
|
2006-09-18 00:57:41 +02:00
|
|
|
|
''' Closes the socket. '''
|
|
|
|
|
gajim.idlequeue.remove_timeout(self.fd)
|
|
|
|
|
gajim.idlequeue.unplug_idle(self.fd)
|
|
|
|
|
try:
|
|
|
|
|
self._sock.shutdown(socket.SHUT_RDWR)
|
|
|
|
|
self._sock.close()
|
|
|
|
|
except:
|
|
|
|
|
# socket is already closed
|
|
|
|
|
pass
|
|
|
|
|
self.fd = -1
|
|
|
|
|
self.state = -1
|
2006-09-19 22:07:44 +02:00
|
|
|
|
if self._owner:
|
|
|
|
|
self._owner.on_disconnect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
|
|
|
|
def _do_send(self):
|
|
|
|
|
if not self.sendbuff:
|
|
|
|
|
if not self.sendqueue:
|
|
|
|
|
return None # nothing to send
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.sendbuff, self.buff_is_message = self.sendqueue.pop(0)
|
2006-09-18 00:57:41 +02:00
|
|
|
|
self.sent_data = self.sendbuff
|
|
|
|
|
try:
|
2006-09-19 17:10:06 +02:00
|
|
|
|
send_count = self._sock.send(self.sendbuff)
|
2006-09-18 00:57:41 +02:00
|
|
|
|
if send_count:
|
|
|
|
|
self.sendbuff = self.sendbuff[send_count:]
|
|
|
|
|
if not self.sendbuff and not self.sendqueue:
|
|
|
|
|
if self.state < 0:
|
|
|
|
|
gajim.idlequeue.unplug_idle(self.fd)
|
|
|
|
|
self._on_send()
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.disconnect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
return
|
|
|
|
|
# we are not waiting for write
|
|
|
|
|
self._plug_idle()
|
|
|
|
|
self._on_send()
|
2006-11-06 22:30:39 +01:00
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
except socket.error, e:
|
|
|
|
|
if e[0] == socket.SSL_ERROR_WANT_WRITE:
|
|
|
|
|
return True
|
|
|
|
|
if self.state < 0:
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self.disconnect()
|
2006-09-18 00:57:41 +02:00
|
|
|
|
return
|
2006-09-19 14:46:09 +02:00
|
|
|
|
self._on_send_failure()
|
|
|
|
|
return
|
2006-09-20 23:49:11 +02:00
|
|
|
|
if self._owner.sock_type == TYPE_CLIENT:
|
|
|
|
|
self.set_timeout(ACTIVITY_TIMEOUT_SECONDS)
|
2006-09-18 00:57:41 +02:00
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _plug_idle(self):
|
|
|
|
|
readable = self.state != 0
|
|
|
|
|
if self.sendqueue or self.sendbuff:
|
|
|
|
|
writable = True
|
|
|
|
|
else:
|
|
|
|
|
writable = False
|
|
|
|
|
if self.writable != writable or self.readable != readable:
|
|
|
|
|
gajim.idlequeue.plug_idle(self, writable, readable)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _on_send(self):
|
|
|
|
|
if self.sent_data and self.sent_data.strip():
|
2006-09-19 19:58:29 +02:00
|
|
|
|
self.DEBUG(self.sent_data,'sent')
|
2006-09-18 00:57:41 +02:00
|
|
|
|
if hasattr(self._owner, 'Dispatcher'):
|
|
|
|
|
self._owner.Dispatcher.Event('', DATA_SENT, self.sent_data)
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.sent_data = None
|
|
|
|
|
if self.buff_is_message:
|
|
|
|
|
self._owner.on_message_sent(self.fd)
|
|
|
|
|
self.buff_is_message = False
|
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
def _on_send_failure(self):
|
|
|
|
|
self.DEBUG("Socket error while sending data",'error')
|
|
|
|
|
self._owner.disconnected()
|
|
|
|
|
self.sent_data = None
|
|
|
|
|
|
|
|
|
|
|
2006-05-29 21:57:39 +02:00
|
|
|
|
class ClientZeroconf:
|
2006-10-02 22:45:49 +02:00
|
|
|
|
def __init__(self, caller):
|
2006-09-18 00:57:41 +02:00
|
|
|
|
self.caller = caller
|
2006-10-02 22:45:49 +02:00
|
|
|
|
self.zeroconf = None
|
|
|
|
|
self.roster = None
|
|
|
|
|
self.last_msg = ''
|
2006-09-19 21:56:04 +02:00
|
|
|
|
self.connections = {}
|
2006-09-19 22:49:34 +02:00
|
|
|
|
self.recipient_to_hash = {}
|
|
|
|
|
self.ip_to_hash = {}
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.hash_to_port = {}
|
2006-10-11 23:26:58 +02:00
|
|
|
|
self.listener = None
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.number_of_awaiting_messages = {}
|
2006-10-02 22:45:49 +02:00
|
|
|
|
|
|
|
|
|
def connect(self, show, msg):
|
|
|
|
|
self.port = self.start_listener(self.caller.port)
|
|
|
|
|
if not self.port:
|
2006-10-12 01:11:54 +02:00
|
|
|
|
return False
|
2006-10-02 22:45:49 +02:00
|
|
|
|
self.zeroconf_init(show, msg)
|
|
|
|
|
if not self.zeroconf.connect():
|
|
|
|
|
self.disconnect()
|
2006-10-12 01:11:54 +02:00
|
|
|
|
return None
|
2006-10-02 22:45:49 +02:00
|
|
|
|
self.roster = roster_zeroconf.Roster(self.zeroconf)
|
2006-10-12 01:11:54 +02:00
|
|
|
|
return True
|
2006-10-02 22:45:49 +02:00
|
|
|
|
|
|
|
|
|
def remove_announce(self):
|
2006-10-03 00:41:48 +02:00
|
|
|
|
if self.zeroconf:
|
|
|
|
|
return self.zeroconf.remove_announce()
|
2006-10-02 22:45:49 +02:00
|
|
|
|
|
|
|
|
|
def announce(self):
|
2006-10-03 00:41:48 +02:00
|
|
|
|
if self.zeroconf:
|
|
|
|
|
return self.zeroconf.announce()
|
2006-10-02 22:45:49 +02:00
|
|
|
|
|
|
|
|
|
def set_show_msg(self, show, msg):
|
2006-10-03 00:41:48 +02:00
|
|
|
|
if self.zeroconf:
|
|
|
|
|
self.zeroconf.txt['msg'] = msg
|
|
|
|
|
self.last_msg = msg
|
|
|
|
|
return self.zeroconf.update_txt(show)
|
2006-10-02 22:45:49 +02:00
|
|
|
|
|
|
|
|
|
def resolve_all(self):
|
2006-10-03 00:41:48 +02:00
|
|
|
|
if self.zeroconf:
|
|
|
|
|
self.zeroconf.resolve_all()
|
2006-10-02 22:45:49 +02:00
|
|
|
|
|
|
|
|
|
def reannounce(self, txt):
|
|
|
|
|
self.remove_announce()
|
|
|
|
|
self.zeroconf.txt = txt
|
|
|
|
|
self.zeroconf.port = self.port
|
|
|
|
|
self.zeroconf.username = self.caller.username
|
|
|
|
|
return self.announce()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def zeroconf_init(self, show, msg):
|
|
|
|
|
self.zeroconf = zeroconf.Zeroconf(self.caller._on_new_service,
|
|
|
|
|
self.caller._on_remove_service, self.caller._on_name_conflictCB,
|
2006-10-12 04:21:23 +02:00
|
|
|
|
self.caller._on_disconnected, self.caller._on_error, self.caller.username, self.caller.host, self.port)
|
2006-10-02 22:45:49 +02:00
|
|
|
|
self.zeroconf.txt['msg'] = msg
|
|
|
|
|
self.zeroconf.txt['status'] = show
|
|
|
|
|
self.zeroconf.txt['1st'] = self.caller.first
|
|
|
|
|
self.zeroconf.txt['last'] = self.caller.last
|
|
|
|
|
self.zeroconf.txt['jid'] = self.caller.jabber_id
|
|
|
|
|
self.zeroconf.txt['email'] = self.caller.email
|
|
|
|
|
self.zeroconf.username = self.caller.username
|
|
|
|
|
self.zeroconf.host = self.caller.host
|
|
|
|
|
self.zeroconf.port = self.port
|
|
|
|
|
self.last_msg = msg
|
|
|
|
|
|
|
|
|
|
def disconnect(self):
|
|
|
|
|
if self.listener:
|
|
|
|
|
self.listener.disconnect()
|
|
|
|
|
self.listener = None
|
|
|
|
|
if self.zeroconf:
|
|
|
|
|
self.zeroconf.disconnect()
|
|
|
|
|
self.zeroconf = None
|
|
|
|
|
if self.roster:
|
|
|
|
|
self.roster.zeroconf = None
|
|
|
|
|
self.roster._data = None
|
|
|
|
|
self.roster = None
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
|
|
|
|
def kill_all_connections(self):
|
|
|
|
|
for connection in self.connections.values():
|
|
|
|
|
connection.force_disconnect()
|
|
|
|
|
|
2006-11-06 22:30:39 +01:00
|
|
|
|
def add_connection(self, connection, ip, port, recipient):
|
2006-09-19 21:56:04 +02:00
|
|
|
|
sock_hash = connection.sock_hash
|
|
|
|
|
if sock_hash not in self.connections:
|
|
|
|
|
self.connections[sock_hash] = connection
|
2006-09-19 22:49:34 +02:00
|
|
|
|
self.ip_to_hash[ip] = sock_hash
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.hash_to_port[sock_hash] = port
|
2006-09-19 22:49:34 +02:00
|
|
|
|
if recipient:
|
|
|
|
|
self.recipient_to_hash[recipient] = sock_hash
|
2006-09-18 00:57:41 +02:00
|
|
|
|
|
2006-09-19 21:56:04 +02:00
|
|
|
|
def remove_connection(self, sock_hash):
|
|
|
|
|
if sock_hash in self.connections:
|
2006-09-19 22:49:34 +02:00
|
|
|
|
del self.connections[sock_hash]
|
|
|
|
|
for i in self.recipient_to_hash:
|
|
|
|
|
if self.recipient_to_hash[i] == sock_hash:
|
|
|
|
|
del self.recipient_to_hash[i]
|
|
|
|
|
break
|
|
|
|
|
for i in self.ip_to_hash:
|
|
|
|
|
if self.ip_to_hash[i] == sock_hash:
|
|
|
|
|
del self.ip_to_hash[i]
|
|
|
|
|
break
|
2006-11-06 22:30:39 +01:00
|
|
|
|
if self.hash_to_port.has_key(sock_hash):
|
|
|
|
|
del self.hash_to_port[sock_hash]
|
|
|
|
|
|
2006-09-18 00:57:41 +02:00
|
|
|
|
def start_listener(self, port):
|
2006-10-02 22:45:49 +02:00
|
|
|
|
for p in range(port, port + 5):
|
|
|
|
|
self.listener = ZeroconfListener(p, self)
|
|
|
|
|
self.listener.bind()
|
|
|
|
|
if self.listener.started:
|
|
|
|
|
return p
|
|
|
|
|
self.listener = None
|
|
|
|
|
return False
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
2006-05-29 21:57:39 +02:00
|
|
|
|
def getRoster(self):
|
2006-10-03 00:41:48 +02:00
|
|
|
|
if self.roster:
|
|
|
|
|
return self.roster.getRoster()
|
|
|
|
|
return {}
|
2006-05-29 21:57:39 +02:00
|
|
|
|
|
2007-03-20 08:13:25 +01:00
|
|
|
|
def send(self, stanza, is_message = False, now = False):
|
2006-11-06 22:30:39 +01:00
|
|
|
|
stanza.setFrom(self.roster.zeroconf.name)
|
|
|
|
|
to = stanza.getTo()
|
|
|
|
|
|
2006-09-19 21:56:04 +02:00
|
|
|
|
try:
|
|
|
|
|
item = self.roster[to]
|
|
|
|
|
except KeyError:
|
2006-11-06 22:30:39 +01:00
|
|
|
|
self.caller.dispatch('MSGERROR', [unicode(to), '-1', _('Contact is offline. Your message could not be sent.'), None, None])
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# look for hashed connections
|
|
|
|
|
if to in self.recipient_to_hash:
|
|
|
|
|
conn = self.connections[self.recipient_to_hash[to]]
|
|
|
|
|
if conn.add_stanza(stanza, is_message):
|
2006-09-19 22:49:34 +02:00
|
|
|
|
return
|
2006-09-19 21:56:04 +02:00
|
|
|
|
|
2006-11-06 22:30:39 +01:00
|
|
|
|
if item['address'] in self.ip_to_hash:
|
|
|
|
|
hash = self.ip_to_hash[item['address']]
|
|
|
|
|
if self.hash_to_port[hash] == item['port']:
|
|
|
|
|
conn = self.connections[hash]
|
|
|
|
|
if conn.add_stanza(stanza, is_message):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# otherwise open new connection
|
|
|
|
|
P2PClient(None, item['address'], item['port'], self, [(stanza, is_message)], to)
|