source: ralphm-patches/roster_server.patch @ 73:f574beee3bca

Last change on this file since 73:f574beee3bca was 73:f574beee3bca, checked in by Ralph Meijer <ralphm@…>, 7 years ago

Minor cleanups, improved error message handling, and upstreamed patch.

  • Upstreamed deprecation of prepareIDNName.
  • MessageProtocol? now properly handles error message stanzas.
  • Renamed methods for RosterServerProtocol?.
  • Prefer connectionInitialized for setting up stanza observers.
File size: 7.1 KB
  • wokkel/test/test_xmppim.py

    Add server side support for the roster protocol.
    
     * Implements roster get by calling `getRoster` and using the returned
       `Roster` to send back the roster.
    
    TODO:
     * Add support for roster sets?
    
    diff --git a/wokkel/test/test_xmppim.py b/wokkel/test/test_xmppim.py
    a b  
    13391339
    13401340
    13411341
     1342class RosterServerProtocolTest(unittest.TestCase, TestableRequestHandlerMixin):
     1343    """
     1344    Tests for L{xmppim.RosterServerProtocol}.
     1345    """
     1346
     1347    def setUp(self):
     1348        self.stub = XmlStreamStub()
     1349        self.service = xmppim.RosterServerProtocol()
     1350        self.service.makeConnection(self.stub.xmlstream)
     1351        self.service.connectionInitialized()
     1352
     1353
     1354    def test_getRosterReceived(self):
     1355        """
     1356        A roster get request should trigger getRoster with the request.
     1357        """
     1358        xml = """
     1359          <iq type='get' from='user@example.org/Home'>
     1360            <query xmlns='jabber:iq:roster'/>
     1361          </iq>
     1362        """
     1363
     1364        def getRosterReceived(request):
     1365            self.assertEqual(JID('user@example.org/Home'), request.sender)
     1366            item = xmppim.RosterItem(JID('other@example.org'), True, False,
     1367                                 'The User')
     1368            roster = xmppim.Roster({item.entity: item})
     1369            return defer.succeed(roster)
     1370
     1371        def cb(element):
     1372            self.assertEquals('query', element.name)
     1373            self.assertEquals(NS_ROSTER, element.uri)
     1374            itemElements = list(domish.generateElementsQNamed(
     1375                element.children, 'item', NS_ROSTER))
     1376            self.assertEqual(1, len(itemElements))
     1377            item = itemElements[0]
     1378            self.assertEquals(u'other@example.org', item.getAttribute('jid'))
     1379            self.assertEquals(u'to', item.getAttribute('subscription'))
     1380            self.assertEquals(u'The User', item.getAttribute('name'))
     1381
     1382        self.service.getRosterReceived = getRosterReceived
     1383        d = self.handleRequest(xml)
     1384        d.addCallback(cb)
     1385        return d
     1386
     1387
     1388    def test_getRosterReceivedNotOverridden(self):
     1389        """
     1390        If getRoster is not overridden, return feature-not-implemented.
     1391        """
     1392        xml = """
     1393          <iq type='get' from='user@example.org/Home'>
     1394            <query xmlns='jabber:iq:roster'/>
     1395          </iq>
     1396        """
     1397
     1398        d = self.handleRequest(xml)
     1399        self.assertFailure(d, NotImplementedError)
     1400        return d
     1401
     1402
     1403    def test_setRosterReceived(self):
     1404        """
     1405        Roster set is not yet supported.
     1406        """
     1407        xml = """
     1408          <iq type='set' from='user@example.org/Home'>
     1409            <query xmlns='jabber:iq:roster'>
     1410              <item jid='other@example.org' name='Other User'/>
     1411            </query>
     1412          </iq>
     1413        """
     1414
     1415        d = self.handleRequest(xml)
     1416        self.assertFailure(d, NotImplementedError)
     1417        return d
     1418
     1419
     1420
    13421421class MessageTest(unittest.TestCase):
    13431422    """
    13441423    Tests for L{xmppim.Message}.
  • wokkel/xmppim.py

    diff --git a/wokkel/xmppim.py b/wokkel/xmppim.py
    a b  
    2626NS_ROSTER = 'jabber:iq:roster'
    2727
    2828XPATH_ROSTER_SET = "/iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
     29XPATH_ROSTER_GET = "/iq[@type='get']/query[@xmlns='%s']" % NS_ROSTER
    2930
    3031
    3132
     
    739740
    740741
    741742    @classmethod
    742     def fromElement(Class, element):
     743    def fromElement(cls, element):
    743744        entity = JID(element['jid'])
    744         item = Class(entity)
     745        item = cls(entity)
    745746        subscription = element.getAttribute('subscription')
    746747        if subscription == 'remove':
    747748            item.remove = True
     
    780781    version = None
    781782    rosterSet = False
    782783
     784
    783785    def parseRequest(self, element):
    784         self.version = element.getAttribute('ver')
    785 
    786         for child in element.elements(NS_ROSTER, 'item'):
    787             self.item = RosterItem.fromElement(child)
     786        roster = Roster.fromElement(element)
     787        self.version = roster.version
     788        for item in roster.itervalues():
     789            self.item = item
    788790            break
     791        return roster
    789792
    790793
    791794    def toElement(self):
    792795        element = Request.toElement(self)
    793         query = element.addElement((NS_ROSTER, 'query'))
    794         if self.version is not None:
    795             query['ver'] = self.version
     796
     797        roster = Roster()
     798        roster.version = self.version
    796799        if self.item:
    797             query.addChild(self.item.toElement(rosterSet=self.rosterSet))
     800            roster[self.item.entity] = self.item
     801
     802        element.addChild(roster.toElement(rosterSet=self.rosterSet))
     803
    798804        return element
    799805
    800806
     
    812818
    813819class Roster(dict):
    814820    """
    815     In-memory roster container.
     821    Roster container.
    816822
    817823    This provides a roster as a mapping from L{JID} to L{RosterItem}. If
    818824    roster versioning is used, the C{version} attribute holds the version
     
    825831    version = None
    826832
    827833
     834    @classmethod
     835    def fromElement(cls, element):
     836        roster = cls()
     837        roster.version = element.getAttribute('ver')
     838        for element in element.elements(NS_ROSTER, 'item'):
     839            item = RosterItem.fromElement(element)
     840            roster[item.entity] = item
     841        return roster
     842
     843
     844    def toElement(self, rosterSet=False):
     845        element = domish.Element((NS_ROSTER, 'query'))
     846
     847        if self.version:
     848            element['ver'] = self.version
     849
     850        for item in self.itervalues():
     851            element.addChild(item.toElement(rosterSet))
     852
     853        return element
     854
     855
    828856
    829857class RosterClientProtocol(XMPPHandler, IQHandlerMixin):
    830858    """
     
    898926
    899927        def processRoster(result):
    900928            if result.query is not None:
    901                 roster = Roster()
    902                 roster.version = result.query.getAttribute('ver')
    903                 for element in result.query.elements(NS_ROSTER, 'item'):
    904                     item = RosterItem.fromElement(element)
    905                     roster[item.entity] = item
    906                 return roster
     929                return Roster.fromElement(result.query)
    907930            else:
    908931                return None
    909932
     
    10021025
    10031026
    10041027
     1028class RosterServerProtocol(XMPPHandler, IQHandlerMixin):
     1029    """
     1030    XMPP subprotocol handler for the roster, server side.
     1031    """
     1032
     1033    iqHandlers = {
     1034            XPATH_ROSTER_GET: '_onRosterGet',
     1035            # XPATH_ROSTER_SET: '_onRosterSet',
     1036            }
     1037
     1038    def connectionInitialized(self):
     1039        self.xmlstream.addObserver(XPATH_ROSTER_GET, self.handleRequest)
     1040        self.xmlstream.addObserver(XPATH_ROSTER_SET, self.handleRequest)
     1041
     1042
     1043    def _onRosterGet(self, iq):
     1044        request = RosterRequest.fromElement(iq)
     1045
     1046        def toResponse(roster):
     1047            return roster.toElement()
     1048
     1049        d = self.getRosterReceived(request)
     1050        d.addCallback(toResponse)
     1051        return d
     1052
     1053
     1054    def getRosterReceived(self, request):
     1055        """
     1056        Called when the roster is requested.
     1057
     1058        @returns: Deferred that fires with a L{Roster}.
     1059        @rtype: L{defer.Deferred}
     1060        """
     1061        raise NotImplementedError()
     1062
     1063
     1064
    10051065class Message(Stanza):
    10061066    """
    10071067    A message stanza.
Note: See TracBrowser for help on using the repository browser.