| ... |
... |
@@ -27,10 +27,18 @@ import uuid |
|
27
|
27
|
import logging
|
|
28
|
28
|
from xml.parsers.expat import ExpatError
|
|
29
|
29
|
|
|
30
|
|
-from . import simplexml
|
|
31
|
|
-from .plugin import PlugIn
|
|
32
|
|
-from .protocol import (NS_STREAMS, NS_HTTP_BIND, Iq, Presence,
|
|
33
|
|
- Message, Protocol, Node, Error, ERR_FEATURE_NOT_IMPLEMENTED)
|
|
|
30
|
+from nbxmpp.simplexml import NodeBuilder
|
|
|
31
|
+from nbxmpp.plugin import PlugIn
|
|
|
32
|
+from nbxmpp.protocol import NS_STREAMS
|
|
|
33
|
+from nbxmpp.protocol import NS_HTTP_BIND
|
|
|
34
|
+from nbxmpp.protocol import NodeProcessed
|
|
|
35
|
+from nbxmpp.protocol import Iq
|
|
|
36
|
+from nbxmpp.protocol import Presence
|
|
|
37
|
+from nbxmpp.protocol import Message
|
|
|
38
|
+from nbxmpp.protocol import Protocol
|
|
|
39
|
+from nbxmpp.protocol import Node
|
|
|
40
|
+from nbxmpp.protocol import Error
|
|
|
41
|
+from nbxmpp.protocol import ERR_FEATURE_NOT_IMPLEMENTED
|
|
34
|
42
|
|
|
35
|
43
|
|
|
36
|
44
|
log = logging.getLogger('nbxmpp.dispatcher_nb')
|
| ... |
... |
@@ -177,7 +185,7 @@ class XMPPDispatcher(PlugIn): |
|
177
|
185
|
Send an initial stream header
|
|
178
|
186
|
"""
|
|
179
|
187
|
self._owner.Connection.sendqueue = []
|
|
180
|
|
- self.Stream = simplexml.NodeBuilder()
|
|
|
188
|
+ self.Stream = NodeBuilder()
|
|
181
|
189
|
self.Stream.dispatch = self.dispatch
|
|
182
|
190
|
self.Stream._dispatch_depth = 2
|
|
183
|
191
|
self.Stream.stream_header_received = self._check_stream_start
|
| ... |
... |
@@ -258,7 +266,7 @@ class XMPPDispatcher(PlugIn): |
|
258
|
266
|
self.RegisterProtocol('unknown', Protocol, xmlns=xmlns)
|
|
259
|
267
|
self.RegisterProtocol('default', Protocol, xmlns=xmlns)
|
|
260
|
268
|
|
|
261
|
|
- def RegisterProtocol(self, tag_name, Proto, xmlns=None, order='info'):
|
|
|
269
|
+ def RegisterProtocol(self, tag_name, proto, xmlns=None, order='info'):
|
|
262
|
270
|
"""
|
|
263
|
271
|
Used to declare some top-level stanza name to dispatcher
|
|
264
|
272
|
|
| ... |
... |
@@ -266,20 +274,18 @@ class XMPPDispatcher(PlugIn): |
|
266
|
274
|
presence protocols are registered by default.
|
|
267
|
275
|
"""
|
|
268
|
276
|
if not xmlns:
|
|
269
|
|
- xmlns=self._owner.defaultNamespace
|
|
270
|
|
- log.debug('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns))
|
|
271
|
|
- self.handlers[xmlns][tag_name] = {type:Proto, 'default':[]}
|
|
|
277
|
+ xmlns = self._owner.defaultNamespace
|
|
|
278
|
+ log.debug('Registering protocol "%s" as %s(%s)', tag_name, proto, xmlns)
|
|
|
279
|
+ self.handlers[xmlns][tag_name] = {'type': proto, 'default': []}
|
|
272
|
280
|
|
|
273
|
|
- def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='',
|
|
274
|
|
- makefirst=0, system=0):
|
|
|
281
|
+ def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='', system=0):
|
|
275
|
282
|
"""
|
|
276
|
283
|
Register handler for processing all stanzas for specified namespace
|
|
277
|
284
|
"""
|
|
278
|
|
- self.RegisterHandler('default', handler, typ, ns, xmlns, makefirst,
|
|
279
|
|
- system)
|
|
|
285
|
+ self.RegisterHandler('default', handler, typ, ns, xmlns, system)
|
|
280
|
286
|
|
|
281
|
287
|
def RegisterHandler(self, name, handler, typ='', ns='', xmlns=None,
|
|
282
|
|
- makefirst=False, system=False):
|
|
|
288
|
+ system=False, priority=50):
|
|
283
|
289
|
"""
|
|
284
|
290
|
Register user callback as stanzas handler of declared type
|
|
285
|
291
|
|
| ... |
... |
@@ -295,40 +301,44 @@ class XMPPDispatcher(PlugIn): |
|
295
|
301
|
value will match
|
|
296
|
302
|
:param ns: namespace of child that stanza must contain.
|
|
297
|
303
|
:param xmlns: xml namespace
|
|
298
|
|
- :param makefirst: insert handler in the beginning of handlers list instead
|
|
299
|
|
- of adding it to the end. Note that more common handlers i.e. w/o "typ"
|
|
300
|
|
- and " will be called first nevertheless.
|
|
301
|
304
|
:param system: call handler even if NodeProcessed Exception were raised
|
|
302
|
|
- already.
|
|
|
305
|
+ already.
|
|
|
306
|
+ :param priority: The priority of the handler, higher get called later
|
|
303
|
307
|
"""
|
|
304
|
308
|
if not xmlns:
|
|
305
|
|
- xmlns=self._owner.defaultNamespace
|
|
306
|
|
- log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' %
|
|
307
|
|
- (handler, name, typ, ns, xmlns))
|
|
|
309
|
+ xmlns = self._owner.defaultNamespace
|
|
|
310
|
+
|
|
308
|
311
|
if not typ and not ns:
|
|
309
|
|
- typ='default'
|
|
|
312
|
+ typ = 'default'
|
|
|
313
|
+
|
|
|
314
|
+ log.debug(
|
|
|
315
|
+ 'Registering handler %s for "%s" type->%s ns->%s(%s) priority->%s',
|
|
|
316
|
+ handler, name, typ, ns, xmlns, priority)
|
|
|
317
|
+
|
|
310
|
318
|
if xmlns not in self.handlers:
|
|
311
|
319
|
self.RegisterNamespace(xmlns, 'warn')
|
|
312
|
320
|
if name not in self.handlers[xmlns]:
|
|
313
|
321
|
self.RegisterProtocol(name, Protocol, xmlns, 'warn')
|
|
314
|
|
- if typ+ns not in self.handlers[xmlns][name]:
|
|
315
|
|
- self.handlers[xmlns][name][typ+ns]=[]
|
|
316
|
|
- if makefirst:
|
|
317
|
|
- self.handlers[xmlns][name][typ+ns].insert(0, {'func':handler,
|
|
318
|
|
- 'system':system})
|
|
319
|
|
- else:
|
|
320
|
|
- self.handlers[xmlns][name][typ+ns].append({'func':handler,
|
|
321
|
|
- 'system':system})
|
|
|
322
|
+
|
|
|
323
|
+ specific = typ + ns
|
|
|
324
|
+ if specific not in self.handlers[xmlns][name]:
|
|
|
325
|
+ self.handlers[xmlns][name][specific] = []
|
|
|
326
|
+
|
|
|
327
|
+ self.handlers[xmlns][name][specific].append(
|
|
|
328
|
+ {'func': handler,
|
|
|
329
|
+ 'system': system,
|
|
|
330
|
+ 'priority': priority,
|
|
|
331
|
+ 'specific': specific})
|
|
322
|
332
|
|
|
323
|
333
|
def RegisterHandlerOnce(self, name, handler, typ='', ns='', xmlns=None,
|
|
324
|
|
- makefirst=0, system=0):
|
|
|
334
|
+ system=0):
|
|
325
|
335
|
"""
|
|
326
|
336
|
Unregister handler after first call (not implemented yet)
|
|
327
|
337
|
"""
|
|
328
|
338
|
# FIXME Drop or implement
|
|
329
|
339
|
if not xmlns:
|
|
330
|
340
|
xmlns = self._owner.defaultNamespace
|
|
331
|
|
- self.RegisterHandler(name, handler, typ, ns, xmlns, makefirst, system)
|
|
|
341
|
+ self.RegisterHandler(name, handler, typ, ns, xmlns, system)
|
|
332
|
342
|
|
|
333
|
343
|
def UnregisterHandler(self, name, handler, typ='', ns='', xmlns=None):
|
|
334
|
344
|
"""
|
| ... |
... |
@@ -338,18 +348,26 @@ class XMPPDispatcher(PlugIn): |
|
338
|
348
|
if not xmlns:
|
|
339
|
349
|
xmlns = self._owner.defaultNamespace
|
|
340
|
350
|
if not typ and not ns:
|
|
341
|
|
- typ='default'
|
|
|
351
|
+ typ = 'default'
|
|
342
|
352
|
if xmlns not in self.handlers:
|
|
343
|
353
|
return
|
|
344
|
354
|
if name not in self.handlers[xmlns]:
|
|
345
|
355
|
return
|
|
346
|
|
- if typ+ns not in self.handlers[xmlns][name]:
|
|
|
356
|
+
|
|
|
357
|
+ specific = typ + ns
|
|
|
358
|
+ if specific not in self.handlers[xmlns][name]:
|
|
347
|
359
|
return
|
|
348
|
|
- for pack in self.handlers[xmlns][name][typ+ns]:
|
|
349
|
|
- if pack['func'] == handler:
|
|
|
360
|
+ for handler_dict in self.handlers[xmlns][name][specific]:
|
|
|
361
|
+ if handler_dict['func'] == handler:
|
|
350
|
362
|
try:
|
|
351
|
|
- self.handlers[xmlns][name][typ+ns].remove(pack)
|
|
|
363
|
+ self.handlers[xmlns][name][specific].remove(handler_dict)
|
|
|
364
|
+ log.debug(
|
|
|
365
|
+ 'Unregister handler %s for "%s" type->%s ns->%s(%s)',
|
|
|
366
|
+ handler, name, typ, ns, xmlns)
|
|
352
|
367
|
except ValueError:
|
|
|
368
|
+ log.warning(
|
|
|
369
|
+ 'Unregister failed: %s for "%s" type->%s ns->%s(%s)',
|
|
|
370
|
+ handler, name, typ, ns, xmlns)
|
|
353
|
371
|
pass
|
|
354
|
372
|
|
|
355
|
373
|
def RegisterDefaultHandler(self, handler):
|
| ... |
... |
@@ -401,16 +419,11 @@ class XMPPDispatcher(PlugIn): |
|
401
|
419
|
else:
|
|
402
|
420
|
log.warning('Received unhandled event: %s' % event)
|
|
403
|
421
|
|
|
404
|
|
- def dispatch(self, stanza, session=None, direct=0):
|
|
|
422
|
+ def dispatch(self, stanza, session=None):
|
|
405
|
423
|
"""
|
|
406
|
424
|
Main procedure that performs XMPP stanza recognition and calling
|
|
407
|
425
|
apppropriate handlers for it. Called by simplexml
|
|
408
|
426
|
"""
|
|
409
|
|
- # FIXME: Where do we set session and direct. Why? What are those intended
|
|
410
|
|
- # to do?
|
|
411
|
|
-
|
|
412
|
|
- #log.info('dispatch called: stanza = %s, session = %s, direct= %s'
|
|
413
|
|
- # % (stanza, session, direct))
|
|
414
|
427
|
|
|
415
|
428
|
self.Event('', 'STANZA RECEIVED', stanza)
|
|
416
|
429
|
|
| ... |
... |
@@ -428,8 +441,6 @@ class XMPPDispatcher(PlugIn): |
|
428
|
441
|
|
|
429
|
442
|
xmlns = stanza.getNamespace()
|
|
430
|
443
|
|
|
431
|
|
- # log.info('in dispatch, getting ns for %s, and the ns is %s'
|
|
432
|
|
- # % (stanza, xmlns))
|
|
433
|
444
|
if xmlns not in self.handlers:
|
|
434
|
445
|
log.warning('Unknown namespace: %s', xmlns)
|
|
435
|
446
|
xmlns = 'unknown'
|
| ... |
... |
@@ -438,14 +449,13 @@ class XMPPDispatcher(PlugIn): |
|
438
|
449
|
if name not in ('features', 'stream'):
|
|
439
|
450
|
log.warning('Unknown stanza: %s', stanza)
|
|
440
|
451
|
else:
|
|
441
|
|
- log.debug('Got %s/%s stanza' % (xmlns, name))
|
|
|
452
|
+ log.debug('Got %s / %s stanza' % (xmlns, name))
|
|
442
|
453
|
name = 'unknown'
|
|
443
|
454
|
else:
|
|
444
|
|
- log.debug('Got %s/%s stanza' % (xmlns, name))
|
|
|
455
|
+ log.debug('Got %s / %s stanza' % (xmlns, name))
|
|
445
|
456
|
|
|
446
|
|
- if stanza.__class__.__name__ == 'Node':
|
|
447
|
|
- # FIXME: this cannot work
|
|
448
|
|
- stanza = self.handlers[xmlns][name][type](node=stanza)
|
|
|
457
|
+ # Convert simplexml to Protocol object
|
|
|
458
|
+ stanza = self.handlers[xmlns][name]['type'](node=stanza)
|
|
449
|
459
|
|
|
450
|
460
|
typ = stanza.getType()
|
|
451
|
461
|
if name == 'message' and not typ:
|
| ... |
... |
@@ -461,48 +471,55 @@ class XMPPDispatcher(PlugIn): |
|
461
|
471
|
if hasattr(self._owner, 'Smacks'):
|
|
462
|
472
|
self._owner.Smacks.count_incoming(stanza.getName())
|
|
463
|
473
|
|
|
464
|
|
- list_ = ['default'] # we will use all handlers:
|
|
465
|
|
- if typ in self.handlers[xmlns][name]:
|
|
466
|
|
- list_.append(typ) # from very common...
|
|
467
|
|
- for prop in stanza.props:
|
|
468
|
|
- if prop in self.handlers[xmlns][name]:
|
|
469
|
|
- list_.append(prop)
|
|
470
|
|
- if typ and typ+prop in self.handlers[xmlns][name]:
|
|
471
|
|
- list_.append(typ+prop) # ...to very particular
|
|
472
|
|
-
|
|
473
|
|
- chain = self.handlers[xmlns]['default']['default']
|
|
474
|
|
- for key in list_:
|
|
475
|
|
- if key:
|
|
476
|
|
- chain = chain + self.handlers[xmlns][name][key]
|
|
477
|
|
- log.debug('Chain: %s', [key for key in list_ if key])
|
|
478
|
|
-
|
|
|
474
|
+ processed = False
|
|
479
|
475
|
if ID in session._expected:
|
|
480
|
|
- user = 0
|
|
481
|
476
|
if isinstance(session._expected[ID], tuple):
|
|
482
|
477
|
cb, args = session._expected[ID]
|
|
483
|
|
- log.debug("Expected stanza arrived. Callback %s(%s) found!" %
|
|
484
|
|
- (cb, args))
|
|
|
478
|
+ log.debug('Expected stanza arrived. Callback %s(%s) found',
|
|
|
479
|
+ cb, args)
|
|
485
|
480
|
try:
|
|
486
|
481
|
cb(session,stanza,**args)
|
|
487
|
|
- except Exception as typ:
|
|
488
|
|
- if typ.__class__.__name__ != 'NodeProcessed':
|
|
489
|
|
- raise
|
|
|
482
|
+ except NodeProcessed:
|
|
|
483
|
+ pass
|
|
|
484
|
+ except Exception:
|
|
|
485
|
+ raise
|
|
490
|
486
|
else:
|
|
491
|
|
- log.debug("Expected stanza arrived!")
|
|
|
487
|
+ log.debug('Expected stanza arrived')
|
|
492
|
488
|
session._expected[ID] = stanza
|
|
493
|
|
- else:
|
|
494
|
|
- user = 1
|
|
|
489
|
+ processed = True
|
|
|
490
|
+
|
|
|
491
|
+ # Gather specifics depending on stanza properties
|
|
|
492
|
+ specifics = ['default']
|
|
|
493
|
+ if typ and typ in self.handlers[xmlns][name]:
|
|
|
494
|
+ specifics.append(typ)
|
|
|
495
|
+ for prop in stanza.props:
|
|
|
496
|
+ if prop in self.handlers[xmlns][name]:
|
|
|
497
|
+ specifics.append(prop)
|
|
|
498
|
+ if typ and typ + prop in self.handlers[xmlns][name]:
|
|
|
499
|
+ specifics.append(typ + prop)
|
|
|
500
|
+
|
|
|
501
|
+ # Create the handler chain
|
|
|
502
|
+ chain = []
|
|
|
503
|
+ chain += self.handlers[xmlns]['default']['default']
|
|
|
504
|
+ for specific in specifics:
|
|
|
505
|
+ chain += self.handlers[xmlns][name][specific]
|
|
|
506
|
+
|
|
|
507
|
+ # Sort chain with priority
|
|
|
508
|
+ chain.sort(key=lambda x: x['priority'])
|
|
|
509
|
+
|
|
495
|
510
|
for handler in chain:
|
|
496
|
|
- if user or handler['system']:
|
|
|
511
|
+ if not processed or handler['system']:
|
|
497
|
512
|
try:
|
|
498
|
|
- log.debug('Call handler: %s', handler['func'])
|
|
|
513
|
+ log.info('Call handler: %s', handler['func'].__qualname__)
|
|
499
|
514
|
handler['func'](session, stanza)
|
|
500
|
|
- except Exception as typ:
|
|
501
|
|
- if typ.__class__.__name__ != 'NodeProcessed':
|
|
502
|
|
- self._pendingExceptions.insert(0, sys.exc_info())
|
|
503
|
|
- return
|
|
504
|
|
- user=0
|
|
505
|
|
- if user and self._defaultHandler:
|
|
|
515
|
+ except NodeProcessed:
|
|
|
516
|
+ processed = True
|
|
|
517
|
+ except Exception:
|
|
|
518
|
+ self._pendingExceptions.insert(0, sys.exc_info())
|
|
|
519
|
+ return
|
|
|
520
|
+
|
|
|
521
|
+ # Stanza was not processed call default handler
|
|
|
522
|
+ if not processed and self._defaultHandler:
|
|
506
|
523
|
self._defaultHandler(session, stanza)
|
|
507
|
524
|
|
|
508
|
525
|
def _WaitForData(self, data):
|
| ... |
... |
@@ -592,7 +609,7 @@ class BOSHDispatcher(XMPPDispatcher): |
|
592
|
609
|
"""
|
|
593
|
610
|
Send an initial stream header
|
|
594
|
611
|
"""
|
|
595
|
|
- self.Stream = simplexml.NodeBuilder()
|
|
|
612
|
+ self.Stream = NodeBuilder()
|
|
596
|
613
|
self.Stream.dispatch = self.dispatch
|
|
597
|
614
|
self.Stream._dispatch_depth = 2
|
|
598
|
615
|
self.Stream.stream_header_received = self._check_stream_start
|
| ... |
... |
@@ -625,7 +642,7 @@ class BOSHDispatcher(XMPPDispatcher): |
|
625
|
642
|
self.restart = False
|
|
626
|
643
|
return XMPPDispatcher.ProcessNonBlocking(self, data)
|
|
627
|
644
|
|
|
628
|
|
- def dispatch(self, stanza, session=None, direct=0):
|
|
|
645
|
+ def dispatch(self, stanza, session=None):
|
|
629
|
646
|
if stanza.getName() == 'body' and stanza.getNamespace() == NS_HTTP_BIND:
|
|
630
|
647
|
|
|
631
|
648
|
stanza_attrs = stanza.getAttrs()
|
| ... |
... |
@@ -643,6 +660,6 @@ class BOSHDispatcher(XMPPDispatcher): |
|
643
|
660
|
# rewrite it to jabber:client
|
|
644
|
661
|
if child.getNamespace() == NS_HTTP_BIND:
|
|
645
|
662
|
child.setNamespace(self._owner.defaultNamespace)
|
|
646
|
|
- XMPPDispatcher.dispatch(self, child, session, direct)
|
|
|
663
|
+ XMPPDispatcher.dispatch(self, child, session)
|
|
647
|
664
|
else:
|
|
648
|
|
- XMPPDispatcher.dispatch(self, stanza, session, direct)
|
|
|
665
|
+ XMPPDispatcher.dispatch(self, stanza, session)
|