new BOSHDispatcher (in dispatcher_nb), improved BOSHClient class, minor changes in other xmpp modules
This commit is contained in:
		
							parent
							
								
									f379d06d2c
								
							
						
					
					
						commit
						e1899f34dc
					
				
					 7 changed files with 553 additions and 319 deletions
				
			
		| 
						 | 
				
			
			@ -173,11 +173,8 @@ class SASL(PlugIn):
 | 
			
		|||
			self.startsasl='success'
 | 
			
		||||
			log.info('Successfully authenticated with remote server.')
 | 
			
		||||
			handlers=self._owner.Dispatcher.dumpHandlers()
 | 
			
		||||
			print '6' * 79
 | 
			
		||||
			print handlers
 | 
			
		||||
			print '6' * 79
 | 
			
		||||
			self._owner.Dispatcher.PlugOut()
 | 
			
		||||
			dispatcher_nb.Dispatcher().PlugIn(self._owner)
 | 
			
		||||
			dispatcher_nb.Dispatcher().PlugIn(self._owner, after_SASL=True)
 | 
			
		||||
			self._owner.Dispatcher.restoreHandlers(handlers)
 | 
			
		||||
			self._owner.User = self.username
 | 
			
		||||
			if self.on_sasl :
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,80 +1,208 @@
 | 
			
		|||
 | 
			
		||||
import protocol, simplexml, locale, random, dispatcher_nb 
 | 
			
		||||
import protocol, locale, random, dispatcher_nb 
 | 
			
		||||
from client_nb import NBCommonClient
 | 
			
		||||
import transports_nb
 | 
			
		||||
import logging
 | 
			
		||||
from simplexml import Node
 | 
			
		||||
