konieczny_jabtran.py script

Download, Add a comment, Back to main page
#!/usr/bin/python

"""
Jabberd 1.4 to Jabber 2.0 user-database translator
(c) 2003 Jacek Konieczny <jajcus@bnet.pl>

This script reads jabber-1.4.x user files and tries to put user information
from them to jabberd2 database. The script was written for PostgreSQL database,
but probably will work with MySQL too (after changing module name below).

Usage:
	./jabtran.py <jid> <user.xml-file>
or:
	./jabtran.py <domain> <spool-directory>

Run this only on empty database!

Good luck!
"""


import sys
import glob
import os
import libxml2
import time
import string

# Change 'pgdb' below to DB-API compatible module for your database
# pgdb is OK for PostgreSQL
import pgdb as db

# Change this to your jabberd2 database settings
DBSTRING="localhost:jabberd2:jabberd2:secret"




def ignore_ns(cur,doc,jid,el):
	pass

def convert_password(cur,doc,jid,el):
	passwd=el.getContent()
	cur.execute("UPDATE authreg SET password=%(passwd)s WHERE username=%(username)s", 
			{'username':jid.split('@')[0],'passwd':passwd})

def convert_zerok(cur,doc,jid,el):
	token=None
	sequence=None
	hash=None
	it=el.children
	while it:
		if it.name=="token":
			token=it.getContent()
		elif it.name=="sequence":
			sequence=it.getContent()
		elif it.name=="hash":
			hash=it.getContent()
		it=it.next
	if None in (token,sequence,hash):
		print "Incomplete zerok info:",el.serialize
		return
	cur.execute("UPDATE authreg SET token=%(token)s,sequence=%(sequence)s,hash=%(hash)s"
			" WHERE username=%(username)s",
				{'token':token,'sequence':sequence,'hash':hash, 'username':jid.split('@')[0]})

def convert_last(cur,doc,jid,el):
	last=el.prop("last")
	cur.execute("INSERT INTO logout (\"collection-owner\",\"object-sequence\",time)" 
			" VALUES (%(jid)s,nextval('object-sequence'),%(last)s)",
			{'jid':jid,'last':last})


def convert_roster(cur,doc,jid,el):
	item=el.children
	while item:
		if item.name!="item":
			print "Unknown roster element:",item.serialize()
			continue
		if not item.hasProp("jid"):
			print "Roster item without jid:",item.serialize()
			continue
		ijid=item.prop("jid")
		if item.hasProp("name"):
			name=item.prop("name")
		else:
			name=None
		if item.hasProp("subscription"):
			s=item.prop("subscription")
			if s=="both":
				fr,to='t','t'
			elif s=="from":
				fr,to='t','n'
			elif s=="to":
				to,fr='t','n'
			else:
				fr,to='n','n'
		if item.hasProp("ask"):
			ask=item.prop("ask")
			if ask=="subscribe":
				ask=1
			elif ask=="unsubscribe":
				ask=2
			else:
				ask=0
		else:
			ask=None

		groups=[]
		child=item.children
		while child:
			if child.name!="group":
				print "Unknown roster item subelement:",child.serialize()
				continue
			if child.getContent():
				groups.append(child.getContent())
			child=child.next
		cur.execute("INSERT INTO \"roster-items\""
				" (\"collection-owner\",\"object-sequence\",jid,name,\"to\",\"from\",ask)"
				" VALUES (%(jid)s,nextval('object-sequence'),"
					"%(ijid)s,%(name)s,%(to)s,%(fr)s,%(ask)s)",
					{'jid':jid,'ijid':ijid,'name':name,'to':to,'fr':fr,'ask':ask})
		for group in groups:
			cur.execute("INSERT INTO \"roster-groups\""
					"(\"collection-owner\",\"object-sequence\",jid,\"group\")"
					" VALUES (%(jid)s,nextval('object-sequence'),%(ijid)s,%(group)s)",
					{'jid':jid,'ijid':ijid,'group':group})
		item=item.next

def convert_offline(cur,doc,jid,el):
	item=el.children
	while item:
		cur.execute("INSERT INTO queue (\"collection-owner\",\"object-sequence\",xml)" 
				" VALUES (%(jid)s,nextval('object-sequence'),%(xml)s)",
				{'jid':jid,'xml':item.serialize})
		item=item.next


vcard_fields=[
		("v:FN","fn"),
		("v:NICKNAME","nickname"),
		("v:URL","url"),
		("v:TEL/NUMBER","tel"),
		("v:EMAIL","email"),
		("v:TITLE","title"),
		("v:ROLE","role"),
		("v:BDAY","bday"),
		("v:DESC","desc"),
		("v:N/GIVEN","n-given"),
		("v:N/FAMILY","n-family"),
		("v:ADR/STREET","adr-street"),
		("v:ADR/EXTADD","adr-extadd"),
		("v:ADR/LOCALITY","adr-locality"),
		("v:ADR/REGION","adr-region"),
		("v:ADR/PCODE","adr-pcode"),
		("v:ADR/COUNTRY","adr-country"),
		("v:ORG/ORGNAME","org-orgname"),
		("v:ORG/ORGUNIT","org-orgunit"),
		]
		

