xmpppy changes:
- Sync with latest CVS version + gajim patches. - streamErrorHandler disabled. If you want enable it - unrem it at line 66 of dispatcher.py - TLS re-enabled. Should work fine now - disconnection TLS plugout re-enabled. Didn't successed in reproducing problem though so maybe not fixed. - My temporary fixes replaced with CVS ones. gajim connection.py changes (in chunks order): - getTags is incorrect. Replaced with getChildren - browseAgents is absent from xmpppy. Fixed with manual node send. Made use of buildReply method and con argument - formatting fix - crude fixed register problem. I do not know how to do it properly. It is client stuff. Honest! I have no idea how to make it non-blocking and yet make library to fallback to older protocols. getInstructions() moved to xmpppy Thanks Alexey !
This commit is contained in:
		
							parent
							
								
									66249dafb5
								
							
						
					
					
						commit
						56d60f3fd5
					
				
					 11 changed files with 194 additions and 88 deletions
				
			
		|  | @ -328,7 +328,7 @@ class Connection: | |||
| 		for key in q.getAttrs().keys(): | ||||
| 			attr[key.encode('utf8')] = q.getAttr(key).encode('utf8') | ||||
| 		identities = [attr] | ||||
| 		for node in q.getTags(): | ||||
| 		for node in q.getChildren(): | ||||
| 			if node.getName() == 'ns': | ||||
| 				features.append(node.getData()) | ||||
| 			else: | ||||
|  | @ -387,24 +387,22 @@ class Connection: | |||
| 				features.append(i.getAttr('var')) | ||||
| 		jid = str(iq_obj.getFrom()) | ||||
| 		if not identities: | ||||
| 			self.connection.browseAgents(jid, node) | ||||
| 			self.connection.send(common.xmpp.Iq(typ = 'get', queryNS = \ | ||||
| 				common.xmpp.NS_AGENTS)) | ||||
| 		else: | ||||
| 			self.dispatch('AGENT_INFO_INFO', (jid, node, identities, features)) | ||||
| 			self.discoverItems(jid, node) | ||||
| 
 | ||||
| 	def _VersionCB(self, con, iq_obj): | ||||
| 		gajim.log.debug('VersionCB') | ||||
| 		f = iq_obj.getFrom() | ||||
| 		iq_obj.setFrom(iq_obj.getTo()) | ||||
| 		iq_obj.setTo(f) | ||||
| 		iq_obj.setType('result') | ||||
| 		iq_obj = iq_obj.buildReply('result') | ||||
| 		qp = iq_obj.getTag('query') | ||||
| 		qp.setTagData('name', 'Gajim') | ||||
| 		qp.setTagData('version', gajim.version) | ||||
| 		send_os = gajim.config.get('send_os_info') | ||||
| 		if send_os: | ||||
| 			qp.setTagData('os', get_os_info()) | ||||
| 		self.connection.send(iq_obj) | ||||
| 		con.send(iq_obj) | ||||
| 
 | ||||
| 	def _VersionResultCB(self, con, iq_obj): | ||||
| 		gajim.log.debug('VersionResultCB') | ||||
|  | @ -638,8 +636,7 @@ class Connection: | |||
| 			prio = str(gajim.config.get_per('accounts', self.name, 'priority')) | ||||
| 			p = common.xmpp.Presence(typ = ptype, priority = prio, show = status, | ||||
| 				status = msg) | ||||
| 			if signed: p.setTag(common.xmpp.NS_SIGNED + ' x').setData( | ||||
| 				signed) | ||||
| 			if signed: p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed) | ||||
| 			self.connection.send(p) | ||||
| 			self.dispatch('STATUS', status) | ||||
| 
 | ||||
|  | @ -712,17 +709,14 @@ class Connection: | |||
| 
 | ||||
| 	def request_agents(self, jid, node): | ||||
| 		if self.connection: | ||||
| 			self.connection.send(common.xmpp.Iq(to = jid, typ = 'get',  | ||||
| 				queryNS = common.xmpp.NS_BROWSE)) | ||||
| 			self.discoverInfo(jid, node) | ||||
| 
 | ||||
| 	def ask_register_agent_info(self, agent): | ||||
| 		if not self.connection: | ||||
| 			return None | ||||
| 		data = common.xmpp.features.getRegInfo(self.connection, agent) # FIXME: blocking | ||||
| 		info = data.asDict() | ||||
| 		instructions = data.getInstructions() | ||||
| 		if instructions: | ||||
| 			info['instructions'] = instructions | ||||
| 		return info | ||||
| 		return common.xmpp.features.getRegInfo(self.connection, agent).asDict() # FIXME: blocking | ||||
| 
 | ||||