log = logging.getLogger('gajim.c.x.bosh')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BOSHClient(NBCommonClient):
 | 
			
		||||
	'''
 | 
			
		||||
	Client class implementing BOSH. 
 | 
			
		||||
	Client class implementing BOSH. Extends common XMPP  
 | 
			
		||||
	'''
 | 
			
		||||
	def __init__(self, *args, **kw):
 | 
			
		||||
	def __init__(self, domain, idlequeue, caller=None):
 | 
			
		||||
		'''Preceeds constructor of NBCommonClient and sets some of values that will
 | 
			
		||||
		be used as attributes in <body> tag'''
 | 
			
		||||
		self.Namespace = protocol.NS_HTTP_BIND
 | 
			
		||||
		# BOSH parameters should be given via Advanced Configuration Editor
 | 
			
		||||
		self.bosh_xml_lang = None
 | 
			
		||||
		self.bosh_hold = 1
 | 
			
		||||
		self.bosh_wait=60
 | 
			
		||||
		self.bosh_rid=None
 | 
			
		||||
		self.bosh_sid=None
 | 
			
		||||
 | 
			
		||||
		self.bosh_httpversion = 'HTTP/1.1'
 | 
			
		||||
		NBCommonClient.__init__(self, *args, **kw)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def connect(self, *args, **kw):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		if locale.getdefaultlocale()[0]:
 | 
			
		||||
			self.bosh_xml_lang = locale.getdefaultlocale()[0].split('_')[0]
 | 
			
		||||
 | 
			
		||||
		# with 50-bit random initial rid, session would have to go up
 | 
			
		||||
		# to 7881299347898368 messages to raise rid over 2**53 
 | 
			
		||||
		# (see http://www.xmpp.org/extensions/xep-0124.html#rids)
 | 
			
		||||
		r = random.Random()
 | 
			
		||||
		r.seed()
 | 
			
		||||
		self.bosh_rid = r.getrandbits(50)
 | 
			
		||||
		self.bosh_sid = None
 | 
			
		||||
 | 
			
		||||
		if locale.getdefaultlocale()[0]:
 | 
			
		||||
			self.bosh_xml_lang = locale.getdefaultlocale()[0].split('_')[0]
 | 
			
		||||
		else:
 | 
			
		||||
			self.bosh_xml_lang = 'en'
 | 
			
		||||
 | 
			
		||||
		self.http_version = 'HTTP/1.1'
 | 
			
		||||
		self.bosh_to = domain
 | 
			
		||||
 | 
			
		||||
		#self.Namespace = protocol.NS_HTTP_BIND
 | 
			
		||||
		#self.defaultNamespace = self.Namespace
 | 
			
		||||
		self.bosh_session_on = False
 | 
			
		||||
 | 
			
		||||
		NBCommonClient.__init__(self, domain, idlequeue, caller)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def connect(self, on_connect, on_connect_failure, proxy, hostname=None, port=5222, 
 | 
			
		||||
		on_proxy_failure=None, secure=None):
 | 
			
		||||
		''' 
 | 
			
		||||
		Open XMPP connection (open XML streams in both directions).
 | 
			
		||||
		:param hostname: hostname of XMPP server from SRV request 
 | 
			
		||||
		:param port: port number of XMPP server
 | 
			
		||||
		:param on_connect: called after stream is successfully opened
 | 
			
		||||
		:param on_connect_failure: called when error occures during connection
 | 
			
		||||
		:param on_proxy_failure: called if error occurres during TCP connection to
 | 
			
		||||
			proxy server or during connection to the proxy
 | 
			
		||||
		:param proxy: dictionary with bosh-related paramters. It should contain at 
 | 
			
		||||
			least values for keys 'host' and 'port' - connection details for proxy
 | 
			
		||||
			server and optionally keys 'user' and 'pass' as proxy credentials
 | 
			
		||||
		:param secure: if 
 | 
			
		||||
		'''
 | 
			
		||||
		NBCommonClient.connect(self, on_connect, on_connect_failure, hostname, port,
 | 
			
		||||
			on_proxy_failure, proxy, secure)
 | 
			
		||||
 | 
			
		||||
		if hostname:
 | 
			
		||||
			self.route_host = hostname
 | 
			
		||||
		else:
 | 
			
		||||
			self.route_host = self.Server
 | 
			
		||||
 | 
			
		||||
		assert(proxy.has_key('type'))
 | 
			
		||||
		assert(proxy['type']=='bosh')
 | 
			
		||||
 | 
			
		||||
		proxy = kw['proxy']
 | 
			
		||||
		#self.bosh_protocol, self.bosh_host, self.bosh_uri = transports_nb.urisplit(proxy['host'])
 | 
			
		||||
		self.bosh_port = proxy['port']
 | 
			
		||||
		self.bosh_wait = proxy['bosh_wait']
 | 
			
		||||
		self.bosh_hold = proxy['bosh_hold']
 | 
			
		||||
		self.bosh_to = proxy['to']
 | 
			
		||||
		#self.bosh_ack = proxy['bosh_ack']
 | 
			
		||||
		#self.bosh_secure = proxy['bosh_secure']
 | 
			
		||||
		NBCommonClient.connect(self, *args, **kw)
 | 
			
		||||
		self.bosh_host = proxy['host']
 | 
			
		||||
		self.bosh_port = proxy['port']
 | 
			
		||||
		self.bosh_content = proxy['bosh_content']
 | 
			
		||||
 | 
			
		||||
		# _on_tcp_failure is callback for errors which occur during name resolving or
 | 
			
		||||
		# TCP connecting.
 | 
			
		||||
		self._on_tcp_failure = self.on_proxy_failure
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
								
 | 
			
		||||
		# in BOSH, client connects to Connection Manager instead of directly to
 | 
			
		||||
		# XMPP server ((hostname, port)). If HTTP Proxy is specified, client connects
 | 
			
		||||
		# to HTTP proxy and Connection Manager is specified at URI and Host header
 | 
			
		||||
		# in HTTP message
 | 
			
		||||
				
 | 
			
		||||
		# tcp_host, tcp_port is hostname and port for socket connection - Connection
 | 
			
		||||
		# Manager or HTTP proxy
 | 
			
		||||
		if proxy.has_key('proxy_host') and proxy['proxy_host'] and \
 | 
			
		||||
			proxy.has_key('proxy_port') and proxy['proxy_port']:
 | 
			
		||||
			
 | 
			
		||||
			tcp_host=proxy['proxy_host']
 | 
			
		||||
			tcp_port=proxy['proxy_port']
 | 
			
		||||
 | 
			
		||||
			# user and password for HTTP proxy
 | 
			
		||||
			if proxy.has_key('user') and proxy['user'] and \
 | 
			
		||||
				proxy.has_key('pass') and proxy['pass']:
 | 
			
		||||
 | 
			
		||||
				proxy_creds=(proxy['user'],proxy['pass'])
 | 
			
		||||
			else:
 | 
			
		||||
				proxy_creds=(None, None)
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			tcp_host = transports_nb.urisplit(proxy['host'])[1]
 | 
			
		||||
			tcp_port=proxy['port']
 | 
			
		||||
 | 
			
		||||
			if tcp_host is None:
 | 
			
		||||
				self._on_connect_failure("Invalid BOSH URI")
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
		self.socket = self.get_socket()
 | 
			
		||||
 | 
			
		||||
		self._resolve_hostname(
 | 
			
		||||
			hostname=tcp_host,
 | 
			
		||||
			port=tcp_port,
 | 
			
		||||
			on_success=self._try_next_ip,
 | 
			
		||||
			on_failure=self._on_tcp_failure)
 | 
			
		||||
 | 
			
		||||
	def _on_stream_start(self):
 | 
			
		||||
		'''
 | 
			
		||||
		Called after XMPP stream is opened. In BOSH, TLS is negotiated on socket
 | 
			
		||||
		connect so success callback can be invoked after TCP connect.
 | 
			
		||||
		(authentication is started from auth() method)
 | 
			
		||||
		'''
 | 
			
		||||
		self.onreceive(None)
 | 
			
		||||
		if self.connected == 'tcp':
 | 
			
		||||
			self._on_connect()
 | 
			
		||||
 | 
			
		||||
	def get_socket(self):
 | 
			
		||||
		tmp = transports_nb.NonBlockingHTTP(
 | 
			
		||||
			raise_event=self.raise_event,
 | 
			
		||||
			on_disconnect=self.on_http_disconnect,
 | 
			
		||||
			http_uri = self.bosh_host,			
 | 
			
		||||
			http_port = self.bosh_port,
 | 
			
		||||
			http_version = self.http_version
 | 
			
		||||
			)
 | 
			
		||||
		tmp.PlugIn(self)
 | 
			
		||||
		return tmp
 | 
			
		||||
 | 
			
		||||
	def on_http_disconnect(self):
 | 
			
		||||
		log.info('HTTP socket disconnected')
 | 
			
		||||
		#import traceback
 | 
			
		||||
		#traceback.print_stack()
 | 
			
		||||
		if self.bosh_session_on:
 | 
			
		||||
                        self.socket.connect(
 | 
			
		||||
				conn_5tuple=self.current_ip,
 | 
			
		||||
				on_connect=self.on_http_reconnect,
 | 
			
		||||
				on_connect_failure=self.on_disconnect)
 | 
			
		||||
		else:
 | 
			
		||||
			self.on_disconnect()
 | 
			
		||||
 | 
			
		||||
	def on_http_reconnect(self):
 | 
			
		||||
		self.socket._plug_idle()
 | 
			
		||||
		log.info('Connected to BOSH CM again')
 | 
			
		||||
		pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def on_http_reconnect_fail(self):
 | 
			
		||||
		log.error('Error when reconnecting to BOSH CM')
 | 
			
		||||
		self.on_disconnect()
 | 
			
		||||
		
 | 
			
		||||
	def send(self, stanza, now = False):
 | 
			
		||||
		(id, stanza_to_send) = self.Dispatcher.assign_id(stanza)
 | 
			
		||||
 | 
			
		||||
		self.Connection.send(
 | 
			
		||||
		self.socket.send(
 | 
			
		||||
			self.boshify_stanza(stanza_to_send),
 | 
			
		||||
			now = now)
 | 
			
		||||
		return id
 | 
			
		||||
 | 
			
		||||
	def get_rid(self):
 | 
			
		||||
		# does this need a lock??"
 | 
			
		||||
		self.bosh_rid = self.bosh_rid + 1
 | 
			
		||||
		return str(self.bosh_rid)
 | 
			
		||||
 | 
			
		||||
	def get_bodytag(self):
 | 
			
		||||
		# this should be called not until after session creation response so sid has
 | 
			
		||||
		# to be initialized. 
 | 
			
		||||
		assert(self.sid is not None)
 | 
			
		||||
		self.rid = self.rid+1
 | 
			
		||||
		assert(hasattr(self, 'bosh_sid'))
 | 
			
		||||
		return protocol.BOSHBody(
 | 
			
		||||
			attrs={	'rid': str(self.bosh_rid),
 | 
			
		||||
			attrs={	'rid': self.get_rid(),
 | 
			
		||||
				'sid': self.bosh_sid})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def get_initial_bodytag(self):
 | 
			
		||||
		return protocol.BOSHBody(
 | 
			
		||||
			attrs={'content': 'text/xml; charset=utf-8',
 | 
			
		||||
	def get_initial_bodytag(self, after_SASL=False):
 | 
			
		||||
		tag = protocol.BOSHBody(
 | 
			
		||||
			attrs={'content': self.bosh_content,
 | 
			
		||||
				'hold': str(self.bosh_hold),
 | 
			
		||||
				'route': '%s:%s' % (self.route_host, self.Port),
 | 
			
		||||
				'to': self.bosh_to,
 | 
			
		||||
				'wait': str(self.bosh_wait),
 | 
			
		||||
				'rid': str(self.bosh_rid),
 | 
			
		||||
				'rid': self.get_rid(),
 | 
			
		||||
				'xml:lang': self.bosh_xml_lang,
 | 
			
		||||
				'xmpp:version': '1.0',
 | 
			
		||||
				'xmlns:xmpp': 'urn:xmpp:xbosh'}
 | 
			
		||||
			)
 | 
			
		||||
				'ver': '1.6',
 | 
			
		||||
				'xmlns:xmpp': 'urn:xmpp:xbosh'})
 | 
			
		||||
		if after_SASL:
 | 
			
		||||
			tag.delAttr('content')
 | 
			
		||||
			tag.delAttr('hold')
 | 
			
		||||
			tag.delAttr('route')
 | 
			
		||||
			tag.delAttr('wait')
 | 
			
		||||
			tag.delAttr('ver')
 | 
			
		||||
			# xmpp:restart attribute is essential for stream restart request
 | 
			
		||||
			tag.setAttr('xmpp:restart','true')
 | 
			
		||||
			tag.setAttr('sid',self.bosh_sid)
 | 
			
		||||
 | 
			
		||||
		return tag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def get_closing_bodytag(self):
 | 
			
		||||
		closing_bodytag = self.get_bodytag()
 | 
			
		||||
| 
						 | 
				
			
			@ -82,31 +210,26 @@ class BOSHClient(NBCommonClient):
 | 
			
		|||
		return closing_bodytag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def boshify_stanza(self, stanza):
 | 
			
		||||
		''' wraps stanza by body tag or modifies message entirely (in case of stream
 | 
			
		||||
		opening and closing'''
 | 
			
		||||
		log.info('boshify_staza - type is: %s' % type(stanza))
 | 
			
		||||
		if isinstance(stanza, simplexml.Node):
 | 
			
		||||
			tag = self.get_bodytag()
 | 
			
		||||
			return tag.setPayload(stanza)
 | 
			
		||||
		else:
 | 
			
		||||
			# only stream initialization and stream terminatoion are not Nodes
 | 
			
		||||
			if stanza.startswith(dispatcher_nb.XML_DECLARATION):
 | 
			
		||||
				# stream init
 | 
			
		||||
				return self.get_initial_bodytag()
 | 
			
		||||
			else:
 | 
			
		||||
				# should be stream closing
 | 
			
		||||
				assert(stanza == dispatcher_nb.STREAM_TERMINATOR)
 | 
			
		||||
				return self.get_closing_bodytag()
 | 
			
		||||
	def boshify_stanza(self, stanza=None, body_attrs=None):
 | 
			
		||||
		''' wraps stanza by body tag with rid and sid '''
 | 
			
		||||
		#log.info('boshify_staza - type is: %s, stanza is %s' % (type(stanza), stanza))
 | 
			
		||||
		tag = self.get_bodytag()
 | 
			
		||||
		tag.setPayload([stanza])
 | 
			
		||||
		return tag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def on_bodytag_attrs(self, body_attrs):
 | 
			
		||||
		#log.info('on_bodytag_attrs: %s' % body_attrs)
 | 
			
		||||
		if body_attrs.has_key('type'):
 | 
			
		||||
			if body_attrs['type']=='terminated':
 | 
			
		||||
				# BOSH session terminated 
 | 
			
		||||
				self.bosh_session_on = False
 | 
			
		||||
			elif body_attrs['type']=='error':
 | 
			
		||||
				# recoverable error
 | 
			
		||||
				pass
 | 
			
		||||
		if not self.bosh_sid:
 | 
			
		||||
			# initial response - when bosh_sid is set
 | 
			
		||||
			self.bosh_session_on = True
 | 
			
		||||
			self.bosh_sid = body_attrs['sid']
 | 
			
		||||
			self.Dispatcher.Stream._document_attrs['id']=body_attrs['authid']
 | 
			
		||||
 | 
			
		||||
	def _on_stream_start(self):
 | 
			
		||||
		'''
 | 
			
		||||
		Called after XMPP stream is opened. In BOSH, TLS is negotiated elsewhere 
 | 
			
		||||
		so success callback can be invoked.
 | 
			
		||||
		(authentication is started from auth() method)
 | 
			
		||||
		'''
 | 
			
		||||
		self.onreceive(None)
 | 
			
		||||
		if self.connected == 'tcp':
 | 
			
		||||
			self._on_connect()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ class PlugIn:
 | 
			
		|||
	def PlugIn(self,owner):
 | 
			
		||||
		""" Attach to main instance and register ourself and all our staff in it. """
 | 
			
		||||
		self._owner=owner
 | 
			
		||||
		log.debug('Plugging %s into %s'%(self,self._owner))
 | 
			
		||||
		log.info('Plugging %s __INTO__ %s' % (self,self._owner))
 | 
			
		||||
		if owner.__dict__.has_key(self.__class__.__name__):
 | 
			
		||||
			log.debug('Plugging ignored: another instance already plugged.')
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			@ -41,17 +41,27 @@ class PlugIn:
 | 
			
		|||
			if owner.__dict__.has_key(method.__name__):
 | 
			
		||||
				self._old_owners_methods.append(owner.__dict__[method.__name__])
 | 
			
		||||
			owner.__dict__[method.__name__]=method
 | 
			
		||||
		owner.__dict__[self.__class__.__name__]=self
 | 
			
		||||
		if self.__class__.__name__.endswith('Dispatcher'):
 | 
			
		||||
			# FIXME: I need BOSHDispatcher or XMPPDispatcher on .Dispatcher 
 | 
			
		||||
			# there must be a better way..
 | 
			
		||||
			owner.__dict__['Dispatcher']=self
 | 
			
		||||
		else:
 | 
			
		||||
			owner.__dict__[self.__class__.__name__]=self
 | 
			
		||||
 | 
			
		||||
		# following will not work for classes inheriting plugin()
 | 
			
		||||
		#if self.__class__.__dict__.has_key('plugin'): return self.plugin(owner)
 | 
			
		||||
		if hasattr(self,'plugin'): return self.plugin(owner)
 | 
			
		||||
 
 | 
			
		||||
	def PlugOut(self):
 | 
			
		||||
		""" Unregister all our staff from main instance and detach from it. """
 | 
			
		||||
		log.debug('Plugging %s out of %s.'%(self,self._owner))
 | 
			
		||||
		log.info('Plugging %s __OUT__ of %s.' % (self,self._owner))
 | 
			
		||||
		for method in self._exported_methods: del self._owner.__dict__[method.__name__]
 | 
			
		||||
		for method in self._old_owners_methods: self._owner.__dict__[method.__name__]=method
 | 
			
		||||
		del self._owner.__dict__[self.__class__.__name__]
 | 
			
		||||
		if self.__class__.__dict__.has_key('plugout'): return self.plugout()
 | 
			
		||||
		if self.__class__.__name__.endswith('Dispatcher'):
 | 
			
		||||
			del self._owner.__dict__['Dispatcher']
 | 
			
		||||
		else:
 | 
			
		||||
			del self._owner.__dict__[self.__class__.__name__]
 | 
			
		||||
		#if self.__class__.__dict__.has_key('plugout'): return self.plugout()
 | 
			
		||||
		if hasattr(self,'plugout'): return self.plugout()
 | 
			
		||||
		del self._owner
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,11 +41,10 @@ class NBCommonClient:
 | 
			
		|||
		:param caller: calling object - it has to implement certain methods (necessary?)
 | 
			
		||||
			
 | 
			
		||||
		'''
 | 
			
		||||
		
 | 
			
		||||
		self.Namespace = protocol.NS_CLIENT
 | 
			
		||||
 | 
			
		||||
		self.idlequeue = idlequeue
 | 
			
		||||
		self.defaultNamespace = self.Namespace
 | 
			
		||||
		
 | 
			
		||||
		self.idlequeue = idlequeue
 | 
			
		||||
		self.disconnect_handlers = []
 | 
			
		||||
 | 
			
		||||
		self.Server = domain
 | 
			
		||||
| 
						 | 
				
			
			@ -85,12 +84,14 @@ class NBCommonClient:
 | 
			
		|||
			self.SASL.PlugOut()
 | 
			
		||||
		if self.__dict__.has_key('NonBlockingTLS'):
 | 
			
		||||
			self.NonBlockingTLS.PlugOut()
 | 
			
		||||
		if self.__dict__.has_key('NBHTTPPROXYsocket'):
 | 
			
		||||
		if self.__dict__.has_key('NBHTTPProxySocket'):
 | 
			
		||||
			self.NBHTTPPROXYsocket.PlugOut()
 | 
			
		||||
		if self.__dict__.has_key('NBSOCKS5PROXYsocket'):
 | 
			
		||||
		if self.__dict__.has_key('NBSOCKS5ProxySocket'):
 | 
			
		||||
			self.NBSOCKS5PROXYsocket.PlugOut()
 | 
			
		||||
		if self.__dict__.has_key('NonBlockingTcp'):
 | 
			
		||||
			self.NonBlockingTcp.PlugOut()
 | 
			
		||||
		if self.__dict__.has_key('NonBlockingTCP'):
 | 
			
		||||
			self.NonBlockingTCP.PlugOut()
 | 
			
		||||
		if self.__dict__.has_key('NonBlockingHTTP'):
 | 
			
		||||
			self.NonBlockingHTTP.PlugOut()
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
	def send(self, stanza, now = False):
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +107,7 @@ class NBCommonClient:
 | 
			
		|||
	def connect(self, on_connect, on_connect_failure, hostname=None, port=5222, 
 | 
			
		||||
		on_proxy_failure=None, proxy=None, secure=None):
 | 
			
		||||
		''' 
 | 
			
		||||
		Open XMPP connection (open streams in both directions).
 | 
			
		||||
		Open XMPP connection (open XML streams in both directions).
 | 
			
		||||
		:param hostname: hostname of XMPP server from SRV request 
 | 
			
		||||
		:param port: port number of XMPP server
 | 
			
		||||
		:param on_connect: called after stream is successfully opened
 | 
			
		||||
| 
						 | 
				
			
			@ -118,70 +119,14 @@ class NBCommonClient:
 | 
			
		|||
			optionally keys 'user' and 'pass' as proxy credentials
 | 
			
		||||
		:param secure:
 | 
			
		||||
		'''
 | 
			
		||||
		self.Port = port
 | 
			
		||||
		if hostname:
 | 
			
		||||
			xmpp_hostname = hostname
 | 
			
		||||
		else:
 | 
			
		||||
			xmpp_hostname = self.Server
 | 
			
		||||
 | 
			
		||||
		self.on_connect = on_connect
 | 
			
		||||
		self.on_connect_failure=on_connect_failure
 | 
			
		||||
		self.on_proxy_failure = on_proxy_failure
 | 
			
		||||
		self._secure = secure
 | 
			
		||||
		self.Connection = None
 | 
			
		||||
		self.Port = port
 | 
			
		||||
 | 
			
		||||
		if proxy:
 | 
			
		||||
			# with proxies, client connects to proxy instead of directly to
 | 
			
		||||
			# XMPP server ((hostname, port))
 | 
			
		||||
			# tcp_server is machine used for socket connection
 | 
			
		||||
			tcp_server=proxy['host']			
 | 
			
		||||
			tcp_port=proxy['port']
 | 
			
		||||
			self._on_tcp_failure = self.on_proxy_failure
 | 
			
		||||
			if proxy.has_key('type'):
 | 
			
		||||
				if proxy.has_key('user') and proxy.has_key('pass'):
 | 
			
		||||
					proxy_creds=(proxy['user'],proxy['pass'])
 | 
			
		||||
				else:
 | 
			
		||||
					proxy_creds=(None, None)
 | 
			
		||||
											
 | 
			
		||||
				type_ = proxy['type']
 | 
			
		||||
				if type_ == 'socks5':
 | 
			
		||||
					# SOCKS5 proxy
 | 
			
		||||
					self.socket = transports_nb.NBSOCKS5ProxySocket(
 | 
			
		||||
						on_disconnect=self.on_disconnect,
 | 
			
		||||
						proxy_creds=proxy_creds,
 | 
			
		||||
						xmpp_server=(xmpp_hostname, self.Port))
 | 
			
		||||
				elif type_ == 'http':
 | 
			
		||||
					# HTTP CONNECT to proxy
 | 
			
		||||
					self.socket = transports_nb.NBHTTPProxySocket(
 | 
			
		||||
						on_disconnect=self.on_disconnect,
 | 
			
		||||
						proxy_creds=proxy_creds,
 | 
			
		||||
						xmpp_server=(xmpp_hostname, self.Port))
 | 
			
		||||
				elif type_ == 'bosh':
 | 
			
		||||
					# BOSH - XMPP over HTTP
 | 
			
		||||
					tcp_server = transports_nb.urisplit(tcp_server)[1]
 | 
			
		||||
					self.socket = transports_nb.NonBlockingHTTP(
 | 
			
		||||
						on_disconnect=self.on_disconnect,
 | 
			
		||||
						http_uri = proxy['host'],
 | 
			
		||||
						http_port = tcp_port)
 | 
			
		||||
			else:
 | 
			
		||||
				# HTTP CONNECT to proxy from environment variables
 | 
			
		||||
				self.socket = transports_nb.NBHTTPProxySocket(
 | 
			
		||||
					on_disconnect=self.on_disconnect,
 | 
			
		||||
					proxy_creds=(None, None),
 | 
			
		||||
					xmpp_server=(xmpp_hostname, self.Port))
 | 
			
		||||
		else: 
 | 
			
		||||
			self._on_tcp_failure = self._on_connect_failure
 | 
			
		||||
			tcp_server=xmpp_hostname
 | 
			
		||||
			tcp_port=self.Port
 | 
			
		||||
			self.socket = transports_nb.NonBlockingTcp(on_disconnect = self.on_disconnect)
 | 
			
		||||
 | 
			
		||||
		self.socket.PlugIn(self)
 | 
			
		||||
 | 
			
		||||
		self._resolve_hostname(
 | 
			
		||||
			hostname=tcp_server,
 | 
			
		||||
			port=tcp_port,
 | 
			
		||||
			on_success=self._try_next_ip,
 | 
			
		||||
			on_failure=self._on_tcp_failure)
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -232,13 +177,14 @@ class NBCommonClient:
 | 
			
		|||
		started, and _on_connect_failure on failure.
 | 
			
		||||
		'''
 | 
			
		||||
		#FIXME: use RegisterHandlerOnce instead of onreceive
 | 
			
		||||
		log.info('=============xmpp_connect_machine() >> mode: %s, data: %s' % (mode,data))
 | 
			
		||||
		log.info('========xmpp_connect_machine() >> mode: %s, data: %s' % (mode,str(data)[:20] ))
 | 
			
		||||
 | 
			
		||||
		def on_next_receive(mode):
 | 
			
		||||
			log.info('setting %s on next receive' % mode)
 | 
			
		||||
			if mode is None:
 | 
			
		||||
				self.onreceive(None)
 | 
			
		||||
			else:
 | 
			
		||||
				self.onreceive(lambda data:self._xmpp_connect_machine(mode, data))
 | 
			
		||||
				self.onreceive(lambda _data:self._xmpp_connect_machine(mode, _data))
 | 
			
		||||
 | 
			
		||||
		if not mode:
 | 
			
		||||
			dispatcher_nb.Dispatcher().PlugIn(self)
 | 
			
		||||
| 
						 | 
				
			
			@ -259,9 +205,11 @@ class NBCommonClient:
 | 
			
		|||
				if not self.Dispatcher.Stream.features: 
 | 
			
		||||
					on_next_receive('RECEIVE_STREAM_FEATURES')
 | 
			
		||||
				else:
 | 
			
		||||
					log.info('got STREAM FEATURES in first read')
 | 
			
		||||
					self._xmpp_connect_machine(mode='STREAM_STARTED')
 | 
			
		||||
 | 
			
		||||
			else:
 | 
			
		||||
				log.info('incoming stream version less than 1.0')
 | 
			
		||||
				self._xmpp_connect_machine(mode='STREAM_STARTED')
 | 
			
		||||
 | 
			
		||||
		elif mode == 'RECEIVE_STREAM_FEATURES':
 | 
			
		||||
| 
						 | 
				
			
			@ -274,6 +222,7 @@ class NBCommonClient:
 | 
			
		|||
					mode='FAILURE',
 | 
			
		||||
					data='Missing <features> in 1.0 stream')
 | 
			
		||||
			else:
 | 
			
		||||
				log.info('got STREAM FEATURES in second read')
 | 
			
		||||
				self._xmpp_connect_machine(mode='STREAM_STARTED')
 | 
			
		||||
 | 
			
		||||
		elif mode == 'STREAM_STARTED':
 | 
			
		||||
| 
						 | 
				
			
			@ -294,6 +243,10 @@ class NBCommonClient:
 | 
			
		|||
		self.onreceive(None)
 | 
			
		||||
		self.on_connect(self, self.connected)
 | 
			
		||||
 | 
			
		||||
	def raise_event(self, event_type, data):
 | 
			
		||||
		log.info('raising event from transport: %s %s' % (event_type,data))
 | 
			
		||||
		if hasattr(self, 'Dispatcher'):
 | 
			
		||||
			self.Dispatcher.Event('', event_type, data)
 | 
			
		||||
		
 | 
			
		||||
	
 | 
			
		||||
	# moved from client.CommonClient:
 | 
			
		||||
| 
						 | 
				
			
			@ -324,8 +277,6 @@ class NBCommonClient:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
	def auth(self, user, password, resource = '', sasl = 1, on_auth = None):
 | 
			
		||||
			
 | 
			
		||||
		print 'auth called'
 | 
			
		||||
		''' Authenticate connnection and bind resource. If resource is not provided
 | 
			
		||||
			random one or library name used. '''
 | 
			
		||||
		self._User, self._Password, self._Resource, self._sasl = user, password, resource, sasl
 | 
			
		||||
| 
						 | 
				
			
			@ -388,39 +339,6 @@ class NBCommonClient:
 | 
			
		|||
			self.on_auth(self, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
class NonBlockingClient(NBCommonClient):
 | 
			
		||||
	''' Example client class, based on CommonClient. '''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def _on_stream_start(self):
 | 
			
		||||
		'''
 | 
			
		||||
		Called after XMPP stream is opened.
 | 
			
		||||
		In pure XMPP client, TLS negotiation may follow after esabilishing a stream.
 | 
			
		||||
		'''
 | 
			
		||||
		self.onreceive(None)
 | 
			
		||||
		if self.connected == 'tcp':
 | 
			
		||||
			if not self.connected or self._secure is not None and not self._secure:
 | 
			
		||||
				# if we are disconnected or TLS/SSL is not desired, return
 | 
			
		||||
				self._on_connect()
 | 
			
		||||
				return 
 | 
			
		||||
			if not self.Dispatcher.Stream.features.getTag('starttls'): 
 | 
			
		||||
				# if server doesn't advertise TLS in init response
 | 
			
		||||
				self._on_connect()
 | 
			
		||||
				return 
 | 
			
		||||
			if self.incoming_stream_version() != '1.0':
 | 
			
		||||
				self._on_connect()
 | 
			
		||||
				return
 | 
			
		||||
			# otherwise start TLS 	
 | 
			
		||||
			tls_nb.NonBlockingTLS().PlugIn(
 | 
			
		||||
				self,
 | 
			
		||||
				on_tls_success=lambda: self._xmpp_connect(socket_type='tls'),
 | 
			
		||||
				on_tls_failure=self._on_connect_failure)
 | 
			
		||||
		elif self.connected == 'tls':
 | 
			
		||||
			self._on_connect()
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
	def initRoster(self):
 | 
			
		||||
		''' Plug in the roster. '''
 | 
			
		||||
		if not self.__dict__.has_key('NonBlockingRoster'): 
 | 
			
		||||
| 
						 | 
				
			
			@ -440,3 +358,101 @@ class NonBlockingClient(NBCommonClient):
 | 
			
		|||
		self.send(dispatcher_nb.Presence(to=jid, typ=typ))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
class NonBlockingClient(NBCommonClient):
 | 
			
		||||
	''' Example client class, based on CommonClient. '''
 | 
			
		||||
 | 
			
		||||
	def __init__(self, domain, idlequeue, caller=None):
 | 
			
		||||
		NBCommonClient.__init__(self, domain, idlequeue, caller)
 | 
			
		||||
 | 
			
		||||
	def connect(self, on_connect, on_connect_failure, hostname=None, port=5222, 
 | 
			
		||||
		on_proxy_failure=None, proxy=None, secure=None):
 | 
			
		||||
 | 
			
		||||
		NBCommonClient.connect(self, on_connect, on_connect_failure, hostname, port,
 | 
			
		||||
			on_proxy_failure, proxy, secure)
 | 
			
		||||
 | 
			
		||||
		if hostname:
 | 
			
		||||
			xmpp_hostname = hostname
 | 
			
		||||
		else:
 | 
			
		||||
			xmpp_hostname = self.Server
 | 
			
		||||
 | 
			
		||||
		if proxy:
 | 
			
		||||
			# with proxies, client connects to proxy instead of directly to
 | 
			
		||||
			# XMPP server ((hostname, port))
 | 
			
		||||
			# tcp_host is machine used for socket connection
 | 
			
		||||
			tcp_host=proxy['host']			
 | 
			
		||||
			tcp_port=proxy['port']
 | 
			
		||||
			self._on_tcp_failure = self.on_proxy_failure
 | 
			
		||||
			if proxy.has_key('type'):
 | 
			
		||||
				assert(proxy['type']!='bosh')
 | 
			
		||||
				if proxy.has_key('user') and proxy.has_key('pass'):
 | 
			
		||||
					proxy_creds=(proxy['user'],proxy['pass'])
 | 
			
		||||
				else:
 | 
			
		||||
					proxy_creds=(None, None)
 | 
			
		||||
											
 | 
			
		||||
				type_ = proxy['type']
 | 
			
		||||
				if type_ == 'socks5':
 | 
			
		||||
					# SOCKS5 proxy
 | 
			
		||||
					self.socket = transports_nb.NBSOCKS5ProxySocket(
 | 
			
		||||
						on_disconnect=self.on_disconnect,
 | 
			
		||||
						proxy_creds=proxy_creds,
 | 
			
		||||
						xmpp_server=(xmpp_hostname, self.Port))
 | 
			
		||||
				elif type_ == 'http':
 | 
			
		||||
					# HTTP CONNECT to proxy
 | 
			
		||||
					self.socket = transports_nb.NBHTTPProxySocket(
 | 
			
		||||
						on_disconnect=self.on_disconnect,
 | 
			
		||||
						proxy_creds=proxy_creds,
 | 
			
		||||
						xmpp_server=(xmpp_hostname, self.Port))
 | 
			
		||||
			else:
 | 
			
		||||
				# HTTP CONNECT to proxy from environment variables
 | 
			
		||||
				self.socket = transports_nb.NBHTTPProxySocket(
 | 
			
		||||
					on_disconnect=self.on_disconnect,
 | 
			
		||||
					proxy_creds=(None, None),
 | 
			
		||||
					xmpp_server=(xmpp_hostname, self.Port))
 | 
			
		||||
		else: 
 | 
			
		||||
			self._on_tcp_failure = self._on_connect_failure
 | 
			
		||||
			tcp_host=xmpp_hostname
 | 
			
		||||
			tcp_port=self.Port
 | 
			
		||||
			self.socket = transports_nb.NonBlockingTCP(
 | 
			
		||||
					raise_event = self.raise_event,
 | 
			
		||||
					on_disconnect = self.on_disconnect)
 | 
			
		||||
 | 
			
		||||
		self.socket.PlugIn(self)
 | 
			
		||||
 | 
			
		||||
		self._resolve_hostname(
 | 
			
		||||
			hostname=tcp_host,
 | 
			
		||||
			port=tcp_port,
 | 
			
		||||
			on_success=self._try_next_ip,
 | 
			
		||||
			on_failure=self._on_tcp_failure)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def _on_stream_start(self):
 | 
			
		||||
		'''
 | 
			
		||||
		Called after XMPP stream is opened.
 | 
			
		||||
		In pure XMPP client, TLS negotiation may follow after esabilishing a stream.
 | 
			
		||||
		'''
 | 
			
		||||
		self.onreceive(None)
 | 
			
		||||
		if self.connected == 'tcp':
 | 
			
		||||
			if not self.connected or not self._secure:
 | 
			
		||||
				# if we are disconnected or TLS/SSL is not desired, return
 | 
			
		||||
				self._on_connect()
 | 
			
		||||
				return 
 | 
			
		||||
			if not self.Dispatcher.Stream.features.getTag('starttls'): 
 | 
			
		||||
				# if server doesn't advertise TLS in init response
 | 
			
		||||
				self._on_connect()
 | 
			
		||||
				return 
 | 
			
		||||
			if self.incoming_stream_version() != '1.0':
 | 
			
		||||
				self._on_connect()
 | 
			
		||||
				return
 | 
			
		||||
			# otherwise start TLS 	
 | 
			
		||||
			tls_nb.NonBlockingTLS().PlugIn(
 | 
			
		||||
				self,
 | 
			
		||||
				on_tls_success=lambda: self._xmpp_connect(socket_type='tls'),
 | 
			
		||||
				on_tls_failure=self._on_connect_failure)
 | 
			
		||||
		elif self.connected == 'tls':
 | 
			
		||||
			self._on_connect()
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,6 @@
 | 
			
		|||
##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
##   GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
# $Id: dispatcher.py,v 1.40 2006/01/18 19:26:43 normanr Exp $
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Main xmpppy mechanism. Provides library with methods to assign different handlers
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +29,7 @@ from client import PlugIn
 | 
			
		|||
 | 
			
		||||
import logging
 | 
			
		||||
log = logging.getLogger('gajim.c.x.dispatcher_nb')
 | 
			
		||||
log.setLevel(logging.INFO)
 | 
			
		||||
 | 
			
		||||
# default timeout to wait for response for our id
 | 
			
		||||
DEFAULT_TIMEOUT_SECONDS = 25
 | 
			
		||||
| 
						 | 
				
			
			@ -38,9 +38,33 @@ ID = 0
 | 
			
		|||
STREAM_TERMINATOR = '</stream:stream>'
 | 
			
		||||
XML_DECLARATION = '<?xml version=\'1.0\'?>'
 | 
			
		||||
 | 
			
		||||
class Dispatcher(PlugIn):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# FIXME: ugly
 | 
			
		||||
from client_nb import NonBlockingClient
 | 
			
		||||
from bosh import BOSHClient
 | 
			
		||||
class Dispatcher():
 | 
			
		||||
# Why is this here - I needed to redefine Dispatcher for BOSH and easiest way
 | 
			
		||||
# was to inherit original Dispatcher (now renamed to XMPPDispatcher). Trouble
 | 
			
		||||
# is that reference used to access dispatcher instance is in Client attribute
 | 
			
		||||
# named by __class__.__name__ of the dispatcher instance .. long story short:
 | 
			
		||||
# I wrote following to avoid changing each client.Dispatcher.whatever() in xmpp/
 | 
			
		||||
 | 
			
		||||
# If having two kinds of dispatcher will go well, I will rewrite the 
 | 
			
		||||
	def PlugIn(self, client_obj, after_SASL=False):
 | 
			
		||||
		if isinstance(client_obj, NonBlockingClient):
 | 
			
		||||
			XMPPDispatcher().PlugIn(client_obj)
 | 
			
		||||
		elif isinstance(client_obj, BOSHClient):
 | 
			
		||||
			BOSHDispatcher().PlugIn(client_obj, after_SASL)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XMPPDispatcher(PlugIn):
 | 
			
		||||
	''' Ancestor of PlugIn class. Handles XMPP stream, i.e. aware of stream headers.
 | 
			
		||||
		Can be plugged out/in to restart these headers (used for SASL f.e.). '''
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		PlugIn.__init__(self)
 | 
			
		||||
		self.handlers={}
 | 
			
		||||
| 
						 | 
				
			
			@ -84,8 +108,6 @@ class Dispatcher(PlugIn):
 | 
			
		|||
		
 | 
			
		||||
	def plugin(self, owner):
 | 
			
		||||
		''' Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally.'''
 | 
			
		||||
		log.debug('Dispatcher plugin')
 | 
			
		||||
		
 | 
			
		||||
		self._init()
 | 
			
		||||
		self._owner.lastErrNode = None
 | 
			
		||||
		self._owner.lastErr = None
 | 
			
		||||
| 
						 | 
				
			
			@ -106,8 +128,8 @@ class Dispatcher(PlugIn):
 | 
			
		|||
	def StreamInit(self):
 | 
			
		||||
		''' Send an initial stream header. '''
 | 
			
		||||
		self.Stream = simplexml.NodeBuilder()
 | 
			
		||||
		self.Stream._dispatch_depth = 2
 | 
			
		||||
		self.Stream.dispatch = self.dispatch
 | 
			
		||||
		self.Stream._dispatch_depth = 2
 | 
			
		||||
		self.Stream.stream_header_received = self._check_stream_start
 | 
			
		||||
		self.Stream.features = None
 | 
			
		||||
		self._metastream = Node('stream:stream')
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +181,7 @@ class Dispatcher(PlugIn):
 | 
			
		|||
		''' Creates internal structures for newly registered namespace.
 | 
			
		||||
			You can register handlers for this namespace afterwards. By default one namespace
 | 
			
		||||
			already registered (jabber:client or jabber:component:accept depending on context. '''
 | 
			
		||||
		log.info('Registering namespace "%s"' % xmlns)
 | 
			
		||||
		log.debug('Registering namespace "%s"' % xmlns)
 | 
			
		||||
		self.handlers[xmlns]={}
 | 
			
		||||
		self.RegisterProtocol('unknown', Protocol, xmlns=xmlns)
 | 
			
		||||
		self.RegisterProtocol('default', Protocol, xmlns=xmlns)
 | 
			
		||||
| 
						 | 
				
			
			@ -169,7 +191,7 @@ class Dispatcher(PlugIn):
 | 
			
		|||
		   Needed to start registering handlers for such stanzas.
 | 
			
		||||
		   Iq, message and presence protocols are registered by default. '''
 | 
			
		||||
		if not xmlns: xmlns=self._owner.defaultNamespace
 | 
			
		||||
		log.info('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns))
 | 
			
		||||
		log.debug('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns))
 | 
			
		||||
		self.handlers[xmlns][tag_name]={type:Proto, 'default':[]}
 | 
			
		||||
 | 
			
		||||
	def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='', makefirst=0, system=0):
 | 
			
		||||
| 
						 | 
				
			
			@ -196,7 +218,7 @@ class Dispatcher(PlugIn):
 | 
			
		|||
			'''
 | 
			
		||||
		if not xmlns: 
 | 
			
		||||
			xmlns=self._owner.defaultNamespace
 | 
			
		||||
		log.info('Registering handler %s for "%s" type->%s ns->%s(%s)' % 
 | 
			
		||||
		log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' % 
 | 
			
		||||
								(handler, name, typ, ns, xmlns))
 | 
			
		||||
		if not typ and not ns: 
 | 
			
		||||
			typ='default'
 | 
			
		||||
| 
						 | 
				
			
			@ -287,32 +309,18 @@ class Dispatcher(PlugIn):
 | 
			
		|||
	def dispatch(self, stanza, session=None, direct=0):
 | 
			
		||||
		''' Main procedure that performs XMPP stanza recognition and calling apppropriate handlers for it.
 | 
			
		||||
			Called internally. '''
 | 
			
		||||
		#log.info('dispatch called: stanza = %s, session = %s, direct= %s' % (stanza, session, direct))
 | 
			
		||||
		if not session: 
 | 
			
		||||
			session = self
 | 
			
		||||
		session.Stream._mini_dom = None
 | 
			
		||||
		name = stanza.getName()
 | 
			
		||||
 | 
			
		||||
		if not direct and self._owner._component:
 | 
			
		||||
			if name == 'route':
 | 
			
		||||
				if stanza.getAttr('error') is None:
 | 
			
		||||
					if len(stanza.getChildren()) == 1:
 | 
			
		||||
						stanza = stanza.getChildren()[0]
 | 
			
		||||
						name=stanza.getName()
 | 
			
		||||
					else:
 | 
			
		||||
						for each in stanza.getChildren():
 | 
			
		||||
							self.dispatch(each,session,direct=1)
 | 
			
		||||
						return
 | 
			
		||||
			elif name == 'presence':
 | 
			
		||||
				return
 | 
			
		||||
			elif name in ('features','bind'):
 | 
			
		||||
				pass
 | 
			
		||||
			else:
 | 
			
		||||
				raise UnsupportedStanzaType(name)
 | 
			
		||||
		
 | 
			
		||||
		if name=='features': 
 | 
			
		||||
			session.Stream.features=stanza
 | 
			
		||||
		
 | 
			
		||||
		xmlns=stanza.getNamespace()
 | 
			
		||||
 | 
			
		||||
		#log.info('in dispatch, getting ns for %s, and the ns is %s' % (stanza, xmlns))
 | 
			
		||||
		if not self.handlers.has_key(xmlns):
 | 
			
		||||
			log.warn("Unknown namespace: " + xmlns)
 | 
			
		||||
			xmlns='unknown'
 | 
			
		||||
| 
						 | 
				
			
			@ -330,7 +338,6 @@ class Dispatcher(PlugIn):
 | 
			
		|||
		stanza.props=stanza.getProperties()
 | 
			
		||||
		ID=stanza.getID()
 | 
			
		||||
		
 | 
			
		||||
		log.debug("Dispatching %s stanza with type->%s props->%s id->%s"%(name,typ,stanza.props,ID))
 | 
			
		||||
		list=['default']                                                     # we will use all handlers:
 | 
			
		||||
		if self.handlers[xmlns][name].has_key(typ): list.append(typ)                # from very common...
 | 
			
		||||
		for prop in stanza.props:
 | 
			
		||||
| 
						 | 
				
			
			@ -427,3 +434,56 @@ class Dispatcher(PlugIn):
 | 
			
		|||
		stanza.setParent(self._metastream)
 | 
			
		||||
		return (_ID, stanza)
 | 
			
		||||
	
 | 
			
		||||
class BOSHDispatcher(XMPPDispatcher):
 | 
			
		||||
 | 
			
		||||
	def PlugIn(self, owner, after_SASL=False):
 | 
			
		||||
		self.after_SASL = after_SASL
 | 
			
		||||
		XMPPDispatcher.PlugIn(self, owner)
 | 
			
		||||
 | 
			
		||||
	def StreamInit(self):
 | 
			
		||||
		''' Send an initial stream header. '''
 | 
			
		||||
		self.Stream = simplexml.NodeBuilder()
 | 
			
		||||
		self.Stream.dispatch = self.dispatch
 | 
			
		||||
		self.Stream._dispatch_depth = 2
 | 
			
		||||
		self.Stream.stream_header_received = self._check_stream_start
 | 
			
		||||
		self.Stream.features = None
 | 
			
		||||
 | 
			
		||||
		self._metastream = Node('stream:stream')
 | 
			
		||||
		self._metastream.setNamespace(self._owner.Namespace)
 | 
			
		||||
		self._metastream.setAttr('version', '1.0')
 | 
			
		||||
		self._metastream.setAttr('xmlns:stream', NS_STREAMS)
 | 
			
		||||
		self._metastream.setAttr('to', self._owner.Server)
 | 
			
		||||
		if locale.getdefaultlocale()[0]:
 | 
			
		||||
			self._metastream.setAttr('xml:lang',
 | 
			
		||||
				locale.getdefaultlocale()[0].split('_')[0])
 | 
			
		||||
		
 | 
			
		||||
		self.restart = True
 | 
			
		||||
		self._owner.Connection.send(self._owner.get_initial_bodytag(self.after_SASL))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def StreamTerminate(self):
 | 
			
		||||
		''' Send a stream terminator. '''
 | 
			
		||||
		self._owner.Connection.send(self._owner.get_closing_bodytag())
 | 
			
		||||
 | 
			
		||||
	def ProcessNonBlocking(self, data=None):
 | 
			
		||||
 | 
			
		||||
		if self.restart:
 | 
			
		||||
			fromstream = self._metastream
 | 
			
		||||
			fromstream.setAttr('from', fromstream.getAttr('to'))
 | 
			
		||||
			fromstream.delAttr('to')
 | 
			
		||||
			data = '%s%s>%s' % (XML_DECLARATION,str(fromstream)[:-2] ,data)
 | 
			
		||||
			self.restart = False
 | 
			
		||||
 | 
			
		||||
		return XMPPDispatcher.ProcessNonBlocking(self, data)
 | 
			
		||||
 | 
			
		||||
	def dispatch(self, stanza, session=None, direct=0):
 | 
			
		||||
		if stanza.getName()=='body' and stanza.getNamespace()==NS_HTTP_BIND:
 | 
			
		||||
			self._owner.on_bodytag_attrs(stanza.getAttrs())
 | 
			
		||||
			#self._owner.send_empty_bodytag()
 | 
			
		||||
			for child in stanza.getChildren():
 | 
			
		||||
				XMPPDispatcher.dispatch(self, child, session, direct)
 | 
			
		||||
		else:
 | 
			
		||||
			XMPPDispatcher.dispatch(self, stanza, session, direct)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ I'm personally using it in many other separate projects. It is designed to be as
 | 
			
		|||
import xml.parsers.expat
 | 
			
		||||
import logging
 | 
			
		||||
log = logging.getLogger('gajim.c.x.simplexml')
 | 
			
		||||
 | 
			
		||||
#log.setLevel(logging.DEBUG)
 | 
			
		||||
 | 
			
		||||
def XMLescape(txt):
 | 
			
		||||
	"""Returns provided string with symbols & < > " replaced by their respective XML entities."""
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +99,10 @@ class Node(object):
 | 
			
		|||
			for a in self.kids:
 | 
			
		||||
				if not fancy and (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt])
 | 
			
		||||
				elif (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt].strip())
 | 
			
		||||
				s = s + a.__str__(fancy and fancy+1)
 | 
			
		||||
				if isinstance(a, str) or isinstance(a, unicode):
 | 
			
		||||
					s = s + a.__str__()
 | 
			
		||||
				else:
 | 
			
		||||
					s = s + a.__str__(fancy and fancy+1)
 | 
			
		||||
				cnt=cnt+1
 | 
			
		||||
		if not fancy and (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
 | 
			
		||||
		elif (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt].strip())
 | 
			
		||||
