Mercurial > libervia-backend
diff sat/plugins/plugin_xep_0054.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 395a3d1c2888 |
children | 378188abe941 |
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0054.py Wed Jun 27 07:51:29 2018 +0200 +++ b/sat/plugins/plugin_xep_0054.py Wed Jun 27 20:14:46 2018 +0200 @@ -21,6 +21,7 @@ from sat.core.i18n import _ from sat.core.constants import Const as C from sat.core.log import getLogger + log = getLogger(__name__) from twisted.internet import threads, defer from twisted.words.protocols.jabber import jid, error @@ -36,10 +37,13 @@ from sat.core import exceptions from sat.memory import persistent import mimetypes + try: from PIL import Image except: - raise exceptions.MissingModule(u"Missing module pillow, please download/install it from https://python-pillow.github.io") + raise exceptions.MissingModule( + u"Missing module pillow, please download/install it from https://python-pillow.github.io" + ) from cStringIO import StringIO try: @@ -48,17 +52,17 @@ from wokkel.subprotocols import XMPPHandler AVATAR_PATH = "avatars" -AVATAR_DIM = (64, 64) # FIXME: dim are not adapted to modern resolutions ! +AVATAR_DIM = (64, 64) # FIXME: dim are not adapted to modern resolutions ! IQ_GET = '/iq[@type="get"]' -NS_VCARD = 'vcard-temp' +NS_VCARD = "vcard-temp" VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' # TODO: manage requests -PRESENCE = '/presence' -NS_VCARD_UPDATE = 'vcard-temp:x:update' +PRESENCE = "/presence" +NS_VCARD_UPDATE = "vcard-temp:x:update" VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]' -CACHED_DATA = {'avatar', 'nick'} +CACHED_DATA = {"avatar", "nick"} MAX_AGE = 60 * 60 * 24 * 365 PLUGIN_INFO = { @@ -70,20 +74,34 @@ C.PI_RECOMMENDATIONS: ["XEP-0045"], C.PI_MAIN: "XEP_0054", C.PI_HANDLER: "yes", - C.PI_DESCRIPTION: _("""Implementation of vcard-temp""") + C.PI_DESCRIPTION: _("""Implementation of vcard-temp"""), } class XEP_0054(object): - #TODO: - check that nickname is ok + # TODO: - check that nickname is ok # - refactor the code/better use of Wokkel # - get missing values def __init__(self, host): log.info(_(u"Plugin XEP_0054 initialization")) self.host = host - host.bridge.addMethod(u"avatarGet", u".plugin", in_sign=u'sbbs', out_sign=u's', method=self._getAvatar, async=True) - host.bridge.addMethod(u"avatarSet", u".plugin", in_sign=u'ss', out_sign=u'', method=self._setAvatar, async=True) + host.bridge.addMethod( + u"avatarGet", + u".plugin", + in_sign=u"sbbs", + out_sign=u"s", + method=self._getAvatar, + async=True, + ) + host.bridge.addMethod( + u"avatarSet", + u".plugin", + in_sign=u"ss", + out_sign=u"", + method=self._setAvatar, + async=True, + ) host.trigger.add(u"presence_available", self.presenceAvailableTrigger) host.memory.setSignalOnUpdate(u"avatar") host.memory.setSignalOnUpdate(u"nick") @@ -98,7 +116,7 @@ @return (bool): True if the bare jid of the entity is a room jid """ try: - muc_plg = self.host.plugins['XEP-0045'] + muc_plg = self.host.plugins["XEP-0045"] except KeyError: return False @@ -123,12 +141,12 @@ def presenceAvailableTrigger(self, presence_elt, client): if client.jid.userhost() in client._cache_0054: try: - avatar_hash = client._cache_0054[client.jid.userhost()]['avatar'] + avatar_hash = client._cache_0054[client.jid.userhost()]["avatar"] except KeyError: log.info(u"No avatar in cache for {}".format(client.jid.userhost())) return True - x_elt = domish.Element((NS_VCARD_UPDATE, 'x')) - x_elt.addElement('photo', content=avatar_hash) + x_elt = domish.Element((NS_VCARD_UPDATE, "x")) + x_elt.addElement("photo", content=avatar_hash) presence_elt.addChild(x_elt) return True @@ -139,7 +157,7 @@ self._fillCachedValues(client.profile) def _fillCachedValues(self, profile): - #FIXME: this may need to be reworked + # FIXME: this may need to be reworked # the current naive approach keeps a map between all jids # in persistent cache, then put avatar hashs in memory. # Hashes should be shared between profiles (or not ? what @@ -153,9 +171,15 @@ try: value = data[name] if value is None: - log.error(u"{name} value for {jid_} is None, ignoring".format(name=name, jid_=jid_)) + log.error( + u"{name} value for {jid_} is None, ignoring".format( + name=name, jid_=jid_ + ) + ) continue - self.host.memory.updateEntityData(jid_, name, data[name], silent=True, profile_key=profile) + self.host.memory.updateEntityData( + jid_, name, data[name], silent=True, profile_key=profile + ) except KeyError: pass @@ -184,7 +208,9 @@ else: client._cache_0054.force(jid_s) else: - self.host.memory.updateEntityData(jid_, name, value, profile_key=client.profile) + self.host.memory.updateEntityData( + jid_, name, value, profile_key=client.profile + ) if name in CACHED_DATA: client._cache_0054.setdefault(jid_s, {})[name] = value client._cache_0054.force(jid_s) @@ -206,7 +232,7 @@ """Parse a <PHOTO> photo_elt and save the picture""" # XXX: this method is launched in a separate thread try: - mime_type = unicode(photo_elt.elements(NS_VCARD, 'TYPE').next()) + mime_type = unicode(photo_elt.elements(NS_VCARD, "TYPE").next()) except StopIteration: log.warning(u"no MIME type found, assuming image/png") mime_type = u"image/png" @@ -220,35 +246,41 @@ mime_type = "image/png" else: # TODO: handle other image formats (svg?) - log.warning(u"following avatar image format is not handled: {type} [{jid}]".format( - type=mime_type, jid=entity_jid.full())) + log.warning( + u"following avatar image format is not handled: {type} [{jid}]".format( + type=mime_type, jid=entity_jid.full() + ) + ) raise Failure(exceptions.DataError()) ext = mimetypes.guess_extension(mime_type, strict=False) assert ext is not None - if ext == u'.jpe': - ext = u'.jpg' - log.debug(u'photo of type {type} with extension {ext} found [{jid}]'.format( - type=mime_type, ext=ext, jid=entity_jid.full())) + if ext == u".jpe": + ext = u".jpg" + log.debug( + u"photo of type {type} with extension {ext} found [{jid}]".format( + type=mime_type, ext=ext, jid=entity_jid.full() + ) + ) try: - buf = str(photo_elt.elements(NS_VCARD, 'BINVAL').next()) + buf = str(photo_elt.elements(NS_VCARD, "BINVAL").next()) except StopIteration: log.warning(u"BINVAL element not found") raise Failure(exceptions.NotFound()) if not buf: log.warning(u"empty avatar for {jid}".format(jid=entity_jid.full())) raise Failure(exceptions.NotFound()) - log.debug(_(u'Decoding binary')) + log.debug(_(u"Decoding binary")) decoded = b64decode(buf) del buf image_hash = sha1(decoded).hexdigest() with client.cache.cacheData( - PLUGIN_INFO['import_name'], + PLUGIN_INFO["import_name"], image_hash, mime_type, # we keep in cache for 1 year - MAX_AGE - ) as f: + MAX_AGE, + ) as f: f.write(decoded) return image_hash @@ -259,39 +291,44 @@ vcard_dict = {} for elem in vcard.elements(): - if elem.name == 'FN': - vcard_dict['fullname'] = unicode(elem) - elif elem.name == 'NICKNAME': - vcard_dict['nick'] = unicode(elem) - self.updateCache(client, entity_jid, 'nick', vcard_dict['nick']) - elif elem.name == 'URL': - vcard_dict['website'] = unicode(elem) - elif elem.name == 'EMAIL': - vcard_dict['email'] = unicode(elem) - elif elem.name == 'BDAY': - vcard_dict['birthday'] = unicode(elem) - elif elem.name == 'PHOTO': + if elem.name == "FN": + vcard_dict["fullname"] = unicode(elem) + elif elem.name == "NICKNAME": + vcard_dict["nick"] = unicode(elem) + self.updateCache(client, entity_jid, "nick", vcard_dict["nick"]) + elif elem.name == "URL": + vcard_dict["website"] = unicode(elem) + elif elem.name == "EMAIL": + vcard_dict["email"] = unicode(elem) + elif elem.name == "BDAY": + vcard_dict["birthday"] = unicode(elem) + elif elem.name == "PHOTO": # TODO: handle EXTVAL try: avatar_hash = yield threads.deferToThread( - self.savePhoto, client, elem, entity_jid) + self.savePhoto, client, elem, entity_jid + ) except (exceptions.DataError, exceptions.NotFound) as e: - avatar_hash = '' - vcard_dict['avatar'] = avatar_hash + avatar_hash = "" + vcard_dict["avatar"] = avatar_hash except Exception as e: log.error(u"avatar saving error: {}".format(e)) avatar_hash = None else: - vcard_dict['avatar'] = avatar_hash - self.updateCache(client, entity_jid, 'avatar', avatar_hash) + vcard_dict["avatar"] = avatar_hash + self.updateCache(client, entity_jid, "avatar", avatar_hash) else: - log.debug(u'FIXME: [{}] VCard tag is not managed yet'.format(elem.name)) + log.debug(u"FIXME: [{}] VCard tag is not managed yet".format(elem.name)) # if a data in cache doesn't exist anymore, we need to delete it # so we check CACHED_DATA no gotten (i.e. not in vcard_dict keys) # and we reset them for datum in CACHED_DATA.difference(vcard_dict.keys()): - log.debug(u"reseting vcard datum [{datum}] for {entity}".format(datum=datum, entity=entity_jid.full())) + log.debug( + u"reseting vcard datum [{datum}] for {entity}".format( + datum=datum, entity=entity_jid.full() + ) + ) self.updateCache(client, entity_jid, datum, None) defer.returnValue(vcard_dict) @@ -309,11 +346,15 @@ def _vCardEb(self, failure_, to_jid, client): """Called when something is wrong with registration""" - log.warning(u"Can't get vCard for {jid}: {failure}".format(jid=to_jid.full, failure=failure_)) + log.warning( + u"Can't get vCard for {jid}: {failure}".format( + jid=to_jid.full, failure=failure_ + ) + ) self.updateCache(client, to_jid, "avatar", None) def _getVcardElt(self, iq_elt): - return iq_elt.elements(NS_VCARD, "vCard").next() + return iq_elt.elements(NS_VCARD, "vCard").next() def getCardRaw(self, client, entity_jid): """get raw vCard XML @@ -322,10 +363,10 @@ """ entity_jid = self.getBareOrFull(client, entity_jid) log.debug(u"Asking for {}'s VCard".format(entity_jid.full())) - reg_request = client.IQ('get') + reg_request = client.IQ("get") reg_request["from"] = client.jid.full() reg_request["to"] = entity_jid.full() - reg_request.addElement('vCard', NS_VCARD) + reg_request.addElement("vCard", NS_VCARD) d = reg_request.send(entity_jid.full()) d.addCallback(self._getVcardElt) return d @@ -337,19 +378,24 @@ @result: id to retrieve the profile """ d = self.getCardRaw(client, entity_jid) - d.addCallbacks(self._vCardCb, self._vCardEb, callbackArgs=[entity_jid, client], errbackArgs=[entity_jid, client]) + d.addCallbacks( + self._vCardCb, + self._vCardEb, + callbackArgs=[entity_jid, client], + errbackArgs=[entity_jid, client], + ) return d def _getCardCb(self, dummy, client, entity): try: - return client._cache_0054[entity.full()]['avatar'] + return client._cache_0054[entity.full()]["avatar"] except KeyError: raise Failure(exceptions.NotFound()) def _getAvatar(self, entity, cache_only, hash_only, profile): client = self.host.getClient(profile) d = self.getAvatar(client, jid.JID(entity), cache_only, hash_only) - d.addErrback(lambda dummy: '') + d.addErrback(lambda dummy: "") return d @@ -370,7 +416,7 @@ try: # we first check if we have avatar in cache - avatar_hash = client._cache_0054[entity.full()]['avatar'] + avatar_hash = client._cache_0054[entity.full()]["avatar"] if avatar_hash: # avatar is known and exists full_path = client.cache.getFilePath(avatar_hash) @@ -379,7 +425,7 @@ raise KeyError else: # avatar has already been checked but it is not set - full_path = u'' + full_path = u"" except KeyError: # avatar is not in cache if cache_only: @@ -406,11 +452,11 @@ @param entity(jid.JID): entity to get nick from @return(unicode, None): nick or None if not found """ - nick = self.getCache(client, entity, u'nick') + nick = self.getCache(client, entity, u"nick") if nick is not None: defer.returnValue(nick) yield self.getCard(client, entity) - defer.returnValue(self.getCache(client, entity, u'nick')) + defer.returnValue(self.getCache(client, entity, u"nick")) @defer.inlineCallbacks def setNick(self, client, nick): @@ -422,22 +468,22 @@ try: vcard_elt = yield self.getCardRaw(client, jid_) except error.StanzaError as e: - if e.condition == 'item-not-found': - vcard_elt = domish.Element((NS_VCARD, 'vCard')) + if e.condition == "item-not-found": + vcard_elt = domish.Element((NS_VCARD, "vCard")) else: raise e try: - nickname_elt = next(vcard_elt.elements(NS_VCARD, u'NICKNAME')) + nickname_elt = next(vcard_elt.elements(NS_VCARD, u"NICKNAME")) except StopIteration: pass else: vcard_elt.children.remove(nickname_elt) - nickname_elt = vcard_elt.addElement((NS_VCARD, u'NICKNAME'), content=nick) + nickname_elt = vcard_elt.addElement((NS_VCARD, u"NICKNAME"), content=nick) iq_elt = client.IQ() vcard_elt = iq_elt.addChild(vcard_elt) yield iq_elt.send() - self.updateCache(client, jid_, u'nick', unicode(nick)) + self.updateCache(client, jid_, u"nick", unicode(nick)) def _buildSetAvatar(self, client, vcard_elt, file_path): # XXX: this method is executed in a separate thread @@ -460,18 +506,15 @@ right -= offset img = img.crop((left, upper, right, lower)) img_buf = StringIO() - img.save(img_buf, 'PNG') + img.save(img_buf, "PNG") - photo_elt = vcard_elt.addElement('PHOTO') - photo_elt.addElement('TYPE', content='image/png') - photo_elt.addElement('BINVAL', content=b64encode(img_buf.getvalue())) + photo_elt = vcard_elt.addElement("PHOTO") + photo_elt.addElement("TYPE", content="image/png") + photo_elt.addElement("BINVAL", content=b64encode(img_buf.getvalue())) image_hash = sha1(img_buf.getvalue()).hexdigest() with client.cache.cacheData( - PLUGIN_INFO['import_name'], - image_hash, - "image/png", - MAX_AGE - ) as f: + PLUGIN_INFO["import_name"], image_hash, "image/png", MAX_AGE + ) as f: f.write(img_buf.getvalue()) return image_hash @@ -489,14 +532,14 @@ # we first check if a vcard already exists, to keep data vcard_elt = yield self.getCardRaw(client, client.jid.userhostJID()) except error.StanzaError as e: - if e.condition == 'item-not-found': - vcard_elt = domish.Element((NS_VCARD, 'vCard')) + if e.condition == "item-not-found": + vcard_elt = domish.Element((NS_VCARD, "vCard")) else: raise e else: # the vcard exists, we need to remove PHOTO element as we'll make a new one try: - photo_elt = next(vcard_elt.elements(NS_VCARD, u'PHOTO')) + photo_elt = next(vcard_elt.elements(NS_VCARD, u"PHOTO")) except StopIteration: pass else: @@ -504,12 +547,14 @@ iq_elt = client.IQ() iq_elt.addChild(vcard_elt) - image_hash = yield threads.deferToThread(self._buildSetAvatar, client, vcard_elt, file_path) + image_hash = yield threads.deferToThread( + self._buildSetAvatar, client, vcard_elt, file_path + ) # image is now at the right size/format - self.updateCache(client, client.jid.userhostJID(), 'avatar', image_hash) + self.updateCache(client, client.jid.userhostJID(), "avatar", image_hash) yield iq_elt.send() - client.presence.available() # FIXME: should send the current presence, not always "available" ! + client.presence.available() # FIXME: should send the current presence, not always "available" ! class XEP_0054_handler(XMPPHandler): @@ -522,21 +567,24 @@ def connectionInitialized(self): self.xmlstream.addObserver(VCARD_UPDATE, self.update) - def getDiscoInfo(self, requestor, target, nodeIdentifier=''): + def getDiscoInfo(self, requestor, target, nodeIdentifier=""): return [disco.DiscoFeature(NS_VCARD)] - def getDiscoItems(self, requestor, target, nodeIdentifier=''): + def getDiscoItems(self, requestor, target, nodeIdentifier=""): return [] def _checkAvatarHash(self, dummy, client, entity, given_hash): """check that hash in cash (i.e. computed hash) is the same as given one""" # XXX: if they differ, the avater will be requested on each connection # TODO: try to avoid re-requesting avatar in this case - computed_hash = self.plugin_parent.getCache(client, entity, 'avatar') + computed_hash = self.plugin_parent.getCache(client, entity, "avatar") if computed_hash != given_hash: - log.warning(u"computed hash differs from given hash for {entity}:\n" + log.warning( + u"computed hash differs from given hash for {entity}:\n" "computed: {computed}\ngiven: {given}".format( - entity=entity, computed=computed_hash, given=given_hash)) + entity=entity, computed=computed_hash, given=given_hash + ) + ) def update(self, presence): """Called on <presence/> stanza with vcard data @@ -545,22 +593,22 @@ @param presend(domish.Element): <presence/> stanza """ client = self.parent - entity_jid = self.plugin_parent.getBareOrFull(client, jid.JID(presence['from'])) - #FIXME: wokkel's data_form should be used here + entity_jid = self.plugin_parent.getBareOrFull(client, jid.JID(presence["from"])) + # FIXME: wokkel's data_form should be used here try: - x_elt = presence.elements(NS_VCARD_UPDATE, 'x').next() + x_elt = presence.elements(NS_VCARD_UPDATE, "x").next() except StopIteration: return try: - photo_elt = x_elt.elements(NS_VCARD_UPDATE, 'photo').next() + photo_elt = x_elt.elements(NS_VCARD_UPDATE, "photo").next() except StopIteration: return hash_ = unicode(photo_elt).strip() if hash_ == C.HASH_SHA1_EMPTY: - hash_ = u'' - old_avatar = self.plugin_parent.getCache(client, entity_jid, 'avatar') + hash_ = u"" + old_avatar = self.plugin_parent.getCache(client, entity_jid, "avatar") if old_avatar == hash_: # no change, we can return... @@ -568,7 +616,11 @@ # ...but we double check that avatar is in cache file_path = client.cache.getFilePath(hash_) if file_path is None: - log.error(u"Avatar for [{}] should be in cache but it is not! We get it".format(entity_jid.full())) + log.error( + u"Avatar for [{}] should be in cache but it is not! We get it".format( + entity_jid.full() + ) + ) self.plugin_parent.getCard(client, entity_jid) else: log.debug(u"avatar for {} already in cache".format(entity_jid.full())) @@ -578,14 +630,20 @@ # the avatar has been removed # XXX: we use empty string instead of None to indicate that we took avatar # but it is empty on purpose - self.plugin_parent.updateCache(client, entity_jid, 'avatar', '') + self.plugin_parent.updateCache(client, entity_jid, "avatar", "") return file_path = client.cache.getFilePath(hash_) if file_path is not None: - log.debug(u"New avatar found for [{}], it's already in cache, we use it".format(entity_jid.full())) - self.plugin_parent.updateCache(client, entity_jid, 'avatar', hash_) + log.debug( + u"New avatar found for [{}], it's already in cache, we use it".format( + entity_jid.full() + ) + ) + self.plugin_parent.updateCache(client, entity_jid, "avatar", hash_) else: - log.debug(u'New avatar found for [{}], requesting vcard'.format(entity_jid.full())) + log.debug( + u"New avatar found for [{}], requesting vcard".format(entity_jid.full()) + ) d = self.plugin_parent.getCard(client, entity_jid) d.addCallback(self._checkAvatarHash, client, entity_jid, hash_)