| 	def register_agent(self, agent, info): | ||||
| 		if not self.connection: | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: auth.py,v 1.25 2005/03/08 19:36:29 snakeru Exp $ | ||||
| # $Id: auth.py,v 1.27 2005/04/30 10:17:20 snakeru Exp $ | ||||
| 
 | ||||
| """ | ||||
| Provides library with all Non-SASL and SASL authentication mechanisms. | ||||
|  | @ -97,7 +97,8 @@ class NonSASL(PlugIn): | |||
| class SASL(PlugIn): | ||||
|     """ Implements SASL authentication. """ | ||||
|     def plugin(self,owner): | ||||
|         self.startsasl=None | ||||
|         if not self._owner.Dispatcher.Stream._document_attrs.has_key('version'): self.startsasl='not-supported' | ||||
|         else: self.startsasl=None | ||||
| 
 | ||||
|     def auth(self,username,password): | ||||
|         """ Start authentication. Result can be obtained via "SASL.startsasl" attribute and will be | ||||
|  | @ -120,7 +121,7 @@ class SASL(PlugIn): | |||
|     def FeaturesHandler(self,conn,feats): | ||||
|         """ Used to determine if server supports SASL auth. Used internally. """ | ||||
|         if not feats.getTag('mechanisms',namespace=NS_SASL): | ||||
|             self.startsasl='failure' | ||||
|             self.startsasl='not-supported' | ||||
|             self.DEBUG('SASL not supported by server','error') | ||||
|             return | ||||
|         mecs=[] | ||||
|  | @ -213,7 +214,7 @@ class Bind(PlugIn): | |||
|             except NodeProcessed: pass | ||||
|         else: self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS) | ||||
| 
 | ||||
|     def plugout(self,owner): | ||||
|     def plugout(self): | ||||
|         """ Remove Bind handler from owner's dispatcher. Used internally. """ | ||||
|         self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS) | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: browser.py,v 1.8 2004/12/24 19:56:35 snakeru Exp $ | ||||
| # $Id: browser.py,v 1.9 2005/04/30 07:13:33 snakeru Exp $ | ||||
| 
 | ||||
