source:
ralphm-patches/session_manager.patch
@
70:cc8ecefb5519
Last change on this file since 70:cc8ecefb5519 was 70:cc8ecefb5519, checked in by Ralph Meijer <ralphm@…>, 10 years ago | |
---|---|
File size: 7.4 KB |
-
wokkel/client.py
# HG changeset patch # Parent bc450d2e7ed710c5605545e39bb6a054c368571f diff --git a/wokkel/client.py b/wokkel/client.py
a b 12 12 13 13 import base64 14 14 15 from zope.interface import implements 16 15 17 from twisted.application import service 16 from twisted.cred import credentials, error as ecred 18 from twisted.cred import credentials, error as ecred, portal 17 19 from twisted.internet import defer, reactor 18 from twisted.python import log 20 from twisted.python import log, randbytes 19 21 from twisted.names.srvconnect import SRVConnector 20 22 from twisted.words.protocols.jabber import client, error, sasl, xmlstream 21 from twisted.words.xish import domish 23 from twisted.words.protocols.jabber.jid import JID, internJID 24 from twisted.words.xish import domish, utility 22 25 23 26 from wokkel import generic 24 27 from wokkel.compat import XmlStreamServerFactory 25 28 from wokkel.iwokkel import IUserSession 26 29 from wokkel.subprotocols import ServerStreamManager 27 30 from wokkel.subprotocols import StreamManager 31 from wokkel.subprotocols import XMPPHandler 28 32 29 33 NS_CLIENT = 'jabber:client' 30 34 … … 482 486 return [ 483 487 generic.StanzaForwarder() 484 488 ] 489 490 491 492 class UserSession(object): 493 494 implements(IUserSession) 495 496 realm = None 497 mind = None 498 499 connected = False 500 interested = False 501 presence = None 502 503 clientStream = None 504 505 def __init__(self, entity): 506 self.entity = entity 507 508 509 def loggedIn(self, realm, mind): 510 self.realm = realm 511 self.mind = mind 512 513 514 def bindResource(self, resource): 515 def cb(entity): 516 self.entity = entity 517 self.connected = True 518 return entity 519 520 d = self.realm.bindResource(self, resource) 521 d.addCallback(cb) 522 return d 523 524 525 def logout(self): 526 self.connected = False 527 self.realm.unbindResource(self) 528 529 530 def send(self, element): 531 self.realm.onElement(element, self) 532 533 534 def receive(self, element): 535 self.mind.send(element) 536 537 538 539 class SessionManager(XMPPHandler): 540 """ 541 Session Manager. 542 543 @ivar xmlstream: XML Stream to inject incoming stanzas from client 544 connections into. Stanzas where the C{'to'} attribute is not set 545 or is directed at the local domain are injected as if received on 546 the XML Stream (using C{dispatch}), other stanzas are injected as if 547 they were sent from the XML Stream (using C{send}). 548 """ 549 550 implements(portal.IRealm) 551 552 def __init__(self, domain, accounts): 553 XMPPHandler.__init__(self) 554 self.domain = domain 555 self.accounts = accounts 556 557 self.sessions = {} 558 self.clientStream = utility.EventDispatcher() 559 self.clientStream.addObserver('/*', self.routeOrDeliver, -1) 560 561 562 def requestAvatar(self, avatarId, mind, *interfaces): 563 if IUserSession not in interfaces: 564 raise NotImplementedError(self, interfaces) 565 566 localpart = avatarId.decode('utf-8') 567 entity = JID(tuple=(localpart, self.domain, None)) 568 session = UserSession(entity) 569 session.loggedIn(self, mind) 570 return IUserSession, session, session.logout 571 572 573 def bindResource(self, session, resource): 574 localpart = session.entity.user 575 576 try: 577 userSessions = self.sessions[localpart] 578 except KeyError: 579 userSessions = self.sessions[localpart] = {} 580 581 if resource is None: 582 resource = randbytes.secureRandom(8).encode('hex') 583 elif resource in self.userSessions: 584 resource = resource + ' ' + randbytes.secureRandom(8).encode('hex') 585 586 entity = JID(tuple=(session.entity.user, session.entity.host, resource)) 587 userSessions[resource] = session 588 589 return defer.succeed(entity) 590 591 592 def lookupSessions(self, entity): 593 """ 594 Return all sessions for a user. 595 596 @param entity: Entity to retrieve sessions for. This the resource part 597 will be ignored. 598 @type entity: L{JID<twisted.words.protocols.jabber.jid.JID>} 599 600 @return: Mapping of sessions keyed by resource. 601 @rtype: C{dict} 602 """ 603 localpart = entity.user 604 605 try: 606 return self.sessions[localpart] 607 except: 608 return {} 609 610 611 def lookupSession(self, entity): 612 """ 613 Return the session for a particular resource of an entity. 614 615 @param entity: Entity to retrieve sessions for. 616 @type entity: L{JID<twisted.words.protocols.jabber.jid.JID>} 617 618 @return: C{UserSession}. 619 """ 620 621 userSessions = self.lookupSessions(entity) 622 return userSessions[entity.resource] 623 624 625 626 def unbindResource(self, session, reason=None): 627 session.connected = False 628 629 localpart = session.entity.user 630 resource = session.entity.resource 631 632 del self.sessions[localpart][resource] 633 if not self.sessions[localpart]: 634 del self.sessions[localpart] 635 636 return defer.succeed(None) 637 638 639 def onElement(self, element, session): 640 # Make sure each stanza has a sender address 641 if (element.name == 'presence' and 642 element.getAttribute('type') in ('subscribe', 'subscribed', 643 'unsubscribe', 'unsubscribed')): 644 element['from'] = session.entity.userhost() 645 else: 646 element['from'] = session.entity.full() 647 648 self.clientStream.dispatch(element) 649 650 651 def routeOrDeliver(self, element): 652 """ 653 Deliver a stanza locally or pass on for routing. 654 """ 655 if element.handled: 656 return 657 658 if (not element.hasAttribute('to') or 659 internJID(element['to']).host == self.domain): 660 # This stanza is for local delivery 661 log.msg("Delivering locally: %r" % element.toXml()) 662 self.xmlstream.dispatch(element) 663 else: 664 # This stanza is for remote routing 665 log.msg("Routing remotely: %r" % element.toXml()) 666 XMPPHandler.send(self, element) 667 668 669 def deliverStanza(self, element, recipient): 670 session = self.lookupSession(recipient) 671 session.receive(element) -
wokkel/test/test_client.py
diff --git a/wokkel/test/test_client.py b/wokkel/test/test_client.py
a b 7 7 8 8 from base64 import b64encode 9 9 10 from zope.interface import implements 10 from zope.interface import implements, verify 11 11 12 12 from twisted.cred.portal import IRealm, Portal 13 13 from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse … … 601 601 602 602 603 603 604 604 605 class XMPPClientListenAuthenticatorTest(unittest.TestCase): 605 606 """ 606 607 Tests for L{client.XMPPClientListenAuthenticator}. … … 670 671 "to='example.com' " 671 672 "version='1.0'>") 672 673 self.xmlstream.assertStreamError(self, condition='host-unknown') 674 675 676 677 class UserSessionTest(unittest.TestCase): 678 679 def setUp(self): 680 self.session = client.UserSession(JID('user@example.org')) 681 682 683 def test_interface(self): 684 verify.verifyObject(client.IUserSession, self.session) 685 686 687 688 class SessionManagerTest(unittest.TestCase): 689 690 def setUp(self): 691 accounts = {'user': None} 692 self.sessionManager = client.SessionManager('example.org', accounts) 693 694 695 def test_interface(self): 696 verify.verifyObject(IRealm, self.sessionManager)
Note: See TracBrowser
for help on using the repository browser.