Mercurial > libervia-backend
diff plugins/plugin_xep_0054.py @ 42:874de3020e1c
Initial VCard (XEP-0054) support + misc fixes
- new xep-0054 plugin, avatar are cached, new getProfile bridge method
- gateways plugin (XEP-0100): new __private__ key in resulting data, used to keep target jid
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 21 Dec 2009 13:22:11 +1100 |
parents | |
children | 8a438a6ff587 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/plugin_xep_0054.py Mon Dec 21 13:22:11 2009 +1100 @@ -0,0 +1,147 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +SAT plugin for managing xep-0054 +Copyright (C) 2009 Jérôme Poisson (goffi@goffi.org) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +""" + +from logging import debug, info, error +from twisted.words.xish import domish +from twisted.internet import protocol, defer, threads, reactor +from twisted.words.protocols.jabber import client, jid, xmlstream +from twisted.words.protocols.jabber import error as jab_error +from twisted.words.protocols.jabber.xmlstream import IQ +import os.path +import pdb + +from zope.interface import implements + +from wokkel import disco, iwokkel + +from base64 import b64decode +from hashlib import sha1 +from time import sleep + +AVATAR_PATH = "/avatars" + +IQ_GET = '/iq[@type="get"]' +NS_VCARD = 'vcard-temp' +VCARD_REQUEST = IQ_GET + '/si[@xmlns="' + NS_VCARD + '"]' #TODO: manage requests + +PLUGIN_INFO = { +"name": "XEP 0054 Plugin", +"import_name": "XEP_0054", +"type": "XEP", +"dependencies": [], +"main": "XEP_0054", +"description": """Implementation of vcard-temp""" +} + +class XEP_0054(): + implements(iwokkel.IDisco) + + def __init__(self, host): + info("Plugin XEP_0054 initialization") + self.host = host + self.avatar_path = os.path.expanduser(self.host.get_const('local_dir') + AVATAR_PATH) + if not os.path.exists(self.avatar_path): + os.makedirs(self.avatar_path) + host.bridge.addMethod("getProfile", ".communication", in_sign='s', out_sign='s', method=self.getProfile) + + def getDiscoInfo(self, requestor, target, nodeIdentifier=''): + return [disco.DiscoFeature(NS_VCARD)] + + def getDiscoItems(self, requestor, target, nodeIdentifier=''): + return [] + + def save_photo(self, photo_xml): + """Parse a <PHOTO> elem and save the picture""" + print "save_photo result" + for elem in photo_xml.elements(): + if elem.name == 'TYPE': + info('Photo of type [%s] found' % str(elem)) + if elem.name == 'BINVAL': + debug('Decoding binary') + decoded = b64decode(str(elem)) + hash = sha1(decoded).hexdigest() + filename = self.avatar_path+'/'+hash + if not os.path.exists(filename): + with open(filename,'wb') as file: + file.write(decoded) + debug("file saved to %s" % hash) + else: + debug("file [%s] already in cache" % hash) + return hash + + @defer.deferredGenerator + def vCard2Dict(self, vcard): + """Convert a VCard to a dict, and save binaries""" + debug ("parsing vcard") + dictionary = {} + d = defer.Deferred() + + for elem in vcard.elements(): + if elem.name == 'FN': + dictionary['fullname'] = unicode(elem) + elif elem.name == 'NICKNAME': + dictionary['nick'] = unicode(elem) + elif elem.name == 'URL': + dictionary['website'] = unicode(elem) + elif elem.name == 'EMAIL': + dictionary['email'] = unicode(elem) + elif elem.name == 'BDAY': + dictionary['birthday'] = unicode(elem) + elif elem.name == 'PHOTO': + debug('photo deferred') + d2 = defer.waitForDeferred( + threads.deferToThread(self.save_photo, elem)) + yield d2 + dictionary["photo"] = d2.getResult() + else: + info ('FIXME: [%s] VCard tag is not managed yet' % elem.name) + + yield dictionary + + def vcard_ok(self, answer): + """Called after the first get IQ""" + debug ("VCard found") + + if answer.firstChildElement().name == "vCard": + d = self.vCard2Dict(answer.firstChildElement()) + d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data)) + else: + error ("FIXME: vCard not found as first child element") + self.host.bridge.actionResult("SUPPRESS", answer['id'], {}) #FIXME: maybe an error message would be best + + def vcard_err(self, failure): + """Called when something is wrong with registration""" + error ("Can't find VCard of %s" % failure.value.stanza['from']) + self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}) #FIXME: maybe an error message would be best + + def getProfile(self, target): + """Ask server for VCard + @param target: jid from which we want the VCard + @result: id to retrieve the profile""" + to_jid = jid.JID(target) + debug("Asking for %s's VCard" % to_jid.full()) + reg_request=IQ(self.host.xmlstream,'get') + reg_request["from"]=self.host.me.full() + reg_request["to"] = to_jid.full() + query=reg_request.addElement('vCard', NS_VCARD) + reg_request.send(to_jid.full()).addCallbacks(self.vcard_ok, self.vcard_err) + return reg_request["id"] +