| 
						 | 
				
			
			@ -343,7 +346,7 @@ class NodeBuilder:
 | 
			
		|||
			attrs[self.namespaces[ns]+attr[sp+1:]]=attrs[attr]
 | 
			
		||||
			del attrs[attr]        #
 | 
			
		||||
		self._inc_depth()
 | 
			
		||||
		log.info("DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`))
 | 
			
		||||
		log.info("STARTTAG.. DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`))
 | 
			
		||||
		if self.__depth == self._dispatch_depth:
 | 
			
		||||
			if not self._mini_dom : 
 | 
			
		||||
				self._mini_dom = Node(tag=tag, attrs=attrs)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
##  
 | 
			
		||||
##   Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov
 | 
			
		||||
##       modified by Dimitur Kirov <dkirov@gmail.com>
 | 
			
		||||
##       modified by Dimitur Kirov <dkirov@gmail.com>
 | 
			
		||||
##
 | 
			
		||||
##   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
 | 
			
		||||
| 
						 | 
				
			
			@ -65,10 +66,21 @@ CONNECTED ='CONNECTED'
 | 
			
		|||
DISCONNECTING ='DISCONNECTING' 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NonBlockingTransport(PlugIn):
 | 
			
		||||
	def __init__(self, on_disconnect):
 | 
			
		||||
	def __init__(self, raise_event, on_disconnect):
 | 
			
		||||
		PlugIn.__init__(self)
 | 
			
		||||
		self.raise_event = raise_event
 | 
			
		||||
		self.on_disconnect = on_disconnect
 | 
			
		||||
		self.on_connect = None
 | 
			
		||||
		self.on_connect_failure = None
 | 
			
		||||
		self.idlequeue = None
 | 
			
		||||
		self.on_receive = None
 | 
			
		||||
		self.server = None
 | 
			
		||||
		self.port = None
 | 
			
		||||
		self.state = DISCONNECTED
 | 
			
		||||
		self._exported_methods=[self.disconnect, self.onreceive]
 | 
			
		||||
 | 
			
		||||
	def plugin(self, owner):
 | 
			
		||||
		owner.Connection=self
 | 
			
		||||
| 
						 | 
				
			
			@ -79,30 +91,72 @@ class NonBlockingTransport(PlugIn):
 | 
			
		|||
		self._owner.Connection = None
 | 
			
		||||
		self._owner = None
 | 
			
		||||
 | 
			
		||||
	def connect(self, conn_5tuple, on_connect, on_connect_failure):
 | 
			
		||||
		self.on_connect = on_connect
 | 
			
		||||
		self.on_connect_failure = on_connect_failure
 | 
			
		||||
		(self.server, self.port) = conn_5tuple[4][:2]
 | 
			
		||||
		log.info('NonBlocking Connect :: About tot connect to %s:%s' % (self.server, self.port))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		||||
	def set_state(self, newstate):
 | 
			
		||||
		assert(newstate in [DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING])
 | 
			
		||||
		if (self.state, newstate) in [(CONNECTING, DISCONNECTING), (DISCONNECTED, DISCONNECTING)]:
 | 
			
		||||
			log.info('strange move: %s -> %s' % (self.state, newstate))
 | 
			
		||||
		self.state = newstate
 | 
			
		||||
 | 
			
		||||
	def _on_connect(self, data):
 | 
			
		||||
		''' preceeds call of on_connect callback '''
 | 
			
		||||
		self.set_state(CONNECTED)
 | 
			
		||||
		self.on_connect()
 | 
			
		||||
 | 
			
		||||
	def _on_connect_failure(self,err_message):
 | 
			
		||||
		''' preceeds call of on_connect_failure callback '''
 | 
			
		||||
		# In case of error while connecting we need to close socket
 | 
			
		||||
		# but we don't want to call DisconnectHandlers from client,
 | 
			
		||||
		# thus the do_callback=False
 | 
			
		||||
		self.disconnect(do_callback=False)
 | 
			
		||||
		self.on_connect_failure(err_message=err_message)
 | 
			
		||||
 | 
			
		||||
	def send(self, raw_data, now=False):
 | 
			
		||||
		if self.state not in [CONNECTED, DISCONNECTING]:
 | 
			
		||||
			# FIXME better handling needed
 | 
			
		||||
			log.error('Trying to send %s when transport is %s.' % 
 | 
			
		||||
				(raw_data, self.state))
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
	def disconnect(self, do_callback=True):
 | 
			
		||||
		self.set_state(DISCONNECTED)
 | 
			
		||||
		if do_callback:
 | 
			
		||||
			# invoke callback given in __init__
 | 
			
		||||
			self.on_disconnect()
 | 
			
		||||
 | 
			
		||||
	def onreceive(self, recv_handler):
 | 
			
		||||
		''' Sets the on_receive callback. Do not confuse it with
 | 
			
		||||
		on_receive() method, which is the callback itself.'''
 | 
			
		||||
		if not recv_handler:
 | 
			
		||||
			if hasattr(self._owner, 'Dispatcher'):
 | 
			
		||||
				self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
 | 
			
		||||
			else:
 | 
			
		||||
				self.on_receive = None
 | 
			
		||||
			return
 | 
			
		||||
		log.info('setting onreceive on %s' % recv_handler)
 | 
			
		||||
		self.on_receive = recv_handler
 | 
			
		||||
 | 
			
		||||
	def tcp_connection_started(self):
 | 
			
		||||
		self.set_state(CONNECTING)
 | 
			
		||||
		# on_connect/on_conn_failure will be called from self.pollin/self.pollout
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NonBlockingTCP(NonBlockingTransport, IdleObject):
 | 
			
		||||
	'''
 | 
			
		||||
	Non-blocking TCP socket wrapper
 | 
			
		||||
	'''
 | 
			
		||||
	def __init__(self, on_disconnect):
 | 
			
		||||
	def __init__(self, raise_event, on_disconnect):
 | 
			
		||||
		'''
 | 
			
		||||
		Class constructor.
 | 
			
		||||
		'''
 | 
			
		||||
 | 
			
		||||
		PlugIn.__init__(self)
 | 
			
		||||
		IdleObject.__init__(self)
 | 
			
		||||
 | 
			
		||||
		self.on_disconnect = on_disconnect
 | 
			
		||||
 | 
			
		||||
		self.on_connect = None
 | 
			
		||||
		self.on_connect_failure = None
 | 
			
		||||
		self.sock = None
 | 
			
		||||
		self.idlequeue = None
 | 
			
		||||
		self.on_receive = None
 | 
			
		||||
		self.DBG_LINE='socket'
 | 
			
		||||
		self.state = DISCONNECTED
 | 
			
		||||
 | 
			
		||||
		NonBlockingTransport.__init__(self, raise_event, on_disconnect)
 | 
			
		||||
		# writable, readable  -  keep state of the last pluged flags
 | 
			
		||||
		# This prevents replug of same object with the same flags
 | 
			
		||||
		self.writable = True
 | 
			
		||||
| 
						 | 
				
			
			@ -122,14 +176,6 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
		self._exported_methods=[self.disconnect, self.onreceive, self.set_send_timeout, 
 | 
			
		||||
			self.set_timeout, self.remove_timeout]
 | 
			
		||||
 | 
			
		||||
	def plugin(self, owner):
 | 
			
		||||
		owner.Connection=self
 | 
			
		||||
		self.idlequeue = owner.idlequeue
 | 
			
		||||
 | 
			
		||||
	def plugout(self):
 | 
			
		||||
		self._owner.Connection = None
 | 
			
		||||
		self._owner = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def get_fd(self):
 | 
			
		||||
		try:
 | 
			
		||||
| 
						 | 
				
			
			@ -147,14 +193,12 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
		:param on_connect_failure: callback called on failure when estabilishing tcp 
 | 
			
		||||
			connection
 | 
			
		||||
		'''
 | 
			
		||||
		self.on_connect = on_connect
 | 
			
		||||
		self.on_connect_failure = on_connect_failure
 | 
			
		||||
		(self.server, self.port) = conn_5tuple[4][:2]
 | 
			
		||||
		log.info('NonBlocking Connect :: About tot connect to %s:%s' % conn_5tuple[4][:2])
 | 
			
		||||
		NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure)
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			self._sock = socket.socket(*conn_5tuple[:3])
 | 
			
		||||
		except socket.error, (errnum, errstr):
 | 
			
		||||
			on_connect_failure('NonBlockingTcp: Error while creating socket: %s %s' % (errnum, errstr))
 | 
			
		||||
			self._on_connect_failure('NonBlockingTCP: Error while creating socket: %s %s' % (errnum, errstr))
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		self._send = self._sock.send
 | 
			
		||||
| 
						 | 
				
			
			@ -177,9 +221,8 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
 | 
			
		||||
		if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):
 | 
			
		||||
			# connecting in progress
 | 
			
		||||
			self.set_state(CONNECTING)
 | 
			
		||||
			log.info('After connect. "%s" raised => CONNECTING' % errstr)
 | 
			
		||||
			# on_connect/failure will be called from self.pollin/self.pollout
 | 
			
		||||
			self.tcp_connection_started()
 | 
			
		||||
			return
 | 
			
		||||
		elif errnum in (0, 10056, errno.EISCONN):
 | 
			
		||||
			# already connected - this branch is very unlikely, nonblocking connect() will
 | 
			
		||||
| 
						 | 
				
			
			@ -195,27 +238,9 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
			(self.server, self.port, errnum, errstr))
 | 
			
		||||
			
 | 
			
		||||
	def _on_connect(self, data):
 | 
			
		||||
		''' preceeds call of on_connect callback '''
 | 
			
		||||
		self.set_state(CONNECTED)
 | 
			
		||||
		''' with TCP socket, we have to remove send-timeout '''
 | 
			
		||||
		self.idlequeue.remove_timeout(self.get_fd())
 | 
			
		||||
		self.on_connect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def set_state(self, newstate):
 | 
			
		||||
		assert(newstate in [DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING])
 | 
			
		||||
		if (self.state, newstate) in [(CONNECTING, DISCONNECTING), (DISCONNECTED, DISCONNECTING)]:
 | 
			
		||||
			log.info('strange move: %s -> %s' % (self.state, newstate))
 | 
			
		||||
		self.state = newstate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def _on_connect_failure(self,err_message):
 | 
			
		||||
		''' preceeds call of on_connect_failure callback '''
 | 
			
		||||
		# In case of error while connecting we need to close socket
 | 
			
		||||
		# but we don't want to call DisconnectHandlers from client,
 | 
			
		||||
		# thus the do_callback=False
 | 
			
		||||
		self.disconnect(do_callback=False)
 | 
			
		||||
		self.on_connect_failure(err_message=err_message)
 | 
			
		||||
 | 
			
		||||
		NonBlockingTransport._on_connect(self, data)
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
	def pollin(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -250,10 +275,7 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
			self._sock.close()
 | 
			
		||||
		except socket.error, (errnum, errstr):
 | 
			
		||||
			log.error('Error disconnecting a socket: %s %s' % (errnum,errstr))
 | 
			
		||||
		self.set_state(DISCONNECTED)
 | 
			
		||||
		if do_callback:
 | 
			
		||||
			# invoke callback given in __init__
 | 
			
		||||
			self.on_disconnect()
 | 
			
		||||
		NonBlockingTransport.disconnect(self, do_callback)
 | 
			
		||||
 | 
			
		||||
	def read_timeout(self):
 | 
			
		||||
		'''
 | 
			
		||||
| 
						 | 
				
			
			@ -295,11 +317,7 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
		'''Append raw_data to the queue of messages to be send. 
 | 
			
		||||
		If supplied data is unicode string, encode it to utf-8.
 | 
			
		||||
		'''
 | 
			
		||||
 | 
			
		||||
		if self.state not in [CONNECTED, DISCONNECTING]:
 | 
			
		||||
			log.error('Trying to send %s when transport is %s.' % 
 | 
			
		||||
				(raw_data, self.state))
 | 
			
		||||
			return
 | 
			
		||||
		NonBlockingTransport.send(self, raw_data, now)
 | 
			
		||||
		r = raw_data
 | 
			
		||||
		if isinstance(r, unicode): 
 | 
			
		||||
			r = r.encode('utf-8')
 | 
			
		||||
| 
						 | 
				
			
			@ -343,31 +361,13 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
				sent_data = self.sendbuff[:send_count]
 | 
			
		||||
				self.sendbuff = self.sendbuff[send_count:]
 | 
			
		||||
				self._plug_idle()
 | 
			
		||||
				self._raise_event(DATA_SENT, sent_data)
 | 
			
		||||
				self.raise_event(DATA_SENT, sent_data)
 | 
			
		||||
 | 
			
		||||
		except socket.error, e:
 | 
			
		||||
			log.error('_do_send:', exc_info=True)
 | 
			
		||||
			traceback.print_exc()
 | 
			
		||||
			self.disconnect()
 | 
			
		||||
 | 
			
		||||
	def _raise_event(self, event_type, data):
 | 
			
		||||
		if data and data.strip():
 | 
			
		||||
			log.info('raising event from transport: %s %s' % (event_type,data))
 | 
			
		||||
			if hasattr(self._owner, 'Dispatcher'):
 | 
			
		||||
				self._owner.Dispatcher.Event('', event_type, data)
 | 
			
		||||
 | 
			
		||||
	def onreceive(self, recv_handler):
 | 
			
		||||
		''' Sets the on_receive callback. Do not confuse it with
 | 
			
		||||
		on_receive() method, which is the callback itself.'''
 | 
			
		||||
		if not recv_handler:
 | 
			
		||||
			if hasattr(self._owner, 'Dispatcher'):
 | 
			
		||||
				self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
 | 
			
		||||
			else:
 | 
			
		||||
				self.on_receive = None
 | 
			
		||||
			return
 | 
			
		||||
		log.info('setting onreceive on %s' % recv_handler)
 | 
			
		||||
		self.on_receive = recv_handler
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def _do_receive(self):
 | 
			
		||||
		''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.'''
 | 
			
		||||
| 
						 | 
				
			
			@ -410,7 +410,7 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
		# pass received data to owner
 | 
			
		||||
		#self.
 | 
			
		||||
		if self.on_receive:
 | 
			
		||||
			self._raise_event(DATA_RECEIVED, received)
 | 
			
		||||
			self.raise_event(DATA_RECEIVED, received)
 | 
			
		||||
			self._on_receive(received)
 | 
			
		||||
		else:
 | 
			
		||||
			# This should never happen, so we need the debug. (If there is no handler
 | 
			
		||||
| 
						 | 
				
			
			@ -418,31 +418,37 @@ class NonBlockingTcp(PlugIn, IdleObject):
 | 
			
		|||
			log.error('SOCKET Unhandled data received: %s' % received)
 | 
			
		||||
			self.disconnect()
 | 
			
		||||
 | 
			
		||||
	def _on_receive(self, data):
 | 
			
		||||
		# Overriding this method allows modifying received data before it is passed
 | 
			
		||||
		# to owner's callback. 
 | 
			
		||||
		log.info('About to call on_receive which is %s' % self.on_receive)
 | 
			
		||||
	def _on_receive(self,data):
 | 
			
		||||
		'''Preceeds passing received data to Client class. Gets rid of HTTP headers
 | 
			
		||||
		and checks them.'''
 | 
			
		||||
		self.on_receive(data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NonBlockingHTTP(NonBlockingTcp):
 | 
			
		||||
 | 
			
		||||
class NonBlockingHTTP(NonBlockingTCP):
 | 
			
		||||
	'''
 | 
			
		||||
	Socket wrapper that cretes HTTP message out of sent data and peels-off 
 | 
			
		||||
	HTTP headers from incoming messages
 | 
			
		||||
	'''
 | 
			
		||||
 | 
			
		||||
	def __init__(self, http_uri, http_port, on_disconnect):
 | 
			
		||||
	def __init__(self, raise_event, on_disconnect, http_uri, http_port, http_version=None):
 | 
			
		||||
		self.http_protocol, self.http_host, self.http_path = urisplit(http_uri)
 | 
			
		||||
		if self.http_protocol is None:
 | 
			
		||||
			self.http_protocol = 'http'
 | 
			
		||||
		if self.http_path == '':
 | 
			
		||||
			http_path = '/'
 | 
			
		||||
		self.http_port = http_port
 | 
			
		||||
		NonBlockingTcp.__init__(self, on_disconnect)
 | 
			
		||||
		if http_version:
 | 
			
		||||
			self.http_version = http_version
 | 
			
		||||
		else:
 | 
			
		||||
			self.http_version = 'HTTP/1.1'
 | 
			
		||||
		# buffer for partial responses
 | 
			
		||||
		self.recvbuff = ''
 | 
			
		||||
		self.expected_length = 0 
 | 
			
		||||
		NonBlockingTCP.__init__(self, raise_event, on_disconnect)
 | 
			
		||||
		
 | 
			
		||||
	def send(self, raw_data, now=False):
 | 
			
		||||
 | 
			
		||||
		NonBlockingTcp.send(
 | 
			
		||||
		NonBlockingTCP.send(
 | 
			
		||||
			self,
 | 
			
		||||
			self.build_http_message(raw_data),
 | 
			
		||||
			now)
 | 
			
		||||
| 
						 | 
				
			
			@ -450,21 +456,43 @@ class NonBlockingHTTP(NonBlockingTcp):
 | 
			
		|||
	def _on_receive(self,data):
 | 
			
		||||
		'''Preceeds passing received data to Client class. Gets rid of HTTP headers
 | 
			
		||||
		and checks them.'''
 | 
			
		||||
		statusline, headers, httpbody = self.parse_http_message(data)
 | 
			
		||||
		if statusline[1] != '200':
 | 
			
		||||
			log.error('HTTP Error: %s %s' % (statusline[1], statusline[2]))
 | 
			
		||||
			self.disconnect()
 | 
			
		||||
		if not self.recvbuff:
 | 
			
		||||
			# recvbuff empty - fresh HTTP message was received
 | 
			
		||||
			statusline, headers, self.recvbuff = self.parse_http_message(data)
 | 
			
		||||
			if statusline[1] != '200':
 | 
			
		||||
				log.error('HTTP Error: %s %s' % (statusline[1], statusline[2]))
 | 
			
		||||
				self.disconnect()
 | 
			
		||||
				return
 | 
			
		||||
			self.expected_length = int(headers['Content-Length'])
 | 
			
		||||
		else:
 | 
			
		||||
			#sth in recvbuff - append currently received data to HTTP mess in buffer 
 | 
			
		||||
			self.recvbuff = '%s%s' % (self.recvbuff, data)
 | 
			
		||||
 | 
			
		||||
		if self.expected_length > len(self.recvbuff):
 | 
			
		||||
			# If we haven't received the whole HTTP mess yet, let's end the thread.
 | 
			
		||||
			# It will be finnished from one of following poll calls on plugged socket.
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		# FIXME the reassembling doesn't work - Connection Manager on jabbim.cz
 | 
			
		||||
		# closes TCP connection before sending <Content-Length> announced bytes.. WTF
 | 
			
		||||
 | 
			
		||||
		# all was received, now call the on_receive callback
 | 
			
		||||
		httpbody = self.recvbuff
 | 
			
		||||
 | 
			
		||||
		self.recvbuff=''
 | 
			
		||||
		self.expected_length=0
 | 
			
		||||
		self.on_receive(httpbody)
 | 
			
		||||
	
 | 
			
		||||
		
 | 
			
		||||
	def build_http_message(self, httpbody):
 | 
			
		||||
	def build_http_message(self, httpbody, method='POST'):
 | 
			
		||||
		'''
 | 
			
		||||
		Builds http message with given body.
 | 
			
		||||
		Values for headers and status line fields are taken from class variables.
 | 
			
		||||
		)  
 | 
			
		||||
		'''
 | 
			
		||||
		headers = ['POST %s HTTP/1.1' % self.http_path,
 | 
			
		||||
		absolute_uri = '%s://%s:%s%s' % (self.http_protocol, self.http_host,
 | 
			
		||||
			self.http_port, self.http_path)
 | 
			
		||||
		headers = ['%s %s %s' % (method, absolute_uri, self.http_version),
 | 
			
		||||
			'Host: %s:%s' % (self.http_host, self.http_port),
 | 
			
		||||
			'Content-Type: text/xml; charset=utf-8',
 | 
			
		||||
			'Content-Length: %s' % len(str(httpbody)),
 | 
			
		||||
| 
						 | 
				
			
			@ -482,7 +510,7 @@ class NonBlockingHTTP(NonBlockingTcp):
 | 
			
		|||
		)  
 | 
			
		||||
		'''
 | 
			
		||||
		message = message.replace('\r','')
 | 
			
		||||
		(header, httpbody) = message.split('\n\n')
 | 
			
		||||
		(header, httpbody) = message.split('\n\n',1)
 | 
			
		||||
		header = header.split('\n')
 | 
			
		||||
		statusline = header[0].split(' ')
 | 
			
		||||
		header = header[1:]
 | 
			
		||||
| 
						 | 
				
			
			@ -494,15 +522,15 @@ class NonBlockingHTTP(NonBlockingTcp):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NBProxySocket(NonBlockingTcp):
 | 
			
		||||
class NBProxySocket(NonBlockingTCP):
 | 
			
		||||
	'''
 | 
			
		||||
	Interface for proxy socket wrappers - when tunnneling XMPP over proxies,
 | 
			
		||||
	some connecting process usually has to be done before opening stream.
 | 
			
		||||
	'''
 | 
			
		||||
	def __init__(self, on_disconnect, xmpp_server, proxy_creds=(None,None)):
 | 
			
		||||
	def __init__(self, raise_event, on_disconnect, xmpp_server, proxy_creds=(None,None)):
 | 
			
		||||
		self.proxy_user, self.proxy_pass = proxy_creds
 | 
			
		||||
		self.xmpp_server = xmpp_server
 | 
			
		||||
		NonBlockingTcp.__init__(self, on_disconnect)
 | 
			
		||||
		NonBlockingTCP.__init__(self, raise_event, on_disconnect)
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
	def connect(self, conn_5tuple, on_connect, on_connect_failure):
 | 
			
		||||
| 
						 | 
				
			
			@ -515,7 +543,7 @@ class NBProxySocket(NonBlockingTcp):
 | 
			
		|||
 | 
			
		||||
		self.after_proxy_connect = on_connect
 | 
			
		||||
		
 | 
			
		||||
		NonBlockingTcp.connect(self,
 | 
			
		||||
		NonBlockingTCP.connect(self,
 | 
			
		||||
				conn_5tuple=conn_5tuple,
 | 
			
		||||
				on_connect =self._on_tcp_connect,
 | 
			
		||||
				on_connect_failure =on_connect_failure)
 | 
			
		||||
| 
						 | 
				
			
			@ -526,7 +554,7 @@ class NBProxySocket(NonBlockingTcp):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class NBHTTPProxySocket(NBProxySocket):
 | 
			
		||||
	''' This class can be used instead of NonBlockingTcp
 | 
			
		||||
	''' This class can be used instead of NonBlockingTCP
 | 
			
		||||
	HTTP (CONNECT) proxy connection class. Allows to use HTTP proxies like squid with
 | 
			
		||||
	(optionally) simple authentication (using login and password). 
 | 
			
		||||
	'''
 | 
			
		||||
| 
						 | 
				
			
			@ -588,7 +616,7 @@ class NBSOCKS5ProxySocket(NBProxySocket):
 | 
			
		|||
		redefines only connect method. Allows to use SOCKS5 proxies with
 | 
			
		||||
		(optionally) simple authentication (only USERNAME/PASSWORD auth). 
 | 
			
		||||
	'''
 | 
			
		||||
	# TODO: replace DEBUG with ordinrar logging, replace on_proxy_failure() with
 | 
			
		||||
	# TODO:  replace on_proxy_failure() with
 | 
			
		||||
	#	_on_connect_failure, at the end call _on_connect()
 | 
			
		||||
 | 
			
		||||
	def _on_tcp_connect(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -620,13 +648,13 @@ class NBSOCKS5ProxySocket(NBProxySocket):
 | 
			
		|||
			self.send(to_send)
 | 
			
		||||
		else:
 | 
			
		||||
			if reply[1] == '\xff':
 | 
			
		||||
				self.DEBUG('Authentification to proxy impossible: no acceptable '
 | 
			
		||||
					'auth method', 'error')
 | 
			
		||||
				log.error('Authentification to proxy impossible: no acceptable '
 | 
			
		||||
					'auth method')
 | 
			
		||||
				self._owner.disconnected()
 | 
			
		||||
				self.on_proxy_failure('Authentification to proxy impossible: no '
 | 
			
		||||
					'acceptable authentification method')
 | 
			
		||||
				return
 | 
			
		||||
			self.DEBUG('Invalid proxy reply', 'error')
 | 
			
		||||
			log.error('Invalid proxy reply')
 | 
			
		||||
			self._owner.disconnected()
 | 
			
		||||
			self.on_proxy_failure('Invalid proxy reply')
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			@ -635,21 +663,21 @@ class NBSOCKS5ProxySocket(NBProxySocket):
 | 
			
		|||
		if reply is None:
 | 
			
		||||
			return
 | 
			
		||||
		if len(reply) != 2:
 | 
			
		||||
			self.DEBUG('Invalid proxy reply', 'error')
 | 
			
		||||
			log.error('Invalid proxy reply')
 | 
			
		||||
			self._owner.disconnected()
 | 
			
		||||
			self.on_proxy_failure('Invalid proxy reply')
 | 
			
		||||
			return
 | 
			
		||||
		if reply[0] != '\x01':
 | 
			
		||||
			self.DEBUG('Invalid proxy reply', 'error')
 | 
			
		||||
			log.error('Invalid proxy reply')
 | 
			
		||||
			self._owner.disconnected()
 | 
			
		||||
			self.on_proxy_failure('Invalid proxy reply')
 | 
			
		||||
			return
 | 
			
		||||
		if reply[1] != '\x00':
 | 
			
		||||
			self.DEBUG('Authentification to proxy failed', 'error')
 | 
			
		||||
			log.error('Authentification to proxy failed')
 | 
			
		||||
			self._owner.disconnected()
 | 
			
		||||
			self.on_proxy_failure('Authentification to proxy failed')
 | 
			
		||||
			return
 | 
			
		||||
		self.DEBUG('Authentification successfull. Jabber server contacted.','ok')
 | 
			
		||||
		log.info('Authentification successfull. Jabber server contacted.')
 | 
			
		||||
		# Request connection
 | 
			
		||||
		req = "\x05\x01\x00"
 | 
			
		||||
		# If the given destination address is an IP address, we'll
 | 
			
		||||
| 
						 | 
				
			
			@ -675,12 +703,12 @@ class NBSOCKS5ProxySocket(NBProxySocket):
 | 
			
		|||
		if reply is None:
 | 
			
		||||
			return
 | 
			
		||||
		if len(reply) < 10:
 | 
			
		||||
			self.DEBUG('Invalid proxy reply', 'error')
 | 
			
		||||
			log.error('Invalid proxy reply')
 | 
			
		||||
			self._owner.disconnected()
 | 
			
		||||
			self.on_proxy_failure('Invalid proxy reply')
 | 
			
		||||
			return
 | 
			
		||||
		if reply[0] != '\x05':
 | 
			
		||||
			self.DEBUG('Invalid proxy reply', 'error')
 | 
			
		||||
			log.error('Invalid proxy reply')
 | 
			
		||||
			self._owner.disconnected()
 | 
			
		||||
			self.on_proxy_failure('Invalid proxy reply')
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			@ -700,7 +728,7 @@ class NBSOCKS5ProxySocket(NBProxySocket):
 | 
			
		|||
				txt = errors[ord(reply[1])-1]
 | 
			
		||||
			else:
 | 
			
		||||
				txt = 'Invalid proxy reply'
 | 
			
		||||
			self.DEBUG(txt, 'error')
 | 
			
		||||
			log.error(txt)
 | 
			
		||||
			self.on_proxy_failure(txt)
 | 
			
		||||
			return
 | 
			
		||||
		# Get the bound address/port
 | 
			
		||||
| 
						 | 
				
			
			@ -709,7 +737,7 @@ class NBSOCKS5ProxySocket(NBProxySocket):
 | 
			
		|||
		elif reply[3] == "\x03":
 | 
			
		||||
			begin, end = 4, 4 + reply[4]
 | 
			
		||||
		else:
 | 
			
		||||
			self.DEBUG('Invalid proxy reply', 'error')
 | 
			
		||||
			log.error('Invalid proxy reply')
 | 
			
		||||
			self._owner.disconnected()
 | 
			
		||||
			self.on_proxy_failure('Invalid proxy reply')
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			@ -717,9 +745,6 @@ class NBSOCKS5ProxySocket(NBProxySocket):
 | 
			
		|||
		if self.on_connect_proxy:
 | 
			
		||||
			self.on_connect_proxy()
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue