Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0054.py @ 1970:200cd707a46d
plugin XEP-0045, quick_frontend + primitivus (chat): cleaning of XEP-0045 (first pass):
- bridge methods/signals now all start with "muc" to follow new convention
- internal method use client instead of profile to follow new convention
- removed excetpions from plugin XEP-0045 in favor of core.exceptions, NotReady added
- cleaned/simplified several part of the code. checkClient removed as it is not needed anymore
- self.clients map removed, muc data are now stored directly in client
- getRoomEntityNick and getRoomNicksOfUsers are removed as they don't look sane.
/!\ This break all room game plugins for the moment
- use of uuid4 instead of uuid1 for getUniqueName, as host ID and current time are used for uuid1
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 27 Jun 2016 21:45:11 +0200 |
parents | 2daf7b4c6756 |
children | 8156f2116dc9 |
comparison
equal
deleted
inserted
replaced
1969:5fbe09b9b568 | 1970:200cd707a46d |
---|---|
107 x_elt.addElement('photo', content=avatar_hash) | 107 x_elt.addElement('photo', content=avatar_hash) |
108 presence_elt.addChild(x_elt) | 108 presence_elt.addChild(x_elt) |
109 | 109 |
110 return True | 110 return True |
111 | 111 |
112 def isInRoom(self, entity_jid, profile): | 112 def isInRoom(self, client, entity_jid): |
113 """Tell if an full jid is a member of a room | 113 """Tell if an full jid is a member of a room |
114 | 114 |
115 @param entity_jid(jid.JID): full jid of the entity | 115 @param entity_jid(jid.JID): full jid of the entity |
116 @return (bool): True if the bare jid of the entity is a room jid | 116 @return (bool): True if the bare jid of the entity is a room jid |
117 """ | 117 """ |
118 try: | 118 try: |
119 return self.host.plugins['XEP-0045'].isRoom(entity_jid.userhostJID(), profile_key=profile) | 119 self.host.plugins['XEP-0045'].checkRoomJoined(client, entity_jid.userhostJID()) |
120 except KeyError: | 120 except exceptions.NotFound: |
121 return False | 121 return False |
122 else: | |
123 return True | |
122 | 124 |
123 def _fillCachedValues(self, profile): | 125 def _fillCachedValues(self, profile): |
124 #FIXME: this is really suboptimal, need to be reworked | 126 #FIXME: this is really suboptimal, need to be reworked |
125 # the current naive approach keeps a map between all jids of all profiles | 127 # the current naive approach keeps a map between all jids of all profiles |
126 # in persistent cache, then put avatar hashs in memory. | 128 # in persistent cache, then put avatar hashs in memory. |
141 | 143 |
142 def profileDisconnected(self, profile): | 144 def profileDisconnected(self, profile): |
143 log.debug(u"Deleting profile cache for avatars") | 145 log.debug(u"Deleting profile cache for avatars") |
144 del self.cache[profile] | 146 del self.cache[profile] |
145 | 147 |
146 def updateCache(self, jid_, name, value, profile): | 148 def updateCache(self, client, jid_, name, value): |
147 """update cache value | 149 """update cache value |
148 | 150 |
149 save value in memory in case of change | 151 save value in memory in case of change |
150 @param jid_(jid.JID): jid of the owner of the vcard | 152 @param jid_(jid.JID): jid of the owner of the vcard |
151 @param name(str): name of the item which changed | 153 @param name(str): name of the item which changed |
152 @param value(unicode): new value of the item | 154 @param value(unicode): new value of the item |
153 @param profile(unicode): profile which received the update | |
154 """ | 155 """ |
155 if jid_.resource: | 156 if jid_.resource: |
156 if not self.isInRoom(jid_, profile): | 157 if not self.isInRoom(client, jid_): |
157 # VCard are retrieved with bare jid | 158 # VCard are retrieved with bare jid |
158 # but MUC room is a special case | 159 # but MUC room is a special case |
159 jid_ = jid.userhostJID() | 160 jid_ = jid.userhostJID() |
160 | 161 |
161 self.host.memory.updateEntityData(jid_, name, value, profile_key=profile) | 162 self.host.memory.updateEntityData(jid_, name, value, profile_key=client.profile) |
162 if name in CACHED_DATA: | 163 if name in CACHED_DATA: |
163 jid_s = jid_.userhost() | 164 jid_s = jid_.userhost() |
164 self.cache[profile].setdefault(jid_s, {})[name] = value | 165 self.cache[client.profile].setdefault(jid_s, {})[name] = value |
165 self.cache[profile].force(jid_s) | 166 self.cache[client.profile].force(jid_s) |
166 | 167 |
167 def getCache(self, entity_jid, name, profile): | 168 def getCache(self, client, entity_jid, name): |
168 """return cached value for jid | 169 """return cached value for jid |
169 | 170 |
170 @param entity_jid: target contact | 171 @param entity_jid: target contact |
171 @param name: name of the value ('nick' or 'avatar') | 172 @param name: name of the value ('nick' or 'avatar') |
172 @param profile: %(doc_profile)s | |
173 @return: wanted value or None""" | 173 @return: wanted value or None""" |
174 if entity_jid.resource: | 174 if entity_jid.resource: |
175 if not self.isInRoom(entity_jid, profile): | 175 if not self.isInRoom(client, entity_jid): |
176 # VCard are retrieved with bare jid | 176 # VCard are retrieved with bare jid |
177 # but MUC room is a special case | 177 # but MUC room is a special case |
178 entity_jid = jid.userhostJID() | 178 entity_jid = jid.userhostJID() |
179 try: | 179 try: |
180 data = self.host.memory.getEntityData(entity_jid, [name], profile) | 180 data = self.host.memory.getEntityData(entity_jid, [name], client.profile) |
181 except exceptions.UnknownEntityError: | 181 except exceptions.UnknownEntityError: |
182 return None | 182 return None |
183 return data.get(name) | 183 return data.get(name) |
184 | 184 |
185 def _getFilename(self, hash_): | 185 def _getFilename(self, hash_): |
215 image_hash = sha1(decoded).hexdigest() | 215 image_hash = sha1(decoded).hexdigest() |
216 self.saveAvatarFile(decoded, image_hash) | 216 self.saveAvatarFile(decoded, image_hash) |
217 return image_hash | 217 return image_hash |
218 | 218 |
219 @defer.inlineCallbacks | 219 @defer.inlineCallbacks |
220 def vCard2Dict(self, vcard, target, profile): | 220 def vCard2Dict(self, client, vcard, target): |
221 """Convert a VCard to a dict, and save binaries""" | 221 """Convert a VCard to a dict, and save binaries""" |
222 log.debug(_("parsing vcard")) | 222 log.debug(_("parsing vcard")) |
223 dictionary = {} | 223 dictionary = {} |
224 | 224 |
225 for elem in vcard.elements(): | 225 for elem in vcard.elements(): |
226 if elem.name == 'FN': | 226 if elem.name == 'FN': |
227 dictionary['fullname'] = unicode(elem) | 227 dictionary['fullname'] = unicode(elem) |
228 elif elem.name == 'NICKNAME': | 228 elif elem.name == 'NICKNAME': |
229 dictionary['nick'] = unicode(elem) | 229 dictionary['nick'] = unicode(elem) |
230 self.updateCache(target, 'nick', dictionary['nick'], profile) | 230 self.updateCache(client, target, 'nick', dictionary['nick']) |
231 elif elem.name == 'URL': | 231 elif elem.name == 'URL': |
232 dictionary['website'] = unicode(elem) | 232 dictionary['website'] = unicode(elem) |
233 elif elem.name == 'EMAIL': | 233 elif elem.name == 'EMAIL': |
234 dictionary['email'] = unicode(elem) | 234 dictionary['email'] = unicode(elem) |
235 elif elem.name == 'BDAY': | 235 elif elem.name == 'BDAY': |
237 elif elem.name == 'PHOTO': | 237 elif elem.name == 'PHOTO': |
238 dictionary["avatar"] = yield threads.deferToThread(self.savePhoto, elem) | 238 dictionary["avatar"] = yield threads.deferToThread(self.savePhoto, elem) |
239 if not dictionary["avatar"]: # can happen in case of e.g. empty photo elem | 239 if not dictionary["avatar"]: # can happen in case of e.g. empty photo elem |
240 del dictionary['avatar'] | 240 del dictionary['avatar'] |
241 else: | 241 else: |
242 self.updateCache(target, 'avatar', dictionary['avatar'], profile) | 242 self.updateCache(client, target, 'avatar', dictionary['avatar']) |
243 else: | 243 else: |
244 log.info(_('FIXME: [%s] VCard tag is not managed yet') % elem.name) | 244 log.info(_('FIXME: [%s] VCard tag is not managed yet') % elem.name) |
245 | 245 |
246 # if a data in cache doesn't exist anymore, we need to reset it | 246 # if a data in cache doesn't exist anymore, we need to reset it |
247 # so we check CACHED_DATA no gotten (i.e. not in dictionary keys) | 247 # so we check CACHED_DATA no gotten (i.e. not in dictionary keys) |
248 # and we reset them | 248 # and we reset them |
249 for datum in CACHED_DATA.difference(dictionary.keys()): | 249 for datum in CACHED_DATA.difference(dictionary.keys()): |
250 log.debug(u"reseting vcard datum [{datum}] for {entity}".format(datum=datum, entity=target.full())) | 250 log.debug(u"reseting vcard datum [{datum}] for {entity}".format(datum=datum, entity=target.full())) |
251 self.updateCache(target, datum, '', profile) | 251 self.updateCache(client, target, datum, '') |
252 | 252 |
253 defer.returnValue(dictionary) | 253 defer.returnValue(dictionary) |
254 | 254 |
255 def _VCardCb(self, answer, profile): | 255 def _VCardCb(self, answer, client): |
256 """Called after the first get IQ""" | 256 """Called after the first get IQ""" |
257 log.debug(_("VCard found")) | 257 log.debug(_("VCard found")) |
258 | 258 |
259 if answer.firstChildElement().name == "vCard": | 259 if answer.firstChildElement().name == "vCard": |
260 _jid, steam = self.host.getJidNStream(profile) | |
261 try: | 260 try: |
262 from_jid = jid.JID(answer["from"]) | 261 from_jid = jid.JID(answer["from"]) |
263 except KeyError: | 262 except KeyError: |
264 from_jid = _jid.userhostJID() | 263 from_jid = client.jid.userhostJID() |
265 d = self.vCard2Dict(answer.firstChildElement(), from_jid, profile) | 264 d = self.vCard2Dict(client, answer.firstChildElement(), from_jid) |
266 d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data, profile)) | 265 d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data, client.profile)) |
267 else: | 266 else: |
268 log.error(_("FIXME: vCard not found as first child element")) | 267 log.error(_("FIXME: vCard not found as first child element")) |
269 self.host.bridge.actionResult("SUPPRESS", answer['id'], {}, profile) # FIXME: maybe an error message would be better | 268 self.host.bridge.actionResult("SUPPRESS", answer['id'], {}, client.profile) # FIXME: maybe an error message would be better |
270 | 269 |
271 def _VCardEb(self, failure, profile): | 270 def _VCardEb(self, failure, client): |
272 """Called when something is wrong with registration""" | 271 """Called when something is wrong with registration""" |
273 try: | 272 try: |
274 self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}, profile) # FIXME: maybe an error message would be better | 273 self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}, client.profile) # FIXME: maybe an error message would be better |
275 log.warning(_(u"Can't find VCard of %s") % failure.value.stanza['from']) | 274 log.warning(_(u"Can't find VCard of %s") % failure.value.stanza['from']) |
276 self.updateCache(jid.JID(failure.value.stanza['from']), "avatar", '', profile) | 275 self.updateCache(client, jid.JID(failure.value.stanza['from']), "avatar", '') |
277 except (AttributeError, KeyError): | 276 except (AttributeError, KeyError): |
278 # 'ConnectionLost' object has no attribute 'stanza' + sometimes 'from' key doesn't exist | 277 # 'ConnectionLost' object has no attribute 'stanza' + sometimes 'from' key doesn't exist |
279 log.warning(_(u"Can't find VCard: %s") % failure.getErrorMessage()) | 278 log.warning(_(u"Can't find VCard: %s") % failure.getErrorMessage()) |
280 | 279 |
281 def _getCard(self, target_s, profile_key=C.PROF_KEY_NONE): | 280 def _getCard(self, target_s, profile_key=C.PROF_KEY_NONE): |
282 return self.getCard(jid.JID(target_s), profile_key) | 281 client = self.host.getClient(profile_key) |
283 | 282 return self.getCard(client, jid.JID(target_s)) |
284 def getCard(self, target, profile_key=C.PROF_KEY_NONE): | 283 |
284 def getCard(self, client, target): | |
285 """Ask server for VCard | 285 """Ask server for VCard |
286 | 286 |
287 @param target(jid.JID): jid from which we want the VCard | 287 @param target(jid.JID): jid from which we want the VCard |
288 @result: id to retrieve the profile | 288 @result: id to retrieve the profile |
289 """ | 289 """ |
290 current_jid, xmlstream = self.host.getJidNStream(profile_key) | |
291 if not xmlstream: | |
292 raise exceptions.ProfileUnknownError('Asking vcard for a non-existant or not connected profile ({})'.format(profile_key)) | |
293 profile = self.host.memory.getProfileName(profile_key) | |
294 to_jid = target.userhostJID() | 290 to_jid = target.userhostJID() |
295 log.debug(_(u"Asking for %s's VCard") % to_jid.userhost()) | 291 log.debug(_(u"Asking for %s's VCard") % to_jid.userhost()) |
296 reg_request = IQ(xmlstream, 'get') | 292 reg_request = client.IQ('get') |
297 reg_request["from"] = current_jid.full() | 293 reg_request["from"] = client.jid.full() |
298 reg_request["to"] = to_jid.userhost() | 294 reg_request["to"] = to_jid.userhost() |
299 reg_request.addElement('vCard', NS_VCARD) | 295 reg_request.addElement('vCard', NS_VCARD) |
300 reg_request.send(to_jid.userhost()).addCallbacks(self._VCardCb, self._VCardEb, callbackArgs=[profile], errbackArgs=[profile]) | 296 reg_request.send(to_jid.userhost()).addCallbacks(self._VCardCb, self._VCardEb, callbackArgs=[client], errbackArgs=[client]) |
301 return reg_request["id"] | 297 return reg_request["id"] |
302 | 298 |
303 def getAvatarFile(self, avatar_hash): | 299 def getAvatarFile(self, avatar_hash): |
304 """Give the full path of avatar from hash | 300 """Give the full path of avatar from hash |
305 @param hash: SHA1 hash | 301 @param hash: SHA1 hash |
352 d = threads.deferToThread(self._buildSetAvatar, vcard_set, filepath) | 348 d = threads.deferToThread(self._buildSetAvatar, vcard_set, filepath) |
353 | 349 |
354 def elementBuilt(result): | 350 def elementBuilt(result): |
355 """Called once the image is at the right size/format, and the vcard set element is build""" | 351 """Called once the image is at the right size/format, and the vcard set element is build""" |
356 set_avatar_elt, img_hash = result | 352 set_avatar_elt, img_hash = result |
357 self.updateCache(client.jid.userhostJID(), 'avatar', img_hash, client.profile) | 353 self.updateCache(client, client.jid.userhostJID(), 'avatar', img_hash) |
358 return set_avatar_elt.send().addCallback(lambda ignore: client.presence.available()) # FIXME: should send the current presence, not always "available" ! | 354 return set_avatar_elt.send().addCallback(lambda ignore: client.presence.available()) # FIXME: should send the current presence, not always "available" ! |
359 | 355 |
360 d.addCallback(elementBuilt) | 356 d.addCallback(elementBuilt) |
361 | 357 |
362 return d | 358 return d |
383 | 379 |
384 Check for avatar information, and get VCard if needed | 380 Check for avatar information, and get VCard if needed |
385 @param presend(domish.Element): <presence/> stanza | 381 @param presend(domish.Element): <presence/> stanza |
386 """ | 382 """ |
387 from_jid = jid.JID(presence['from']) | 383 from_jid = jid.JID(presence['from']) |
388 if from_jid.resource and not self.plugin_parent.isInRoom(from_jid, self.parent.profile): | 384 if from_jid.resource and not self.plugin_parent.isInRoom(self.parent, from_jid): |
389 from_jid = from_jid.userhostJID() | 385 from_jid = from_jid.userhostJID() |
390 #FIXME: wokkel's data_form should be used here | 386 #FIXME: wokkel's data_form should be used here |
391 try: | 387 try: |
392 x_elt = presence.elements(NS_VCARD_UPDATE, 'x').next() | 388 x_elt = presence.elements(NS_VCARD_UPDATE, 'x').next() |
393 except StopIteration: | 389 except StopIteration: |
399 return | 395 return |
400 | 396 |
401 hash_ = str(photo_elt) | 397 hash_ = str(photo_elt) |
402 if not hash_: | 398 if not hash_: |
403 return | 399 return |
404 old_avatar = self.plugin_parent.getCache(from_jid, 'avatar', self.parent.profile) | 400 old_avatar = self.plugin_parent.getCache(self.parent, from_jid, 'avatar') |
405 filename = self.plugin_parent._getFilename(hash_) | 401 filename = self.plugin_parent._getFilename(hash_) |
406 if not old_avatar or old_avatar != hash_: | 402 if not old_avatar or old_avatar != hash_: |
407 if os.path.exists(filename): | 403 if os.path.exists(filename): |
408 log.debug(u"New avatar found for [{}], it's already in cache, we use it".format(from_jid.full())) | 404 log.debug(u"New avatar found for [{}], it's already in cache, we use it".format(from_jid.full())) |
409 self.plugin_parent.updateCache(from_jid, 'avatar', hash_, self.parent.profile) | 405 self.plugin_parent.updateCache(self.parent, from_jid, 'avatar', hash_) |
410 else: | 406 else: |
411 log.debug(u'New avatar found for [{}], requesting vcard'.format(from_jid.full())) | 407 log.debug(u'New avatar found for [{}], requesting vcard'.format(from_jid.full())) |
412 self.plugin_parent.getCard(from_jid, self.parent.profile) | 408 self.plugin_parent.getCard(from_jid, self.parent.profile) |
413 else: | 409 else: |
414 if os.path.exists(filename): | 410 if os.path.exists(filename): |