XMPP vCard Python Script

3 June 2009 by Dan

Couldn't find a script to update my Jabber/XMPP vCard photo (a/k/a avatar), so I wrote one. It requires xmpppy (a/k/a python-xmpp). It should work with gTalk, but I have not tested it.

Credit to pastebin for some code snippets.

Hope this saves someone some time and effort.

#!/usr/bin/python
'''vcard.py - Update your XMPP vcard photo with the image you provide

Usage: vcard.py image_file jid password
'''

from xmpp import JID, Client, Iq, Presence, NS_VERSION, NS_VCARD
import sys
import os
import time
from base64 import encode, decode
from hashlib import sha1

try:
    file=os.path.expanduser(sys.argv[1])
    jid=sys.argv[2]
    password=sys.argv[3]
    resource='vcard'
except:
    print >>sys.stderr, __doc__
    sys.exit(2)

NS_VCARD_UPDATE = 'vcard-temp:x:update'
NS_NICK = 'http://jabber.org/protocol/nick'

def hash_img(img):
    return sha1(img).hexdigest()

def base64_img(img):
    return img.encode('base64')

def get_img(file):
    try:
        os.stat(file)[6]
        fh = open(file, 'rb')
        img = fh.read()
        return img
    except Exception, e:
        print >>sys.stderr, e
        sys.exit(2)

def get_mime_type(file):
    try:
        ext = file[-4:]
        if ext == '.png':
            mime_type = 'image/png'
        elif ext == '.gif':
            mime_type = 'image/gif'
        elif ext == '.jpg' or ext == '.jpeg':
            mime_type = 'image/jpeg'
        else:
            raise ValueError, "Wrong mime-type detected. Check file suffix."
    except ValueError, e:
        print >>sys.stderr, e
        sys.exit(2)
    return mime_type

def send_vcard(conn, base64_img, mime_type, nick):
    iq_vcard = Iq(typ='set')
    vcard = iq_vcard.addChild(name='vCard', namespace=NS_VCARD)
    vcard.addChild(name='NICKNAME', payload=[nick])
    photo = vcard.addChild(name='PHOTO')
    photo.setTagData(tag='TYPE', val=mime_type)
    photo.setTagData(tag='BINVAL', val=base64_img)
    conn.send(iq_vcard)

def send_presence(conn, status, hash1, nick):
    presence = Presence(status = status, show = 'xa', priority = '-1')
    presence.setTag(name='x',namespace=NS_VCARD_UPDATE).setTag(name='photo',namespace=NS_VCARD_UPDATE).setData(hash1)
    presence.setTag(name='nick',namespace=NS_NICK).setData(nick)
    conn.send(presence)

if __name__ == '__main__':
    img = get_img(file)
    j=JID(jid)
    cl=Client(j.getDomain(),debug=[])
    conn=cl.connect()
    if not conn:
        raise Exception, 'failed to start connection'
    auth=cl.auth(j.getNode(),password,resource,sasl=1)
    if not auth:
        raise Exception, 'could not authenticate'
    send_vcard(cl, base64_img(img), get_mime_type(file), j.getNode())
    send_presence(cl, 'Updated vCard Image', hash_img(img), j.getNode())
    time.sleep(1)
    cl.disconnect()

8 comments to “XMPP vCard Python Script”

  1. Needz:

    Hi there,
    I was just testing out your code, but for some reason it doesn't work for me.
    I understand that you pass the variables through the command prompt but it doesn't recognize my file and I tried hard coding the file extension and user name, etc into the code and it says 'permission denied'. Any ideas why that would be? I am a newbie to XMPP and just discovered the VCARD, I wanted to use the avatar.
    Should I be naming the image file vcard? Because I have tried that too.

    Any help is appreciated,
    Thanks,
    Needz

  2. Dan:

    Two possibilities come to mind: (1) the python script is not executable -- remember to run "chmod 755 vcard.py" (2) you (or more specifically, the script) don't have permission to read the image file -- try making the image file world readable with chmod 644.

  3. Needz:

    Thanks for replying, but I am not using Linux. I run "python vcard.py ............" and then the variables and that is when I get the error. I am actually using windows vista. Could it be something to do with this line:
    "file=os.path.expanduser(sys.argv[1])"

    Perhaps I am passing something wrongly here?
    What do you think?

  4. Dan:

    Oh. Vista. Can't help you there -- I don't have Python installed on a Vista machine. I would imagine that the User Account Controls could be a possible culprit. Or the line you mentioned. Have you tried hard-coding the filename in lieu of sys.argv[1]?

  5. Collin Anderson:

    Cool. I wrote a similar one that downloads other people's instead of updating your own. Nice to know you can go the other direction too.

    http://collincode.wordpress.com/2009/01/31/xmpp-jabber-photo-module-2/

  6. Needz:

    Yes, I have hard-coded it, and have had to make a few changes. That problem does not occur anymore.
    I have been debugging and for some reason it will only accept a png file, strange, but now the problem is the following:
    """
    Traceback (most recent call last):
    File "C:\Users\Nida\Documents\CMPT 275\vcard.py", line 86, in
    send_presence(cl, 'Updated vCard Image', hash_img(img), j.getNode())
    File "C:\Users\Nida\Documents\CMPT 275\vcard.py", line 71, in send_presence
    conn.send(presence)
    File "C:\Python26\lib\site-packages\xmpppy-0.5.0rc1-py2.6.egg\xmpp\dispatcher.py", line 367, in send
    self._owner_send(stanza)
    File "C:\Python26\lib\site-packages\xmpppy-0.5.0rc1-py2.6.egg\xmpp\transports.py", line 195, in send
    self._owner.disconnected()
    File "C:\Python26\lib\site-packages\xmpppy-0.5.0rc1-py2.6.egg\xmpp\client.py", line 129, in disconnected
    for i in self.disconnect_handlers: i()
    File "C:\Python26\lib\site-packages\xmpppy-0.5.0rc1-py2.6.egg\xmpp\client.py", line 137, in DisconnectHandler
    raise IOError('Disconnected from server.')
    IOError: Disconnected from server.
    """

    I guess it now has a problem with conn.send(presence), I have to do some more debugging before I can fully understand why this is happening.
    Any ideas?

  7. Needz:

    I have realized that by simply switching the order of these two statements:
    " send_vcard(cl, base64_img(img), get_mime_type(file), j.getNode())
    send_presence(cl, 'Updated vCard Image', hash_img(img), j.getNode())
    "
    The code will run smoothly and update the vcard photo on jabber accounts.

    However it does not work for gmail accounts. For gmail, the log in process is a little different, and when I do that, it doesn't work anymore. Does gmail support Vcards?

  8. Dan:

    I did notice a typo on the get_mime_type function, but as for gmail, like I said, I don't know if it works. Sounds like maybe not. Google Talk certainly supports vcard avatars, but maybe you can't set it from outside the GMail or Google Talk service.