diff --git a/src/common/xmpp/__init__.py b/src/common/xmpp/__init__.py index acb39726d..705e4e133 100644 --- a/src/common/xmpp/__init__.py +++ b/src/common/xmpp/__init__.py @@ -26,6 +26,6 @@ and use only methods for access all values you should not have any problems. """ -import simplexml,protocol,debug,auth,transports,roster,dispatcher,features,browser,filetransfer +import simplexml,protocol,debug,auth,transports,roster,dispatcher,features,browser,filetransfer,commands from client import * from protocol import * diff --git a/src/common/xmpp/auth.py b/src/common/xmpp/auth.py index 5fb866153..52637b26e 100644 --- a/src/common/xmpp/auth.py +++ b/src/common/xmpp/auth.py @@ -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.33 2005/11/30 17:05:40 normanr Exp $ +# $Id: auth.py,v 1.35 2006/01/18 19:26:43 normanr Exp $ """ Provides library with all Non-SASL and SASL authentication mechanisms. @@ -259,3 +259,48 @@ class Bind(PlugIn): else: self.DEBUG('Binding failed: timeout expired.','error') return '' + +class ComponentBind(PlugIn): + """ ComponentBind some JID to the current connection to allow router know of our location.""" + def __init__(self): + PlugIn.__init__(self) + self.DBG_LINE='bind' + self.bound=None + self.needsUnregister=None + + def plugin(self,owner): + """ Start resource binding, if allowed at this time. Used internally. """ + if self._owner.Dispatcher.Stream.features: + try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features) + except NodeProcessed: pass + else: + self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS) + self.needsUnregister=1 + + def plugout(self): + """ Remove ComponentBind handler from owner's dispatcher. Used internally. """ + if self.needsUnregister: + self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS) + + def FeaturesHandler(self,conn,feats): + """ Determine if server supports resource binding and set some internal attributes accordingly. """ + if not feats.getTag('bind',namespace=NS_BIND): + self.bound='failure' + self.DEBUG('Server does not requested binding.','error') + return + if feats.getTag('session',namespace=NS_SESSION): self.session=1 + else: self.session=-1 + self.bound=[] + + def Bind(self,domain=None): + """ Perform binding. Use provided domain name (if not provided). """ + while self.bound is None and self._owner.Process(1): pass + resp=self._owner.SendAndWaitForResponse(Protocol('bind',attrs={'name':domain},xmlns=NS_COMPONENT_1)) + if resp and resp.getAttr('error'): + self.DEBUG('Binding failed: %s.'%resp.getAttr('error'),'error') + elif resp: + self.DEBUG('Successfully bound.','ok') + return 'ok' + else: + self.DEBUG('Binding failed: timeout expired.','error') + return '' diff --git a/src/common/xmpp/client.py b/src/common/xmpp/client.py index 1930d4dc5..0c444a390 100644 --- a/src/common/xmpp/client.py +++ b/src/common/xmpp/client.py @@ -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.35 2005/04/30 10:17:19 snakeru Exp $ +# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $ """ Provides PlugIn class functionality to develop extentions for xmpppy. @@ -31,6 +31,7 @@ Debug.Debug.colors['nodebuilder']=debug.color_brown Debug.Debug.colors['client']=debug.color_cyan Debug.Debug.colors['component']=debug.color_cyan Debug.Debug.colors['dispatcher']=debug.color_green +Debug.Debug.colors['browser']=debug.color_blue Debug.Debug.colors['auth']=debug.color_yellow Debug.Debug.colors['roster']=debug.color_magenta Debug.Debug.colors['ibb']=debug.color_yellow @@ -111,6 +112,7 @@ class CommonClient: self._registered_name=None self.RegisterDisconnectHandler(self.DisconnectHandler) self.connected='' + self._component=0 def RegisterDisconnectHandler(self,handler): """ Register handler that will be called on disconnect.""" @@ -147,6 +149,11 @@ class CommonClient: """ Example of reconnection method. In fact, it can be used to batch connection and auth as well. """ handlerssave=self.Dispatcher.dumpHandlers() self.Dispatcher.PlugOut() + if self.__dict__.has_key('NonSASL'): self.NonSASL.PlugOut() + if self.__dict__.has_key('SASL'): self.SASL.PlugOut() + if self.__dict__.has_key('TLS'): self.TLS.PlugOut() + if self.__dict__.has_key('HTTPPROXYsocket'): self.HTTPPROXYsocket.PlugOut() + if self.__dict__.has_key('TCPsocket'): self.TCPsocket.PlugOut() if not self.connect(server=self._Server,proxy=self._Proxy): return if not self.auth(self._User,self._Password,self._Resource): return self.Dispatcher.restoreHandlers(handlerssave) @@ -159,16 +166,20 @@ class CommonClient: if hasattr(self, 'Connection'): return self.Connection._sock.getsockname() - def connect(self,server=None,proxy=None, ssl=None): - """ Make a tcp/ip connection, protect it with tls/ssl if possible and start XMPP stream. """ + def connect(self,server=None,proxy=None,ssl=None,use_srv=None): + """ Make a tcp/ip connection, protect it with tls/ssl if possible and start XMPP stream. + Returns None or 'tcp' or 'tls', depending on the result.""" if not server: server=(self.Server,self.Port) - if proxy: connected=transports.HTTPPROXYsocket(proxy,server).PlugIn(self) - else: connected=transports.TCPsocket(server).PlugIn(self) - if not connected: return + if proxy: socket=transports.HTTPPROXYsocket(proxy,server,use_srv) + else: socket=transports.TCPsocket(server,use_srv) + connected=socket.PlugIn(self) + if not connected: + socket.PlugOut() + return self._Server,self._Proxy=server,proxy self.connected='tcp' if (ssl is None and self.Connection.getPort() in (5223, 443)) or ssl: - try: + try: # FIXME. This should be done in transports.py transports.TLS().PlugIn(self,now=1) self.connected='ssl' except socket.sslerror: @@ -182,16 +193,16 @@ class CommonClient: class Client(CommonClient): """ Example client class, based on CommonClient. """ - def connect(self,server=None,proxy=None,secure=None): + def connect(self,server=None,proxy=None,secure=None,use_srv=True): """ Connect to jabber server. If you want to specify different ip/port to connect to you can - pass it as tuple as first parameter. If there is HTTP proxy between you and server - - specify it's address and credentials (if needed) in the second argument + pass it as tuple as first parameter. If there is HTTP proxy between you and server + specify it's address and credentials (if needed) in the second argument. If you want ssl/tls support to be discovered and enable automatically - leave third argument as None. (ssl will be autodetected only if port is 5223 or 443) - If you want to force SSL start (i.e. if port 5223 or 443 is remapped to some non-standard port) then set it to 1 - If you want to disable tls/ssl support completely, set it to 0 + If you want to force SSL start (i.e. if port 5223 or 443 is remapped to some non-standard port) then set it to 1. + If you want to disable tls/ssl support completely, set it to 0. Example: connect(('192.168.5.5',5222),{'host':'proxy.my.net','port':8080,'user':'me','password':'secret'}) - Returns '' (on no connection) or 'tcp' or 'tls', depending on the result.""" - if not CommonClient.connect(self,server,proxy,secure) or secure<>None and not secure: return self.connected + Returns '' or 'tcp' or 'tls', depending on the result.""" + if not CommonClient.connect(self,server,proxy,secure,use_srv) or secure<>None and not secure: return self.connected 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 @@ -247,19 +258,31 @@ class Client(CommonClient): class Component(CommonClient): """ Component class. The only difference from CommonClient is ability to perform component authentication. """ - def __init__(self,server,port=5347,typ=None,debug=['always', 'nodebuilder']): + def __init__(self,server,port=5347,typ=None,debug=['always', 'nodebuilder'],domains=None,component=0): """ Init function for Components. As components use a different auth mechanism which includes the namespace of the component. Jabberd1.4 and Ejabberd use the default namespace then for all client messages. - Jabberd2 uses jabber:client.""" + Jabberd2 uses jabber:client. + 'server' argument is a server name that you are connecting to (f.e. "localhost"). + 'port' can be specified if 'server' resolves to correct IP. If it is not then you'll need to specify IP + and port while calling "connect()".""" CommonClient.__init__(self,server,port=port,debug=debug) self.typ=typ + self.component=component + if domains: + self.domains=domains + else: + self.domains=[server] def connect(self,server=None,proxy=None): """ This will connect to the server, and if the features tag is found then set - the namespace to be jabber:client as that is required for jabberd2""" + the namespace to be jabber:client as that is required for jabberd2. + 'server' and 'proxy' arguments have the same meaning as in xmpp.Client.connect() """ + if self.component: + self.Namespace=auth.NS_COMPONENT_1 + self.Server=server[0] CommonClient.connect(self,server=server,proxy=proxy) - if self.typ=='jabberd2' or not self.typ and self.Dispatcher.Stream.features != None: + if self.connected and (self.typ=='jabberd2' or not self.typ and self.Dispatcher.Stream.features != None): self.defaultNamespace=auth.NS_CLIENT self.Dispatcher.RegisterNamespace(self.defaultNamespace) self.Dispatcher.RegisterProtocol('iq',dispatcher.Iq) @@ -267,7 +290,32 @@ class Component(CommonClient): self.Dispatcher.RegisterProtocol('presence',dispatcher.Presence) return self.connected - def auth(self,name,password,dup=None): + def auth(self,name,password,dup=None,sasl=0): """ Authenticate component "name" with password "password".""" self._User,self._Password,self._Resource=name,password,'' - return auth.NonSASL(name,password,'').PlugIn(self) + try: + if self.component: sasl=1 + if sasl: auth.SASL(name,password).PlugIn(self) + if not sasl or self.SASL.startsasl=='not-supported': + if auth.NonSASL(name,password,'').PlugIn(self): + self.connected+='+old_auth' + return 'old_auth' + return + self.SASL.auth() + while self.SASL.startsasl=='in-process' and self.Process(): pass + if self.SASL.startsasl=='success': + if self.component: + self._component=self.component + for domain in self.domains: + auth.ComponentBind().PlugIn(self) + while self.ComponentBind.bound is None: self.Process() + if (not self.ComponentBind.Bind(domain)): + self.ComponentBind.PlugOut() + return + self.ComponentBind.PlugOut() + self.connected+='+sasl' + return 'sasl' + else: + raise auth.NotAuthorized(self.SASL.startsasl) + except: + self.DEBUG(self.DBG,"Failed to authenticate %s"%name,'error') diff --git a/src/common/xmpp/commands.py b/src/common/xmpp/commands.py index 89e3612d5..474afc6f0 100644 --- a/src/common/xmpp/commands.py +++ b/src/common/xmpp/commands.py @@ -1,4 +1,4 @@ -## $Id: commands.py,v 1.10 2005/10/07 23:17:09 normanr Exp $ +## $Id: commands.py,v 1.11 2005/11/30 17:03:11 normanr Exp $ ## Ad-Hoc Command manager ## Mike Albon (c) 5th January 2005 @@ -125,7 +125,7 @@ class Commands(PlugIn): conn.send(Error(request,ERR_ITEM_NOT_FOUND)) raise NodeProcessed elif typ == 'info': - return {'ids':[],'features':[]} + return {'ids':[{'category':'automation','type':'command-list'}],'features':[]} def addCommand(self,name,cmddisco,cmdexecute,jid=''): """The method to call if adding a new command to the session, the requred parameters of cmddisco and cmdexecute are the methods to enable that command to be executed""" @@ -197,7 +197,7 @@ class Command_Handler_Prototype(PlugIn): self.sessioncount = 0 self.sessions = {} # Disco information for command list pre-formatted as a tuple - self.discoinfo = {'ids':[{'category':'automation','type':'command','name':self.description}],'features': self.discofeatures} + self.discoinfo = {'ids':[{'category':'automation','type':'command-node','name':self.description}],'features': self.discofeatures} self._jid = jid def plugin(self,owner): diff --git a/src/common/xmpp/debug.py b/src/common/xmpp/debug.py index ded0dbc41..6c28eb62c 100644 --- a/src/common/xmpp/debug.py +++ b/src/common/xmpp/debug.py @@ -40,16 +40,16 @@ in this code import sys +import traceback import time import os import types - - -colornames = ['none', 'black', 'red', 'green', 'brown', 'blue', 'magenta', -'cyan', 'light_gray', 'dark_gray', 'bright_red', 'bright_green', 'yellow', -'bright_blue', 'purple', 'bright_cyan', 'white'] +if os.environ.has_key('TERM'): + colors_enabled=True +else: + colors_enabled=False color_none = chr(27) + "[0m" color_black = chr(27) + "[30m" @@ -69,11 +69,7 @@ color_purple = chr(27) + "[35;1m" color_bright_cyan = chr(27) + "[36;1m" color_white = chr(27) + "[37;1m" -if os.name == 'nt': - for colorname in colornames: - name = 'color_' + colorname - mod = compile("%s = ''" % name, 'gajim', 'exec') - eval(mod) + """ Define your flags in yor modules like this: @@ -125,7 +121,7 @@ class Debug: # # active_flags are those that will trigger output # - active_flags = None, + active_flags = None, # # Log file should be file object or file namne # @@ -136,7 +132,7 @@ class Debug: # with prefix = chr(27) + '[34m' # sufix = chr(27) + '[37;1m\n' # - prefix = 'DEBUG: ', + prefix = 'DEBUG: ', sufix = '\n', # # If you want unix style timestamps, @@ -144,7 +140,7 @@ class Debug: # 1 before prefix, good when prefix is a string # 2 after prefix, good when prefix is a color # - time_stamp = 0, + time_stamp = 0, # # flag_show should normaly be of, but can be turned on to get a # good view of what flags are actually used for calls, @@ -204,7 +200,7 @@ class Debug: mod_name = "" self.show('Debug created for %s%s' % (caller.f_code.co_filename, mod_name )) - self.show(' flags defined: %s' % ' '.join( self.active )) + self.show(' flags defined: %s' % ','.join( self.active )) if type(flag_show) in (type(''), type(None)): self.flag_show = flag_show @@ -397,12 +393,19 @@ class Debug: colors={} def Show(self, flag, msg, prefix=''): - msg=msg.replace('\r','\\r').replace('\n','\\n') - if self.colors.has_key(prefix): msg=self.colors[prefix]+msg+color_none + msg=msg.replace('\r','\\r').replace('\n','\\n').replace('><','>\n <') + if not colors_enabled: pass + elif self.colors.has_key(prefix): msg=self.colors[prefix]+msg+color_none else: msg=color_none+msg - if self.colors.has_key(flag): prefixcolor=self.colors[flag] + if not colors_enabled: prefixcolor='' + elif self.colors.has_key(flag): prefixcolor=self.colors[flag] else: prefixcolor=color_none + if prefix=='error': + _exception = sys.exc_info() + if _exception[0]: + msg=msg+'\n'+''.join(traceback.format_exception(_exception[0], _exception[1], _exception[2])).rstrip() + prefix= self.prefix+prefixcolor+(flag+' '*12)[:12]+' '+(prefix+' '*6)[:6] self.show(msg, flag, prefix) diff --git a/src/common/xmpp/dispatcher.py b/src/common/xmpp/dispatcher.py index bf456ecb9..4354a7031 100644 --- a/src/common/xmpp/dispatcher.py +++ b/src/common/xmpp/dispatcher.py @@ -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.35 2005/05/07 03:26:51 snakeru Exp $ +# $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 @@ -115,18 +115,21 @@ 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. + """ for handler in self._cycleHandlers: handler(self) if len(self._pendingExceptions) > 0: _pendingException = self._pendingExceptions.pop() raise _pendingException[0], _pendingException[1], _pendingException[2] if self._owner.Connection.pending_data(timeout): - try: data=self._owner.Connection.receive() + try: data=self._owner.Connection.receive() except IOError: return self.Stream.Parse(data) if len(self._pendingExceptions) > 0: - _pendingException = self._pendingExceptions.pop() - raise _pendingException[0], _pendingException[1], _pendingException[2] + _pendingException = self._pendingExceptions.pop() + raise _pendingException[0], _pendingException[1], _pendingException[2] return len(data) return '0' # It means that nothing is received but link is alive. @@ -234,13 +237,30 @@ class Dispatcher(PlugIn): 3) data that comes along with event. Depends on event.""" if self._eventHandler: self._eventHandler(realm,event,data) - def dispatch(self,stanza,session=None): + def dispatch(self,stanza,session=None,direct=0): """ Main procedure that performs XMPP stanza recognition and calling apppropriate handlers for it. Called internally. """ 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') == 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() @@ -251,7 +271,7 @@ class Dispatcher(PlugIn): self.DEBUG("Unknown stanza: " + name,'warn') name='unknown' else: - self.DEBUG("Got %s stanza"%name, 'ok') + self.DEBUG("Got %s/%s stanza"%(xmlns,name), 'ok') if stanza.__class__.__name__=='Node': stanza=self.handlers[xmlns][name][type](node=stanza) @@ -281,7 +301,6 @@ class Dispatcher(PlugIn): try: cb(session,stanza,**args) except Exception, typ: if typ.__class__.__name__<>'NodeProcessed': raise - else: session.DEBUG("Expected stanza arrived!",'ok') session._expected[ID]=stanza @@ -344,6 +363,16 @@ class Dispatcher(PlugIn): stanza.setID(_ID) else: _ID=stanza.getID() if self._owner._registered_name and not stanza.getAttr('from'): stanza.setAttr('from',self._owner._registered_name) + if self._owner._component and stanza.getName()!='bind': + to=self._owner.Server + if stanza.getTo() and stanza.getTo().getDomain(): + to=stanza.getTo().getDomain() + frm=stanza.getFrom() + if frm.getDomain(): + frm=frm.getDomain() + route=Protocol('route',to=to,frm=frm,payload=[stanza]) + stanza=route + stanza.setNamespace(self._owner.Namespace) stanza.setParent(self._metastream) self._owner_send(stanza) return _ID diff --git a/src/common/xmpp/features.py b/src/common/xmpp/features.py index f20461548..0cef16d99 100644 --- a/src/common/xmpp/features.py +++ b/src/common/xmpp/features.py @@ -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.20 2005/04/30 07:43:01 snakeru Exp $ +# $Id: features.py,v 1.22 2005/09/30 20:13:04 mikealbon Exp $ """ This module contains variable stuff that is not worth splitting into separate modules. @@ -60,7 +60,7 @@ def discoverInfo(disp,jid,node=None): """ Query remote object about info that it publishes. Returns identities and features lists.""" """ According to JEP-0030: query MAY have node attribute - identity: MUST HAVE category and type attributes and MAY HAVE name attribute. + identity: MUST HAVE category and name attributes and MAY HAVE type attribute. feature: MUST HAVE var attribute""" identities , features = [] , [] for i in _discover(disp,NS_DISCO_INFO,jid,node): diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py index 9d90fc6d9..ff35b0825 100644 --- a/src/common/xmpp/protocol.py +++ b/src/common/xmpp/protocol.py @@ -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.41 2005/05/02 08:36:41 snakeru Exp $ +# $Id: protocol.py,v 1.52 2006/01/09 22:08:57 normanr Exp $ """ Protocol module contains tools that is needed for processing of @@ -21,61 +21,79 @@ xmpp-related data structures. from simplexml import Node,ustr import time - +NS_ACTIVITY ='http://jabber.org/protocol/activity' # JEP-0108 +NS_ADDRESS ='http://jabber.org/protocol/address' # JEP-0033 NS_AGENTS ='jabber:iq:agents' NS_AMP ='http://jabber.org/protocol/amp' +NS_AMP_ERRORS =NS_AMP+'#errors' NS_AUTH ='jabber:iq:auth' NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind' NS_BROWSE ='jabber:iq:browse' +NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065 +NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115 +NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085 NS_CLIENT ='jabber:client' NS_COMMANDS ='http://jabber.org/protocol/commands' NS_COMPONENT_ACCEPT='jabber:component:accept' +NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0' +NS_COMPRESS ='http://jabber.org/protocol/compress' # JEP-0138 NS_CONFERENCE ='jabber:x:conference' -NS_DATA ='jabber:x:data' # JEP-0004 +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_DISCO ='http://jabber.org/protocol/disco' +NS_DISCO_INFO =NS_DISCO+'#info' +NS_DISCO_ITEMS =NS_DISCO+'#items' +NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027 +NS_EVENT ='jabber:x:event' # JEP-0022 +NS_FEATURE ='http://jabber.org/protocol/feature-neg' +NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096 +NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080 NS_GROUPCHAT ='gc-1.0' +NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # JEP-0070 +NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # JEP-0124 NS_IBB ='http://jabber.org/protocol/ibb' -NS_INVISIBLE ='presence-invisible' # jabberd2 -NS_IQ ='iq' # jabberd2 +NS_INVISIBLE ='presence-invisible' # Jabberd2 +NS_IQ ='iq' # Jabberd2 NS_LAST ='jabber:iq:last' -NS_MESSAGE ='message' # jabberd2 +NS_MESSAGE ='message' # Jabberd2 +NS_MOOD ='http://jabber.org/protocol/mood' # JEP-0107 NS_MUC ='http://jabber.org/protocol/muc' -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_MUC_USER =NS_MUC+'#user' +NS_MUC_ADMIN =NS_MUC+'#admin' +NS_MUC_OWNER =NS_MUC+'#owner' +NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # JEP-0013 +NS_PHYSLOC ='http://jabber.org/protocol/physloc' # JEP-0112 +NS_PRESENCE ='presence' # Jabberd2 NS_PRIVACY ='jabber:iq:privacy' NS_PRIVATE ='jabber:iq:private' +NS_PUBSUB ='http://jabber.org/protocol/pubsub' # JEP-0060 NS_REGISTER ='jabber:iq:register' NS_ROSTER ='jabber:iq:roster' -NS_RPC ='jabber:iq:rpc' # JEP-0009 +NS_ROSTERX ='http://jabber.org/protocol/rosterx' # JEP-0144 +NS_RPC ='jabber:iq:rpc' # JEP-0009 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_SI ='http://jabber.org/protocol/si' # JEP-0096 +NS_SI_PUB ='http://jabber.org/protocol/sipub' # JEP-0137 +NS_SIGNED ='jabber:x:signed' # JEP-0027 NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas' +NS_STREAM ='http://affinix.com/jabber/stream' NS_STREAMS ='http://etherx.jabber.org/streams' NS_TIME ='jabber:iq:time' NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls' NS_VACATION ='http://jabber.org/protocol/vacation' NS_VCARD ='vcard-temp' -NS_GMAILNOTIFY ='google:mail:notify' +NS_GMAILNOTIFY ='google:mail:notify' NS_VCARD_UPDATE =NS_VCARD+':x:update' NS_VERSION ='jabber:iq:version' -NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027 +NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # JEP-0130 +NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # JEP-0071 +NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # JEP-0141 +NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # JEP-0122 NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams' -NS_SIGNED ='jabber:x:signed' # JEP-0027 -NS_SI ='http://jabber.org/protocol/si' # JEP-0096 -NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096 -NS_FEATURE ='http://jabber.org/protocol/feature-neg' -NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065 -NS_DISCO ='http://jabber.org/protocol/disco#info' # JEP-0095 -NS_STREAM ='http://affinix.com/jabber/stream' -NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # JEP-0070 xmpp_stream_error_conditions=""" bad-format -- -- -- The entity has sent XML that cannot be processed. @@ -676,7 +694,7 @@ class DataForm(Node): for field in self.getTags('field'): name=field.getAttr('var') typ=field.getType() - if type(typ) in [type(''),type(u'')] and typ[-6:]=='multi': + if type(typ) in [type(''),type(u'')] and typ[-6:]=='-multi': val=[] for i in field.getTags('value'): val.append(i.getData()) else: val=field.getTagData('value') diff --git a/src/common/xmpp/transports.py b/src/common/xmpp/transports.py index d43b415dd..6af8d5082 100644 --- a/src/common/xmpp/transports.py +++ b/src/common/xmpp/transports.py @@ -66,7 +66,7 @@ class error: class TCPsocket(PlugIn): """ This class defines direct TCP connection method. """ - def __init__(self, server=None): + def __init__(self, server=None, use_srv=True): """ Cache connection point 'server'. 'server' is the tuple of (host, port) absolutely the same as standard tcp socket uses. """ PlugIn.__init__(self) @@ -165,12 +165,12 @@ class HTTPPROXYsocket(TCPsocket): """ HTTP (CONNECT) proxy connection class. Uses TCPsocket as the base class redefines only connect method. Allows to use HTTP proxies like squid with (optionally) simple authentication (using login and password). """ - def __init__(self,proxy,server): + def __init__(self,proxy,server,use_srv=True): """ Caches proxy and target addresses. 'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address) and optional keys 'user' and 'password' to use for authentication. 'server' argument is a tuple of host and port - just like TCPsocket uses. """ - TCPsocket.__init__(self,server) + TCPsocket.__init__(self,server,use_srv) self.DBG_LINE=DBG_CONNECT_PROXY self._proxy=proxy @@ -196,14 +196,23 @@ 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' @@ -247,8 +256,13 @@ class TLS(PlugIn): self._owner.Connection.send(''%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()