fix zeroconf to be ablt to use IPv6. Fixes #3683
This commit is contained in:
parent
5b55264c92
commit
92690da36b
|
@ -30,6 +30,9 @@ import socket
|
||||||
import errno
|
import errno
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger('gajim.c.z.client_zeroconf')
|
||||||
|
|
||||||
from common.zeroconf import roster_zeroconf
|
from common.zeroconf import roster_zeroconf
|
||||||
|
|
||||||
MAX_BUFF_LEN = 65536
|
MAX_BUFF_LEN = 65536
|
||||||
|
@ -109,7 +112,7 @@ class ZeroconfListener(IdleObject):
|
||||||
return _sock
|
return _sock
|
||||||
|
|
||||||
class P2PClient(IdleObject):
|
class P2PClient(IdleObject):
|
||||||
def __init__(self, _sock, host, port, conn_holder, stanzaqueue = [], to = None):
|
def __init__(self, _sock, host, port, conn_holder, stanzaqueue = [], to = None, on_ok=None, on_not_ok=None):
|
||||||
self._owner = self
|
self._owner = self
|
||||||
self.Namespace = 'jabber:client'
|
self.Namespace = 'jabber:client'
|
||||||
self.defaultNamespace = self.Namespace
|
self.defaultNamespace = self.Namespace
|
||||||
|
@ -120,6 +123,8 @@ class P2PClient(IdleObject):
|
||||||
self.stanzaqueue = stanzaqueue
|
self.stanzaqueue = stanzaqueue
|
||||||
self.to = to
|
self.to = to
|
||||||
self.Server = host
|
self.Server = host
|
||||||
|
self.on_ok = on_ok
|
||||||
|
self.on_not_ok = on_not_ok
|
||||||
self.DBG = 'client'
|
self.DBG = 'client'
|
||||||
self.Connection = None
|
self.Connection = None
|
||||||
if gajim.verbose:
|
if gajim.verbose:
|
||||||
|
@ -137,6 +142,11 @@ class P2PClient(IdleObject):
|
||||||
self.sock_type = TYPE_CLIENT
|
self.sock_type = TYPE_CLIENT
|
||||||
self.fd = -1
|
self.fd = -1
|
||||||
conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect, self)
|
conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect, self)
|
||||||
|
if not self.conn_holder:
|
||||||
|
# An error occured, disconnect() has been called
|
||||||
|
if on_not_ok:
|
||||||
|
on_not_ok('Connection to host could not be established.')
|
||||||
|
return
|
||||||
self.sock_hash = conn._sock.__hash__
|
self.sock_hash = conn._sock.__hash__
|
||||||
self.fd = conn.fd
|
self.fd = conn.fd
|
||||||
self.conn_holder.add_connection(self, self.Server, port, self.to)
|
self.conn_holder.add_connection(self, self.Server, port, self.to)
|
||||||
|
@ -145,14 +155,14 @@ class P2PClient(IdleObject):
|
||||||
stanza, is_message = val
|
stanza, is_message = val
|
||||||
if is_message:
|
if is_message:
|
||||||
if self.fd == -1:
|
if self.fd == -1:
|
||||||
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
|
if on_not_ok:
|
||||||
_('Connection to host could not be established'), None, None])
|
on_not_ok('Connection to host could not be established.')
|
||||||
|
return
|
||||||
|
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
|
||||||
|
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
|
||||||
else:
|
else:
|
||||||
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
|
self.conn_holder.number_of_awaiting_messages[self.fd]=1
|
||||||
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
|
|
||||||
else:
|
|
||||||
self.conn_holder.number_of_awaiting_messages[self.fd]=1
|
|
||||||
|
|
||||||
def add_stanza(self, stanza, is_message = False):
|
def add_stanza(self, stanza, is_message = False):
|
||||||
if self.Connection:
|
if self.Connection:
|
||||||
if self.Connection.state == -1:
|
if self.Connection.state == -1:
|
||||||
|
@ -177,6 +187,8 @@ class P2PClient(IdleObject):
|
||||||
self.Connection.PlugIn(self)
|
self.Connection.PlugIn(self)
|
||||||
dispatcher_nb.Dispatcher().PlugIn(self)
|
dispatcher_nb.Dispatcher().PlugIn(self)
|
||||||
self._register_handlers()
|
self._register_handlers()
|
||||||
|
if self.on_ok:
|
||||||
|
self.on_ok()
|
||||||
|
|
||||||
def StreamInit(self):
|
def StreamInit(self):
|
||||||
''' Send an initial stream header. '''
|
''' Send an initial stream header. '''
|
||||||
|
@ -202,10 +214,10 @@ class P2PClient(IdleObject):
|
||||||
|
|
||||||
def _check_stream_start(self, ns, tag, attrs):
|
def _check_stream_start(self, ns, tag, attrs):
|
||||||
if ns<>NS_STREAMS or tag<>'stream':
|
if ns<>NS_STREAMS or tag<>'stream':
|
||||||
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
|
|
||||||
_('Connection to host could not be established: Incorrect answer from server.'), None, None])
|
|
||||||
self.Connection.DEBUG('Incorrect stream start: (%s,%s).Terminating! ' % (tag, ns), 'error')
|
self.Connection.DEBUG('Incorrect stream start: (%s,%s).Terminating! ' % (tag, ns), 'error')
|
||||||
self.Connection.disconnect()
|
self.Connection.disconnect()
|
||||||
|
if self.on_not_ok:
|
||||||
|
self.on_not_ok('Connection to host could not be established: Incorrect answer from server.')
|
||||||
return
|
return
|
||||||
if self.sock_type == TYPE_SERVER:
|
if self.sock_type == TYPE_SERVER:
|
||||||
if attrs.has_key('from'):
|
if attrs.has_key('from'):
|
||||||
|
@ -227,9 +239,6 @@ class P2PClient(IdleObject):
|
||||||
def on_disconnect(self):
|
def on_disconnect(self):
|
||||||
if self.conn_holder:
|
if self.conn_holder:
|
||||||
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
|
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
|
||||||
if self.conn_holder.number_of_awaiting_messages[self.fd] > 0:
|
|
||||||
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
|
|
||||||
_('Connection to host could not be established'), None, None])
|
|
||||||
del self.conn_holder.number_of_awaiting_messages[self.fd]
|
del self.conn_holder.number_of_awaiting_messages[self.fd]
|
||||||
self.conn_holder.remove_connection(self.sock_hash)
|
self.conn_holder.remove_connection(self.sock_hash)
|
||||||
if self.__dict__.has_key('Dispatcher'):
|
if self.__dict__.has_key('Dispatcher'):
|
||||||
|
@ -302,13 +311,35 @@ class P2PConnection(IdleObject, PlugIn):
|
||||||
self.on_connect(self)
|
self.on_connect(self)
|
||||||
else:
|
else:
|
||||||
self.state = 0
|
self.state = 0
|
||||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
try:
|
||||||
|
self.ais = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
|
||||||
|
except socket.gaierror, e:
|
||||||
|
log.info("Lookup failure for %s: %s[%s]", host, e[1], repr(e[0]), exc_info=True)
|
||||||
|
else:
|
||||||
|
self.connect_to_next_ip()
|
||||||
|
|
||||||
|
def connect_to_next_ip(self):
|
||||||
|
if len(self.ais) == 0:
|
||||||
|
log.error('Connection failure to %s', self.host, exc_info=True)
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
ai = self.ais.pop(0)
|
||||||
|
log.info('Trying to connect to %s through %s:%s', self.host, ai[4][0],
|
||||||
|
ai[4][1], exc_info=True)
|
||||||
|
try:
|
||||||
|
self._sock = socket.socket(*ai[:3])
|
||||||
self._sock.setblocking(False)
|
self._sock.setblocking(False)
|
||||||
self.fd = self._sock.fileno()
|
self._server=ai[4]
|
||||||
gajim.idlequeue.plug_idle(self, True, False)
|
except:
|
||||||
self.set_timeout(CONNECT_TIMEOUT_SECONDS)
|
if sys.exc_value[0] != errno.EINPROGRESS:
|
||||||
self.do_connect()
|
#for all errors, we try other addresses
|
||||||
|
self.connect_to_next_ip()
|
||||||
|
return
|
||||||
|
self.fd = self._sock.fileno()
|
||||||
|
gajim.idlequeue.plug_idle(self, True, False)
|
||||||
|
self.set_timeout(CONNECT_TIMEOUT_SECONDS)
|
||||||
|
self.do_connect()
|
||||||
|
|
||||||
def set_timeout(self, timeout):
|
def set_timeout(self, timeout):
|
||||||
gajim.idlequeue.remove_timeout(self.fd)
|
gajim.idlequeue.remove_timeout(self.fd)
|
||||||
if self.state >= 0:
|
if self.state >= 0:
|
||||||
|
@ -370,7 +401,7 @@ class P2PConnection(IdleObject, PlugIn):
|
||||||
def do_connect(self):
|
def do_connect(self):
|
||||||
errnum = 0
|
errnum = 0
|
||||||
try:
|
try:
|
||||||
self._sock.connect((self.host, self.port))
|
self._sock.connect(self._server)
|
||||||
self._sock.setblocking(False)
|
self._sock.setblocking(False)
|
||||||
except Exception, ee:
|
except Exception, ee:
|
||||||
(errnum, errstr) = ee
|
(errnum, errstr) = ee
|
||||||
|
@ -378,13 +409,15 @@ class P2PConnection(IdleObject, PlugIn):
|
||||||
return
|
return
|
||||||
# win32 needs this
|
# win32 needs this
|
||||||
elif errnum not in (0, 10056, errno.EISCONN) or self.state != 0:
|
elif errnum not in (0, 10056, errno.EISCONN) or self.state != 0:
|
||||||
self.disconnect()
|
log.error('Could not connect to %s: %s [%s]', self.host, errnum,
|
||||||
return None
|
errstr)
|
||||||
|
self.connect_to_next_ip()
|
||||||
|
return
|
||||||
else: # socket is already connected
|
else: # socket is already connected
|
||||||
self._sock.setblocking(False)
|
self._sock.setblocking(False)
|
||||||
self.state = 1 # connected
|
self.state = 1 # connected
|
||||||
|
# we are connected
|
||||||
self.on_connect(self)
|
self.on_connect(self)
|
||||||
return 1 # we are connected
|
|
||||||
|
|
||||||
|
|
||||||
def pollout(self):
|
def pollout(self):
|
||||||
|
@ -642,28 +675,33 @@ class ClientZeroconf:
|
||||||
return self.roster.getRoster()
|
return self.roster.getRoster()
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def send(self, stanza, is_message = False, now = False):
|
def send(self, stanza, is_message = False, now = False, on_ok=None,
|
||||||
|
on_not_ok=None):
|
||||||
stanza.setFrom(self.roster.zeroconf.name)
|
stanza.setFrom(self.roster.zeroconf.name)
|
||||||
to = stanza.getTo()
|
to = stanza.getTo()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item = self.roster[to]
|
item = self.roster[to]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.caller.dispatch('MSGERROR', [unicode(to), '-1', _('Contact is offline. Your message could not be sent.'), None, None])
|
# Contact offline
|
||||||
return False
|
return -1
|
||||||
|
|
||||||
# look for hashed connections
|
# look for hashed connections
|
||||||
if to in self.recipient_to_hash:
|
if to in self.recipient_to_hash:
|
||||||
conn = self.connections[self.recipient_to_hash[to]]
|
conn = self.connections[self.recipient_to_hash[to]]
|
||||||
if conn.add_stanza(stanza, is_message):
|
if conn.add_stanza(stanza, is_message):
|
||||||
return
|
if on_ok:
|
||||||
|
on_ok()
|
||||||
|
return 0
|
||||||
|
|
||||||
if item['address'] in self.ip_to_hash:
|
if item['address'] in self.ip_to_hash:
|
||||||
hash = self.ip_to_hash[item['address']]
|
hash = self.ip_to_hash[item['address']]
|
||||||
if self.hash_to_port[hash] == item['port']:
|
if self.hash_to_port[hash] == item['port']:
|
||||||
conn = self.connections[hash]
|
conn = self.connections[hash]
|
||||||
if conn.add_stanza(stanza, is_message):
|
if conn.add_stanza(stanza, is_message):
|
||||||
return
|
if on_ok:
|
||||||
|
on_ok()
|
||||||
|
return 0
|
||||||
|
|
||||||
# otherwise open new connection
|
# otherwise open new connection
|
||||||
P2PClient(None, item['address'], item['port'], self, [(stanza, is_message)], to)
|
P2PClient(None, item['address'], item['port'], self, [(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok)
|
||||||
|
|
|
@ -435,24 +435,32 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
|
||||||
if session.enable_encryption:
|
if session.enable_encryption:
|
||||||
msg_iq = session.encrypt_stanza(msg_iq)
|
msg_iq = session.encrypt_stanza(msg_iq)
|
||||||
|
|
||||||
if not self.connection.send(msg_iq, msg != None):
|
def on_send_ok():
|
||||||
return
|
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
|
||||||
|
ji = gajim.get_jid_without_resource(jid)
|
||||||
|
if session.is_loggable() and self.name not in no_log_for and\
|
||||||
|
ji not in no_log_for:
|
||||||
|
log_msg = msg
|
||||||
|
if subject:
|
||||||
|
log_msg = _('Subject: %s\n%s') % (subject, msg)
|
||||||
|
if log_msg:
|
||||||
|
if type == 'chat':
|
||||||
|
kind = 'chat_msg_sent'
|
||||||
|
else:
|
||||||
|
kind = 'single_msg_sent'
|
||||||
|
gajim.logger.write(kind, jid, log_msg)
|
||||||
|
|
||||||
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
|
self.dispatch('MSGSENT', (jid, msg, keyID))
|
||||||
ji = gajim.get_jid_without_resource(jid)
|
|
||||||
if session.is_loggable() and self.name not in no_log_for and\
|
|
||||||
ji not in no_log_for:
|
|
||||||
log_msg = msg
|
|
||||||
if subject:
|
|
||||||
log_msg = _('Subject: %s\n%s') % (subject, msg)
|
|
||||||
if log_msg:
|
|
||||||
if type == 'chat':
|
|
||||||
kind = 'chat_msg_sent'
|
|
||||||
else:
|
|
||||||
kind = 'single_msg_sent'
|
|
||||||
gajim.logger.write(kind, jid, log_msg)
|
|
||||||
|
|
||||||
self.dispatch('MSGSENT', (jid, msg, keyID))
|
def on_send_not_ok(reason):
|
||||||
|
reason += ' ' + _('Your message could not be sent.')
|
||||||
|
self.dispatch('MSGERROR', [jid, '-1', reason, None, None])
|
||||||
|
|
||||||
|
ret = self.connection.send(msg_iq, msg != None, on_ok=on_send_ok,
|
||||||
|
on_not_ok=on_send_not_ok)
|
||||||
|
if ret == -1:
|
||||||
|
# Contact Offline
|
||||||
|
self.dispatch('MSGERROR', [jid, '-1', _('Contact is offline. Your message could not be sent.'), None, None])
|
||||||
|
|
||||||
def send_stanza(self, stanza):
|
def send_stanza(self, stanza):
|
||||||
# send a stanza untouched
|
# send a stanza untouched
|
||||||
|
|
Loading…
Reference in New Issue