| """Browser module provides DISCO server framework for your application. | ||||
| This functionality can be used for very different purposes - from publishing | ||||
|  | @ -27,7 +27,9 @@ from dispatcher import * | |||
| from client import PlugIn | ||||
| 
 | ||||
| class Browser(PlugIn): | ||||
|     """ Standart xmpppy class that is ancestor of PlugIn and can be attached | ||||
|     """ WARNING! This class is for components only. It will not work in client mode! | ||||
| 
 | ||||
|         Standart xmpppy class that is ancestor of PlugIn and can be attached | ||||
|         to your application. | ||||
|         All processing will be performed in the handlers registered in the browser | ||||
|         instance. You can register any number of handlers ensuring that for each | ||||
|  | @ -85,14 +87,14 @@ class Browser(PlugIn): | |||
|     def plugin(self, owner): | ||||
|         """ Registers it's own iq handlers in your application dispatcher instance. | ||||
|             Used internally.""" | ||||
|         owner.RegisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_INFO) | ||||
|         owner.RegisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_ITEMS) | ||||
|         owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_INFO) | ||||
|         owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_ITEMS) | ||||
| 
 | ||||
|     def plugout(self): | ||||
|         """ Unregisters browser's iq handlers from your application dispatcher instance. | ||||
|             Used internally.""" | ||||
|         self._owner.UnregisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_INFO) | ||||
|         self._owner.UnregisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_ITEMS) | ||||
|         self._owner.UnregisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_INFO) | ||||
|         self._owner.UnregisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_ITEMS) | ||||
| 
 | ||||
|     def _traversePath(self,node,jid,set=0): | ||||
|         """ Returns dictionary and key or None,None | ||||
|  | @ -136,7 +138,7 @@ class Browser(PlugIn): | |||
|                           {'jid':'jid2','action':'action2','node':'node2','name':'name2'}, | ||||
|                           {'jid':'jid3','node':'node3','name':'name3'}, | ||||
|                           {'jid':'jid4','node':'node4'} | ||||
|                         ] | ||||
|                         ], | ||||
|                 'info' :{ | ||||
|                           'ids':[ | ||||
|                                   {'category':'category1','type':'type1','name':'name1'}, | ||||
|  | @ -182,7 +184,9 @@ class Browser(PlugIn): | |||
|             to handle the request. Used internally. | ||||
|         """ | ||||
|         handler=self.getDiscoHandler(request.getQuerynode(),request.getTo()) | ||||
|         if not handler: return conn.send(Error(request,ERR_ITEM_NOT_FOUND)) | ||||
|         if not handler: | ||||
|             conn.send(Error(request,ERR_ITEM_NOT_FOUND)) | ||||
|             raise NodeProcessed | ||||
|         rep=request.buildReply('result') | ||||
|         q=rep.getTag('query') | ||||
|         if request.getQueryNS()==NS_DISCO_ITEMS: | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: client.py,v 1.33 2005/04/10 08:09:23 snakeru Exp $ | ||||
| # $Id: client.py,v 1.35 2005/04/30 10:17:19 snakeru Exp $ | ||||
| 
 | ||||
| """ | ||||
| Provides PlugIn class functionality to develop extentions for xmpppy. | ||||
|  | @ -105,7 +105,7 @@ class CommonClient: | |||
|         self.debug_flags.append(self.DBG) | ||||
|         self._owner=self | ||||
|         self._registered_name=None | ||||
| #        self.RegisterDisconnectHandler(self.DisconnectHandler) | ||||
|         self.RegisterDisconnectHandler(self.DisconnectHandler) | ||||
|         self.connected='' | ||||
| 
 | ||||
|     def RegisterDisconnectHandler(self,handler): | ||||
|  | @ -123,7 +123,7 @@ class CommonClient: | |||
|         self.disconnect_handlers.reverse() | ||||
|         for i in self.disconnect_handlers: i() | ||||
|         self.disconnect_handlers.reverse() | ||||
| #        if self.__dict__.has_key('TLS'): self.TLS.PlugOut() | ||||
|         if self.__dict__.has_key('TLS'): self.TLS.PlugOut() | ||||
| 
 | ||||
|     def DisconnectHandler(self): | ||||
|         """ Default disconnect handler. Just raises an IOError. | ||||
|  | @ -156,9 +156,9 @@ class CommonClient: | |||
|         if not connected: return | ||||
|         self._Server,self._Proxy=server,proxy | ||||
|         self.connected='tcp' | ||||
| #        if self.Connection.getPort()==5223: | ||||
| #            transports.TLS().PlugIn(self,now=1) | ||||
| #            self.connected='tls' | ||||
|         if self.Connection.getPort()==5223: | ||||
|             transports.TLS().PlugIn(self,now=1) | ||||
|             self.connected='tls' | ||||
|         dispatcher.Dispatcher().PlugIn(self) | ||||
|         while self.Dispatcher.Stream._document_attrs is None: self.Process(1) | ||||
|         if self.Dispatcher.Stream._document_attrs.has_key('version') and self.Dispatcher.Stream._document_attrs['version']=='1.0': | ||||
|  | @ -173,32 +173,32 @@ class Client(CommonClient): | |||
|             specify it's address and credentials (if needed) in the second argument. | ||||
|             Example: connect(('192.168.5.5':5222),{'host':'proxy.my.net','port':8080,'user':'me','password':'secret'})""" | ||||
|         if not CommonClient.connect(self,server,proxy): return self.connected | ||||
| #        transports.TLS().PlugIn(self) | ||||
|         transports.TLS().PlugIn(self) | ||||
|         if not self.Dispatcher.Stream._document_attrs.has_key('version') or not self.Dispatcher.Stream._document_attrs['version']=='1.0': return self.connected | ||||
|         while not self.Dispatcher.Stream.features and self.Process(): pass      # If we get version 1.0 stream the features tag MUST BE presented | ||||
| #        if not self.Dispatcher.Stream.features.getTag('starttls'): return self.connected       # TLS not supported by server | ||||
| #        while not self.TLS.starttls and self.Process(): pass | ||||
| #        if self.TLS.starttls<>'success': self.event('tls_failed'); return self.connected | ||||
| #        self.connected='tls' | ||||
|         if not self.Dispatcher.Stream.features.getTag('starttls'): return self.connected       # TLS not supported by server | ||||
|         while not self.TLS.starttls and self.Process(): pass | ||||
|         if self.TLS.starttls<>'success': self.event('tls_failed'); return self.connected | ||||
|         self.connected='tls' | ||||
|         return self.connected | ||||
| 
 | ||||
|     def auth(self,user,password,resource=''): | ||||
|         """ Authenticate connnection and bind resource. If resource is not provided | ||||
|             random one or library name used. """ | ||||
|         self._User,self._Password,self._Resource=user,password,resource | ||||
|         auth.SASL().PlugIn(self) | ||||
|         self.SASL.auth(user,password) | ||||
|         while not self.Dispatcher.Stream._document_attrs and self.Process(): pass | ||||
|         if self.Dispatcher.Stream._document_attrs.has_key('version') and self.Dispatcher.Stream._document_attrs['version']=='1.0': | ||||
|             while not self.Dispatcher.Stream.features and self.Process(): pass      # If we get version 1.0 stream the features tag MUST BE presented | ||||
|             while self.SASL.startsasl=='in-process' and self.Process(): pass | ||||
|         else: self.SASL.startsasl='failure' | ||||
|         if self.SASL.startsasl=='failure': | ||||
|         auth.SASL().PlugIn(self) | ||||
|         if self.SASL.startsasl=='not-supported': | ||||
|             if not resource: resource='xmpppy' | ||||
|             if auth.NonSASL(user,password,resource).PlugIn(self): | ||||
|                 self.connected+='+old_auth' | ||||
|                 return 'old_auth' | ||||
|         else: | ||||
|             return | ||||
|         self.SASL.auth(user,password) | ||||
|         while self.SASL.startsasl=='in-process' and self.Process(): pass | ||||
|         if self.SASL.startsasl=='success': | ||||
|             auth.Bind().PlugIn(self) | ||||
|             while self.Bind.bound is None: self.Process() | ||||
|             if self.Bind.Bind(resource): | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| ## $Id: commands.py,v 1.3 2005/03/08 19:50:43 snakeru Exp $ | ||||
| ## $Id: commands.py,v 1.4 2005/04/30 07:33:11 snakeru Exp $ | ||||
| 
 | ||||
| ## Ad-Hoc Command manager | ||||
| ## Mike Albon (c) 5th January 2005 | ||||
|  | @ -34,8 +34,6 @@ What it supplies: | |||
| from xmpp.protocol import * | ||||
| from xmpp.client import PlugIn | ||||
| 
 | ||||
| NS_COMMANDS='http://jabber.org/protocol/commands' | ||||
| 
 | ||||
| class Commands(PlugIn): | ||||
|     """Commands is an ancestor of Plugin and can be attached to any session. | ||||
|      | ||||
|  | @ -167,7 +165,10 @@ class Commands(PlugIn): | |||
|             return self._handlers[jid][name] | ||||
|          | ||||
| class Command_Handler_Prototype(PlugIn): | ||||
|     """This is a prototype command handler, as each command uses a disco method and execute method you can implement it any way you like, however this is my first attempt at making a generic handler that you can hang process stages on too. There is an example command below. | ||||
|     """This is a prototype command handler, as each command uses a disco method  | ||||
|        and execute method you can implement it any way you like, however this is  | ||||
|        my first attempt at making a generic handler that you can hang process  | ||||
|        stages on too. There is an example command below. | ||||
|      | ||||
|     The parameters are as follows: | ||||
|     name : the name of the command within the jabber environment | ||||
|  | @ -241,14 +242,19 @@ class Command_Handler_Prototype(PlugIn): | |||
|             return self.discoinfo | ||||
|          | ||||
| class TestCommand(Command_Handler_Prototype): | ||||
|     """ Example class. You should read source if you wish to understate how it works.  | ||||
|         Generally, it presents a "master" that giudes user through to calculate something. | ||||
|     """ | ||||
|     name = 'testcommand' | ||||
|     description = 'a noddy example command' | ||||
|     def __init__(self): | ||||
|         """ Init internal constants. """ | ||||
|         Command_Handler_Prototype.__init__(self) | ||||
|         self.pi = 3.14 | ||||
|         self.initial = {'execute':self.cmdFirstStage} | ||||
|      | ||||
|     def cmdFirstStage(self,conn,request): | ||||
|         """ Determine """ | ||||
|         # This is the only place this should be repeated as all other stages should have SessionIDs | ||||
|         try: | ||||
|             session = request.getTagAttr('command','sessionid') | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: dispatcher.py,v 1.31 2005/03/08 19:36:29 snakeru Exp $ | ||||
| # $Id: dispatcher.py,v 1.34 2005/05/02 08:36:41 snakeru Exp $ | ||||
| 
 | ||||
| """ | ||||
| Main xmpppy mechanism. Provides library with methods to assign different handlers | ||||
|  | @ -55,6 +55,7 @@ class Dispatcher(PlugIn): | |||
|         self.handlers=handlers | ||||
| 
 | ||||
|     def _init(self): | ||||
|         """ Registers default namespaces/protocols/handlers. Used internally.  """ | ||||
|         self.RegisterNamespace('unknown') | ||||
|         self.RegisterNamespace(NS_STREAMS) | ||||
|         self.RegisterNamespace(self._owner.defaultNamespace) | ||||
|  | @ -62,6 +63,7 @@ class Dispatcher(PlugIn): | |||
|         self.RegisterProtocol('presence',Presence) | ||||
|         self.RegisterProtocol('message',Message) | ||||
|         self.RegisterDefaultHandler(self.returnStanzaHandler) | ||||
| #        self.RegisterHandler('error',self.streamErrorHandler,xmlns=NS_STREAMS) | ||||
| 
 | ||||
|     def plugin(self, owner): | ||||
|         """ Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally.""" | ||||
|  | @ -74,6 +76,7 @@ class Dispatcher(PlugIn): | |||
|         self.StreamInit() | ||||
| 
 | ||||
|     def plugout(self): | ||||
|         """ Prepares instance to be destructed. """ | ||||
|         self.Stream.dispatch=None | ||||
|         self.Stream.DEBUG=None | ||||
|         self.Stream.features=None | ||||
|  | @ -99,7 +102,10 @@ class Dispatcher(PlugIn): | |||
|             Returns: | ||||
|             1) length of processed data if some data were processed; | ||||
|             2) '0' string if no data were processed but link is alive; | ||||
|             3) 0 (zero) if underlying connection is closed.""" | ||||
|             3) 0 (zero) if underlying connection is closed. | ||||
|             Take note that in case of disconnection detect during Process() call | ||||
|             disconnect handlers are called automatically. | ||||
|         """ | ||||
|         if time.time() > self._lastIncome + 60: #1 min | ||||
|             iq = Iq('get', NS_LAST, to=self._owner.Server) | ||||
|             self.send(iq) | ||||
|  | @ -107,8 +113,10 @@ class Dispatcher(PlugIn): | |||
|             self.disconnected() | ||||
|         for handler in self._cycleHandlers: handler(self) | ||||
|         if self._owner.Connection.pending_data(timeout): | ||||
|             data=self._owner.Connection.receive() | ||||
|             try: data=self._owner.Connection.receive() | ||||
|             except IOError: return | ||||
|             self.Stream.Parse(data) | ||||
|             if data: | ||||
|                 self._lastIncome = time.time() | ||||
|                 return len(data) | ||||
|         return '0'      # It means that nothing is received but link is alive. | ||||
|  | @ -190,6 +198,16 @@ class Dispatcher(PlugIn): | |||
|         if stanza.getType() in ['get','set']: | ||||
|             conn.send(Error(stanza,ERR_FEATURE_NOT_IMPLEMENTED)) | ||||
| 
 | ||||
