diff src/core/xmpp.py @ 466:448ce3c9e2ac

core: Roster cache refactoring: cache is now managed by client's SatRosterProtocol instance.
author Goffi <goffi@goffi.org>
date Mon, 26 Mar 2012 00:22:49 +0200
parents 4e361d295bca
children 47af60767013
line wrap: on
line diff
--- a/src/core/xmpp.py	Sat Mar 24 17:48:12 2012 +0100
+++ b/src/core/xmpp.py	Mon Mar 26 00:22:49 2012 +0200
@@ -120,7 +120,9 @@
     def __init__(self, host):
         xmppim.RosterClientProtocol.__init__(self)
         self.host = host
-        self._groups=set()
+        #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
     
     def rosterCb(self, roster):
         for raw_jid, item in roster.iteritems():
@@ -157,6 +159,18 @@
         for group in roster_item.groups:
             item.addElement('group', content=group)
         return iq.send()
+
+    def getAttributes(self, item):
+        """Return dictionary of attributes as used in bridge from a RosterItem
+        @param item: RosterItem
+        @return: dictionary of attributes"""
+        item_attr = {'to': str(item.subscriptionTo),
+                     'from': str(item.subscriptionFrom),
+                     'ask': str(item.ask)
+                     }
+        if item.name:
+            item_attr['name'] = item.name
+        return item_attr
     
     def onRosterSet(self, item):
         """Called when a new/update roster item is received"""
@@ -167,26 +181,53 @@
             #may change in the future
             self.removeItem(item.jid)
             return
-        item_attr = {'to': str(item.subscriptionTo),
-                     'from': str(item.subscriptionFrom),
-                     'ask': str(item.ask)
-                     }
-        if item.name:
-            item_attr['name'] = item.name
         info (_("new contact in roster list: %s"), item.jid.full())
-        self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile)
-        self.host.bridge.newContact(item.jid.full(), item_attr, item.groups, self.parent.profile)
-        self._groups.update(item.groups) 
+        #self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile)
+       
+        bare_jid = item.jid.userhost()
+        self._jids[bare_jid] = item
+        for group in item.groups:
+            self._groups.get(group,set()).add(bare_jid)
+        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"""
         print _("removing %s from roster list") % entity.full()
-        self.host.memory.delContact(entity, self.parent.profile)
+        bare_jid = entity.userhost()
+
+        #we first remove item from local cache (self._groups and self._jids)
+        try:
+            item = self._jids.pop(bare_jid)
+        except KeyError:
+            log.warning("Received a roster remove event for an item not in cache")
+            return
+        for group in item.groups:
+            try:
+                jids_set = self._groups[group]
+                jids_set.remove(bare_jid)
+                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})
+                                                                                                                            
+        #then we send the bridge signal
         self.host.bridge.contactDeleted(entity.userhost(), self.parent.profile)
 
     def getGroups(self):
-        """Return a set of groups"""
-        return self._groups
+        """Return a list of groups"""
+        return self._groups.keys()
+
+    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.userhost(), None)
+
+    def getItems(self):
+        """Return all items of the roster"""
+        return self._jids
+        
 
 class SatPresenceProtocol(xmppim.PresenceClientProtocol):
 
@@ -243,8 +284,8 @@
     def subscribed(self, entity):
         xmppim.PresenceClientProtocol.subscribed(self, entity)
         self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile)
-        contact = self.host.memory.getContact(entity, self.parent.profile) 
-        if not contact or contact[0]['to'] == 'False': #we automatically subscribe to 'to' presence
+        item = self.parent.roster.getItem(entity)
+        if not item or not item.subscriptionTo: #we automatically subscribe to 'to' presence
             debug(_('sending automatic "from" subscription request'))
             self.subscribe(entity)
 
@@ -262,8 +303,8 @@
 
     def subscribeReceived(self, entity):
         debug (_("subscription request from [%s]") % entity.userhost())
-        contact = self.host.memory.getContact(entity, self.parent.profile) 
-        if contact and contact[0]['to'] == 'True':
+        item = self.parent.roster.getItem(entity)
+        if item and item.subscriptionTo:
             #We automatically accept subscription if we are already subscribed to contact presence
             debug(_('sending automatic subscription acceptance'))
             self.subscribed(entity)
@@ -273,8 +314,8 @@
 
     def unsubscribeReceived(self, entity):
         debug (_("unsubscription asked for [%s]") % entity.userhost())
-        contact = self.host.memory.getContact(entity, self.parent.profile) 
-        if contact and contact[0]['from'] == 'True': #we automatically remove contact
+        item = self.parent.roster.getItem(entity)
+        if item and item.subscriptionFrom: #we automatically remove contact
             debug(_('automatic contact deletion'))
             self.host.delContact(entity.userhost(), self.parent.profile)
         self.host.bridge.subscribe('unsubscribe', entity.userhost(), self.parent.profile)