def convert_vcard(cur,doc,jid,el):
	xp=doc.xpathNewContext()
	xp.xpathRegisterNs("v","vcard-temp")
	xp.setContextNode(el)

	fields=[]
	values=[]
	for xpath,field in vcard_fields:
		f=xp.xpathEval(xpath)
		if not f:
			continue
		v=f[0].getContent()
		if not v:
			continue
		fields.append('"%s"' % (field,))
		v=v.replace("'","''")
		values.append("'%s'" % (v,))

	if not fields:
		return
	fields=string.join(fields,",")
	values=string.join(values,",")
	cur.execute("INSERT INTO vcard (\"collection-owner\",\"object-sequence\",%s)" 
			" VALUES (%%(jid)s,nextval('object-sequence'),%s)" % (fields,values),
				{'jid':jid})

namespace_handlers={
	("jabber:iq:auth","jabber:iq:auth"):convert_password,
	("jabber:iq:auth:0k","jabber:iq:auth:0k"):convert_zerok,
	("jabber:iq:roster","jabber:iq:roster"):convert_roster,
	("jabber:iq:last","jabber:iq:last"):convert_last,
	("jabber:x:offline","jabber:x:offline"):convert_offline,
	("vcard-temp","vcard-temp"):convert_vcard,

	("jabber:xdb:nslist","jabber:xdb:nslist"):ignore_ns,
	("jabber:iq:register","jabber:iq:register"):ignore_ns,
	("jabber:iq:filter","jabber:iq:filter"):ignore_ns,
	("jabber:iq:browse","jabber:iq:browse"):ignore_ns,
	}


def convert_user(dbconn,jid,filename,realm):
	print "Converting: %s (JID: %s)" % (filename,jid)
	doc=libxml2.parseFile(filename)

	cur=dbconn.cursor()

	cur.execute("INSERT INTO authreg(username,realm) VALUES(%(username)s,%(realm)s)",
			{'username':jid.split('@')[0],'realm':realm})
	cur.execute("INSERT INTO active(\"collection-owner\",\"object-sequence\",time)"
			" VALUES(%(jid)s,nextval('object-sequence'),%(time)s)",
			{'jid':jid,'time':time.time()})

	private_namespaces=[]
	xp=doc.xpathNewContext()
	xp.xpathRegisterNs("nsl","jabber:xdb:nslist")
	nslist=xp.xpathEval("//nsl:*[@xdbns='jabber:xdb:nslist']/nsl:ns")
	if nslist:
		for ns in nslist:
			if not ns.hasProp("type") or ns.prop("type")!="private":
				print "Warning: non-private xdb namespace '%s' made private" % (ns.getContent())
			private_namespaces.append(ns.getContent())

	el=doc.getRootElement().children
	while el:
		try:
			ns=el.ns()
		except libxml2.treeError:
			ns=None
		if ns:
			ns=ns.getContent()
		xdbns=el.prop("xdbns")
		
		handler=namespace_handlers.get((xdbns,ns))
		if handler:
			handler(cur,doc,jid,el)
		elif not xdbns: 
			print "Element without xdbns:",el.serialize()
		elif xdbns in private_namespaces:
			cur.execute("INSERT INTO private"
					"(\"collection-owner\",\"object-sequence\",ns,xml)"
					" VALUES ( %(jid)s,nextval('object-sequence'),"
						" %(xdbns)s,%(xml)s)",
					{'jid':jid,'xdbns':xdbns,'xml':el.serialize()})
		else:
			print "Element in unknown xdb namespace:",el.serialize()
		el=el.next
	
	xp.xpathFreeContext()
	doc.freeDoc()
	dbconn.commit()
	print

if len(sys.argv)<3:
	print "Usage:"
	print "	jubgrade.py <jid> <file>"
	print "or:"
	print " jubgrade.py <domain> <directory>"
	sys.exit(1)

dbconn=db.connect(DBSTRING)

jid=sys.argv[1]
if "@" in jid:
	filename=sys.argv[2]
	convert_user(dbconn,jid,file,jid.split("@")[1])
else:
	dirname=sys.argv[2]
	domain=jid
	files=glob.glob("%s/*.xml" % (dirname,))
	for f in files:
		username=os.path.splitext(os.path.basename(f))[0]
		jid="%s@%s" % (username.lower(),domain)
		convert_user(dbconn,jid,f,domain)