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"] 
+