|     def streamErrorHandler(self,conn,error): | ||||
|         name,text='error',error.getData() | ||||
|         for tag in error.getChildren(): | ||||
|             if tag.getNamespace()==NS_XMPP_STREAMS: | ||||
|                 if tag.getName()=='text': text=tag.getData() | ||||
|                 else: name=tag.getName() | ||||
|         if name in stream_exceptions.keys(): exc=stream_exceptions[name] | ||||
|         else: exc=StreamError | ||||
|         raise exc((name,text)) | ||||
| 
 | ||||
|     def RegisterCycleHandler(self,handler): | ||||
|         """ Register handler that will be called on every Dispatcher.Process() call. """ | ||||
|         if handler not in self._cycleHandlers: self._cycleHandlers.append(handler) | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: features.py,v 1.19 2004/12/25 20:06:59 snakeru Exp $ | ||||
| # $Id: features.py,v 1.20 2005/04/30 07:43:01 snakeru Exp $ | ||||
| 
 | ||||
| """ | ||||
| This module contains variable stuff that is not worth splitting into separate modules. | ||||
|  | @ -88,10 +88,9 @@ def getRegInfo(disp,host,info={}): | |||
|     if df: return DataForm(node=df) | ||||
|     df=DataForm(typ='form') | ||||
|     for i in resp.getQueryPayload(): | ||||
|         try: #FIXME: temporary patch by Alexey to make it work :| | ||||
|             if i.getName()=='instructions': df.addInstructions(i.getData()) | ||||
|         if type(i)<>type(iq): pass | ||||
|         elif i.getName()=='instructions': df.addInstructions(i.getData()) | ||||
|         else: df.setField(i.getName()).setValue(i.getData()) | ||||
|         except: pass | ||||
|     return df | ||||
| 
 | ||||
| def register(disp,host,info): | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: protocol.py,v 1.39 2005/03/08 19:36:29 snakeru Exp $ | ||||
| # $Id: protocol.py,v 1.41 2005/05/02 08:36:41 snakeru Exp $ | ||||
| 
 | ||||
| """ | ||||
| Protocol module contains tools that is needed for processing of  | ||||
|  | @ -28,13 +28,13 @@ NS_AUTH         ='jabber:iq:auth' | |||
| NS_BIND         ='urn:ietf:params:xml:ns:xmpp-bind' | ||||
| NS_BROWSE       ='jabber:iq:browse' | ||||
| NS_CLIENT       ='jabber:client' | ||||
| NS_COMMANDS     ='http://jabber.org/protocol/commands' | ||||
| NS_COMPONENT_ACCEPT='jabber:component:accept' | ||||
| NS_DATA         ='jabber:x:data'                                # JEP-0004 | ||||
| NS_DELAY        ='jabber:x:delay' | ||||
| NS_DIALBACK     ='jabber:server:dialback' | ||||
| NS_DISCO_INFO   ='http://jabber.org/protocol/disco#info' | ||||
| NS_DISCO_ITEMS  ='http://jabber.org/protocol/disco#items' | ||||
| NS_ENCRYPTED    ='jabber:x:encrypted'                           # JEP-0027 | ||||
| NS_GROUPCHAT    ='gc-1.0' | ||||
| NS_IBB          ='http://jabber.org/protocol/ibb' | ||||
| NS_INVISIBLE    ='presence-invisible'                           # jabberd2 | ||||
|  | @ -42,8 +42,9 @@ NS_IQ           ='iq'                                           # jabberd2 | |||
| NS_LAST         ='jabber:iq:last' | ||||
| NS_MESSAGE      ='message'                                      # jabberd2 | ||||
| NS_MUC          ='http://jabber.org/protocol/muc' | ||||
| NS_MUC_OWNER    ='http://jabber.org/protocol/muc#owner' | ||||
| NS_MUC_USER     ='http://jabber.org/protocol/muc#user' | ||||
| NS_MUC_ADMIN    ='http://jabber.org/protocol/muc#admin' | ||||
| NS_MUC_OWNER    ='http://jabber.org/protocol/muc#owner' | ||||
| NS_OFFLINE      ='http://www.jabber.org/jeps/jep-0030.html'     # JEP-0013    | ||||
| NS_PRESENCE     ='presence'                                     # jabberd2 | ||||
| NS_PRIVACY      ='jabber:iq:privacy' | ||||
|  | @ -55,7 +56,6 @@ NS_SASL         ='urn:ietf:params:xml:ns:xmpp-sasl' | |||
| NS_SEARCH       ='jabber:iq:search' | ||||
| NS_SERVER       ='jabber:server' | ||||
| NS_SESSION      ='urn:ietf:params:xml:ns:xmpp-session' | ||||
| NS_SIGNED       ='jabber:x:signed'                              # JEP-0027 | ||||
| NS_STANZAS      ='urn:ietf:params:xml:ns:xmpp-stanzas' | ||||
| NS_STREAMS      ='http://etherx.jabber.org/streams' | ||||
| NS_TIME         ='jabber:iq:time' | ||||
|  | @ -63,7 +63,9 @@ NS_TLS          ='urn:ietf:params:xml:ns:xmpp-tls' | |||
| NS_VACATION     ='http://jabber.org/protocol/vacation' | ||||
| NS_VCARD        ='vcard-temp' | ||||
| NS_VERSION      ='jabber:iq:version' | ||||
| NS_ENCRYPTED    ='jabber:x:encrypted'                           # JEP-0027 | ||||
| NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams' | ||||
| NS_SIGNED       ='jabber:x:signed'                              # JEP-0027 | ||||
| 
 | ||||
