# HG changeset patch # User Goffi # Date 1332714169 -7200 # Node ID 448ce3c9e2ac101f32ba6b18b176b87d8fd8955c # Parent 78e67a59d51d7a239de0855695238823baf81b66 core: Roster cache refactoring: cache is now managed by client's SatRosterProtocol instance. diff -r 78e67a59d51d -r 448ce3c9e2ac src/core/sat_main.py --- a/src/core/sat_main.py Sat Mar 24 17:48:12 2012 +0100 +++ b/src/core/sat_main.py Mon Mar 26 00:22:49 2012 +0200 @@ -123,7 +123,7 @@ self.bridge.register("connect", self.connect) self.bridge.register("asyncConnect", self.asyncConnect) self.bridge.register("disconnect", self.disconnect) - self.bridge.register("getContacts", self.memory.getContacts) + self.bridge.register("getContacts", self.getContacts) self.bridge.register("getLastResource", self.memory.getLastResource) self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus) self.bridge.register("getWaitingSub", self.memory.getWaitingSub) @@ -270,6 +270,18 @@ if disconnected_cb: disconnected_cb(profile) + def getContacts(self, profile_key): + client = self.getClient(profile_key) + if not client: + error(_('Asking contacts for a non-existant profile')) + return [] + ret = [] + for item in client.roster.getItems(): #we get all item for client's roster + #and convert them to expected format + attr = client.roster.getAttributes(item) + ret.append([item.jid.userhost(), attr, item.groups]) + return ret + def purgeClient(self, profile): """Remove reference to a profile client and purge cache the garbage collector can then free the memory""" diff -r 78e67a59d51d -r 448ce3c9e2ac src/core/xmpp.py --- 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) diff -r 78e67a59d51d -r 448ce3c9e2ac src/memory/memory.py --- a/src/memory/memory.py Sat Mar 24 17:48:12 2012 +0100 +++ b/src/memory/memory.py Mon Mar 26 00:22:49 2012 +0200 @@ -491,7 +491,6 @@ info (_("Memory manager init")) self.initialized = defer.Deferred() self.host = host - self.contacts={} self.presenceStatus={} self.lastResource={} #tmp, will be refactored with bdd integration self.subscriptions={} @@ -655,51 +654,6 @@ assert(self.server_features.has_key(profile)) return feature in self.server_features[profile] - - def addContact(self, contact_jid, attributes, groups, profile_key): - debug("Memory addContact: %s",contact_jid.userhost()) - profile = self.getProfileName(profile_key) - if not profile: - error (_('Trying to add a contact to a non-existant profile')) - return - assert(isinstance(attributes,dict)) - assert(isinstance(groups,set)) - if not self.contacts.has_key(profile): - self.contacts[profile] = {} - self.contacts[profile][contact_jid.userhost()]=[attributes, groups] - - def delContact(self, contact_jid, profile_key): - debug("Memory delContact: %s",contact_jid.userhost()) - profile = self.getProfileName(profile_key) - if not profile: - error (_('Trying to delete a contact for a non-existant profile')) - return - if self.contacts.has_key(profile) and self.contacts[profile].has_key(contact_jid.userhost()): - del self.contacts[profile][contact_jid.userhost()] - - def getContact(self, contact_jid, profile_key): - profile = self.getProfileName(profile_key) - if not profile: - error(_('Asking a contact for a non-existant profile')) - return None - if self.contacts.has_key(profile) and self.contacts[profile].has_key(contact_jid.userhost()): - return self.contacts[profile][contact_jid.userhost()] - - def getContacts(self, profile_key): - """Return list of contacts for given profile - @param profile_key: profile key - @return list of [contact, attr, groups]""" - profile = self.getProfileName(profile_key) - if not profile: - error(_('Asking contacts for a non-existant profile')) - return [] - ret=[] - if self.contacts.has_key(profile): - for contact in self.contacts[profile]: - attr, groups = self.contacts[profile][contact] - ret.append([contact, attr, groups ]) - return ret - def getLastResource(self, contact, profile_key): """Return the last resource used by a contact @param contact: contact jid (unicode)