summaryrefslogtreecommitdiff
path: root/src/xmpppy-0.5.0rc1/xmpp/transports.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpppy-0.5.0rc1/xmpp/transports.py')
-rw-r--r--src/xmpppy-0.5.0rc1/xmpp/transports.py339
1 files changed, 0 insertions, 339 deletions
diff --git a/src/xmpppy-0.5.0rc1/xmpp/transports.py b/src/xmpppy-0.5.0rc1/xmpp/transports.py
deleted file mode 100644
index 0e3eec90..00000000
--- a/src/xmpppy-0.5.0rc1/xmpp/transports.py
+++ /dev/null
@@ -1,339 +0,0 @@
-## transports.py
-##
-## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: transports.py,v 1.35 2009/04/07 08:34:09 snakeru Exp $
-
-"""
-This module contains the low-level implementations of xmpppy connect methods or
-(in other words) transports for xmpp-stanzas.
-Currently here is three transports:
-direct TCP connect - TCPsocket class
-proxied TCP connect - HTTPPROXYsocket class (CONNECT proxies)
-TLS connection - TLS class. Can be used for SSL connections also.
-
-Transports are stackable so you - f.e. TLS use HTPPROXYsocket or TCPsocket as more low-level transport.
-
-Also exception 'error' is defined to allow capture of this module specific exceptions.
-"""
-
-import socket,select,base64,dispatcher,sys
-from simplexml import ustr
-from client import PlugIn
-from protocol import *
-
-# determine which DNS resolution library is available
-HAVE_DNSPYTHON = False
-HAVE_PYDNS = False
-try:
- import dns.resolver # http://dnspython.org/
- HAVE_DNSPYTHON = True
-except ImportError:
- try:
- import DNS # http://pydns.sf.net/
- HAVE_PYDNS = True
- except ImportError:
- pass
-
-DATA_RECEIVED='DATA RECEIVED'
-DATA_SENT='DATA SENT'
-
-class error:
- """An exception to be raised in case of low-level errors in methods of 'transports' module."""
- def __init__(self,comment):
- """Cache the descriptive string"""
- self._comment=comment
-
- def __str__(self):
- """Serialise exception into pre-cached descriptive string."""
- return self._comment
-
-BUFLEN=1024
-class TCPsocket(PlugIn):
- """ This class defines direct TCP connection method. """
- def __init__(self, server=None, use_srv=True):
- """ Cache connection point 'server'. 'server' is the tuple of (host, port)
- absolutely the same as standard tcp socket uses. However library will lookup for
- ('_xmpp-client._tcp.' + host) SRV record in DNS and connect to the found (if it is)
- server instead
- """
- PlugIn.__init__(self)
- self.DBG_LINE='socket'
- self._exported_methods=[self.send,self.disconnect]
- self._server, self.use_srv = server, use_srv
-
- def srv_lookup(self, server):
- " SRV resolver. Takes server=(host, port) as argument. Returns new (host, port) pair "
- if HAVE_DNSPYTHON or HAVE_PYDNS:
- host, port = server
- possible_queries = ['_xmpp-client._tcp.' + host]
-
- for query in possible_queries:
- try:
- if HAVE_DNSPYTHON:
- answers = [x for x in dns.resolver.query(query, 'SRV')]
- if answers:
- host = str(answers[0].target)
- port = int(answers[0].port)
- break
- elif HAVE_PYDNS:
- # ensure we haven't cached an old configuration
- DNS.DiscoverNameServers()
- response = DNS.Request().req(query, qtype='SRV')
- answers = response.answers
- if len(answers) > 0:
- # ignore the priority and weight for now
- _, _, port, host = answers[0]['data']
- del _
- port = int(port)
- break
- except:
- self.DEBUG('An error occurred while looking up %s' % query, 'warn')
- server = (host, port)
- else:
- self.DEBUG("Could not load one of the supported DNS libraries (dnspython or pydns). SRV records will not be queried and you may need to set custom hostname/port for some servers to be accessible.\n",'warn')
- # end of SRV resolver
- return server
-
- def plugin(self, owner):
- """ Fire up connection. Return non-empty string on success.
- Also registers self.disconnected method in the owner's dispatcher.
- Called internally. """
- if not self._server: self._server=(self._owner.Server,5222)
- if self.use_srv: server=self.srv_lookup(self._server)
- else: server=self._server
- if not self.connect(server): return
- self._owner.Connection=self
- self._owner.RegisterDisconnectHandler(self.disconnected)
- return 'ok'
-
- def getHost(self):
- """ Return the 'host' value that is connection is [will be] made to."""
- return self._server[0]
- def getPort(self):
- """ Return the 'port' value that is connection is [will be] made to."""
- return self._server[1]
-
- def connect(self,server=None):
- """ Try to connect to the given host/port. Does not lookup for SRV record.
- Returns non-empty string on success. """
- try:
- if not server: server=self._server
- self._sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self._sock.connect((server[0], int(server[1])))
- self._send=self._sock.sendall
- self._recv=self._sock.recv
- self.DEBUG("Successfully connected to remote host %s"%`server`,'start')
- return 'ok'
- except socket.error, (errno, strerror):
- self.DEBUG("Failed to connect to remote host %s: %s (%s)"%(`server`, strerror, errno),'error')
- except: pass
-
- def plugout(self):
- """ Disconnect from the remote server and unregister self.disconnected method from
- the owner's dispatcher. """
- self._sock.close()
- if self._owner.__dict__.has_key('Connection'):
- del self._owner.Connection
- self._owner.UnregisterDisconnectHandler(self.disconnected)
-
- def receive(self):
- """ Reads all pending incoming data.
- In case of disconnection calls owner's disconnected() method and then raises IOError exception."""
- try: received = self._recv(BUFLEN)
- except socket.sslerror,e:
- self._seen_data=0
- if e[0]==socket.SSL_ERROR_WANT_READ: return ''
- if e[0]==socket.SSL_ERROR_WANT_WRITE: return ''
- self.DEBUG('Socket error while receiving data','error')
- sys.exc_clear()
- self._owner.disconnected()
- raise IOError("Disconnected from server")
- except: received = ''
-
- while self.pending_data(0):
- try: add = self._recv(BUFLEN)
- except: add=''
- received +=add
- if not add: break
-
- if len(received): # length of 0 means disconnect
- self._seen_data=1
- self.DEBUG(received,'got')
- if hasattr(self._owner, 'Dispatcher'):
- self._owner.Dispatcher.Event('', DATA_RECEIVED, received)
- else:
- self.DEBUG('Socket error while receiving data','error')
- self._owner.disconnected()
- raise IOError("Disconnected from server")
- return received
-
- def send(self,raw_data):
- """ Writes raw outgoing data. Blocks until done.
- If supplied data is unicode string, encodes it to utf-8 before send."""
- if type(raw_data)==type(u''): raw_data = raw_data.encode('utf-8')
- elif type(raw_data)<>type(''): raw_data = ustr(raw_data).encode('utf-8')
- try:
- self._send(raw_data)
- # Avoid printing messages that are empty keepalive packets.
- if raw_data.strip():
- self.DEBUG(raw_data,'sent')
- if hasattr(self._owner, 'Dispatcher'): # HTTPPROXYsocket will send data before we have a Dispatcher
- self._owner.Dispatcher.Event('', DATA_SENT, raw_data)
- except:
- self.DEBUG("Socket error while sending data",'error')
- self._owner.disconnected()
-
- def pending_data(self,timeout=0):
- """ Returns true if there is a data ready to be read. """
- return select.select([self._sock],[],[],timeout)[0]
-
- def disconnect(self):
- """ Closes the socket. """
- self.DEBUG("Closing socket",'stop')
- self._sock.close()
-
- def disconnected(self):
- """ Called when a Network Error or disconnection occurs.
- Designed to be overidden. """
- self.DEBUG("Socket operation failed",'error')
-
-DBG_CONNECT_PROXY='CONNECTproxy'
-class HTTPPROXYsocket(TCPsocket):
- """ HTTP (CONNECT) proxy connection class. Uses TCPsocket as the base class
- redefines only connect method. Allows to use HTTP proxies like squid with
- (optionally) simple authentication (using login and password). """
- def __init__(self,proxy,server,use_srv=True):
- """ Caches proxy and target addresses.
- 'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address)
- and optional keys 'user' and 'password' to use for authentication.
- 'server' argument is a tuple of host and port - just like TCPsocket uses. """
- TCPsocket.__init__(self,server,use_srv)
- self.DBG_LINE=DBG_CONNECT_PROXY
- self._proxy=proxy
-
- def plugin(self, owner):
- """ Starts connection. Used interally. Returns non-empty string on success."""
- owner.debug_flags.append(DBG_CONNECT_PROXY)
- return TCPsocket.plugin(self,owner)
-
- def connect(self,dupe=None):
- """ Starts connection. Connects to proxy, supplies login and password to it
- (if were specified while creating instance). Instructs proxy to make
- connection to the target server. Returns non-empty sting on success. """
- if not TCPsocket.connect(self,(self._proxy['host'],self._proxy['port'])): return
- self.DEBUG("Proxy server contacted, performing authentification",'start')
- connector = ['CONNECT %s:%s HTTP/1.0'%self._server,
- 'Proxy-Connection: Keep-Alive',
- 'Pragma: no-cache',
- 'Host: %s:%s'%self._server,
- 'User-Agent: HTTPPROXYsocket/v0.1']
- if self._proxy.has_key('user') and self._proxy.has_key('password'):
- credentials = '%s:%s'%(self._proxy['user'],self._proxy['password'])
- credentials = base64.encodestring(credentials).strip()
- connector.append('Proxy-Authorization: Basic '+credentials)
- connector.append('\r\n')
- self.send('\r\n'.join(connector))
- try: reply = self.receive().replace('\r','')
- except IOError:
- self.DEBUG('Proxy suddenly disconnected','error')
- self._owner.disconnected()
- return
- try: proto,code,desc=reply.split('\n')[0].split(' ',2)
- except: raise error('Invalid proxy reply')
- if code<>'200':
- self.DEBUG('Invalid proxy reply: %s %s %s'%(proto,code,desc),'error')
- self._owner.disconnected()
- return
- while reply.find('\n\n') == -1:
- try: reply += self.receive().replace('\r','')
- except IOError:
- self.DEBUG('Proxy suddenly disconnected','error')
- self._owner.disconnected()
- return
- self.DEBUG("Authentification successfull. Jabber server contacted.",'ok')
- return 'ok'
-
- def DEBUG(self,text,severity):
- """Overwrites DEBUG tag to allow debug output be presented as "CONNECTproxy"."""
- return self._owner.DEBUG(DBG_CONNECT_PROXY,text,severity)
-
-class TLS(PlugIn):
- """ TLS connection used to encrypts already estabilished tcp connection."""
- def PlugIn(self,owner,now=0):
- """ If the 'now' argument is true then starts using encryption immidiatedly.
- If 'now' in false then starts encryption as soon as TLS feature is
- declared by the server (if it were already declared - it is ok).
- """
- if owner.__dict__.has_key('TLS'): return # Already enabled.
- PlugIn.PlugIn(self,owner)
- DBG_LINE='TLS'
- if now: return self._startSSL()
- if self._owner.Dispatcher.Stream.features:
- try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features)
- except NodeProcessed: pass
- else: self._owner.RegisterHandlerOnce('features',self.FeaturesHandler,xmlns=NS_STREAMS)
- self.starttls=None
-
- def plugout(self,now=0):
- """ Unregisters TLS handler's from owner's dispatcher. Take note that encription
- can not be stopped once started. You can only break the connection and start over."""
- self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
- self._owner.UnregisterHandler('proceed',self.StartTLSHandler,xmlns=NS_TLS)
- self._owner.UnregisterHandler('failure',self.StartTLSHandler,xmlns=NS_TLS)
-
- def FeaturesHandler(self, conn, feats):
- """ Used to analyse server <features/> tag for TLS support.
- If TLS is supported starts the encryption negotiation. Used internally"""
- if not feats.getTag('starttls',namespace=NS_TLS):
- self.DEBUG("TLS unsupported by remote server.",'warn')
- return
- self.DEBUG("TLS supported by remote server. Requesting TLS start.",'ok')
- self._owner.RegisterHandlerOnce('proceed',self.StartTLSHandler,xmlns=NS_TLS)
- self._owner.RegisterHandlerOnce('failure',self.StartTLSHandler,xmlns=NS_TLS)
- self._owner.Connection.send('<starttls xmlns="%s"/>'%NS_TLS)
- raise NodeProcessed
-
- def pending_data(self,timeout=0):
- """ Returns true if there possible is a data ready to be read. """
- return self._tcpsock._seen_data or select.select([self._tcpsock._sock],[],[],timeout)[0]
-
- def _startSSL(self):
- """ Immidiatedly switch socket to TLS mode. Used internally."""
- """ Here we should switch pending_data to hint mode."""
- tcpsock=self._owner.Connection
- tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None)
- tcpsock._sslIssuer = tcpsock._sslObj.issuer()
- tcpsock._sslServer = tcpsock._sslObj.server()
- tcpsock._recv = tcpsock._sslObj.read
- tcpsock._send = tcpsock._sslObj.write
-
- tcpsock._seen_data=1
- self._tcpsock=tcpsock
- tcpsock.pending_data=self.pending_data
- tcpsock._sock.setblocking(0)
-
- self.starttls='success'
-
- def StartTLSHandler(self, conn, starttls):
- """ Handle server reply if TLS is allowed to process. Behaves accordingly.
- Used internally."""
- if starttls.getNamespace()<>NS_TLS: return
- self.starttls=starttls.getName()
- if self.starttls=='failure':
- self.DEBUG("Got starttls response: "+self.starttls,'error')
- return
- self.DEBUG("Got starttls proceed response. Switching to TLS/SSL...",'ok')
- self._startSSL()
- self._owner.Dispatcher.PlugOut()
- dispatcher.Dispatcher().PlugIn(self._owner)