| xmpp_stream_error_conditions=""" | ||||
| bad-format --  --  -- The entity has sent XML that cannot be processed. | ||||
|  | @ -140,8 +142,60 @@ def isResultNode(node): | |||
| def isErrorNode(node): | ||||
|     """ Returns true if the node is a negative reply. """ | ||||
|     return node and node.getType()=='error' | ||||
| 
 | ||||
| class NodeProcessed(Exception): | ||||
|     """ Exception that should be raised by handler when the handling should be stopped. """ | ||||
| class StreamError(Exception): | ||||
|     """ Base exception class for stream errors.""" | ||||
| class BadFormat(StreamError): pass | ||||
| class BadNamespacePrefix(StreamError): pass | ||||
| class Conflict(StreamError): pass | ||||
| class ConnectionTimeout(StreamError): pass | ||||
| class HostGone(StreamError): pass | ||||
| class HostUnknown(StreamError): pass | ||||
| class ImproperAddressing(StreamError): pass | ||||
| class InternalServerError(StreamError): pass | ||||
| class InvalidFrom(StreamError): pass | ||||
| class InvalidID(StreamError): pass | ||||
| class InvalidNamespace(StreamError): pass | ||||
| class InvalidXML(StreamError): pass | ||||
| class NotAuthorized(StreamError): pass | ||||
| class PolicyViolation(StreamError): pass | ||||
| class RemoteConnectionFailed(StreamError): pass | ||||
| class ResourceConstraint(StreamError): pass | ||||
| class RestrictedXML(StreamError): pass | ||||
| class SeeOtherHost(StreamError): pass | ||||
| class SystemShutdown(StreamError): pass | ||||
| class UndefinedCondition(StreamError): pass | ||||
| class UnsupportedEncoding(StreamError): pass | ||||
| class UnsupportedStanzaType(StreamError): pass | ||||
| class UnsupportedVersion(StreamError): pass | ||||
| class XMLNotWellFormed(StreamError): pass | ||||
| 
 | ||||
| stream_exceptions = {'bad-format': BadFormat, | ||||
|                      'bad-namespace-prefix': BadNamespacePrefix, | ||||
|                      'conflict': Conflict, | ||||
|                      'connection-timeout': ConnectionTimeout, | ||||
|                      'host-gone': HostGone, | ||||
|                      'host-unknown': HostUnknown, | ||||
|                      'improper-addressing': ImproperAddressing, | ||||
|                      'internal-server-error': InternalServerError, | ||||
|                      'invalid-from': InvalidFrom, | ||||
|                      'invalid-id': InvalidID, | ||||
|                      'invalid-namespace': InvalidNamespace, | ||||
|                      'invalid-xml': InvalidXML, | ||||
|                      'not-authorized': NotAuthorized, | ||||
|                      'policy-violation': PolicyViolation, | ||||
|                      'remote-connection-failed': RemoteConnectionFailed, | ||||
|                      'resource-constraint': ResourceConstraint, | ||||
|                      'restricted-xml': RestrictedXML, | ||||
|                      'see-other-host': SeeOtherHost, | ||||
|                      'system-shutdown': SystemShutdown, | ||||
|                      'undefined-condition': UndefinedCondition, | ||||
|                      'unsupported-encoding': UnsupportedEncoding, | ||||
|                      'unsupported-stanza-type': UnsupportedStanzaType, | ||||
|                      'unsupported-version': UnsupportedVersion, | ||||
|                      'xml-not-well-formed': XMLNotWellFormed} | ||||
| 
 | ||||
| class JID: | ||||
|     """ JID object. JID can be built from string, modified, compared, serialised into string. """ | ||||
|  | @ -345,6 +399,16 @@ class Presence(Protocol): | |||
|     def getStatus(self): | ||||
|         """ Returns the status string of the message. """ | ||||
|         return self.getTagData('status') | ||||
|     def setPriority(self,val): | ||||
|         """ Sets the priority of the message. """ | ||||
|         self.setTagData('priority',val) | ||||
|     def setShow(self,val): | ||||
|         """ Sets the show value of the message. """ | ||||
|         self.setTagData('show',val) | ||||
|     def setStatus(self,val): | ||||
|         """ Sets the status string of the message. """ | ||||
|         self.setTagData('status',val) | ||||
| 
 | ||||
|     def _muc_getItemAttr(self,tag,attr): | ||||
|         for xtag in self.getTags('x'): | ||||
|             for child in xtag.getTags(tag): | ||||
|  | @ -373,15 +437,6 @@ class Presence(Protocol): | |||
|     def getStatusCode(self): | ||||
|         """Returns the status code of the presence (for groupchat)""" | ||||
|         return self._muc_getItemAttr('status','code') | ||||
|     def setPriority(self,val): | ||||
|         """ Sets the priority of the message. """ | ||||
|         self.setTagData('priority',val) | ||||
|     def setShow(self,val): | ||||
|         """ Sets the show value of the message. """ | ||||
|         self.setTagData('show',val) | ||||
|     def setStatus(self,val): | ||||
|         """ Sets the status string of the message. """ | ||||
|         self.setTagData('status',val) | ||||
| 
 | ||||
| class Iq(Protocol):  | ||||
|     """ XMPP Iq object - get/set dialog mechanism. """ | ||||
|  | @ -613,6 +668,7 @@ class DataForm(Node): | |||
|                 for i in field.getTags('value'): val.append(i.getData()) | ||||
|             else: val=field.getTagData('value') | ||||
|             ret[name]=val | ||||
|         if self.getTag('instructions'): ret['instructions']=self.getInstructions() | ||||
|         return ret | ||||
|     def __getitem__(self,name): | ||||
|         """ Simple dictionary interface for getting datafields values by their names.""" | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: roster.py,v 1.16 2005/02/25 05:49:03 snakeru Exp $ | ||||
| # $Id: roster.py,v 1.17 2005/05/02 08:38:49 snakeru Exp $ | ||||
| 
 | ||||
| """ | ||||
| Simple roster implementation. Can be used though for different tasks like | ||||
|  | @ -98,7 +98,7 @@ class Roster(PlugIn): | |||
|             if not pres.getTimestamp(): pres.setTimestamp() | ||||
|             res['timestamp']=pres.getTimestamp() | ||||
|         elif typ=='unavailable' and item['resources'].has_key(jid.getResource()): del item['resources'][jid.getResource()] | ||||
| # Need to handle type='error' also | ||||
|         # Need to handle type='error' also | ||||
| 
 | ||||
