diff src/core/xmpp.py @ 1262:f8a8434dbac7 frontends_multi_profiles

core: improved roster management + misc: - updated methods to no use anymore methods deprecated in Wokkel - use of full jid when it make sense instead of bare jid - getContacts, updateContact and delContact are now asynchronous
author Goffi <goffi@goffi.org>
date Wed, 10 Dec 2014 18:32:33 +0100
parents 8b891f9be183
children 3360074a2f00
line wrap: on
line diff
--- a/src/core/xmpp.py	Mon Nov 24 17:20:51 2014 +0100
+++ b/src/core/xmpp.py	Wed Dec 10 18:32:33 2014 +0100
@@ -172,14 +172,42 @@
     def __init__(self, host):
         xmppim.RosterClientProtocol.__init__(self)
         self.host = host
-        self.got_roster = defer.Deferred()
+        self.got_roster = defer.Deferred() # called when roster is received and ready
         #XXX: the two following dicts keep a local copy of the roster
-        self._groups = {}  # map from groups to bare jids: key=group value=set of bare jids
-        self._jids = {}  # map from bare jids to RosterItem: key=jid value=RosterItem
+        self._groups = {}  # map from groups to jids: key=group value=set of jids
+        self._jids = None  # map from jids to RosterItem: key=jid value=RosterItem
 
     def rosterCb(self, roster):
-        for raw_jid, item in roster.iteritems():
-            self.onRosterSet(item)
+        assert roster is not None # FIXME: must be managed with roster versioning
+        self._jids = roster
+
+    def _registerItem(self, item):
+        """Register item in local cache
+
+        item must be already registered in self._jids before this method is called
+        @param item (RosterIem): item added
+        """
+        log.debug("registering item: {}".format(item.jid.full()))
+        if item.entity.resource:
+            log.warning("Received a roster item with a resource, this is not common but not restricted by RFC 6121, this case may be not well tested.")
+            import ipdb; ipdb.set_trace()
+        if not item.subscriptionTo and not item.subscriptionFrom and not item.ask:
+            #XXX: current behaviour: we don't want contact in our roster list
+            # if there is no presence subscription
+            # may change in the future
+            self.removeItem(item.jid) # FIXME: to be checked
+            return
+        if not item.subscriptionTo:
+            if not item.subscriptionFrom:
+                log.info(_("There's no subscription between you and [{}]!").format(item.jid.full()))
+            else:
+                log.info(_("You are not subscribed to [{}]!").format(item.jid.full()))
+        if not item.subscriptionFrom:
+            log.info(_("[{}] is not subscribed to you!").format(item.jid.full()))
+        #self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile)
+
+        for group in item.groups:
+            self._groups.setdefault(group, set()).add(item.entity)
 
     def requestRoster(self):
         """ ask the server for Roster list """
