Coding standards and documentation improvements in dispatcher_nb.py. Many open questions remain at my side. Please check FIXMEs.
This commit is contained in:
parent
de73b76771
commit
b7400724b7
|
@ -16,36 +16,39 @@
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Main xmpppy mechanism. Provides library with methods to assign different handlers
|
Main xmpp decision making logic. Provides library with methods to assign
|
||||||
to different XMPP stanzas.
|
different handlers to different XMPP stanzas and namespaces.
|
||||||
Contains one tunable attribute: DefaultTimeout (25 seconds by default). It defines time that
|
|
||||||
Dispatcher.SendAndWaitForResponce method will wait for reply stanza before giving up.
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import simplexml, sys, locale
|
import simplexml, sys, locale
|
||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
from protocol import *
|
|
||||||
from client import PlugIn
|
from client import PlugIn
|
||||||
|
from protocol import NS_STREAMS, NS_XMPP_STREAMS, NS_HTTP_BIND, Iq, Presence, \
|
||||||
|
Message, Protocol, Node, Error, ERR_FEATURE_NOT_IMPLEMENTED, StreamError, \
|
||||||
|
stream_exceptions
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger('gajim.c.x.dispatcher_nb')
|
log = logging.getLogger('gajim.c.x.dispatcher_nb')
|
||||||
|
|
||||||
# default timeout to wait for response for our id
|
#: default timeout to wait for response for our id
|
||||||
DEFAULT_TIMEOUT_SECONDS = 25
|
DEFAULT_TIMEOUT_SECONDS = 25
|
||||||
ID = 0
|
outgoingID = 0
|
||||||
|
|
||||||
XML_DECLARATION = '<?xml version=\'1.0\'?>'
|
XML_DECLARATION = '<?xml version=\'1.0\'?>'
|
||||||
|
|
||||||
# FIXME: ugly
|
# FIXME: ugly
|
||||||
class Dispatcher():
|
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
|
Why is this here - I needed to redefine Dispatcher for BOSH and easiest way
|
||||||
# is that reference used to access dispatcher instance is in Client attribute
|
was to inherit original Dispatcher (now renamed to XMPPDispatcher). Trouble
|
||||||
# named by __class__.__name__ of the dispatcher instance .. long story short:
|
is that reference used to access dispatcher instance is in Client attribute
|
||||||
# I wrote following to avoid changing each client.Dispatcher.whatever() in xmpp/
|
named by __class__.__name__ of the dispatcher instance .. long story short:
|
||||||
|
|
||||||
# If having two kinds of dispatcher will go well, I will rewrite the dispatcher
|
I wrote following to avoid changing each client.Dispatcher.whatever() in xmpp
|
||||||
# references in other scripts
|
|
||||||
|
If having two kinds of dispatcher will go well, I will rewrite the dispatcher
|
||||||
|
references in other scripts
|
||||||
|
'''
|
||||||
def PlugIn(self, client_obj, after_SASL=False, old_features=None):
|
def PlugIn(self, client_obj, after_SASL=False, old_features=None):
|
||||||
if client_obj.protocol_type == 'XMPP':
|
if client_obj.protocol_type == 'XMPP':
|
||||||
XMPPDispatcher().PlugIn(client_obj)
|
XMPPDispatcher().PlugIn(client_obj)
|
||||||
|
@ -53,42 +56,53 @@ class Dispatcher():
|
||||||
BOSHDispatcher().PlugIn(client_obj, after_SASL, old_features)
|
BOSHDispatcher().PlugIn(client_obj, after_SASL, old_features)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class XMPPDispatcher(PlugIn):
|
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.). '''
|
Handles XMPP stream and is the first who takes control over a fresh stanza.
|
||||||
|
|
||||||
|
Is plugged into NonBlockingClient but can be replugged to restart handled
|
||||||
|
stream headers (used by SASL f.e.).
|
||||||
|
'''
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
PlugIn.__init__(self)
|
PlugIn.__init__(self)
|
||||||
self.handlers={}
|
self.handlers = {}
|
||||||
self._expected={}
|
self._expected = {}
|
||||||
self._defaultHandler=None
|
self._defaultHandler = None
|
||||||
self._pendingExceptions=[]
|
self._pendingExceptions = []
|
||||||
self._eventHandler=None
|
self._eventHandler = None
|
||||||
self._cycleHandlers=[]
|
self._cycleHandlers = []
|
||||||
self._exported_methods=[self.RegisterHandler, self.RegisterDefaultHandler, \
|
self._exported_methods=[self.RegisterHandler, self.RegisterDefaultHandler,
|
||||||
self.RegisterEventHandler, self.UnregisterCycleHandler, self.RegisterCycleHandler, \
|
self.RegisterEventHandler, self.UnregisterCycleHandler,
|
||||||
self.RegisterHandlerOnce, self.UnregisterHandler, self.RegisterProtocol, \
|
self.RegisterCycleHandler, self.RegisterHandlerOnce,
|
||||||
self.SendAndWaitForResponse, \
|
self.UnregisterHandler, self.RegisterProtocol,
|
||||||
self.SendAndCallForResponse, self.getAnID, self.Event, self.send]
|
self.SendAndWaitForResponse, self.SendAndCallForResponse,
|
||||||
|
self.getAnID, self.Event, self.send]
|
||||||
|
|
||||||
def getAnID(self):
|
def getAnID(self):
|
||||||
global ID
|
global outgoingID
|
||||||
ID += 1
|
outgoingID += 1
|
||||||
return repr(ID)
|
return repr(outgoingID)
|
||||||
|
|
||||||
def dumpHandlers(self):
|
def dumpHandlers(self):
|
||||||
''' Return set of user-registered callbacks in it's internal format.
|
'''
|
||||||
Used within the library to carry user handlers set over Dispatcher replugins. '''
|
Return set of user-registered callbacks in it's internal format.
|
||||||
|
Used within the library to carry user handlers set over Dispatcher
|
||||||
|
replugins.
|
||||||
|
'''
|
||||||
return self.handlers
|
return self.handlers
|
||||||
|
|
||||||
def restoreHandlers(self, handlers):
|
def restoreHandlers(self, handlers):
|
||||||
''' Restores user-registered callbacks structure from dump previously obtained via dumpHandlers.
|
'''
|
||||||
Used within the library to carry user handlers set over Dispatcher replugins. '''
|
Restores user-registered callbacks structure from dump previously
|
||||||
|
obtained via dumpHandlers. Used within the library to carry user
|
||||||
|
handlers set over Dispatcher replugins.
|
||||||
|
'''
|
||||||
self.handlers = handlers
|
self.handlers = handlers
|
||||||
|
|
||||||
def _init(self):
|
def _init(self):
|
||||||
''' Registers default namespaces/protocols/handlers. Used internally. '''
|
'''
|
||||||
|
Registers default namespaces/protocols/handlers. Used internally.
|
||||||
|
'''
|
||||||
self.RegisterNamespace('unknown')
|
self.RegisterNamespace('unknown')
|
||||||
self.RegisterNamespace(NS_STREAMS)
|
self.RegisterNamespace(NS_STREAMS)
|
||||||
self.RegisterNamespace(self._owner.defaultNamespace)
|
self.RegisterNamespace(self._owner.defaultNamespace)
|
||||||
|
@ -100,7 +114,10 @@ class XMPPDispatcher(PlugIn):
|
||||||
self.on_responses = {}
|
self.on_responses = {}
|
||||||
|
|
||||||
def plugin(self, owner):
|
def plugin(self, owner):
|
||||||
''' Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally.'''
|
'''
|
||||||
|
Plug the Dispatcher instance into Client class instance and send
|
||||||
|
initial stream header. Used internally.
|
||||||
|
'''
|
||||||
self._init()
|
self._init()
|
||||||
self._owner.lastErrNode = None
|
self._owner.lastErrNode = None
|
||||||
self._owner.lastErr = None
|
self._owner.lastErr = None
|
||||||
|
@ -130,18 +147,23 @@ class XMPPDispatcher(PlugIn):
|
||||||
if locale.getdefaultlocale()[0]:
|
if locale.getdefaultlocale()[0]:
|
||||||
self._metastream.setAttr('xml:lang',
|
self._metastream.setAttr('xml:lang',
|
||||||
locale.getdefaultlocale()[0].split('_')[0])
|
locale.getdefaultlocale()[0].split('_')[0])
|
||||||
self._owner.send("%s%s>" % (XML_DECLARATION,str(self._metastream)[:-2]))
|
self._owner.send("%s%s>" % (XML_DECLARATION, str(self._metastream)[:-2]))
|
||||||
|
|
||||||
def _check_stream_start(self, ns, tag, attrs):
|
def _check_stream_start(self, ns, tag, attrs):
|
||||||
if ns!=NS_STREAMS or tag!='stream':
|
if ns != NS_STREAMS or tag!='stream':
|
||||||
raise ValueError('Incorrect stream start: (%s,%s). Terminating.' % (tag, ns))
|
raise ValueError('Incorrect stream start: (%s,%s). Terminating.'
|
||||||
|
% (tag, ns))
|
||||||
|
|
||||||
def ProcessNonBlocking(self, data=None):
|
def ProcessNonBlocking(self, data=None):
|
||||||
''' Check incoming stream for data waiting. If "timeout" is positive - block for as max. this time.
|
'''
|
||||||
Returns:
|
Check incoming stream for data waiting.
|
||||||
|
|
||||||
|
:param data: data received from transports/IO sockets
|
||||||
|
:return:
|
||||||
1) length of processed data if some data were processed;
|
1) length of processed data if some data were processed;
|
||||||
2) '0' string if no data were processed but link is alive;
|
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.
|
||||||
|
'''
|
||||||
for handler in self._cycleHandlers:
|
for handler in self._cycleHandlers:
|
||||||
handler(self)
|
handler(self)
|
||||||
if len(self._pendingExceptions) > 0:
|
if len(self._pendingExceptions) > 0:
|
||||||
|
@ -158,54 +180,72 @@ class XMPPDispatcher(PlugIn):
|
||||||
self._owner.Connection.disconnect()
|
self._owner.Connection.disconnect()
|
||||||
return 0
|
return 0
|
||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
self.DEBUG(str(e), 'error')
|
log.debug('ValueError: %s' % str(e))
|
||||||
self._owner.Connection.pollend()
|
self._owner.Connection.pollend()
|
||||||
return 0
|
return 0
|
||||||
if len(self._pendingExceptions) > 0:
|
if len(self._pendingExceptions) > 0:
|
||||||
_pendingException = self._pendingExceptions.pop()
|
_pendingException = self._pendingExceptions.pop()
|
||||||
raise _pendingException[0], _pendingException[1], _pendingException[2]
|
raise _pendingException[0], _pendingException[1], _pendingException[2]
|
||||||
if len(data) == 0: return '0'
|
if len(data) == 0:
|
||||||
|
return '0'
|
||||||
return len(data)
|
return len(data)
|
||||||
|
|
||||||
def RegisterNamespace(self, xmlns, order='info'):
|
def RegisterNamespace(self, xmlns, order='info'):
|
||||||
''' Creates internal structures for newly registered namespace.
|
'''
|
||||||
You can register handlers for this namespace afterwards. By default one namespace
|
Creates internal structures for newly registered namespace.
|
||||||
already registered (jabber:client or jabber:component:accept depending on context. '''
|
You can register handlers for this namespace afterwards. By default
|
||||||
|
one namespace is already registered
|
||||||
|
(jabber:client or jabber:component:accept depending on context.
|
||||||
|
'''
|
||||||
log.debug('Registering namespace "%s"' % xmlns)
|
log.debug('Registering namespace "%s"' % xmlns)
|
||||||
self.handlers[xmlns]={}
|
self.handlers[xmlns] = {}
|
||||||
self.RegisterProtocol('unknown', Protocol, xmlns=xmlns)
|
self.RegisterProtocol('unknown', Protocol, xmlns=xmlns)
|
||||||
self.RegisterProtocol('default', Protocol, xmlns=xmlns)
|
self.RegisterProtocol('default', Protocol, xmlns=xmlns)
|
||||||
|
|
||||||
def RegisterProtocol(self, tag_name, Proto, xmlns=None, order='info'):
|
def RegisterProtocol(self, tag_name, Proto, xmlns=None, order='info'):
|
||||||
''' Used to declare some top-level stanza name to dispatcher.
|
|
||||||
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.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):
|
|
||||||
''' Register handler for processing all stanzas for specified namespace. '''
|
|
||||||
self.RegisterHandler('default', handler, typ, ns, xmlns, makefirst, system)
|
|
||||||
|
|
||||||
def RegisterHandler(self, name, handler, typ='', ns='', xmlns=None, makefirst=0, system=0):
|
|
||||||
'''Register user callback as stanzas handler of declared type. Callback must take
|
|
||||||
(if chained, see later) arguments: dispatcher instance (for replying), incomed
|
|
||||||
return of previous handlers.
|
|
||||||
The callback must raise xmpp.NodeProcessed just before return if it want preven
|
|
||||||
callbacks to be called with the same stanza as argument _and_, more importantly
|
|
||||||
library from returning stanza to sender with error set (to be enabled in 0.2 ve
|
|
||||||
Arguments:
|
|
||||||
"name" - name of stanza. F.e. "iq".
|
|
||||||
"handler" - user callback.
|
|
||||||
"typ" - value of stanza's "type" attribute. If not specified any value match
|
|
||||||
"ns" - namespace of child that stanza must contain.
|
|
||||||
"chained" - chain together output of several handlers.
|
|
||||||
"makefirst" - insert handler in the beginning of handlers list instead of
|
|
||||||
adding it to the end. Note that more common handlers (i.e. w/o "typ" and "
|
|
||||||
will be called first nevertheless.
|
|
||||||
"system" - call handler even if NodeProcessed Exception were raised already.
|
|
||||||
'''
|
'''
|
||||||
|
Used to declare some top-level stanza name to dispatcher.
|
||||||
|
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.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):
|
||||||
|
'''
|
||||||
|
Register handler for processing all stanzas for specified namespace.
|
||||||
|
'''
|
||||||
|
self.RegisterHandler('default', handler, typ, ns, xmlns, makefirst,
|
||||||
|
system)
|
||||||
|
|
||||||
|
def RegisterHandler(self, name, handler, typ='', ns='', xmlns=None,
|
||||||
|
makefirst=False, system=False):
|
||||||
|
'''
|
||||||
|
Register user callback as stanzas handler of declared type.
|
||||||
|
|
||||||
|
Callback must take (if chained, see later) arguments:
|
||||||
|
dispatcher instance (for replying), incoming return of previous handlers.
|
||||||
|
The callback must raise xmpp.NodeProcessed just before return if it wants
|
||||||
|
other callbacks to be called with the same stanza as argument _and_, more
|
||||||
|
importantly library from returning stanza to sender with error set.
|
||||||
|
|
||||||
|
:param name: name of stanza. F.e. "iq".
|
||||||
|
:param handler: user callback.
|
||||||
|
:param typ: value of stanza's "type" attribute. If not specified any
|
||||||
|
value will match
|
||||||
|
:param ns: namespace of child that stanza must contain.
|
||||||
|
:param chained: chain together output of several handlers.
|
||||||
|
:param makefirst: insert handler in the beginning of handlers list instea
|
||||||
|
of adding it to the end. Note that more common handlers i.e. w/o "typ"
|
||||||
|
and " will be called first nevertheless.
|
||||||
|
:param system: call handler even if NodeProcessed Exception were raised
|
||||||
|
already.
|
||||||
|
'''
|
||||||
|
# FIXME: What does chain mean and where is it handled?
|
||||||
if not xmlns:
|
if not xmlns:
|
||||||
xmlns=self._owner.defaultNamespace
|
xmlns=self._owner.defaultNamespace
|
||||||
log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' %
|
log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' %
|
||||||
|
@ -219,20 +259,27 @@ class XMPPDispatcher(PlugIn):
|
||||||
if typ+ns not in self.handlers[xmlns][name]:
|
if typ+ns not in self.handlers[xmlns][name]:
|
||||||
self.handlers[xmlns][name][typ+ns]=[]
|
self.handlers[xmlns][name][typ+ns]=[]
|
||||||
if makefirst:
|
if makefirst:
|
||||||
self.handlers[xmlns][name][typ+ns].insert(0,{'func':handler,'system':system})
|
self.handlers[xmlns][name][typ+ns].insert(0,{'func':handler,
|
||||||
|
'system':system})
|
||||||
else:
|
else:
|
||||||
self.handlers[xmlns][name][typ+ns].append({'func':handler,'system':system})
|
self.handlers[xmlns][name][typ+ns].append({'func':handler,
|
||||||
|
'system':system})
|
||||||
|
|
||||||
def RegisterHandlerOnce(self,name,handler,typ='',ns='',xmlns=None,makefirst=0, system=0):
|
def RegisterHandlerOnce(self, name, handler, typ='', ns='', xmlns=None,
|
||||||
|
makefirst=0, system=0):
|
||||||
''' Unregister handler after first call (not implemented yet). '''
|
''' Unregister handler after first call (not implemented yet). '''
|
||||||
|
# FIXME Drop or implement
|
||||||
if not xmlns:
|
if not xmlns:
|
||||||
xmlns=self._owner.defaultNamespace
|
xmlns = self._owner.defaultNamespace
|
||||||
self.RegisterHandler(name, handler, typ, ns, xmlns, makefirst, system)
|
self.RegisterHandler(name, handler, typ, ns, xmlns, makefirst, system)
|
||||||
|
|
||||||
def UnregisterHandler(self, name, handler, typ='', ns='', xmlns=None):
|
def UnregisterHandler(self, name, handler, typ='', ns='', xmlns=None):
|
||||||
''' Unregister handler. "typ" and "ns" must be specified exactly the same as with registering.'''
|
'''
|
||||||
|
Unregister handler. "typ" and "ns" must be specified exactly the same as
|
||||||
|
with registering.
|
||||||
|
'''
|
||||||
if not xmlns:
|
if not xmlns:
|
||||||
xmlns=self._owner.defaultNamespace
|
xmlns = self._owner.defaultNamespace
|
||||||
if not typ and not ns:
|
if not typ and not ns:
|
||||||
typ='default'
|
typ='default'
|
||||||
if xmlns not in self.handlers:
|
if xmlns not in self.handlers:
|
||||||
|
@ -248,21 +295,29 @@ class XMPPDispatcher(PlugIn):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def RegisterDefaultHandler(self,handler):
|
def RegisterDefaultHandler(self, handler):
|
||||||
''' Specify the handler that will be used if no NodeProcessed exception were raised.
|
'''
|
||||||
This is returnStanzaHandler by default. '''
|
Specify the handler that will be used if no NodeProcessed exception were
|
||||||
self._defaultHandler=handler
|
raised. This is returnStanzaHandler by default.
|
||||||
|
'''
|
||||||
|
self._defaultHandler = handler
|
||||||
|
|
||||||
def RegisterEventHandler(self,handler):
|
def RegisterEventHandler(self, handler):
|
||||||
''' Register handler that will process events. F.e. "FILERECEIVED" event. '''
|
'''
|
||||||
self._eventHandler=handler
|
Register handler that will process events. F.e.
|
||||||
|
"FILERECEIVED" event. See common/connection: _event_dispatcher()
|
||||||
|
'''
|
||||||
|
self._eventHandler = handler
|
||||||
|
|
||||||
def returnStanzaHandler(self,conn,stanza):
|
def returnStanzaHandler(self,conn,stanza):
|
||||||
''' Return stanza back to the sender with <feature-not-implemennted/> error set. '''
|
'''
|
||||||
|
Return stanza back to the sender with <feature-not-implemented/> error set
|
||||||
|
'''
|
||||||
if stanza.getType() in ('get','set'):
|
if stanza.getType() in ('get','set'):
|
||||||
conn._owner.send(Error(stanza,ERR_FEATURE_NOT_IMPLEMENTED))
|
conn._owner.send(Error(stanza, ERR_FEATURE_NOT_IMPLEMENTED))
|
||||||
|
|
||||||
def streamErrorHandler(self,conn,error):
|
def streamErrorHandler(self,conn,error):
|
||||||
|
# FIXME: looks like it is unused
|
||||||
name,text='error',error.getData()
|
name,text='error',error.getData()
|
||||||
for tag in error.getChildren():
|
for tag in error.getChildren():
|
||||||
if tag.getNamespace()==NS_XMPP_STREAMS:
|
if tag.getNamespace()==NS_XMPP_STREAMS:
|
||||||
|
@ -277,41 +332,56 @@ class XMPPDispatcher(PlugIn):
|
||||||
raise exc((name,text))
|
raise exc((name,text))
|
||||||
|
|
||||||
def RegisterCycleHandler(self, handler):
|
def RegisterCycleHandler(self, handler):
|
||||||
''' Register handler that will be called on every Dispatcher.Process() call. '''
|
'''
|
||||||
|
Register handler that will be called on every Dispatcher.Process() call.
|
||||||
|
'''
|
||||||
if handler not in self._cycleHandlers:
|
if handler not in self._cycleHandlers:
|
||||||
self._cycleHandlers.append(handler)
|
self._cycleHandlers.append(handler)
|
||||||
|
|
||||||
def UnregisterCycleHandler(self, handler):
|
def UnregisterCycleHandler(self, handler):
|
||||||
''' Unregister handler that will is called on every Dispatcher.Process() call.'''
|
'''
|
||||||
|
Unregister handler that will is called on every Dispatcher.Process() call
|
||||||
|
'''
|
||||||
if handler in self._cycleHandlers:
|
if handler in self._cycleHandlers:
|
||||||
self._cycleHandlers.remove(handler)
|
self._cycleHandlers.remove(handler)
|
||||||
|
|
||||||
def Event(self, realm, event, data):
|
def Event(self, realm, event, data):
|
||||||
''' Raise some event. Takes three arguments:
|
'''
|
||||||
1) "realm" - scope of event. Usually a namespace.
|
Raise some event.
|
||||||
2) "event" - the event itself. F.e. "SUCESSFULL SEND".
|
|
||||||
3) data that comes along with event. Depends on event.'''
|
:param realm: scope of event. Usually a namespace.
|
||||||
if self._eventHandler: self._eventHandler(realm,event,data)
|
:param event: the event itself. F.e. "SUCCESSFUL SEND".
|
||||||
|
:param data: data that comes along with event. Depends on event.
|
||||||
|
'''
|
||||||
|
if self._eventHandler:
|
||||||
|
self._eventHandler(realm, event, data)
|
||||||
|
|
||||||
def dispatch(self, stanza, session=None, direct=0):
|
def dispatch(self, stanza, session=None, direct=0):
|
||||||
''' Main procedure that performs XMPP stanza recognition and calling apppropriate handlers for it.
|
'''
|
||||||
Called internally. '''
|
Main procedure that performs XMPP stanza recognition and calling
|
||||||
#log.info('dispatch called: stanza = %s, session = %s, direct= %s' % (stanza, session, direct))
|
apppropriate handlers for it. Called by simplexml.
|
||||||
|
'''
|
||||||
|
# FIXME: Where do we set session and direct. Why? What are those intended
|
||||||
|
# to do?
|
||||||
|
|
||||||
|
#log.info('dispatch called: stanza = %s, session = %s, direct= %s'
|
||||||
|
# % (stanza, session, direct))
|
||||||
if not session:
|
if not session:
|
||||||
session = self
|
session = self
|
||||||
session.Stream._mini_dom = None
|
session.Stream._mini_dom = None
|
||||||
name = stanza.getName()
|
name = stanza.getName()
|
||||||
|
|
||||||
if name=='features':
|
if name == 'features':
|
||||||
self._owner.got_features = True
|
self._owner.got_features = True
|
||||||
session.Stream.features=stanza
|
session.Stream.features = stanza
|
||||||
|
|
||||||
xmlns=stanza.getNamespace()
|
xmlns = stanza.getNamespace()
|
||||||
|
|
||||||
#log.info('in dispatch, getting ns for %s, and the ns is %s' % (stanza, xmlns))
|
# log.info('in dispatch, getting ns for %s, and the ns is %s'
|
||||||
|
# % (stanza, xmlns))
|
||||||
if xmlns not in self.handlers:
|
if xmlns not in self.handlers:
|
||||||
log.warn("Unknown namespace: " + xmlns)
|
log.warn("Unknown namespace: " + xmlns)
|
||||||
xmlns='unknown'
|
xmlns = 'unknown'
|
||||||
# features stanza has been handled before
|
# features stanza has been handled before
|
||||||
if name not in self.handlers[xmlns]:
|
if name not in self.handlers[xmlns]:
|
||||||
if name != 'features':
|
if name != 'features':
|
||||||
|
@ -322,42 +392,50 @@ class XMPPDispatcher(PlugIn):
|
||||||
else:
|
else:
|
||||||
log.debug("Got %s/%s stanza" % (xmlns, name))
|
log.debug("Got %s/%s stanza" % (xmlns, name))
|
||||||
|
|
||||||
if stanza.__class__.__name__=='Node':
|
if stanza.__class__.__name__ == 'Node':
|
||||||
|
# FIXME: this cannot work
|
||||||
stanza=self.handlers[xmlns][name][type](node=stanza)
|
stanza=self.handlers[xmlns][name][type](node=stanza)
|
||||||
|
|
||||||
typ=stanza.getType()
|
typ = stanza.getType()
|
||||||
if not typ: typ=''
|
if not typ:
|
||||||
stanza.props=stanza.getProperties()
|
typ = ''
|
||||||
ID=stanza.getID()
|
stanza.props = stanza.getProperties()
|
||||||
|
ID = stanza.getID()
|
||||||
|
|
||||||
list_=['default'] # we will use all handlers:
|
list_ = ['default'] # we will use all handlers:
|
||||||
if typ in self.handlers[xmlns][name]: list_.append(typ) # from very common...
|
if typ in self.handlers[xmlns][name]:
|
||||||
|
list_.append(typ) # from very common...
|
||||||
for prop in stanza.props:
|
for prop in stanza.props:
|
||||||
if prop in self.handlers[xmlns][name]: list_.append(prop)
|
if prop in self.handlers[xmlns][name]:
|
||||||
if typ and typ+prop in self.handlers[xmlns][name]: list_.append(typ+prop) # ...to very particular
|
list_.append(prop)
|
||||||
|
if typ and typ+prop in self.handlers[xmlns][name]:
|
||||||
|
list_.append(typ+prop) # ...to very particular
|
||||||
|
|
||||||
chain=self.handlers[xmlns]['default']['default']
|
chain = self.handlers[xmlns]['default']['default']
|
||||||
for key in list_:
|
for key in list_:
|
||||||
if key: chain = chain + self.handlers[xmlns][name][key]
|
if key:
|
||||||
|
chain = chain + self.handlers[xmlns][name][key]
|
||||||
|
|
||||||
if ID in session._expected:
|
if ID in session._expected:
|
||||||
user=0
|
user = 0
|
||||||
if isinstance(session._expected[ID], tuple):
|
if isinstance(session._expected[ID], tuple):
|
||||||
cb,args = session._expected[ID]
|
cb, args = session._expected[ID]
|
||||||
log.debug("Expected stanza arrived. Callback %s(%s) found!" % (cb, args))
|
log.debug("Expected stanza arrived. Callback %s(%s) found!" %
|
||||||
|
(cb, args))
|
||||||
try:
|
try:
|
||||||
cb(session,stanza,**args)
|
cb(session,stanza,**args)
|
||||||
except Exception, typ:
|
except Exception, typ:
|
||||||
if typ.__class__.__name__ !='NodeProcessed': raise
|
if typ.__class__.__name__ != 'NodeProcessed':
|
||||||
|
raise
|
||||||
else:
|
else:
|
||||||
log.debug("Expected stanza arrived!")
|
log.debug("Expected stanza arrived!")
|
||||||
session._expected[ID]=stanza
|
session._expected[ID] = stanza
|
||||||
else:
|
else:
|
||||||
user=1
|
user = 1
|
||||||
for handler in chain:
|
for handler in chain:
|
||||||
if user or handler['system']:
|
if user or handler['system']:
|
||||||
try:
|
try:
|
||||||
handler['func'](session,stanza)
|
handler['func'](session, stanza)
|
||||||
except Exception, typ:
|
except Exception, typ:
|
||||||
if typ.__class__.__name__ != 'NodeProcessed':
|
if typ.__class__.__name__ != 'NodeProcessed':
|
||||||
self._pendingExceptions.insert(0, sys.exc_info())
|
self._pendingExceptions.insert(0, sys.exc_info())
|
||||||
|
@ -366,7 +444,10 @@ class XMPPDispatcher(PlugIn):
|
||||||
if user and self._defaultHandler:
|
if user and self._defaultHandler:
|
||||||
self._defaultHandler(session, stanza)
|
self._defaultHandler(session, stanza)
|
||||||
|
|
||||||
def WaitForData(self, data):
|
def _WaitForData(self, data):
|
||||||
|
'''
|
||||||
|
Internal wrapper around ProcessNonBlocking. Will check for
|
||||||
|
'''
|
||||||
if data is None:
|
if data is None:
|
||||||
return
|
return
|
||||||
res = self.ProcessNonBlocking(data)
|
res = self.ProcessNonBlocking(data)
|
||||||
|
@ -376,6 +457,8 @@ class XMPPDispatcher(PlugIn):
|
||||||
return
|
return
|
||||||
self._owner.remove_timeout()
|
self._owner.remove_timeout()
|
||||||
if self._expected[self._witid] is None:
|
if self._expected[self._witid] is None:
|
||||||
|
# If the expected Stanza would have arrived, ProcessNonBlocking would
|
||||||
|
# have placed the reply stanza in there
|
||||||
return
|
return
|
||||||
if self._witid in self.on_responses:
|
if self._witid in self.on_responses:
|
||||||
i = self._witid # copy id cause it can change in resp() call
|
i = self._witid # copy id cause it can change in resp() call
|
||||||
|
@ -389,7 +472,12 @@ class XMPPDispatcher(PlugIn):
|
||||||
del self._expected[i]
|
del self._expected[i]
|
||||||
|
|
||||||
def SendAndWaitForResponse(self, stanza, timeout=None, func=None, args=None):
|
def SendAndWaitForResponse(self, stanza, timeout=None, func=None, args=None):
|
||||||
''' Put stanza on the wire and wait for recipient's response to it. '''
|
'''
|
||||||
|
Send stanza and wait for recipient's response to it. Will call transports
|
||||||
|
on_timeout callback if response is not retrieved in time.
|
||||||
|
|
||||||
|
Be aware: Only timeout of latest call of SendAndWait is active.
|
||||||
|
'''
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
timeout = DEFAULT_TIMEOUT_SECONDS
|
timeout = DEFAULT_TIMEOUT_SECONDS
|
||||||
self._witid = self.send(stanza)
|
self._witid = self.send(stanza)
|
||||||
|
@ -397,7 +485,7 @@ class XMPPDispatcher(PlugIn):
|
||||||
self.on_responses[self._witid] = (func, args)
|
self.on_responses[self._witid] = (func, args)
|
||||||
if timeout:
|
if timeout:
|
||||||
self._owner.set_timeout(timeout)
|
self._owner.set_timeout(timeout)
|
||||||
self._owner.onreceive(self.WaitForData)
|
self._owner.onreceive(self._WaitForData)
|
||||||
self._expected[self._witid] = None
|
self._expected[self._witid] = None
|
||||||
return self._witid
|
return self._witid
|
||||||
|
|
||||||
|
@ -407,17 +495,22 @@ class XMPPDispatcher(PlugIn):
|
||||||
self.SendAndWaitForResponse(stanza, 0, func, args)
|
self.SendAndWaitForResponse(stanza, 0, func, args)
|
||||||
|
|
||||||
def send(self, stanza, now=False):
|
def send(self, stanza, now=False):
|
||||||
id = None
|
'''
|
||||||
|
Wraps transports send method when plugged into NonBlockingClient.
|
||||||
|
Makes sure stanzas get ID and from tag.
|
||||||
|
'''
|
||||||
|
ID = None
|
||||||
if type(stanza) not in [type(''), type(u'')]:
|
if type(stanza) not in [type(''), type(u'')]:
|
||||||
if isinstance(stanza, Protocol):
|
if isinstance(stanza, Protocol):
|
||||||
id = stanza.getID()
|
ID = stanza.getID()
|
||||||
if id is None:
|
if ID is None:
|
||||||
stanza.setID(self.getAnID())
|
stanza.setID(self.getAnID())
|
||||||
id = stanza.getID()
|
ID = stanza.getID()
|
||||||
if self._owner._registered_name and not stanza.getAttr('from'):
|
if self._owner._registered_name and not stanza.getAttr('from'):
|
||||||
stanza.setAttr('from', self._owner._registered_name)
|
stanza.setAttr('from', self._owner._registered_name)
|
||||||
self._owner.Connection.send(stanza, now)
|
self._owner.Connection.send(stanza, now)
|
||||||
return id
|
return ID
|
||||||
|
|
||||||
|
|
||||||
class BOSHDispatcher(XMPPDispatcher):
|
class BOSHDispatcher(XMPPDispatcher):
|
||||||
|
|
||||||
|
@ -446,41 +539,35 @@ class BOSHDispatcher(XMPPDispatcher):
|
||||||
self.restart = True
|
self.restart = True
|
||||||
self._owner.Connection.send_init(after_SASL=self.after_SASL)
|
self._owner.Connection.send_init(after_SASL=self.after_SASL)
|
||||||
|
|
||||||
|
|
||||||
def StreamTerminate(self):
|
def StreamTerminate(self):
|
||||||
''' Send a stream terminator. '''
|
''' Send a stream terminator. '''
|
||||||
self._owner.Connection.send_terminator()
|
self._owner.Connection.send_terminator()
|
||||||
|
|
||||||
def ProcessNonBlocking(self, data=None):
|
def ProcessNonBlocking(self, data=None):
|
||||||
|
|
||||||
if self.restart:
|
if self.restart:
|
||||||
fromstream = self._metastream
|
fromstream = self._metastream
|
||||||
fromstream.setAttr('from', fromstream.getAttr('to'))
|
fromstream.setAttr('from', fromstream.getAttr('to'))
|
||||||
fromstream.delAttr('to')
|
fromstream.delAttr('to')
|
||||||
data = '%s%s>%s' % (XML_DECLARATION,str(fromstream)[:-2] ,data)
|
data = '%s%s>%s' % (XML_DECLARATION,str(fromstream)[:-2] ,data)
|
||||||
self.restart = False
|
self.restart = False
|
||||||
|
|
||||||
return XMPPDispatcher.ProcessNonBlocking(self, data)
|
return XMPPDispatcher.ProcessNonBlocking(self, data)
|
||||||
|
|
||||||
def dispatch(self, stanza, session=None, direct=0):
|
def dispatch(self, stanza, session=None, direct=0):
|
||||||
|
if stanza.getName()=='body' and stanza.getNamespace() == NS_HTTP_BIND:
|
||||||
if stanza.getName()=='body' and stanza.getNamespace()==NS_HTTP_BIND:
|
|
||||||
|
|
||||||
stanza_attrs = stanza.getAttrs()
|
stanza_attrs = stanza.getAttrs()
|
||||||
if stanza_attrs.has_key('authid'):
|
if 'authid' in stanza_attrs:
|
||||||
# should be only in init response
|
# should be only in init response
|
||||||
# auth module expects id of stream in document attributes
|
# auth module expects id of stream in document attributes
|
||||||
self.Stream._document_attrs['id'] = stanza_attrs['authid']
|
self.Stream._document_attrs['id'] = stanza_attrs['authid']
|
||||||
|
|
||||||
self._owner.Connection.handle_body_attrs(stanza_attrs)
|
self._owner.Connection.handle_body_attrs(stanza_attrs)
|
||||||
|
|
||||||
children = stanza.getChildren()
|
children = stanza.getChildren()
|
||||||
|
|
||||||
if children:
|
if children:
|
||||||
for child in children:
|
for child in children:
|
||||||
# if child doesn't have any ns specified, simplexml (or expat) thinks it's
|
# if child doesn't have any ns specified, simplexml (or expat)
|
||||||
# of parent's (BOSH body) namespace, so we have to rewrite it to
|
# thinks it's of parent's (BOSH body) namespace, so we have to
|
||||||
# jabber:client
|
# rewrite it to jabber:client
|
||||||
if child.getNamespace() == NS_HTTP_BIND:
|
if child.getNamespace() == NS_HTTP_BIND:
|
||||||
child.setNamespace(self._owner.defaultNamespace)
|
child.setNamespace(self._owner.defaultNamespace)
|
||||||
XMPPDispatcher.dispatch(self, child, session, direct)
|
XMPPDispatcher.dispatch(self, child, session, direct)
|
||||||
|
|
Loading…
Reference in New Issue