|     def _getItemData(self,jid,dataname): | ||||
|         """ Return specific jid's representation in internal format. Used internally. """ | ||||
|  | @ -159,9 +159,6 @@ class Roster(PlugIn): | |||
|     def getItems(self): | ||||
|         """ Return list of all [bare] JIDs that the roster is currently tracks.""" | ||||
|         return self._data.keys() | ||||
|     def getRaw(self): | ||||
|         """ Returns internal representation of roster.""" | ||||
|         return self._data | ||||
|     def keys(self): | ||||
|         """ Same as getItems. Provided for the sake of dictionary interface.""" | ||||
|         return self._data.keys() | ||||
|  | @ -184,3 +181,6 @@ class Roster(PlugIn): | |||
|         """ Unauthorise JID 'jid'. Use for declining authorisation request  | ||||
|             or for removing existing authorization. """ | ||||
|         self._owner.send(Presence(jid,'unsubscribed')) | ||||
|     def getRaw(self): | ||||
|         """Returns the internal data representation of the roster.""" | ||||
|         return self._data | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: simplexml.py,v 1.26 2005/04/10 08:21:35 snakeru Exp $ | ||||
| # $Id: simplexml.py,v 1.27 2005/04/30 07:20:27 snakeru Exp $ | ||||
| 
 | ||||
