Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0054.py @ 504:65ecbb473cbb
core, quick frontend, plugin xep-0054, bridge: use of memory's entities data for vcard:
- bridge: new bridge method getEntityData and signal entityDataUpdated
- core: entityDataUpdated signal sent on new string data
- quick frontend: fixed avatars/vcard infos, fixed _replace in quick_contact_list
- plugin xep-0054: dropped updatedValue signal, use entities data instead
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 26 Sep 2012 01:23:56 +0200 |
parents | ee95ff721b68 |
children | 2c4016921403 |
comparison
equal
deleted
inserted
replaced
503:10119c2a9d33 | 504:65ecbb473cbb |
---|---|
18 You should have received a copy of the GNU Affero General Public License | 18 You should have received a copy of the GNU Affero General Public License |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 """ | 20 """ |
21 | 21 |
22 from logging import debug, info, error | 22 from logging import debug, info, error |
23 from twisted.words.xish import domish | 23 from twisted.internet import threads |
24 from twisted.internet import protocol, defer, threads, reactor | |
25 from twisted.internet.defer import inlineCallbacks, returnValue | 24 from twisted.internet.defer import inlineCallbacks, returnValue |
26 from twisted.words.protocols.jabber import client, jid, xmlstream | 25 from twisted.words.protocols.jabber import jid |
27 from twisted.words.protocols.jabber import error as jab_error | |
28 from twisted.words.protocols.jabber.xmlstream import IQ | 26 from twisted.words.protocols.jabber.xmlstream import IQ |
29 import os.path | 27 import os.path |
30 | 28 |
31 from zope.interface import implements | 29 from zope.interface import implements |
32 | 30 |
33 from wokkel import disco, iwokkel | 31 from wokkel import disco, iwokkel |
34 | 32 |
35 from base64 import b64decode | 33 from base64 import b64decode |
36 from hashlib import sha1 | 34 from hashlib import sha1 |
37 from time import sleep | 35 from sat.core import exceptions |
38 from sat.memory.persistent import PersistentBinaryDict | |
39 | 36 |
40 try: | 37 try: |
41 from twisted.words.protocols.xmlstream import XMPPHandler | 38 from twisted.words.protocols.xmlstream import XMPPHandler |
42 except ImportError: | 39 except ImportError: |
43 from wokkel.subprotocols import XMPPHandler | 40 from wokkel.subprotocols import XMPPHandler |
62 "handler": "yes", | 59 "handler": "yes", |
63 "description": _("""Implementation of vcard-temp""") | 60 "description": _("""Implementation of vcard-temp""") |
64 } | 61 } |
65 | 62 |
66 class XEP_0054(): | 63 class XEP_0054(): |
64 #TODO: - check that nickname is ok | |
65 # - refactor the code/better use of Wokkel | |
66 # - get missing values | |
67 | 67 |
68 def __init__(self, host): | 68 def __init__(self, host): |
69 info(_("Plugin XEP_0054 initialization")) | 69 info(_("Plugin XEP_0054 initialization")) |
70 self.host = host | 70 self.host = host |
71 self.avatar_path = os.path.join(self.host.memory.getConfig('', 'local_dir'), AVATAR_PATH) | 71 self.avatar_path = os.path.join(self.host.memory.getConfig('', 'local_dir'), AVATAR_PATH) |
72 if not os.path.exists(self.avatar_path): | 72 if not os.path.exists(self.avatar_path): |
73 os.makedirs(self.avatar_path) | 73 os.makedirs(self.avatar_path) |
74 host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self.getCard) | 74 host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self.getCard) |
75 host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile) | 75 host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile) |
76 host.bridge.addMethod("getCardCache", ".plugin", in_sign='ss', out_sign='a{ss}', method=self.getCardCache) | |
77 | 76 |
78 def getHandler(self, profile): | 77 def getHandler(self, profile): |
79 return XEP_0054_handler(self) | 78 return XEP_0054_handler(self) |
80 | 79 |
81 def update_cache(self, jid, name, value, profile): | 80 def update_cache(self, jid, name, value, profile): |
82 """update cache value | 81 """update cache value |
83 - save value in memory in case of change | 82 - save value in memory in case of change |
84 - send updatedValue signal if the value is new or updated | |
85 @param jid: jid of the owner of the vcard | 83 @param jid: jid of the owner of the vcard |
86 @param name: name of the item which changed | 84 @param name: name of the item which changed |
87 @param value: new value of the item | 85 @param value: new value of the item |
88 @param profile: profile which received the update | 86 @param profile: profile which received the update |
89 """ | 87 """ |
90 client = self.host.getClient(profile) | 88 try: |
91 if not jid.userhost() in client._vcard_cache: | 89 cached = self.host.memory.getEntityData(jid, [name], profile) |
92 client._vcard_cache[jid.userhost()] = {} | 90 except exceptions.UnknownEntityError: |
93 | 91 cached = {} |
94 cache = client._vcard_cache[jid.userhost()] | 92 if not name in cached or cached[name] != value: |
95 old_value = cache[name] if name in cache else None | 93 self.host.memory.updateEntityData(jid, name, value, profile) |
96 if not old_value or value != old_value: | 94 |
97 cache[name] = value | |
98 client._vcard_cache.force(jid.userhost()) #we force saving of data to storage | |
99 self.host.bridge.updatedValue('card_'+name, {'jid':jid.userhost(), name:value}, profile) | |
100 | |
101 def get_cache(self, jid, name, profile): | 95 def get_cache(self, jid, name, profile): |
102 """return cached value for jid | 96 """return cached value for jid |
103 @param jid: target contact | 97 @param jid: target contact |
104 @param name: name of the value ('nick' or 'avatar') | 98 @param name: name of the value ('nick' or 'avatar') |
105 @param profile: %(doc_profile)s | 99 @param profile: %(doc_profile)s |
106 @return: wanted value or None""" | 100 @return: wanted value or None""" |
107 client = self.host.getClient(profile) | |
108 try: | 101 try: |
109 return client._vcard_cache[jid.userhost()][name] | 102 data = self.host.memory.getEntityData(jid, [name], profile) |
110 except KeyError: | 103 except exceptions.UnknownEntityError: |
111 return None | 104 return None |
112 | 105 return data.get(name) |
113 | 106 |
114 def save_photo(self, photo_xml): | 107 def save_photo(self, photo_xml): |
115 """Parse a <PHOTO> elem and save the picture""" | 108 """Parse a <PHOTO> elem and save the picture""" |
116 for elem in photo_xml.elements(): | 109 for elem in photo_xml.elements(): |
117 if elem.name == 'TYPE': | 110 if elem.name == 'TYPE': |
132 @inlineCallbacks | 125 @inlineCallbacks |
133 def vCard2Dict(self, vcard, target, profile): | 126 def vCard2Dict(self, vcard, target, profile): |
134 """Convert a VCard to a dict, and save binaries""" | 127 """Convert a VCard to a dict, and save binaries""" |
135 debug (_("parsing vcard")) | 128 debug (_("parsing vcard")) |
136 dictionary = {} | 129 dictionary = {} |
137 d = defer.Deferred() | |
138 | 130 |
139 for elem in vcard.elements(): | 131 for elem in vcard.elements(): |
140 if elem.name == 'FN': | 132 if elem.name == 'FN': |
141 dictionary['fullname'] = unicode(elem) | 133 dictionary['fullname'] = unicode(elem) |
142 elif elem.name == 'NICKNAME': | 134 elif elem.name == 'NICKNAME': |
187 to_jid = jid.JID(target) | 179 to_jid = jid.JID(target) |
188 debug(_("Asking for %s's VCard") % to_jid.userhost()) | 180 debug(_("Asking for %s's VCard") % to_jid.userhost()) |
189 reg_request=IQ(xmlstream,'get') | 181 reg_request=IQ(xmlstream,'get') |
190 reg_request["from"]=current_jid.full() | 182 reg_request["from"]=current_jid.full() |
191 reg_request["to"] = to_jid.userhost() | 183 reg_request["to"] = to_jid.userhost() |
192 query=reg_request.addElement('vCard', NS_VCARD) | 184 reg_request.addElement('vCard', NS_VCARD) |
193 reg_request.send(to_jid.userhost()).addCallbacks(self.vcard_ok, self.vcard_err, [profile]) | 185 reg_request.send(to_jid.userhost()).addCallbacks(self.vcard_ok, self.vcard_err, [profile]) |
194 return reg_request["id"] | 186 return reg_request["id"] |
195 | 187 |
196 def getAvatarFile(self, hash): | 188 def getAvatarFile(self, hash): |
197 """Give the full path of avatar from hash | 189 """Give the full path of avatar from hash |
202 if not os.path.exists(filename): | 194 if not os.path.exists(filename): |
203 error (_("Asking for an uncached avatar [%s]") % hash) | 195 error (_("Asking for an uncached avatar [%s]") % hash) |
204 return "" | 196 return "" |
205 return filename | 197 return filename |
206 | 198 |
207 def getCardCache(self, target, profile_key): | |
208 """Request for cached values of profile | |
209 return the cached nickname and avatar if exists, else get VCard | |
210 @param target: target's jid | |
211 @param profile_key: %(doc_profile_key)s | |
212 """ | |
213 profile = self.host.memory.getProfileName(profile_key) | |
214 if not profile: | |
215 error(_("Profile not found")) | |
216 return {} | |
217 to_jid = jid.JID(target) | |
218 result = {} | |
219 nick = self.get_cache(to_jid, 'nick', profile) | |
220 if nick: | |
221 result['nick'] = nick | |
222 avatar = self.get_cache(to_jid, 'avatar', profile) | |
223 if avatar: | |
224 result['avatar'] = avatar | |
225 return result | |
226 | |
227 | |
228 | 199 |
229 class XEP_0054_handler(XMPPHandler): | 200 class XEP_0054_handler(XMPPHandler): |
230 implements(iwokkel.IDisco) | 201 implements(iwokkel.IDisco) |
231 | 202 |
232 def __init__(self, plugin_parent): | 203 def __init__(self, plugin_parent): |
233 self.plugin_parent = plugin_parent | 204 self.plugin_parent = plugin_parent |
234 self.host = plugin_parent.host | 205 self.host = plugin_parent.host |
235 | 206 |
236 def connectionInitialized(self): | 207 def connectionInitialized(self): |
237 self.parent._vcard_cache = PersistentBinaryDict(NS_VCARD, self.parent.profile) | |
238 self.parent._vcard_cache.load() | |
239 self.xmlstream.addObserver(VCARD_UPDATE, self.update) | 208 self.xmlstream.addObserver(VCARD_UPDATE, self.update) |
240 | 209 |
241 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 210 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |
242 return [disco.DiscoFeature(NS_VCARD)] | 211 return [disco.DiscoFeature(NS_VCARD)] |
243 | 212 |