@@ -190,31 +218,9 @@
     def removeItem(self, to_jid):
         """Remove a contact from roster list
         @param to_jid: a JID instance
-        """
-        xmppim.RosterClientProtocol.removeItem(self, to_jid)
-        #TODO: check IQ result
-
-    #XXX: disabled (cf http://wokkel.ik.nu/ticket/56))
-    #def addItem(self, to):
-        #"""Add a contact to roster list"""
-        #xmppim.RosterClientProtocol.addItem(self, to)
-        #TODO: check IQ result"""
-
-    def updateItem(self, roster_item):
+        @return: Deferred
         """
-        Update an item of the contact list.
-
-        @param roster_item: item to update
-        """
-        iq = compat.IQ(self.xmlstream, 'set')
-        iq.addElement((xmppim.NS_ROSTER, 'query'))
-        item = iq.query.addElement('item')
-        item['jid'] = roster_item.jid.userhost()
-        if roster_item.name:
-            item['name'] = roster_item.name
-        for group in roster_item.groups:
-            item.addElement('group', content=group)
-        return iq.send()
+        return xmppim.RosterClientProtocol.removeItem(self, to_jid)
 
     def getAttributes(self, item):
         """Return dictionary of attributes as used in bridge from a RosterItem
@@ -228,54 +234,34 @@
             item_attr['name'] = item.name
         return item_attr
 
-    def onRosterSet(self, item):
-        """Called when a new/update roster item is received"""
-        #TODO: send a signal to frontends
-        if not item.subscriptionTo and not item.subscriptionFrom and not item.ask:
-            #XXX: current behaviour: we don't want contact in our roster list
-            # if there is no presence subscription
-            # may change in the future
-            self.removeItem(item.jid)
-            return
-        log.debug(_("New contact in roster list: %s") % item.jid.full())
-        if not item.subscriptionTo:
-            if not item.subscriptionFrom:
-                log.info(_("There's no subscription between you and [%s]!") % item.jid.full())
-            else:
-                log.info(_("You are not subscribed to [%s]!") % item.jid.full())
-        if not item.subscriptionFrom:
-            log.info(_("[%s] is not subscribed to you!") % item.jid.full())
-        #self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile)
-
-        bare_jid = item.jid.userhostJID()
-        self._jids[bare_jid] = item
-        for group in item.groups:
-            self._groups.setdefault(group, set()).add(bare_jid)
+    def setReceived(self, request):
+        #TODO: implement roster versioning (cf RFC 6121 ยง2.6)
+        item = request.item
+        self._jids[item.entity] = item
         self.host.bridge.newContact(item.jid.full(), self.getAttributes(item), item.groups, self.parent.profile)
 
-    def onRosterRemove(self, entity):
-        """Called when a roster removal event is received"""
+    def removeReceived(self, request):
+        entity = request.item.entity
         print _("removing %s from roster list") % entity.full()
-        bare_jid = entity.userhostJID()
 
         # we first remove item from local cache (self._groups and self._jids)
         try:
-            item = self._jids.pop(bare_jid)
+            item = self._jids.pop(entity)
         except KeyError:
-            log.warning("Received a roster remove event for an item not in cache")
+            log.error("Received a roster remove event for an item not in cache ({})".format(entity))
             return
         for group in item.groups:
             try:
                 jids_set = self._groups[group]
-                jids_set.remove(bare_jid)
+                jids_set.remove(entity)
                 if not jids_set:
                     del self._groups[group]
             except KeyError:
                 log.warning("there is not cache for the group [%(groups)s] of the removed roster item [%(jid)s]" %
-                        {"group": group, "jid": bare_jid})
+                        {"group": group, "jid": entity})
 
         # then we send the bridge signal
-        self.host.bridge.contactDeleted(entity.userhost(), self.parent.profile)
+        self.host.bridge.contactDeleted(entity.full(), self.parent.profile)
 
     def getGroups(self):
         """Return a list of groups"""
@@ -283,17 +269,19 @@
 
     def getItem(self, jid):
         """Return RosterItem for a given jid
+
         @param jid: jid of the contact
-        @return: RosterItem or None if contact is not in cache"""
-        return self._jids.get(jid.userhostJID(), None)
+        @return: RosterItem or None if contact is not in cache
+        """
+        return self._jids.get(jid, None)
 
-    def getBareJids(self):
-        """Return all bare jids (as unicode) of the roster"""
+    def getJids(self):
+        """Return all jids of the roster"""
         return self._jids.keys()
 
     def isJidInRoster(self, entity_jid):
         """Return True if jid is in roster"""
-        return entity_jid.userhostJID() in self._jids
+        return entity_jid in self._jids
 
     def getItems(self):
         """Return all items of the roster"""
@@ -318,7 +306,7 @@
         super(SatPresenceProtocol, self).send(obj)
 
     def availableReceived(self, entity, show=None, statuses=None, priority=0):
-        log.debug(_("presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)") % {'entity': entity, 'show': show, 'statuses': statuses, 'priority': priority})
+        log.debug(_("presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)") % {'entity': entity, C.PRESENCE_SHOW: show, C.PRESENCE_STATUSES: statuses, C.PRESENCE_PRIORITY: priority})
 
         if not statuses:
             statuses = {}
@@ -341,7 +329,7 @@
                                         self.parent.profile)
 
     def unavailableReceived(self, entity, statuses=None):
-        log.debug(_("presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {'entity': entity, 'statuses': statuses})
+        log.debug(_("presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {'entity': entity, C.PRESENCE_STATUSES: statuses})
 
         if not statuses:
             statuses = {}