| """Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams. | ||||
| I'm personally using it in many other separate projects. It is designed to be as standalone as possible.""" | ||||
|  | @ -77,9 +77,9 @@ class Node: | |||
|             else: self.data.append(ustr(i)) | ||||
| 
 | ||||
|     def __str__(self,fancy=0): | ||||
|         s = (fancy-1) * 2 * ' ' + "<" + self.name | ||||
|         """ Method used to dump node into textual representation. | ||||
|             if "fancy" argument is set to True produces indented output for readability.""" | ||||
|         s = (fancy-1) * 2 * ' ' + "<" + self.name | ||||
|         if self.namespace: | ||||
|             if not self.parent or self.parent.namespace!=self.namespace: | ||||
|                 s = s + ' xmlns="%s"'%self.namespace | ||||
|  | @ -249,7 +249,7 @@ class Node: | |||
|         raise AttributeError | ||||
| 
 | ||||
| class T: | ||||
|     """ Auxiliary class used to quick acces to node's child nodes. """ | ||||
|     """ Auxiliary class used to quick access to node's child nodes. """ | ||||
|     def __init__(self,node): self.__dict__['node']=node | ||||
|     def __getattr__(self,attr): return self.node.setTag(attr) | ||||
|     def __setattr__(self,attr,val): | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ##   GNU General Public License for more details. | ||||
| 
 | ||||
