comparison 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
comparison
equal deleted inserted replaced
465:78e67a59d51d 466:448ce3c9e2ac
118 class SatRosterProtocol(xmppim.RosterClientProtocol): 118 class SatRosterProtocol(xmppim.RosterClientProtocol):
119 119
120 def __init__(self, host): 120 def __init__(self, host):
121 xmppim.RosterClientProtocol.__init__(self) 121 xmppim.RosterClientProtocol.__init__(self)
122 self.host = host 122 self.host = host
123 self._groups=set() 123 #XXX: the two following dicts keep a local copy of the roster
124 self._groups = {} #map from groups to bare jids: key=group value=set of bare jids
125 self._jids = {} #map from bare jids to RosterItem: key=jid value=RosterItem
124 126
125 def rosterCb(self, roster): 127 def rosterCb(self, roster):
126 for raw_jid, item in roster.iteritems(): 128 for raw_jid, item in roster.iteritems():
127 self.onRosterSet(item) 129 self.onRosterSet(item)
128 130
155 if roster_item.name: 157 if roster_item.name:
156 item['name'] = roster_item.name 158 item['name'] = roster_item.name
157 for group in roster_item.groups: 159 for group in roster_item.groups:
158 item.addElement('group', content=group) 160 item.addElement('group', content=group)
159 return iq.send() 161 return iq.send()
162
163 def getAttributes(self, item):
164 """Return dictionary of attributes as used in bridge from a RosterItem
165 @param item: RosterItem
166 @return: dictionary of attributes"""
167 item_attr = {'to': str(item.subscriptionTo),
168 'from': str(item.subscriptionFrom),
169 'ask': str(item.ask)
170 }
171 if item.name:
172 item_attr['name'] = item.name
173 return item_attr
160 174
161 def onRosterSet(self, item): 175 def onRosterSet(self, item):
162 """Called when a new/update roster item is received""" 176 """Called when a new/update roster item is received"""
163 #TODO: send a signal to frontends 177 #TODO: send a signal to frontends
164 if not item.subscriptionTo and not item.subscriptionFrom and not item.ask: 178 if not item.subscriptionTo and not item.subscriptionFrom and not item.ask:
165 #XXX: current behaviour: we don't want contact in our roster list 179 #XXX: current behaviour: we don't want contact in our roster list
166 #if there is no presence subscription 180 #if there is no presence subscription
167 #may change in the future 181 #may change in the future
168 self.removeItem(item.jid) 182 self.removeItem(item.jid)
169 return 183 return
170 item_attr = {'to': str(item.subscriptionTo),
171 'from': str(item.subscriptionFrom),
172 'ask': str(item.ask)
173 }
174 if item.name:
175 item_attr['name'] = item.name
176 info (_("new contact in roster list: %s"), item.jid.full()) 184 info (_("new contact in roster list: %s"), item.jid.full())
177 self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile) 185 #self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile)
178 self.host.bridge.newContact(item.jid.full(), item_attr, item.groups, self.parent.profile) 186
179 self._groups.update(item.groups) 187 bare_jid = item.jid.userhost()
188 self._jids[bare_jid] = item
189 for group in item.groups:
190 self._groups.get(group,set()).add(bare_jid)
191 self.host.bridge.newContact(item.jid.full(), self.getAttributes(item), item.groups, self.parent.profile)
180 192
181 def onRosterRemove(self, entity): 193 def onRosterRemove(self, entity):
182 """Called when a roster removal event is received""" 194 """Called when a roster removal event is received"""
183 print _("removing %s from roster list") % entity.full() 195 print _("removing %s from roster list") % entity.full()
184 self.host.memory.delContact(entity, self.parent.profile) 196 bare_jid = entity.userhost()
197
198 #we first remove item from local cache (self._groups and self._jids)
199 try:
200 item = self._jids.pop(bare_jid)
201 except KeyError:
202 log.warning("Received a roster remove event for an item not in cache")
203 return
204 for group in item.groups:
205 try:
206 jids_set = self._groups[group]
207 jids_set.remove(bare_jid)
208 if not jids_set:
209 del self._groups[group]
210 except KeyError:
211 log.warning("there is not cache for the group [%(groups)s] of the removed roster item [%(jid)s]" %
212 {"group": group, "jid": bare_jid})
213
214 #then we send the bridge signal
185 self.host.bridge.contactDeleted(entity.userhost(), self.parent.profile) 215 self.host.bridge.contactDeleted(entity.userhost(), self.parent.profile)
186 216
187 def getGroups(self): 217 def getGroups(self):
188 """Return a set of groups""" 218 """Return a list of groups"""
189 return self._groups 219 return self._groups.keys()
220
221 def getItem(self, jid):
222 """Return RosterItem for a given jid
223 @param jid: jid of the contact
224 @return: RosterItem or None if contact is not in cache"""
225 return self._jids.get(jid.userhost(), None)
226
227 def getItems(self):
228 """Return all items of the roster"""
229 return self._jids
230
190 231
191 class SatPresenceProtocol(xmppim.PresenceClientProtocol): 232 class SatPresenceProtocol(xmppim.PresenceClientProtocol):
192 233
193 def __init__(self, host): 234 def __init__(self, host):
194 xmppim.PresenceClientProtocol.__init__(self) 235 xmppim.PresenceClientProtocol.__init__(self)
241 del statuses[None] 282 del statuses[None]
242 283
243 def subscribed(self, entity): 284 def subscribed(self, entity):
244 xmppim.PresenceClientProtocol.subscribed(self, entity) 285 xmppim.PresenceClientProtocol.subscribed(self, entity)
245 self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile) 286 self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile)
246 contact = self.host.memory.getContact(entity, self.parent.profile) 287 item = self.parent.roster.getItem(entity)
247 if not contact or contact[0]['to'] == 'False': #we automatically subscribe to 'to' presence 288 if not item or not item.subscriptionTo: #we automatically subscribe to 'to' presence
248 debug(_('sending automatic "from" subscription request')) 289 debug(_('sending automatic "from" subscription request'))
249 self.subscribe(entity) 290 self.subscribe(entity)
250 291
251 def unsubscribed(self, entity): 292 def unsubscribed(self, entity):
252 xmppim.PresenceClientProtocol.unsubscribed(self, entity) 293 xmppim.PresenceClientProtocol.unsubscribed(self, entity)
260 debug (_("unsubscription confirmed for [%s]") % entity.userhost()) 301 debug (_("unsubscription confirmed for [%s]") % entity.userhost())
261 self.host.bridge.subscribe('unsubscribed', entity.userhost(), self.parent.profile) 302 self.host.bridge.subscribe('unsubscribed', entity.userhost(), self.parent.profile)
262 303
263 def subscribeReceived(self, entity): 304 def subscribeReceived(self, entity):
264 debug (_("subscription request from [%s]") % entity.userhost()) 305 debug (_("subscription request from [%s]") % entity.userhost())
265 contact = self.host.memory.getContact(entity, self.parent.profile) 306 item = self.parent.roster.getItem(entity)
266 if contact and contact[0]['to'] == 'True': 307 if item and item.subscriptionTo:
267 #We automatically accept subscription if we are already subscribed to contact presence 308 #We automatically accept subscription if we are already subscribed to contact presence
268 debug(_('sending automatic subscription acceptance')) 309 debug(_('sending automatic subscription acceptance'))
269 self.subscribed(entity) 310 self.subscribed(entity)
270 else: 311 else:
271 self.host.memory.addWaitingSub('subscribe', entity.userhost(), self.parent.profile) 312 self.host.memory.addWaitingSub('subscribe', entity.userhost(), self.parent.profile)
272 self.host.bridge.subscribe('subscribe', entity.userhost(), self.parent.profile) 313 self.host.bridge.subscribe('subscribe', entity.userhost(), self.parent.profile)
273 314
274 def unsubscribeReceived(self, entity): 315 def unsubscribeReceived(self, entity):
275 debug (_("unsubscription asked for [%s]") % entity.userhost()) 316 debug (_("unsubscription asked for [%s]") % entity.userhost())
276 contact = self.host.memory.getContact(entity, self.parent.profile) 317 item = self.parent.roster.getItem(entity)
277 if contact and contact[0]['from'] == 'True': #we automatically remove contact 318 if item and item.subscriptionFrom: #we automatically remove contact
278 debug(_('automatic contact deletion')) 319 debug(_('automatic contact deletion'))
279 self.host.delContact(entity.userhost(), self.parent.profile) 320 self.host.delContact(entity.userhost(), self.parent.profile)
280 self.host.bridge.subscribe('unsubscribe', entity.userhost(), self.parent.profile) 321 self.host.bridge.subscribe('unsubscribe', entity.userhost(), self.parent.profile)
281 322
282 class SatDiscoProtocol(disco.DiscoClientProtocol): 323 class SatDiscoProtocol(disco.DiscoClientProtocol):