core, plugin XEP-0054: avatar upload:
- plugin XEP-0054: new setAvatar bridge method
- new "presence_available" trigger
- new DataError
This commit is contained in:
parent
faac62cfd2
commit
181d08f2e4
1 changed files with 96 additions and 24 deletions
|
|
@ -24,16 +24,19 @@ from twisted.internet import threads
|
|||
from twisted.internet.defer import inlineCallbacks, returnValue
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber.xmlstream import IQ
|
||||
from twisted.words.xish import domish
|
||||
import os.path
|
||||
|
||||
from zope.interface import implements
|
||||
|
||||
from wokkel import disco, iwokkel
|
||||
|
||||
from base64 import b64decode
|
||||
from base64 import b64decode,b64encode
|
||||
from hashlib import sha1
|
||||
from sat.core import exceptions
|
||||
from sat.memory.persistent import PersistentDict
|
||||
import Image
|
||||
from cStringIO import StringIO
|
||||
|
||||
try:
|
||||
from twisted.words.protocols.xmlstream import XMPPHandler
|
||||
|
|
@ -76,9 +79,19 @@ class XEP_0054():
|
|||
self.avatars_cache.load() #FIXME: resulting deferred must be correctly managed
|
||||
host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self.getCard)
|
||||
host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile)
|
||||
host.bridge.addMethod("setAvatar", ".plugin", in_sign='ss', out_sign='', method=self.setAvatar, async = True)
|
||||
host.trigger.add("presence_available", self.presenceTrigger)
|
||||
|
||||
def getHandler(self, profile):
|
||||
return XEP_0054_handler(self)
|
||||
return XEP_0054_handler(self)
|
||||
|
||||
def presenceTrigger(self, presence_elt, client):
|
||||
if client.jid.userhost() in self.avatars_cache:
|
||||
x_elt = domish.Element((NS_VCARD_UPDATE, 'x'))
|
||||
x_elt.addElement('photo', content=self.avatars_cache[client.jid.userhost()])
|
||||
presence_elt.addChild(x_elt)
|
||||
|
||||
return True
|
||||
|
||||
def _fillCachedValues(self, result, client):
|
||||
#FIXME: this is really suboptimal, need to be reworked
|
||||
|
|
@ -130,15 +143,15 @@ class XEP_0054():
|
|||
if elem.name == 'BINVAL':
|
||||
debug(_('Decoding binary'))
|
||||
decoded = b64decode(str(elem))
|
||||
hash = sha1(decoded).hexdigest()
|
||||
filename = self.avatar_path+'/'+hash
|
||||
image_hash = sha1(decoded).hexdigest()
|
||||
filename = self.avatar_path+'/'+image_hash
|
||||
if not os.path.exists(filename):
|
||||
with open(filename,'wb') as file:
|
||||
file.write(decoded)
|
||||
debug(_("file saved to %s") % hash)
|
||||
debug(_("file saved to %s") % image_hash)
|
||||
else:
|
||||
debug(_("file [%s] already in cache") % hash)
|
||||
return hash
|
||||
debug(_("file [%s] already in cache") % image_hash)
|
||||
return image_hash
|
||||
|
||||
@inlineCallbacks
|
||||
def vCard2Dict(self, vcard, target, profile):
|
||||
|
|
@ -174,7 +187,12 @@ class XEP_0054():
|
|||
debug (_("VCard found"))
|
||||
|
||||
if answer.firstChildElement().name == "vCard":
|
||||
d = self.vCard2Dict(answer.firstChildElement(), jid.JID(answer["from"]), profile)
|
||||
_jid, steam = self.host.getJidNStream(profile)
|
||||
try:
|
||||
from_jid = jid.JID(answer["from"])
|
||||
except KeyError:
|
||||
from_jid = _jid.userhostJID()
|
||||
d = self.vCard2Dict(answer.firstChildElement(), from_jid, profile)
|
||||
d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data, profile))
|
||||
else:
|
||||
error (_("FIXME: vCard not found as first child element"))
|
||||
|
|
@ -191,7 +209,7 @@ class XEP_0054():
|
|||
@result: id to retrieve the profile"""
|
||||
current_jid, xmlstream = self.host.getJidNStream(profile_key)
|
||||
if not xmlstream:
|
||||
error (_('Asking vcard for an non-existant or not connected profile'))
|
||||
error (_('Asking vcard for a non-existant or not connected profile'))
|
||||
return ""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
to_jid = jid.JID(target_s)
|
||||
|
|
@ -203,17 +221,58 @@ class XEP_0054():
|
|||
reg_request.send(to_jid.userhost()).addCallbacks(self.vcard_ok, self.vcard_err, callbackArgs=[profile], errbackArgs=[profile])
|
||||
return reg_request["id"]
|
||||
|
||||
def getAvatarFile(self, hash):
|
||||
def getAvatarFile(self, avatar_hash):
|
||||
"""Give the full path of avatar from hash
|
||||
@param hash: SHA1 hash
|
||||
@return full_path
|
||||
"""
|
||||
filename = self.avatar_path+'/'+hash
|
||||
filename = self.avatar_path+'/'+avatar_hash
|
||||
if not os.path.exists(filename):
|
||||
error (_("Asking for an uncached avatar [%s]") % hash)
|
||||
error (_("Asking for an uncached avatar [%s]") % avatar_hash)
|
||||
return ""
|
||||
return filename
|
||||
|
||||
def _buildSetAvatar(self, vcard_set, filepath):
|
||||
try:
|
||||
img = Image.open(filepath)
|
||||
except IOError:
|
||||
raise exceptions.DataError("Can't open image")
|
||||
|
||||
if img.size != (64, 64):
|
||||
img.resize((64, 64))
|
||||
img_buf = StringIO()
|
||||
img.save(img_buf, 'PNG')
|
||||
|
||||
vcard_elt = vcard_set.addElement('vCard', NS_VCARD)
|
||||
photo_elt = vcard_elt.addElement('PHOTO')
|
||||
photo_elt.addElement('TYPE', content='image/png')
|
||||
photo_elt.addElement('BINVAL', content=b64encode(img_buf.getvalue()))
|
||||
img_hash = sha1(img_buf.getvalue()).hexdigest()
|
||||
return (vcard_set, img_hash)
|
||||
|
||||
def setAvatar(self, filepath, profile_key='@DEFAULT@'):
|
||||
"""Set avatar of the profile
|
||||
@param filepath: path of the image of the avatar"""
|
||||
#TODO: This is a temporary way of setting avatar, as other VCard informations are not managed.
|
||||
# A proper full VCard management should be done (and more generaly a public/private profile)
|
||||
client = self.host.getClient(profile_key)
|
||||
if not client:
|
||||
raise exceptions.NotConnectedProfileError(_('Trying to set avatar for a non-existant or not connected profile'))
|
||||
|
||||
vcard_set = IQ(client.xmlstream,'set')
|
||||
d = threads.deferToThread(self._buildSetAvatar, vcard_set, filepath)
|
||||
|
||||
def elementBuilt(result):
|
||||
"""Called once the image is at the right size/format, and the vcard set element is build"""
|
||||
set_avatar_elt, img_hash = result
|
||||
self.avatars_cache[client.jid.userhost()] = img_hash # we need to update the hash, so we can send a new presence
|
||||
# element with the right hash
|
||||
return set_avatar_elt.send().addCallback(lambda ignore: client.presence.available())
|
||||
|
||||
d.addCallback(elementBuilt)
|
||||
|
||||
return d
|
||||
|
||||
|
||||
class XEP_0054_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
|
|
|||
Loading…
Reference in a new issue