| # $Id: transports.py,v 1.15 2004/12/25 20:06:59 snakeru Exp $ | ||||
| # $Id: transports.py,v 1.18 2005/04/30 08:56:36 snakeru Exp $ | ||||
| 
 | ||||
| """ | ||||
| This module contains the low-level implementations of xmpppy connect methods or | ||||
|  | @ -42,6 +42,7 @@ class error: | |||
|         """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): | ||||
|  | @ -89,21 +90,27 @@ class TCPsocket(PlugIn): | |||
|         del self._owner.Connection | ||||
| 
 | ||||
|     def receive(self): | ||||
|         """ Reads all pending incoming data. Calls owner's disconnected() method if appropriate.""" | ||||
|         try: received = self._recv(1024) | ||||
|         """ 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: | ||||
|             self._seen_data=0 | ||||
|             return '' | ||||
|         except: received = '' | ||||
| 
 | ||||
|         while select.select([self._sock],[],[],0)[0]: | ||||
|             try: add = self._recv(1024) | ||||
|         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') | ||||
|         else: | ||||
|             self.DEBUG('Socket error while receiving data','error') | ||||
|             self._owner.disconnected() | ||||
|             raise IOError("Disconnected from server") | ||||
|         return received | ||||
| 
 | ||||
|     def send(self,raw_data): | ||||
|  | @ -167,18 +174,28 @@ class HTTPPROXYsocket(TCPsocket): | |||
|             connector.append('Proxy-Authorization: Basic '+credentials) | ||||
|         connector.append('\r\n') | ||||
|         self.send('\r\n'.join(connector)) | ||||
|         reply = self.receive().replace('\r','') | ||||
|         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: reply += self.receive().replace('\r','') | ||||
|         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): | ||||
|  | @ -202,8 +219,8 @@ class TLS(PlugIn): | |||
|         """ 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) | ||||
|         self._owner.UnregisterHandlerOnce('proceed',self.StartTLSHandler,xmlns=NS_TLS) | ||||
|         self._owner.UnregisterHandlerOnce('failure',self.StartTLSHandler,xmlns=NS_TLS) | ||||
| 
 | ||||
|     def FeaturesHandler(self, conn, feats): | ||||
|         """ Used to analyse server <features/> tag for TLS support. | ||||
|  | @ -217,14 +234,25 @@ class TLS(PlugIn): | |||
|         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): | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue