comparison src/plugins/plugin_xep_0054.py @ 1341:6dbeb2ef966c frontends_multi_profiles

plugin XEP-0054: "nick" is now keeped in cache: - cache now manage different kind of data (instead of only avatar) - PeristentBinaryDict is used to keep cache (because we now store a dict, not just strings) - CACHED_DATA is a set of data kept in cache (only nick and avatar so far) - a signal update flag is put on "nick" - data in cache are reset if they are not present in cache (this avoid to keep the old value)
author Goffi <goffi@goffi.org>
date Tue, 24 Feb 2015 17:01:33 +0100
parents bd69d341d969
children 069ad98b360d
comparison
equal deleted inserted replaced
1340:91e72da1d093 1341:6dbeb2ef966c
34 from wokkel import disco, iwokkel 34 from wokkel import disco, iwokkel
35 35
36 from base64 import b64decode, b64encode 36 from base64 import b64decode, b64encode
37 from hashlib import sha1 37 from hashlib import sha1
38 from sat.core import exceptions 38 from sat.core import exceptions
39 from sat.memory.persistent import PersistentDict 39 from sat.memory import persistent
40 from PIL import Image 40 from PIL import Image
41 from cStringIO import StringIO 41 from cStringIO import StringIO
42 42
43 try: 43 try:
44 from twisted.words.protocols.xmlstream import XMPPHandler 44 from twisted.words.protocols.xmlstream import XMPPHandler
53 VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' # TODO: manage requests 53 VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' # TODO: manage requests
54 54
55 PRESENCE = '/presence' 55 PRESENCE = '/presence'
56 NS_VCARD_UPDATE = 'vcard-temp:x:update' 56 NS_VCARD_UPDATE = 'vcard-temp:x:update'
57 VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]' 57 VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]'
58
59 CACHED_DATA = {'avatar', 'nick'}
58 60
59 PLUGIN_INFO = { 61 PLUGIN_INFO = {
60 "name": "XEP 0054 Plugin", 62 "name": "XEP 0054 Plugin",
61 "import_name": "XEP-0054", 63 "import_name": "XEP-0054",
62 "type": "XEP", 64 "type": "XEP",
77 log.info(_("Plugin XEP_0054 initialization")) 79 log.info(_("Plugin XEP_0054 initialization"))
78 self.host = host 80 self.host = host
79 self.avatar_path = os.path.join(self.host.memory.getConfig('', 'local_dir'), AVATAR_PATH) 81 self.avatar_path = os.path.join(self.host.memory.getConfig('', 'local_dir'), AVATAR_PATH)
80 if not os.path.exists(self.avatar_path): 82 if not os.path.exists(self.avatar_path):
81 os.makedirs(self.avatar_path) 83 os.makedirs(self.avatar_path)
82 self.avatars_cache = {} 84 self.cache = {}
83 host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self._getCard) 85 host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self._getCard)
84 host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile) 86 host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile)
85 host.bridge.addMethod("setAvatar", ".plugin", in_sign='ss', out_sign='', method=self.setAvatar, async=True) 87 host.bridge.addMethod("setAvatar", ".plugin", in_sign='ss', out_sign='', method=self.setAvatar, async=True)
86 host.trigger.add("presence_available", self.presenceTrigger) 88 host.trigger.add("presence_available", self.presenceTrigger)
87 host.memory.setSignalOnUpdate("avatar") 89 host.memory.setSignalOnUpdate("avatar")
90 host.memory.setSignalOnUpdate("nick")
88 91
89 def getHandler(self, profile): 92 def getHandler(self, profile):
90 return XEP_0054_handler(self) 93 return XEP_0054_handler(self)
91 94
92 def presenceTrigger(self, presence_elt, client): 95 def presenceTrigger(self, presence_elt, client):
93 if client.jid.userhost() in self.avatars_cache[client.profile]: 96 if client.jid.userhost() in self.cache[client.profile]:
94 x_elt = domish.Element((NS_VCARD_UPDATE, 'x')) 97 x_elt = domish.Element((NS_VCARD_UPDATE, 'x'))
95 x_elt.addElement('photo', content=self.avatars_cache[client.profile][client.jid.userhost()]) 98 x_elt.addElement('photo', content=self.cache[client.profile][client.jid.userhost()]['avatar'])
96 presence_elt.addChild(x_elt) 99 presence_elt.addChild(x_elt)
97 100
98 return True 101 return True
99 102
100 def _fillCachedValues(self, profile): 103 def _fillCachedValues(self, profile):
101 #FIXME: this is really suboptimal, need to be reworked 104 #FIXME: this is really suboptimal, need to be reworked
102 # the current naive approach keeps a map between all jids of all profiles 105 # the current naive approach keeps a map between all jids of all profiles
103 # in persistent cache, then put avatar 106 # in persistent cache, then put avatar
104 # hashs in memory. Hashed should be shared between profiles 107 # hashs in memory. Hashed should be shared between profiles
105 for jid_s, avatar_hash in self.avatars_cache[profile].iteritems(): 108 for jid_s, data in self.cache[profile].iteritems():
106 jid_ = jid.JID(jid_s) 109 jid_ = jid.JID(jid_s)
107 self.host.memory.updateEntityData(jid_, "avatar", avatar_hash, silent=True, profile_key=profile) 110 for name in CACHED_DATA:
111 try:
112 self.host.memory.updateEntityData(jid_, name, data[name], silent=True, profile_key=profile)
113 except KeyError:
114 pass
108 115
109 @defer.inlineCallbacks 116 @defer.inlineCallbacks
110 def profileConnecting(self, profile): 117 def profileConnecting(self, profile):
111 self.avatars_cache[profile] = PersistentDict(NS_VCARD, profile) 118 self.cache[profile] = persistent.PersistentBinaryDict(NS_VCARD, profile)
112 yield self.avatars_cache[profile].load() 119 yield self.cache[profile].load()
113 self._fillCachedValues(profile) 120 self._fillCachedValues(profile)
114 121
115 def profileDisconnected(self, profile): 122 def profileDisconnected(self, profile):
116 log.debug(u"Deleting profile cache for avatars") 123 log.debug(u"Deleting profile cache for avatars")
117 del self.avatars_cache[profile] 124 del self.cache[profile]
118 125
119 def updateCache(self, jid_, name, value, profile): 126 def updateCache(self, jid_, name, value, profile):
120 """update cache value 127 """update cache value
121 128
122 save value in memory in case of change 129 save value in memory in case of change
125 @param value(unicode): new value of the item 132 @param value(unicode): new value of the item
126 @param profile(unicode): profile which received the update 133 @param profile(unicode): profile which received the update
127 """ 134 """
128 assert not jid_.resource # VCard are retrieved with bare jid 135 assert not jid_.resource # VCard are retrieved with bare jid
129 self.host.memory.updateEntityData(jid_, name, value, profile_key=profile) 136 self.host.memory.updateEntityData(jid_, name, value, profile_key=profile)
130 if name == "avatar": 137 if name in CACHED_DATA:
131 self.avatars_cache[profile][jid_.userhost()] = value 138 jid_s = jid_.userhost()
139 self.cache[profile].setdefault(jid_s, {})[name] = value
140 self.cache[profile].force(jid_s)
132 141
133 def getCache(self, entity_jid, name, profile): 142 def getCache(self, entity_jid, name, profile):
134 """return cached value for jid 143 """return cached value for jid
135 144
136 @param entity_jid: target contact 145 @param entity_jid: target contact
202 del dictionary['avatar'] 211 del dictionary['avatar']
203 else: 212 else:
204 self.updateCache(target, 'avatar', dictionary['avatar'], profile) 213 self.updateCache(target, 'avatar', dictionary['avatar'], profile)
205 else: 214 else:
206 log.info(_('FIXME: [%s] VCard tag is not managed yet') % elem.name) 215 log.info(_('FIXME: [%s] VCard tag is not managed yet') % elem.name)
216
217 # if a data in cache doesn't exist anymore, we need to reset it
218 # so we check CACHED_DATA no gotten (i.e. not in dictionary keys)
219 # and we reset them
220 for datum in CACHED_DATA.difference(dictionary.keys()):
221 log.debug(u"reseting vcard datum [{datum}] for {entity}".format(datum=datum, entity=target.full()))
222 self.updateCache(target, datum, '', profile)
207 223
208 defer.returnValue(dictionary) 224 defer.returnValue(dictionary)
209 225
210 def _VCardCb(self, answer, profile): 226 def _VCardCb(self, answer, profile):
211 """Called after the first get IQ""" 227 """Called after the first get IQ"""