#!/usr/bin/python # -*- coding: utf-8 -*- # According to http://search.eb.com/shakespeare/article-9000043 # Lucio from Measure for Measure is a scandalmonger import sys import xmpp import os import os.path import ConfigParser import optparse import StringIO import lxml.etree from xml.dom.ext import PrettyPrint debug = False NS_XEP136="http://www.xmpp.org/extensions/xep-0136.html#ns" NS_RSM = "http://jabber.org/protocol/rsm" class FoundMessage(Exception): pass class MsgType: msg = 0 iq = 1 presence = 2 class WatchStream(xmpp.Client): def __init__(self,jidStr=None,password="",initDebug=[]): if not(jidStr and password): configFN = os.path.abspath(os.path.expanduser("~/.xsendrc")) self.jid,self.passwd = self.__getAuthenticators(configFN) else: self.jid=xmpp.protocol.JID(jidStr) self.passwd=password if initDebug: debugList = ['always', 'nodebuilder'] else: debugList = [] # FIXME: why are the following two properties not generated # automagically by xmpp.Client? self.Namespace=xmpp.NS_CLIENT self.DBG=xmpp.client.DBG_CLIENT xmpp.Client.__init__(self,self.jid.getDomain(),debug=debugList) if self.connect() == "": raise RuntimeError, "Cannot connect." if self.auth(self.jid.getNode(),self.passwd, self.jid.getResource()) == None: raise RuntimeError, "Authentication failed." self.__typeOfMsg = None self.__msgid = "" self.__retMessage = None self.RegisterHandler('message', self.__messageCB) self.RegisterHandler('iq', self.__iqCB) self.sendInitPresence(requestRoster=0) def __getAuthenticators(self,filename): conf = {"jid":None,"password":None} try: configFile=file(filename,"r") except: print >>sys.stderr,"Cannot open configuration file %s." % filename raise for line in configFile: key,value = line.strip().lower().split("=") conf[key]=value if not(conf["jid"] and conf["password"]): raise RuntimeError,"Authentication tokens not available." return conf["jid"],conf["password"] def __messageCB(self,conn,msg): if (str(msg.getID()) == self.__msgid) and self.__typeOfMsg == MsgType.msg: if debug: print "Sender: " + str(msg.getFrom()) print "Content: " + str(msg.getBody()) self.__retMessage = msg raise FoundMessage def __iqCB(self,conn,msg): if (str(msg.getID()) == self.__msgid) and self.__typeOfMsg == MsgType.iq: if debug: print "Sender: " + str(msg.getFrom()) print "Type: " + str(msg.getType()) print "QueryNS: " + str(msg.getQueryNS()) print "Payload: " + \ str(",".join([str(x) for x in msg.getQueryPayload()])) self.__retMessage = msg raise FoundMessage def __stepOn(self): try: self.Process(1) except (KeyboardInterrupt,FoundMessage): return False return True def getResponse(self,idNo,msgType=MsgType.iq): self.__msgid = idNo self.__typeOfMsg = msgType while self.__stepOn(): pass return self.__retMessage class IqSource(xmpp.Iq): def __init__(self,jid=None,maxKarma=30): xmpp.Iq.__init__(self,typ="get") self.maxResp=maxKarma self.sJid = jid exNode = self.ExecNode() self.addChild(node=exNode) def SetNode(self): setNode = xmpp.Node("set") setNode.setNamespace(NS_RSM) setNode.addChild(name="max",payload=str(self.maxResp)) return setNode def ExecNode(self,name="dummy"): node = xmpp.Node(name) node.setNamespace(NS_XEP136) node.addChild(node=self.SetNode()) if self.sJid: node.setAttr("with",self.sJid) def getData(self,cl): idReturned = cl.send(self) return cl.getResponse(idReturned) # from http://infix.se/2007/02/06/gentlemen-indent-your-xml def __xmlIndent(self,elem,level=0): i = "\n" + level*" " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " for e in elem: xmlIndent(e, level+1) if not e.tail or not e.tail.strip(): e.tail = i + " " if not e.tail or not e.tail.strip(): e.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i def __prettyPrint(self): doc_file = StringIO.StringIO(xmpp.ustr(self)) out_file = StringIO.StringIO() doc = lxml.etree.parse(doc_file) self.__xmlIndent(doc.getroot()) doc.write(out_file,"utf-8") return unicode(out_file.getvalue()) def __own_str__(self): # if the following doesn't work, use: # self.prettyPrint() return xmpp.Iq.__str__(fancy=1) class Collections(IqSource): def __init__(self,jid=None,start=None,end=None): IqSource.__init__(self,jid,30) def ExecNode(self): node=IqSource.ExecNode(self,"list") class Chats(IqSource): def __init__(self,jid=None,start=None,end=None): self.startTime = start self.endTime = end IqSource.__init__(self,jid,30) def ExecNode(self): node=IqSource.ExecNode("retrieve") if self.startTime: node.setAttr("start",self.startTime) if self.endTime: node.setAttr("end",self.endTime) def main(): searchJid = None cmdlineparser = optparse.OptionParser() (options,args) = cmdlineparser.parse_args() if (len(args) > 0): searchJid = args[0] cl = WatchStream("jid@domain/resource", "passwd",debug) # disco request idReturned = cl.send(xmpp.Iq("get",xmpp.NS_DISCO_INFO)) response = cl.getResponse(idReturned,MsgType.iq) # get collections # # question: # # # # 30 # # # # # response: # # # # # 63367484175 # 63367484175 # 1 # # # coll = Collections(searchJid) coll.getData(cl) print "%s\n%s" % ("=" * 80,str(coll)) # parse response startTimes = {} listTag = response.getTag("list") print >>sys.stderr,listTag xchats = response.getTag("list").getTags("chat") for xchat in xchats: prettyPrint(xchat) who = xchat.getAttr("with") start = xchat.getAttr("start") if not(startTimes.hasKey(who)): startTimes[who]=[] startTimes[who].append(start) print >>sys.stderr,startTimes # FIXME: testing measure print >>sys.stderr,"This is the end for now." startTimes = {} # get content of a collection # # question: # # # # 100 # # # # # long reply: # # # # are you there? # # # yes, I am # # ... a lot of stuff deleted # # 0 # 7 # 7 # # # for currJid in startTimes.keys(): print "%s\nCurrent JID: %s\n%s" % ("=" * 80, currJid, "-" * 80) for startT in startTimes[currJid]: chatList = Collections(searchJid) chatList.getData(cl) print "%s\n%s" % ("-" * 80,str(chatList)) cl.disconnect() if __name__ == '__main__': main()