Changeset 49:537d1413b661 in ralphm-patches


Ignore:
Timestamp:
May 25, 2011, 9:50:21 AM (10 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Message:

Save work after moving stuff to keep from wokkel.xmppim to wokkel.im.

Files:
4 added
4 edited

Legend:

Unmodified
Added
Removed
  • roster_item.patch

    r36 r49  
    1 diff -r c9d1bf0b783d wokkel/test/test_xmppim.py
    2 --- a/wokkel/test/test_xmppim.py        Sun Jan 10 15:01:41 2010 +0100
    3 +++ b/wokkel/test/test_xmppim.py        Fri Feb 12 19:49:36 2010 +0100
    4 @@ -449,6 +449,96 @@
    5  
    6  
    7  
    8 +class RosterItemTest(unittest.TestCase):
    9 +    """
    10 +    Tests for L{xmppim.RosterItem}.
    11 +    """
    12 +
    13 +    def test_toElement(self):
    14 +        item = xmppim.RosterItem(JID('user@example.org'))
    15 +        element = item.toElement()
    16 +        self.assertEquals('item', element.name)
    17 +        self.assertEquals(NS_ROSTER, element.uri)
    18 +        self.assertTrue(element.hasAttribute('subscription'))
    19 +        self.assertFalse(element.hasAttribute('ask'))
    20 +        self.assertFalse(element.hasAttribute('name'))
    21 +        self.assertEquals(0, len(list(element.elements())))
    22 +
    23 +
    24 +    def test_toElementJID(self):
    25 +        item = xmppim.RosterItem(JID('user@example.org'))
    26 +        element = item.toElement()
    27 +        self.assertEquals(u'user@example.org', element.getAttribute('jid'))
    28 +
    29 +
    30 +    def test_toElementSubscriptionNone(self):
    31 +        item = xmppim.RosterItem(JID('user@example.org'),
    32 +                                 subscriptionTo=False,
    33 +                                 subscriptionFrom=False)
    34 +        element = item.toElement()
    35 +        self.assertEquals('none', element.getAttribute('subscription'))
    36 +
    37 +
    38 +    def test_toElementSubscriptionTo(self):
    39 +        item = xmppim.RosterItem(JID('user@example.org'),
    40 +                                 subscriptionTo=True,
    41 +                                 subscriptionFrom=False)
    42 +        element = item.toElement()
    43 +        self.assertEquals('to', element.getAttribute('subscription'))
    44 +
    45 +
    46 +    def test_toElementSubscriptionFrom(self):
    47 +        item = xmppim.RosterItem(JID('user@example.org'),
    48 +                                 subscriptionTo=False,
    49 +                                 subscriptionFrom=True)
    50 +        element = item.toElement()
    51 +        self.assertEquals('from', element.getAttribute('subscription'))
    52 +
    53 +
    54 +    def test_toElementSubscriptionBoth(self):
    55 +        item = xmppim.RosterItem(JID('user@example.org'),
    56 +                                 subscriptionTo=True,
    57 +                                 subscriptionFrom=True)
    58 +        element = item.toElement()
    59 +        self.assertEquals('both', element.getAttribute('subscription'))
    60 +
    61 +
    62 +    def test_toElementRemove(self):
    63 +        item = xmppim.RosterItem(JID('user@example.org'),
    64 +                                 remove=True)
    65 +        element = item.toElement()
    66 +        self.assertEquals('remove', element.getAttribute('subscription'))
    67 +
    68 +
    69 +    def test_toElementAsk(self):
    70 +        item = xmppim.RosterItem(JID('user@example.org'),
    71 +                                 ask=True)
    72 +        element = item.toElement()
    73 +        self.assertEquals('subscribe', element.getAttribute('ask'))
    74 +
    75 +
    76 +    def test_toElementName(self):
    77 +        item = xmppim.RosterItem(JID('user@example.org'),
    78 +                                 name='Joe User')
    79 +        element = item.toElement()
    80 +        self.assertEquals(u'Joe User', element.getAttribute('name'))
    81 +
    82 +
    83 +    def test_toElementGroups(self):
    84 +        groups = set(['Friends', 'Jabber'])
    85 +        item = xmppim.RosterItem(JID('user@example.org'),
    86 +                                 groups=groups)
    87 +
    88 +        element = item.toElement()
    89 +        foundGroups = set()
    90 +        for child in element.elements():
    91 +            if child.uri == NS_ROSTER and child.name == 'group':
    92 +                foundGroups.add(unicode(child))
    93 +
    94 +        self.assertEqual(groups, foundGroups)
    95 +
    96 +
    97 +
    98  class RosterClientProtocolTest(unittest.TestCase):
     1diff -r c91f18811c37 wokkel/im.py
     2--- a/wokkel/im.py      Mon May 23 18:19:44 2011 +0200
     3+++ b/wokkel/im.py      Wed May 25 09:24:29 2011 +0200
     4@@ -7,21 +7,27 @@
     5 XMPP IM protocol support.
     6 
     7 This module provides generic implementations for the protocols defined in
     8-U{RFC 3921<http://www.xmpp.org/rfcs/rfc3921.html>} (XMPP IM).
     9-
     10-All of it should eventually move to Twisted.
     11+U{RFC 6121<http://www.xmpp.org/rfcs/rfc6121.html>} (XMPP IM).
     12 """
     13 
     14+import warnings
     15+
     16+from twisted.internet import defer
     17 from twisted.words.protocols.jabber.jid import JID, internJID
     18+from twisted.words.protocols.jabber import error
     19 from twisted.words.xish import domish
     20 
     21 from wokkel.compat import IQ
     22 from wokkel.generic import ErrorStanza, Stanza
     23-from wokkel.subprotocols import XMPPHandler
     24+from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
     25 
     26 NS_XML = 'http://www.w3.org/XML/1998/namespace'
     27 NS_ROSTER = 'jabber:iq:roster'
     28 
     29+XPATH_ROSTER_SET = "/iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
     30+
     31+
     32+
     33 class BasePresence(Stanza):
    9934     """
    100      Tests for L{xmppim.RosterClientProtocol}.
    101 diff -r c9d1bf0b783d wokkel/xmppim.py
    102 --- a/wokkel/xmppim.py  Sun Jan 10 15:01:41 2010 +0100
    103 +++ b/wokkel/xmppim.py  Fri Feb 12 19:49:36 2010 +0100
    104 @@ -588,7 +588,7 @@
     35     Stanza of kind presence.
     36@@ -349,8 +355,8 @@
     37 
     38     This represents one contact from an XMPP contact list known as roster.
     39 
     40-    @ivar jid: The JID of the contact.
     41-    @type jid: L{JID}
     42+    @ivar entity: The JID of the contact.
     43+    @type entity: L{JID}
     44     @ivar name: The optional associated nickname for this contact.
     45     @type name: C{unicode}
     46     @ivar subscriptionTo: Subscription state to contact's presence. If C{True},
     47@@ -360,47 +366,126 @@
    10548     @ivar subscriptionFrom: Contact's subscription state. If C{True}, the
    10649                             contact is subscribed to the presence information
    10750                             of the roster owner.
    10851-    @type subscriptionTo: C{bool}
     52-    @ivar ask: Whether subscription is pending.
     53-    @type ask: C{bool}
    10954+    @type subscriptionFrom: C{bool}
    110      @ivar ask: Whether subscription is pending.
    111      @type ask: C{bool}
     55+    @ivar pendingOut: Whether the subscription request to this contact is
     56+        pending.
     57+    @type pendingOut: C{bool}
    11258     @ivar groups: Set of groups this contact is categorized in. Groups are
    113 @@ -596,13 +596,44 @@
     59                   represented by an opaque identifier of type C{unicode}.
    11460     @type groups: C{set}
    11561     """
    11662 
    11763-    def __init__(self, jid):
    118 +    subscriptionStates = {(False, False): 'none',
    119 +                          (True, False): 'to',
    120 +                          (False, True): 'from',
    121 +                          (True, True): 'both'}
    122 +
    123 +    def __init__(self, jid, subscriptionTo=False, subscriptionFrom=False,
    124 +                       ask=None, name=False, groups=None, remove=False):
    125          self.jid = jid
     64-        self.jid = jid
    12665-        self.name = None
    12766-        self.subscriptionTo = False
     
    12968-        self.ask = None
    13069-        self.groups = set()
     70+    __subscriptionStates = {(False, False): None,
     71+                            (True, False): 'to',
     72+                            (False, True): 'from',
     73+                            (True, True): 'both'}
     74 
     75+    def __init__(self, entity, subscriptionTo=False, subscriptionFrom=False,
     76+                       name=None, groups=None):
     77+        self.entity = entity
    13178+        self.subscriptionTo = subscriptionTo
    13279+        self.subscriptionFrom = subscriptionFrom
    133 +        self.ask = ask
    13480+        self.name = name
    13581+        self.groups = groups or set()
    136 +        self.remove = remove
     82 
     83+        self.pendingOut = False
     84+        self.approved = False
     85+        self.remove = False
     86 
     87-class RosterClientProtocol(XMPPHandler):
     88+
     89+    def __getJID(self):
     90+        warnings.warn("Use RosterItem.entity instead.", DeprecationWarning)
     91+        return self.entity
     92+
     93+
     94+    def __setJID(self, value):
     95+        warnings.warn("Use RosterItem.entity instead.", DeprecationWarning)
     96+        self.entity = value
     97+
     98+
     99+    jid = property(__getJID, __setJID, doc="""
     100+            JID of the contact. Deprecated in favour of C{entity}.""")
     101+
     102+
     103+    def __getAsk(self):
     104+        warnings.warn("Use RosterItem.pendingOut instead.", DeprecationWarning)
     105+        return self.pendingOut
     106+
     107+
     108+    def __setAsk(self, value):
     109+        warnings.warn("Use RosterItem.pendingOut instead.", DeprecationWarning)
     110+        self.pendingOut = value
     111+
     112+
     113+    ask = property(__getAsk, __setAsk, doc="""
     114+            Pending out subscription. Deprecated in favour of C{pendingOut}.""")
    137115+
    138116+
    139117+    def toElement(self):
    140118+        element = domish.Element((NS_ROSTER, 'item'))
    141 +        element['jid'] = self.jid.full()
     119+        element['jid'] = self.entity.full()
    142120+
    143121+        if self.remove:
    144122+            subscription = 'remove'
    145123+        else:
    146 +            subscription = self.subscriptionStates[self.subscriptionTo,
    147 +                                                   self.subscriptionFrom]
    148 +        element['subscription'] = subscription
    149 +
    150 +        if self.ask:
    151 +            element['ask'] = 'subscribe'
    152 +        if self.name:
    153 +            element['name'] = self.name
    154 +
    155 +        if self.groups:
    156 +            for group in self.groups:
    157 +                element.addElement('group', content=group)
     124+            subscription = self.__subscriptionStates[self.subscriptionTo,
     125+                                                     self.subscriptionFrom]
     126+
     127+            if self.pendingOut:
     128+                element['ask'] = u'subscribe'
     129+
     130+            if self.name:
     131+                element['name'] = self.name
     132+
     133+            if self.approved:
     134+                element['approved'] = u'true'
     135+
     136+            if self.groups:
     137+                for group in self.groups:
     138+                    element.addElement('group', content=group)
     139+
     140+        if subscription:
     141+            element['subscription'] = subscription
    158142+
    159143+        return element
    160144+
    161  
    162  
    163  class RosterClientProtocol(XMPPHandler):
    164 @@ -662,9 +693,8 @@
     145+
     146+    @classmethod
     147+    def fromElement(Class, element):
     148+        entity = internJID(element['jid'])
     149+        item = Class(entity)
     150+        subscription = element.getAttribute('subscription')
     151+        if subscription == 'remove':
     152+            item.remove = True
     153+        else:
     154+            item.name = element.getAttribute('name')
     155+            item.subscriptionTo = subscription in ('to', 'both')
     156+            item.subscriptionFrom = subscription in ('from', 'both')
     157+            item.pendingOut = element.getAttribute('ask') == 'subscribe'
     158+            item.approved = element.getAttribute('approved') in ('true', '1')
     159+            for subElement in domish.generateElementsQNamed(element.children,
     160+                                                            'group', NS_ROSTER):
     161+                item.groups.add(unicode(subElement))
     162+        return item
     163+
     164+
     165+
     166+class RosterPushIgnored(Exception):
     167+    """
     168+    Raised when this entity doesn't want to accept/trust a roster push.
     169+    """
     170+
     171+
     172+
     173+class RosterClientProtocol(XMPPHandler, IQHandlerMixin):
     174     """
     175     Client side XMPP roster protocol.
     176     """
     177 
     178+    iqHandlers = {XPATH_ROSTER_SET: "_onRosterSet"}
     179+
     180     def connectionInitialized(self):
     181-        ROSTER_SET = "/iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
     182-        self.xmlstream.addObserver(ROSTER_SET, self._onRosterSet)
     183-
     184-
     185-    def _parseRosterItem(self, element):
     186-        jid = internJID(element['jid'])
     187-        item = RosterItem(jid)
     188-        item.name = element.getAttribute('name')
     189-        subscription = element.getAttribute('subscription')
     190-        item.subscriptionTo = subscription in ('to', 'both')
     191-        item.subscriptionFrom = subscription in ('from', 'both')
     192-        item.ask = element.getAttribute('ask') == 'subscribe'
     193-        for subElement in domish.generateElementsQNamed(element.children,
     194-                                                        'group', NS_ROSTER):
     195-            item.groups.add(unicode(subElement))
     196-
     197-        return item
     198+        self.xmlstream.addObserver(XPATH_ROSTER_SET, self.handleRequest)
     199 
     200 
     201     def getRoster(self):
     202@@ -415,8 +500,8 @@
     203             roster = {}
     204             for element in domish.generateElementsQNamed(result.query.children,
     205                                                          'item', NS_ROSTER):
     206-                item = self._parseRosterItem(element)
     207-                roster[item.jid.userhost()] = item
     208+                item = RosterItem.fromElement(element)
     209+                roster[item.entity] = item
     210 
     211             return roster
     212 
     213@@ -437,32 +522,37 @@
    165214         """
    166215         iq = IQ(self.xmlstream, 'set')
     
    169218-        item['jid'] = entity.full()
    170219-        item['subscription'] = 'remove'
    171 +        item = RosterItem(entity, remove=True)
     220+        item = RosterItem(entity)
     221+        item.remove = True
    172222+        iq.query.addChild(item.toElement())
    173223         return iq.send()
    174224 
    175225 
     226     def _onRosterSet(self, iq):
     227-        if iq.handled or \
     228-           iq.hasAttribute('from') and iq['from'] != self.xmlstream:
     229-            return
     230-
     231-        iq.handled = True
     232+        def eb(failure):
     233+            failure.trap(RosterPushIgnored)
     234+            raise error.StanzaError('service-unavailable')
     235 
     236         itemElement = iq.query.item
     237+        item = RosterItem.fromElement(iq.query.item)
     238 
     239-        if unicode(itemElement['subscription']) == 'remove':
     240-            self.onRosterRemove(internJID(itemElement['jid']))
     241+        if item.remove:
     242+            d = defer.maybeDeferred(self.onRosterRemove, item.entity)
     243         else:
     244-            item = self._parseRosterItem(iq.query.item)
     245-            self.onRosterSet(item)
     246+            d = defer.maybeDeferred(self.onRosterSet, item)
     247+
     248+        d.addErrback(eb)
     249+        return d
     250 
     251 
     252     def onRosterSet(self, item):
     253         """
     254         Called when a roster push for a new or update item was received.
     255 
     256+        Raise L{RosterPushIgnored} when not accepting this roster push
     257+        (directly or via Deferred). This will result in a
     258+        L{'service-unavailable'} error being sent in return.
     259+
     260         @param item: The pushed roster item.
     261         @type item: L{RosterItem}
     262         """
     263@@ -472,6 +562,10 @@
     264         """
     265         Called when a roster push for the removal of an item was received.
     266 
     267+        Raise L{RosterPushIgnored} when not accepting this roster push
     268+        (directly or via Deferred). This will result in a
     269+        L{'service-unavailable'} error being sent in return.
     270+
     271         @param entity: The entity for which the roster item has been removed.
     272         @type entity: L{JID}
     273         """
     274diff -r c91f18811c37 wokkel/test/test_im.py
     275--- a/wokkel/test/test_im.py    Mon May 23 18:19:44 2011 +0200
     276+++ b/wokkel/test/test_im.py    Wed May 25 09:24:29 2011 +0200
     277@@ -7,13 +7,14 @@
     278 
     279 from twisted.internet import defer
     280 from twisted.trial import unittest
     281+from twisted.words.protocols.jabber import error
     282 from twisted.words.protocols.jabber.jid import JID
     283 from twisted.words.protocols.jabber.xmlstream import toResponse
     284 from twisted.words.xish import domish, utility
     285 
     286 from wokkel import im
     287 from wokkel.generic import ErrorStanza, parseXml
     288-from wokkel.test.helpers import XmlStreamStub
     289+from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
     290 
     291 NS_XML = 'http://www.w3.org/XML/1998/namespace'
     292 NS_ROSTER = 'jabber:iq:roster'
     293@@ -389,23 +390,356 @@
     294 
     295 
     296 
     297-class RosterClientProtocolTest(unittest.TestCase):
     298+class RosterItemTest(unittest.TestCase):
     299+    """
     300+    Tests for L{im.RosterItem}.
     301+    """
     302+
     303+    def test_jidDeprecationGet(self):
     304+        """
     305+        Getting the jid attribute works as entity and warns deprecation.
     306+        """
     307+        item = im.RosterItem(JID('user@example.org'))
     308+        entity = self.assertWarns(DeprecationWarning,
     309+                                  "Use RosterItem.entity instead.",
     310+                                  im.__file__,
     311+                                  getattr, item, 'jid')
     312+        self.assertIdentical(entity, item.entity)
     313+
     314+
     315+    def test_jidDeprecationSet(self):
     316+        """
     317+        Setting the jid attribute works as entity and warns deprecation.
     318+        """
     319+        item = im.RosterItem(JID('user@example.org'))
     320+        entity = self.assertWarns(DeprecationWarning,
     321+                                  "Use RosterItem.entity instead.",
     322+                                  im.__file__,
     323+                                  setattr, item, 'jid',
     324+                                  JID('other@example.org'))
     325+        self.assertEquals(JID('other@example.org'), item.entity)
     326+
     327+
     328+    def test_askDeprecationGet(self):
     329+        """
     330+        Getting the ask attribute works as entity and warns deprecation.
     331+        """
     332+        item = im.RosterItem(JID('user@example.org'))
     333+        item.pendingOut = True
     334+        ask = self.assertWarns(DeprecationWarning,
     335+                               "Use RosterItem.pendingOut instead.",
     336+                               im.__file__,
     337+                               getattr, item, 'ask')
     338+        self.assertTrue(ask)
     339+
     340+
     341+    def test_askDeprecationSet(self):
     342+        """
     343+        Setting the ask attribute works as entity and warns deprecation.
     344+        """
     345+        item = im.RosterItem(JID('user@example.org'))
     346+        entity = self.assertWarns(DeprecationWarning,
     347+                                  "Use RosterItem.pendingOut instead.",
     348+                                  im.__file__,
     349+                                  setattr, item, 'ask',
     350+                                  True)
     351+        self.assertTrue(item.pendingOut)
     352+
     353+
     354+    def test_toElement(self):
     355+        item = im.RosterItem(JID('user@example.org'))
     356+        element = item.toElement()
     357+        self.assertEquals('item', element.name)
     358+        self.assertEquals(NS_ROSTER, element.uri)
     359+        self.assertFalse(element.hasAttribute('subscription'))
     360+        self.assertFalse(element.hasAttribute('ask'))
     361+        self.assertFalse(element.hasAttribute('name'))
     362+        self.assertFalse(element.hasAttribute('approved'))
     363+        self.assertEquals(0, len(list(element.elements())))
     364+
     365+
     366+    def test_toElementMinimal(self):
     367+        item = im.RosterItem(JID('user@example.org'))
     368+        element = item.toElement()
     369+        self.assertEquals(u'user@example.org', element.getAttribute('jid'))
     370+
     371+
     372+    def test_toElementSubscriptionNone(self):
     373+        item = im.RosterItem(JID('user@example.org'),
     374+                                 subscriptionTo=False,
     375+                                 subscriptionFrom=False)
     376+        element = item.toElement()
     377+        self.assertIdentical(None, element.getAttribute('subscription'))
     378+
     379+
     380+    def test_toElementSubscriptionTo(self):
     381+        item = im.RosterItem(JID('user@example.org'),
     382+                                 subscriptionTo=True,
     383+                                 subscriptionFrom=False)
     384+        element = item.toElement()
     385+        self.assertEquals('to', element.getAttribute('subscription'))
     386+
     387+
     388+    def test_toElementSubscriptionFrom(self):
     389+        item = im.RosterItem(JID('user@example.org'),
     390+                                 subscriptionTo=False,
     391+                                 subscriptionFrom=True)
     392+        element = item.toElement()
     393+        self.assertEquals('from', element.getAttribute('subscription'))
     394+
     395+
     396+    def test_toElementSubscriptionBoth(self):
     397+        item = im.RosterItem(JID('user@example.org'),
     398+                                 subscriptionTo=True,
     399+                                 subscriptionFrom=True)
     400+        element = item.toElement()
     401+        self.assertEquals('both', element.getAttribute('subscription'))
     402+
     403+
     404+    def test_toElementSubscriptionRemove(self):
     405+        item = im.RosterItem(JID('user@example.org'))
     406+        item.remove = True
     407+        element = item.toElement()
     408+        self.assertEquals('remove', element.getAttribute('subscription'))
     409+
     410+
     411+    def test_toElementAsk(self):
     412+        item = im.RosterItem(JID('user@example.org'))
     413+        item.pendingOut = True
     414+        element = item.toElement()
     415+        self.assertEquals('subscribe', element.getAttribute('ask'))
     416+
     417+
     418+    def test_toElementName(self):
     419+        item = im.RosterItem(JID('user@example.org'),
     420+                                 name='Joe User')
     421+        element = item.toElement()
     422+        self.assertEquals(u'Joe User', element.getAttribute('name'))
     423+
     424+
     425+    def test_toElementGroups(self):
     426+        groups = set(['Friends', 'Jabber'])
     427+        item = im.RosterItem(JID('user@example.org'),
     428+                                 groups=groups)
     429+
     430+        element = item.toElement()
     431+        foundGroups = set()
     432+        for child in element.elements():
     433+            if child.uri == NS_ROSTER and child.name == 'group':
     434+                foundGroups.add(unicode(child))
     435+
     436+        self.assertEqual(groups, foundGroups)
     437+
     438+
     439+    def test_toElementApproved(self):
     440+        """
     441+        A pre-approved subscription for a roster item has an 'approved' flag.
     442+        """
     443+        item = im.RosterItem(JID('user@example.org'))
     444+        item.approved = True
     445+        element = item.toElement()
     446+        self.assertEquals(u'true', element.getAttribute('approved'))
     447+
     448+
     449+    def test_fromElementMinimal(self):
     450+        """
     451+        A minimal roster item has a reference to the JID of the contact.
     452+        """
     453+
     454+        xml = """
     455+            <item xmlns="jabber:iq:roster"
     456+                  jid="test@example.org"/>
     457+        """
     458+
     459+        item = im.RosterItem.fromElement(parseXml(xml))
     460+        self.assertEqual(JID(u"test@example.org"), item.entity)
     461+        self.assertIdentical(None, item.name)
     462+        self.assertFalse(item.subscriptionTo)
     463+        self.assertFalse(item.subscriptionFrom)
     464+        self.assertFalse(item.pendingOut)
     465+        self.assertFalse(item.approved)
     466+        self.assertEquals(set(), item.groups)
     467+
     468+
     469+    def test_fromElementName(self):
     470+        """
     471+        A roster item may have an optional name.
     472+        """
     473+
     474+        xml = """
     475+            <item xmlns="jabber:iq:roster"
     476+                  jid="test@example.org"
     477+                  name="Test User"/>
     478+        """
     479+
     480+        item = im.RosterItem.fromElement(parseXml(xml))
     481+        self.assertEqual(u"Test User", item.name)
     482+
     483+
     484+    def test_fromElementGroups(self):
     485+        """
     486+        A roster item may have one or more groups.
     487+        """
     488+
     489+        xml = """
     490+            <item xmlns="jabber:iq:roster"
     491+                  jid="test@example.org">
     492+              <group>Friends</group>
     493+              <group>Twisted</group>
     494+            </item>
     495+        """
     496+
     497+        item = im.RosterItem.fromElement(parseXml(xml))
     498+        self.assertIn(u"Twisted", item.groups)
     499+        self.assertIn(u"Friends", item.groups)
     500+
     501+
     502+    def test_fromElementSubscriptionNone(self):
     503+        """
     504+        Subscription 'none' sets both attributes to False.
     505+        """
     506+
     507+        xml = """
     508+            <item xmlns="jabber:iq:roster"
     509+                  jid="test@example.org"
     510+                  subscription="none"/>
     511+        """
     512+
     513+        item = im.RosterItem.fromElement(parseXml(xml))
     514+        self.assertFalse(item.remove)
     515+        self.assertFalse(item.subscriptionTo)
     516+        self.assertFalse(item.subscriptionFrom)
     517+
     518+
     519+    def test_fromElementSubscriptionTo(self):
     520+        """
     521+        Subscription 'to' sets the corresponding attribute to True.
     522+        """
     523+
     524+        xml = """
     525+            <item xmlns="jabber:iq:roster"
     526+                  jid="test@example.org"
     527+                  subscription="to"/>
     528+        """
     529+
     530+        item = im.RosterItem.fromElement(parseXml(xml))
     531+        self.assertFalse(item.remove)
     532+        self.assertTrue(item.subscriptionTo)
     533+        self.assertFalse(item.subscriptionFrom)
     534+
     535+
     536+    def test_fromElementSubscriptionFrom(self):
     537+        """
     538+        Subscription 'from' sets the corresponding attribute to True.
     539+        """
     540+
     541+        xml = """
     542+            <item xmlns="jabber:iq:roster"
     543+                  jid="test@example.org"
     544+                  subscription="from"/>
     545+        """
     546+
     547+        item = im.RosterItem.fromElement(parseXml(xml))
     548+        self.assertFalse(item.remove)
     549+        self.assertFalse(item.subscriptionTo)
     550+        self.assertTrue(item.subscriptionFrom)
     551+
     552+
     553+    def test_fromElementSubscriptionBoth(self):
     554+        """
     555+        Subscription 'both' sets both attributes to True.
     556+        """
     557+
     558+        xml = """
     559+            <item xmlns="jabber:iq:roster"
     560+                  jid="test@example.org"
     561+                  subscription="both"/>
     562+        """
     563+
     564+        item = im.RosterItem.fromElement(parseXml(xml))
     565+        self.assertFalse(item.remove)
     566+        self.assertTrue(item.subscriptionTo)
     567+        self.assertTrue(item.subscriptionFrom)
     568+
     569+
     570+    def test_fromElementSubscriptionRemove(self):
     571+        """
     572+        Subscription 'remove' sets the remove attribute.
     573+        """
     574+
     575+        xml = """
     576+            <item xmlns="jabber:iq:roster"
     577+                  jid="test@example.org"
     578+                  subscription="remove"/>
     579+        """
     580+
     581+        item = im.RosterItem.fromElement(parseXml(xml))
     582+        self.assertTrue(item.remove)
     583+
     584+
     585+    def test_fromElementPendingOut(self):
     586+        """
     587+        The ask attribute, if set to 'subscription', means pending out.
     588+        """
     589+
     590+        xml = """
     591+            <item xmlns="jabber:iq:roster"
     592+                  jid="test@example.org"
     593+                  ask="subscribe"/>
     594+        """
     595+
     596+        item = im.RosterItem.fromElement(parseXml(xml))
     597+        self.assertTrue(item.pendingOut)
     598+
     599+
     600+    def test_fromElementApprovedTrue(self):
     601+        """
     602+        The approved attribute (true) signals a pre-approved subscription.
     603+        """
     604+
     605+        xml = """
     606+            <item xmlns="jabber:iq:roster"
     607+                  jid="test@example.org"
     608+                  approved="true"/>
     609+        """
     610+
     611+        item = im.RosterItem.fromElement(parseXml(xml))
     612+        self.assertTrue(item.approved)
     613+
     614+
     615+    def test_fromElementApproved1(self):
     616+        """
     617+        The approved attribute (1) signals a pre-approved subscription.
     618+        """
     619+
     620+        xml = """
     621+            <item xmlns="jabber:iq:roster"
     622+                  jid="test@example.org"
     623+                  approved="1"/>
     624+        """
     625+
     626+        item = im.RosterItem.fromElement(parseXml(xml))
     627+        self.assertTrue(item.approved)
     628+
     629+
     630+
     631+class RosterClientProtocolTest(unittest.TestCase, TestableRequestHandlerMixin):
     632     """
     633     Tests for L{im.RosterClientProtocol}.
     634     """
     635 
     636     def setUp(self):
     637         self.stub = XmlStreamStub()
     638-        self.protocol = im.RosterClientProtocol()
     639-        self.protocol.xmlstream = self.stub.xmlstream
     640-        self.protocol.connectionInitialized()
     641+        self.service = im.RosterClientProtocol()
     642+        self.service.makeConnection(self.stub.xmlstream)
     643+        self.service.connectionInitialized()
     644 
     645 
     646     def test_removeItem(self):
     647         """
     648         Removing a roster item is setting an item with subscription C{remove}.
     649         """
     650-        d = self.protocol.removeItem(JID('test@example.org'))
     651+        d = self.service.removeItem(JID('test@example.org'))
     652 
     653         # Inspect outgoing iq request
     654 
     655@@ -419,10 +753,117 @@
     656         self.assertEquals(1, len(children))
     657         child = children[0]
     658         self.assertEquals('test@example.org', child['jid'])
     659-        self.assertEquals('remove', child['subscription'])
     660+        self.assertEquals('remove', child.getAttribute('subscription'))
     661 
     662         # Fake successful response
     663 
     664         response = toResponse(iq, 'result')
     665         self.stub.send(response)
     666         return d
     667+
     668+
     669+    def test_getRoster(self):
     670+        def cb(roster):
     671+            self.assertIn(JID('user@example.org'), roster)
     672+
     673+
     674+        d = self.service.getRoster()
     675+        d.addCallback(cb)
     676+
     677+        # Inspect outgoing iq request
     678+
     679+        iq = self.stub.output[-1]
     680+        self.assertEquals('get', iq.getAttribute('type'))
     681+        self.assertNotIdentical(None, iq.query)
     682+        self.assertEquals(NS_ROSTER, iq.query.uri)
     683+
     684+        # Fake successful response
     685+        response = toResponse(iq, 'result')
     686+        query = response.addElement((NS_ROSTER, 'query'))
     687+        item = query.addElement('item')
     688+        item['jid'] = 'user@example.org'
     689+
     690+        self.stub.send(response)
     691+        return d
     692+
     693+
     694+    def test_onRosterSet(self):
     695+        """
     696+        A roster push causes onRosterSet to be called with the parsed item.
     697+        """
     698+        xml = """
     699+          <iq type='set'>
     700+            <query xmlns='jabber:iq:roster'>
     701+              <item jid='user@example.org'/>
     702+            </query>
     703+          </iq>
     704+        """
     705+
     706+        items = []
     707+
     708+        def onRosterSet(item):
     709+            items.append(item)
     710+
     711+        def cb(result):
     712+            self.assertEquals(1, len(items))
     713+            self.assertEquals(JID('user@example.org'), items[0].entity)
     714+
     715+        self.service.onRosterSet = onRosterSet
     716+
     717+        d = self.handleRequest(xml)
     718+        d.addCallback(cb)
     719+        return d
     720+
     721+
     722+    def test_onRosterSetUntrusted(self):
     723+        """
     724+        Roster pushes from untrusted sources will be not be handled.
     725+        """
     726+        xml = """
     727+          <iq type='set' from='bad@example.org'>
     728+            <query xmlns='jabber:iq:roster'>
     729+              <item jid='user@example.org'/>
     730+            </query>
     731+          </iq>
     732+        """
     733+
     734+        def onRosterSet(item):
     735+            raise im.RosterPushIgnored()
     736+
     737+        def cb(result):
     738+            self.assertEquals('service-unavailable', result.condition)
     739+
     740+        self.service.onRosterSet = onRosterSet
     741+
     742+        d = self.handleRequest(xml)
     743+        self.assertFailure(d, error.StanzaError)
     744+        d.addCallback(cb)
     745+        return d
     746+
     747+
     748+    def test_onRosterRemove(self):
     749+        """
     750+        A roster push causes onRosterSet to be called with the parsed item.
     751+        """
     752+        xml = """
     753+          <iq type='set'>
     754+            <query xmlns='jabber:iq:roster'>
     755+              <item jid='user@example.org' subscription='remove'/>
     756+            </query>
     757+          </iq>
     758+        """
     759+
     760+        entities = []
     761+
     762+        def onRosterRemove(entity):
     763+            entities.append(entity)
     764+
     765+        def cb(result):
     766+            self.assertEquals([JID('user@example.org')], entities)
     767+
     768+        self.service.onRosterRemove = onRosterRemove
     769+
     770+        d = self.handleRequest(xml)
     771+        d.addCallback(cb)
     772+        return d
     773+
  • roster_server.patch

    r37 r49  
    1 diff -r 7b9f484b0b44 wokkel/xmppim.py
    2 --- a/wokkel/xmppim.py  Fri Feb 12 19:49:36 2010 +0100
    3 +++ b/wokkel/xmppim.py  Sat Feb 13 18:57:26 2010 +0100
    4 @@ -12,6 +12,7 @@
    5  All of it should eventually move to Twisted.
    6  """
     1diff -r 3c5fb05162e1 wokkel/im.py
     2--- a/wokkel/im.py      Wed May 25 09:39:42 2011 +0200
     3+++ b/wokkel/im.py      Wed May 25 09:40:28 2011 +0200
     4@@ -14,7 +14,7 @@
    75 
     6 from twisted.internet import defer
     7 from twisted.words.protocols.jabber.jid import JID, internJID
     8-from twisted.words.protocols.jabber import error
    89+from twisted.words.protocols.jabber import error, xmlstream
    9  from twisted.words.protocols.jabber.jid import JID
    1010 from twisted.words.xish import domish
    1111 
    12 @@ -22,6 +23,9 @@
    13  NS_XML = 'http://www.w3.org/XML/1998/namespace'
     12 from wokkel.compat import IQ
     13@@ -25,6 +25,7 @@
    1414 NS_ROSTER = 'jabber:iq:roster'
    1515 
    16 +XPATH_ROSTER_GET = "//iq[@type='get']/query[@xmlns='%s']" % NS_ROSTER
    17 +XPATH_ROSTER_SET = "//iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
    18 +
    19  class Presence(domish.Element):
    20      def __init__(self, to=None, type=None):
    21          domish.Element.__init__(self, (None, "presence"))
    22 @@ -636,6 +640,7 @@
     16 XPATH_ROSTER_SET = "/iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
     17+XPATH_ROSTER_GET = "/iq[@type='get']/query[@xmlns='%s']" % NS_ROSTER
    2318 
    2419 
    2520 
    26 +
    27  class RosterClientProtocol(XMPPHandler):
    28      """
    29      Client side XMPP roster protocol.
    30 @@ -645,6 +650,7 @@
    31          ROSTER_SET = "/iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
    32          self.xmlstream.addObserver(ROSTER_SET, self._onRosterSet)
    33  
    34 +
    35      def _parseRosterItem(self, element):
    36          jid = JID(element['jid'])
    37          item = RosterItem(jid)
    38 @@ -659,6 +665,7 @@
    39  
    40          return item
    41  
    42 +
    43      def getRoster(self):
    44          """
    45          Retrieve contact list.
    46 @@ -713,6 +720,7 @@
    47              item = self._parseRosterItem(iq.query.item)
    48              self.onRosterSet(item)
    49  
    50 +
    51      def onRosterSet(self, item):
    52          """
    53          Called when a roster push for a new or update item was received.
    54 @@ -721,6 +729,7 @@
    55          @type item: L{RosterItem}
    56          """
    57  
    58 +
    59      def onRosterRemove(self, entity):
    60          """
    61          Called when a roster push for the removal of an item was received.
    62 @@ -729,6 +738,48 @@
     21@@ -579,3 +580,43 @@
     22         @param entity: The entity for which the roster item has been removed.
    6323         @type entity: L{JID}
    6424         """
    65  
     25+
    6626+
    6727+
     
    8949+        iq.handled = True
    9050+
    91 +        d = self.getRoster(JID(iq["from"]))
     51+        d = self.getRoster(internJID(iq["from"]))
    9252+        d.addCallback(self._toRosterReply, iq)
    9353+        d.addErrback(lambda _: error.ErrorStanza('internal-error').toResponse(iq))
     
    10363+    def getRoster(self, entity):
    10464+        raise NotImplemented
    105 +
    106 +
    107 +
    108  class MessageProtocol(XMPPHandler):
    109      """
    110      Generic XMPP subprotocol handler for incoming message stanzas.
  • series

    r48 r49  
    1 request-tracking.patch
    2 request-xmpphandler.patch
    3 request-stanza.patch
    4 disco-addressing.patch
     1copy_xmppim.patch
     2jid_cleanup.patch
     3roster_item.patch #+c2s
     4roster_item_more.patch
     5roster_server.patch #+c2s
     6xmpp_client_service.patch #+c2s
     7
     8request-tracking.patch #+request
     9request-xmpphandler.patch #+request
     10request-stanza.patch #+request
     11disco-addressing.patch #+request
     12
     13pubsub_resource_example.patch
     14
    515disco_warning.patch #-compatible
     16pubsub-item.patch #-compatible
     17
    618pubsub-default-type-attribute.patch #+deferred
    7 pubsub-item.patch #-compatible
    8 roster_item.patch
    9 roster_server.patch
    10 xmpp_client_service.patch
    1119disco_simplify_gatherResults.patch #+deferred
    1220deprecate_xmpphandler.patch #+deferred
  • xmpp_client_service.patch

    r41 r49  
    1 diff -r 62f841ed2a99 doc/examples/client_service.tac
     1diff -r d7fa09914b70 doc/examples/client_service.tac
    22--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
    3 +++ b/doc/examples/client_service.tac   Thu Feb 25 09:47:52 2010 +0100
     3+++ b/doc/examples/client_service.tac   Wed May 25 09:46:06 2011 +0200
    44@@ -0,0 +1,74 @@
    55+from twisted.application import service, strports
     
    7777+
    7878+sessionManager.connectionManager = c2sFactory
    79 diff -r 62f841ed2a99 wokkel/client.py
    80 --- a/wokkel/client.py  Sat Feb 13 18:57:26 2010 +0100
    81 +++ b/wokkel/client.py  Thu Feb 25 09:47:52 2010 +0100
     79diff -r d7fa09914b70 wokkel/client.py
     80--- a/wokkel/client.py  Wed May 25 09:40:28 2011 +0200
     81+++ b/wokkel/client.py  Wed May 25 09:46:06 2011 +0200
    8282@@ -10,13 +10,27 @@
    8383 that should probably eventually move there.
     
    470470+        else:
    471471+            raise Exception("No connection manager set")
    472 diff -r 62f841ed2a99 wokkel/component.py
    473 --- a/wokkel/component.py       Sat Feb 13 18:57:26 2010 +0100
    474 +++ b/wokkel/component.py       Thu Feb 25 09:47:52 2010 +0100
     472diff -r d7fa09914b70 wokkel/component.py
     473--- a/wokkel/component.py       Wed May 25 09:40:28 2011 +0200
     474+++ b/wokkel/component.py       Wed May 25 09:46:06 2011 +0200
    475475@@ -313,12 +313,25 @@
    476476         """
     
    501501 
    502502 
    503 diff -r 62f841ed2a99 wokkel/xmppim.py
    504 --- a/wokkel/xmppim.py  Sat Feb 13 18:57:26 2010 +0100
    505 +++ b/wokkel/xmppim.py  Thu Feb 25 09:47:52 2010 +0100
    506 @@ -12,8 +12,11 @@
    507  All of it should eventually move to Twisted.
    508  """
    509  
    510 +import copy
    511 +
    512 +from twisted.python import log
    513  from twisted.words.protocols.jabber import error, xmlstream
    514 -from twisted.words.protocols.jabber.jid import JID
    515 +from twisted.words.protocols.jabber.jid import JID, internJID
    516  from twisted.words.xish import domish
    517  
    518  from wokkel.compat import IQ
    519 @@ -85,7 +88,7 @@
    520              handler(presence)
    521  
    522      def _onPresenceAvailable(self, presence):
    523 -        entity = JID(presence["from"])
    524 +        entity = internJID(presence["from"])
    525  
    526          show = unicode(presence.show or '')
    527          if show not in ['away', 'xa', 'chat', 'dnd']:
    528 @@ -101,23 +104,23 @@
    529          self.availableReceived(entity, show, statuses, priority)
    530  
    531      def _onPresenceUnavailable(self, presence):
    532 -        entity = JID(presence["from"])
    533 +        entity = internJID(presence["from"])
    534  
    535          statuses = self._getStatuses(presence)
    536  
    537          self.unavailableReceived(entity, statuses)
    538  
    539      def _onPresenceSubscribed(self, presence):
    540 -        self.subscribedReceived(JID(presence["from"]))
    541 +        self.subscribedReceived(internJID(presence["from"]))
    542  
    543      def _onPresenceUnsubscribed(self, presence):
    544 -        self.unsubscribedReceived(JID(presence["from"]))
    545 +        self.unsubscribedReceived(internJID(presence["from"]))
    546  
    547      def _onPresenceSubscribe(self, presence):
    548 -        self.subscribeReceived(JID(presence["from"]))
    549 +        self.subscribeReceived(internJID(presence["from"]))
    550  
    551      def _onPresenceUnsubscribe(self, presence):
    552 -        self.unsubscribeReceived(JID(presence["from"]))
    553 +        self.unsubscribeReceived(internJID(presence["from"]))
    554  
    555  
    556      def availableReceived(self, entity, show=None, statuses=None, priority=0):
    557 @@ -125,7 +128,7 @@
    558          Available presence was received.
    559  
    560          @param entity: entity from which the presence was received.
    561 -        @type entity: {JID}
    562 +        @type entity: L{JID}
    563          @param show: detailed presence information. One of C{'away'}, C{'xa'},
    564                       C{'chat'}, C{'dnd'} or C{None}.
    565          @type show: C{str} or C{NoneType}
    566 @@ -143,7 +146,7 @@
    567          Unavailable presence was received.
    568  
    569          @param entity: entity from which the presence was received.
    570 -        @type entity: {JID}
    571 +        @type entity: L{JID}
    572          @param statuses: dictionary of natural language descriptions of the
    573                           availability status, keyed by the language
    574                           descriptor. A status without a language
    575 @@ -156,7 +159,7 @@
    576          Subscription approval confirmation was received.
    577  
    578          @param entity: entity from which the confirmation was received.
    579 -        @type entity: {JID}
    580 +        @type entity: L{JID}
    581          """
    582  
    583      def unsubscribedReceived(self, entity):
    584 @@ -164,7 +167,7 @@
    585          Unsubscription confirmation was received.
    586  
    587          @param entity: entity from which the confirmation was received.
    588 -        @type entity: {JID}
    589 +        @type entity: L{JID}
    590          """
    591  
    592      def subscribeReceived(self, entity):
    593 @@ -172,7 +175,7 @@
    594          Subscription request was received.
    595  
    596          @param entity: entity from which the request was received.
    597 -        @type entity: {JID}
    598 +        @type entity: L{JID}
    599          """
    600  
    601      def unsubscribeReceived(self, entity):
    602 @@ -180,7 +183,7 @@
    603          Unsubscription request was received.
    604  
    605          @param entity: entity from which the request was received.
    606 -        @type entity: {JID}
    607 +        @type entity: L{JID}
    608          """
    609  
    610      def available(self, entity=None, show=None, statuses=None, priority=0):
    611 @@ -188,7 +191,7 @@
    612          Send available presence.
    613  
    614          @param entity: optional entity to which the presence should be sent.
    615 -        @type entity: {JID}
    616 +        @type entity: L{JID}
    617          @param show: optional detailed presence information. One of C{'away'},
    618                       C{'xa'}, C{'chat'}, C{'dnd'}.
    619          @type show: C{str}
    620 @@ -207,7 +210,7 @@
    621          Send unavailable presence.
    622  
    623          @param entity: optional entity to which the presence should be sent.
    624 -        @type entity: {JID}
    625 +        @type entity: L{JID}
    626          @param statuses: dictionary of natural language descriptions of the
    627                           availability status, keyed by the language
    628                           descriptor. A status without a language
    629 @@ -221,7 +224,7 @@
    630          Send subscription request
    631  
    632          @param entity: entity to subscribe to.
    633 -        @type entity: {JID}
    634 +        @type entity: L{JID}
    635          """
    636          self.send(Presence(to=entity, type='subscribe'))
    637  
    638 @@ -230,7 +233,7 @@
    639          Send unsubscription request
    640  
    641          @param entity: entity to unsubscribe from.
    642 -        @type entity: {JID}
    643 +        @type entity: L{JID}
    644          """
    645          self.send(Presence(to=entity, type='unsubscribe'))
    646  
    647 @@ -239,7 +242,7 @@
    648          Send subscription confirmation.
    649  
    650          @param entity: entity that subscribed.
    651 -        @type entity: {JID}
    652 +        @type entity: L{JID}
    653          """
    654          self.send(Presence(to=entity, type='subscribed'))
    655  
    656 @@ -248,7 +251,7 @@
    657          Send unsubscription confirmation.
    658  
    659          @param entity: entity that unsubscribed.
    660 -        @type entity: {JID}
    661 +        @type entity: L{JID}
    662          """
    663          self.send(Presence(to=entity, type='unsubscribed'))
    664  
    665 @@ -395,7 +398,10 @@
     503diff -r d7fa09914b70 wokkel/im.py
     504--- a/wokkel/im.py      Wed May 25 09:40:28 2011 +0200
     505+++ b/wokkel/im.py      Wed May 25 09:46:06 2011 +0200
     506@@ -170,7 +170,10 @@
    666507         self.xmlstream.addObserver("/presence", self._onPresence)
    667508 
     
    675516 
    676517         presenceType = stanza.stanzaType or 'available'
    677 @@ -405,14 +411,19 @@
     518@@ -180,14 +183,19 @@
    678519         except KeyError:
    679520             return
     
    697538 
    698539     def errorReceived(self, presence):
    699 @@ -478,7 +489,7 @@
    700  
    701          @param recipient: Optional Recipient to which the presence should be
    702              sent.
    703 -        @type recipient: {JID}
    704 +        @type recipient: L{JID}
    705  
    706          @param show: Optional detailed presence information. One of C{'away'},
    707              C{'xa'}, C{'chat'}, C{'dnd'}.
    708 @@ -503,7 +514,7 @@
    709          Send unavailable presence.
    710  
    711          @param recipient: Optional entity to which the presence should be sent.
    712 -        @type recipient: {JID}
    713 +        @type recipient: L{JID}
    714  
    715          @param statuses: dictionary of natural language descriptions of the
    716              availability status, keyed by the language descriptor. A status
    717 @@ -520,7 +531,7 @@
    718          Send subscription request
    719  
    720          @param recipient: Entity to subscribe to.
    721 -        @type recipient: {JID}
    722 +        @type recipient: L{JID}
    723          """
    724          presence = SubscriptionPresence(recipient=recipient, sender=sender)
    725          presence.stanzaType = 'subscribe'
    726 @@ -532,7 +543,7 @@
    727          Send unsubscription request
    728  
    729          @param recipient: Entity to unsubscribe from.
    730 -        @type recipient: {JID}
    731 +        @type recipient: L{JID}
    732          """
    733          presence = SubscriptionPresence(recipient=recipient, sender=sender)
    734          presence.stanzaType = 'unsubscribe'
    735 @@ -544,7 +555,7 @@
    736          Send subscription confirmation.
    737  
    738          @param recipient: Entity that subscribed.
    739 -        @type recipient: {JID}
    740 +        @type recipient: L{JID}
    741          """
    742          presence = SubscriptionPresence(recipient=recipient, sender=sender)
    743          presence.stanzaType = 'subscribed'
    744 @@ -556,7 +567,7 @@
    745          Send unsubscription confirmation.
    746  
    747          @param recipient: Entity that unsubscribed.
    748 -        @type recipient: {JID}
    749 +        @type recipient: L{JID}
    750          """
    751          presence = SubscriptionPresence(recipient=recipient, sender=sender)
    752          presence.stanzaType = 'unsubscribed'
    753 @@ -568,7 +579,7 @@
    754          Send presence probe.
    755  
    756          @param recipient: Entity to be probed.
    757 -        @type recipient: {JID}
    758 +        @type recipient: L{JID}
    759          """
    760          presence = ProbePresence(recipient=recipient, sender=sender)
    761          self.send(presence.toElement())
    762 @@ -652,7 +663,7 @@
    763  
    764  
    765      def _parseRosterItem(self, element):
    766 -        jid = JID(element['jid'])
    767 +        jid = internJID(element['jid'])
    768          item = RosterItem(jid)
    769          item.name = element.getAttribute('name')
    770          subscription = element.getAttribute('subscription')
    771 @@ -715,7 +726,7 @@
    772          itemElement = iq.query.item
    773  
    774          if unicode(itemElement['subscription']) == 'remove':
    775 -            self.onRosterRemove(JID(itemElement['jid']))
    776 +            self.onRosterRemove(internJID(itemElement['jid']))
    777          else:
    778              item = self._parseRosterItem(iq.query.item)
    779              self.onRosterSet(item)
    780 @@ -763,7 +774,7 @@
    781      def _onRosterGet(self, iq):
    782          iq.handled = True
    783  
    784 -        d = self.getRoster(JID(iq["from"]))
    785 +        d = self.getRoster(internJID(iq["from"]))
    786          d.addCallback(self._toRosterReply, iq)
    787          d.addErrback(lambda _: error.ErrorStanza('internal-error').toResponse(iq))
    788          d.addBoth(self.send)
    789 @@ -808,3 +819,433 @@
    790          """
    791          Called when a message stanza was received.
    792          """
    793 +
    794 +
    795 +
     540@@ -583,6 +591,436 @@
     541 
     542 
     543 
    796544+class AccountIQHandler(XMPPHandler):
    797545+
     
    1221969+                outPresence['to'] = fromJID.userhost()
    1222970+                self.send(outPresence)
     971+
     972+
     973+
     974 class RosterServerProtocol(XMPPHandler):
     975     """
     976     XMPP subprotocol handler for the roster, server side.
Note: See TracChangeset for help on using the changeset viewer.