Mercurial > libervia-backend
comparison 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 |
comparison
equal
deleted
inserted
replaced
1261:93bce9e4c9c8 | 1262:f8a8434dbac7 |
---|---|
170 class SatRosterProtocol(xmppim.RosterClientProtocol): | 170 class SatRosterProtocol(xmppim.RosterClientProtocol): |
171 | 171 |
172 def __init__(self, host): | 172 def __init__(self, host): |
173 xmppim.RosterClientProtocol.__init__(self) | 173 xmppim.RosterClientProtocol.__init__(self) |
174 self.host = host | 174 self.host = host |
175 self.got_roster = defer.Deferred() | 175 self.got_roster = defer.Deferred() # called when roster is received and ready |
176 #XXX: the two following dicts keep a local copy of the roster | 176 #XXX: the two following dicts keep a local copy of the roster |
177 self._groups = {} # map from groups to bare jids: key=group value=set of bare jids | 177 self._groups = {} # map from groups to jids: key=group value=set of jids |
178 self._jids = {} # map from bare jids to RosterItem: key=jid value=RosterItem | 178 self._jids = None # map from jids to RosterItem: key=jid value=RosterItem |
179 | 179 |
180 def rosterCb(self, roster): | 180 def rosterCb(self, roster): |
181 for raw_jid, item in roster.iteritems(): | 181 assert roster is not None # FIXME: must be managed with roster versioning |
182 self.onRosterSet(item) | 182 self._jids = roster |
183 | |
184 def _registerItem(self, item): | |
185 """Register item in local cache | |
186 | |
187 item must be already registered in self._jids before this method is called | |
188 @param item (RosterIem): item added | |
189 """ | |
190 log.debug("registering item: {}".format(item.jid.full())) | |
191 if item.entity.resource: | |
192 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.") | |
193 import ipdb; ipdb.set_trace() | |
194 if not item.subscriptionTo and not item.subscriptionFrom and not item.ask: | |
195 #XXX: current behaviour: we don't want contact in our roster list | |
196 # if there is no presence subscription | |
197 # may change in the future | |
198 self.removeItem(item.jid) # FIXME: to be checked | |
199 return | |
200 if not item.subscriptionTo: | |
201 if not item.subscriptionFrom: | |
202 log.info(_("There's no subscription between you and [{}]!").format(item.jid.full())) | |
203 else: | |
204 log.info(_("You are not subscribed to [{}]!").format(item.jid.full())) | |
205 if not item.subscriptionFrom: | |
206 log.info(_("[{}] is not subscribed to you!").format(item.jid.full())) | |
207 #self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile) | |
208 | |
209 for group in item.groups: | |
210 self._groups.setdefault(group, set()).add(item.entity) | |
183 | 211 |
184 def requestRoster(self): | 212 def requestRoster(self): |
185 """ ask the server for Roster list """ | 213 """ ask the server for Roster list """ |
186 log.debug("requestRoster") | 214 log.debug("requestRoster") |
187 d = self.getRoster().addCallback(self.rosterCb) | 215 d = self.getRoster().addCallback(self.rosterCb) |
188 d.chainDeferred(self.got_roster) | 216 d.chainDeferred(self.got_roster) |
189 | 217 |
190 def removeItem(self, to_jid): | 218 def removeItem(self, to_jid): |
191 """Remove a contact from roster list | 219 """Remove a contact from roster list |
192 @param to_jid: a JID instance | 220 @param to_jid: a JID instance |
221 @return: Deferred | |
193 """ | 222 """ |
194 xmppim.RosterClientProtocol.removeItem(self, to_jid) | 223 return xmppim.RosterClientProtocol.removeItem(self, to_jid) |
195 #TODO: check IQ result | |
196 | |
197 #XXX: disabled (cf http://wokkel.ik.nu/ticket/56)) | |
198 #def addItem(self, to): | |
199 #"""Add a contact to roster list""" | |
200 #xmppim.RosterClientProtocol.addItem(self, to) | |
201 #TODO: check IQ result""" | |
202 | |
203 def updateItem(self, roster_item): | |
204 """ | |
205 Update an item of the contact list. | |
206 | |
207 @param roster_item: item to update | |
208 """ | |
209 iq = compat.IQ(self.xmlstream, 'set') | |
210 iq.addElement((xmppim.NS_ROSTER, 'query')) | |
211 item = iq.query.addElement('item') | |
212 item['jid'] = roster_item.jid.userhost() | |
213 if roster_item.name: | |
214 item['name'] = roster_item.name | |
215 for group in roster_item.groups: | |
216 item.addElement('group', content=group) | |
217 return iq.send() | |
218 | 224 |
219 def getAttributes(self, item): | 225 def getAttributes(self, item): |
220 """Return dictionary of attributes as used in bridge from a RosterItem | 226 """Return dictionary of attributes as used in bridge from a RosterItem |
221 @param item: RosterItem | 227 @param item: RosterItem |
222 @return: dictionary of attributes""" | 228 @return: dictionary of attributes""" |
226 } | 232 } |
227 if item.name: | 233 if item.name: |
228 item_attr['name'] = item.name | 234 item_attr['name'] = item.name |
229 return item_attr | 235 return item_attr |
230 | 236 |
231 def onRosterSet(self, item): | 237 def setReceived(self, request): |
232 """Called when a new/update roster item is received""" | 238 #TODO: implement roster versioning (cf RFC 6121 ยง2.6) |
233 #TODO: send a signal to frontends | 239 item = request.item |
234 if not item.subscriptionTo and not item.subscriptionFrom and not item.ask: | 240 self._jids[item.entity] = item |
235 #XXX: current behaviour: we don't want contact in our roster list | |
236 # if there is no presence subscription | |
237 # may change in the future | |
238 self.removeItem(item.jid) | |
239 return | |
240 log.debug(_("New contact in roster list: %s") % item.jid.full()) | |
241 if not item.subscriptionTo: | |
242 if not item.subscriptionFrom: | |
243 log.info(_("There's no subscription between you and [%s]!") % item.jid.full()) | |
244 else: | |
245 log.info(_("You are not subscribed to [%s]!") % item.jid.full()) | |
246 if not item.subscriptionFrom: | |
247 log.info(_("[%s] is not subscribed to you!") % item.jid.full()) | |
248 #self.host.memory.addContact(item.jid, item_attr, item.groups, self.parent.profile) | |
249 | |
250 bare_jid = item.jid.userhostJID() | |
251 self._jids[bare_jid] = item | |
252 for group in item.groups: | |
253 self._groups.setdefault(group, set()).add(bare_jid) | |
254 self.host.bridge.newContact(item.jid.full(), self.getAttributes(item), item.groups, self.parent.profile) | 241 self.host.bridge.newContact(item.jid.full(), self.getAttributes(item), item.groups, self.parent.profile) |
255 | 242 |
256 def onRosterRemove(self, entity): | 243 def removeReceived(self, request): |
257 """Called when a roster removal event is received""" | 244 entity = request.item.entity |
258 print _("removing %s from roster list") % entity.full() | 245 print _("removing %s from roster list") % entity.full() |
259 bare_jid = entity.userhostJID() | |
260 | 246 |
261 # we first remove item from local cache (self._groups and self._jids) | 247 # we first remove item from local cache (self._groups and self._jids) |
262 try: | 248 try: |
263 item = self._jids.pop(bare_jid) | 249 item = self._jids.pop(entity) |
264 except KeyError: | 250 except KeyError: |
265 log.warning("Received a roster remove event for an item not in cache") | 251 log.error("Received a roster remove event for an item not in cache ({})".format(entity)) |
266 return | 252 return |
267 for group in item.groups: | 253 for group in item.groups: |
268 try: | 254 try: |
269 jids_set = self._groups[group] | 255 jids_set = self._groups[group] |
270 jids_set.remove(bare_jid) | 256 jids_set.remove(entity) |
271 if not jids_set: | 257 if not jids_set: |
272 del self._groups[group] | 258 del self._groups[group] |
273 except KeyError: | 259 except KeyError: |
274 log.warning("there is not cache for the group [%(groups)s] of the removed roster item [%(jid)s]" % | 260 log.warning("there is not cache for the group [%(groups)s] of the removed roster item [%(jid)s]" % |
275 {"group": group, "jid": bare_jid}) | 261 {"group": group, "jid": entity}) |
276 | 262 |
277 # then we send the bridge signal | 263 # then we send the bridge signal |
278 self.host.bridge.contactDeleted(entity.userhost(), self.parent.profile) | 264 self.host.bridge.contactDeleted(entity.full(), self.parent.profile) |
279 | 265 |
280 def getGroups(self): | 266 def getGroups(self): |
281 """Return a list of groups""" | 267 """Return a list of groups""" |
282 return self._groups.keys() | 268 return self._groups.keys() |
283 | 269 |
284 def getItem(self, jid): | 270 def getItem(self, jid): |
285 """Return RosterItem for a given jid | 271 """Return RosterItem for a given jid |
272 | |
286 @param jid: jid of the contact | 273 @param jid: jid of the contact |
287 @return: RosterItem or None if contact is not in cache""" | 274 @return: RosterItem or None if contact is not in cache |
288 return self._jids.get(jid.userhostJID(), None) | 275 """ |
289 | 276 return self._jids.get(jid, None) |
290 def getBareJids(self): | 277 |
291 """Return all bare jids (as unicode) of the roster""" | 278 def getJids(self): |
279 """Return all jids of the roster""" | |
292 return self._jids.keys() | 280 return self._jids.keys() |
293 | 281 |
294 def isJidInRoster(self, entity_jid): | 282 def isJidInRoster(self, entity_jid): |
295 """Return True if jid is in roster""" | 283 """Return True if jid is in roster""" |
296 return entity_jid.userhostJID() in self._jids | 284 return entity_jid in self._jids |
297 | 285 |
298 def getItems(self): | 286 def getItems(self): |
299 """Return all items of the roster""" | 287 """Return all items of the roster""" |
300 return self._jids.values() | 288 return self._jids.values() |
301 | 289 |
316 if not self.host.trigger.point("Presence send", self.parent, obj): | 304 if not self.host.trigger.point("Presence send", self.parent, obj): |
317 return | 305 return |
318 super(SatPresenceProtocol, self).send(obj) | 306 super(SatPresenceProtocol, self).send(obj) |
319 | 307 |
320 def availableReceived(self, entity, show=None, statuses=None, priority=0): | 308 def availableReceived(self, entity, show=None, statuses=None, priority=0): |
321 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}) | 309 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}) |
322 | 310 |
323 if not statuses: | 311 if not statuses: |
324 statuses = {} | 312 statuses = {} |
325 | 313 |
326 if None in statuses: # we only want string keys | 314 if None in statuses: # we only want string keys |
339 self.host.bridge.presenceUpdate(entity.full(), show or "", | 327 self.host.bridge.presenceUpdate(entity.full(), show or "", |
340 int(priority), statuses, | 328 int(priority), statuses, |
341 self.parent.profile) | 329 self.parent.profile) |
342 | 330 |
343 def unavailableReceived(self, entity, statuses=None): | 331 def unavailableReceived(self, entity, statuses=None): |
344 log.debug(_("presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {'entity': entity, 'statuses': statuses}) | 332 log.debug(_("presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {'entity': entity, C.PRESENCE_STATUSES: statuses}) |
345 | 333 |
346 if not statuses: | 334 if not statuses: |
347 statuses = {} | 335 statuses = {} |
348 | 336 |
349 if None in statuses: # we only want string keys | 337 if None in statuses: # we only want string keys |