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