# Copyright 2004-2006 Daniel Henninger # Licensed for distribution under the GPL version 2, check COPYING for details import utils from twisted.words.xish.domish import Element from twisted.words.protocols.jabber.jid import internJID from twisted.internet.defer import Deferred from twisted.internet import reactor from debug import LogEvent, INFO, WARN, ERROR import lang class IqHandler: """ Handles everything IQ related. You can send IQ stanzas and receive a Deferred to notify you when a response comes, or if there's a timeout. """ def __init__ (self, pytrans): LogEvent(INFO) self.pytrans = pytrans self.prefixhandlers = {} # A dict indexed by namespace prefix of handlers to fire given a starts-with match self.handlers = {} # A dict indexed by namespace of handlers to fire given a particular namespace self.deferredIqs = {} # A dict indexed by (jid, id) of deferreds to fire def sendIq(self, el, timeout=15): """ Used for sending IQ packets. The id attribute for the IQ will be autogenerated if it is not there yet. Returns a deferred which will fire with the matching IQ response as it's sole argument. """ def checkDeferred(): if not d.called: d.errback(Exception("Timeout")) del self.deferredIqs[(jid, ID)] jid = el.getAttribute("to") ID = el.getAttribute("id") if not ID: ID = self.pytrans.makeMessageID() el.attributes["id"] = ID self.pytrans.send(el) d = Deferred() self.deferredIqs[(jid, ID)] = d reactor.callLater(timeout, checkDeferred) return d def onIq(self, el): """ Decides what to do with an IQ """ fro = el.getAttribute("from") to = el.getAttribute("to") ID = el.getAttribute("id") iqType = el.getAttribute("type") ulang = utils.getLang(el) try: # StringPrep froj = internJID(fro) to = internJID(to).full() except Exception, e: LogEvent(INFO, msg="Dropping IQ because of stringprep error") # Check if it's a response to a sent IQ if self.deferredIqs.has_key((fro, ID)) and (iqType == "error" or iqType == "result"): LogEvent(INFO, msg="Doing callback") self.deferredIqs[(fro, ID)].callback(el) del self.deferredIqs[(fro, ID)] return if not (iqType == "get" or iqType == "set"): return # Not interested LogEvent(INFO, msg="Looking for handler") for query in el.elements(): xmlns = query.uri node = query.getAttribute("node") if self.handlers.has_key(xmlns): LogEvent(INFO, msg="Namespace handler found") handler = self.handlers[xmlns] if handler: handler(el) return for prefix in self.prefixhandlers.keys(): if xmlns.startswith(prefix): LogEvent(INFO, msg="Namespace prefix handler found") handler = self.prefixhandlers[prefix] if handler: handler(el) return # Still hasn't been handled LogEvent(WARN, msg="Unknown Iq Request") self.sendIqError(to=fro, fro=to, ID=ID, xmlns=xmlns, etype="cancel", condition="feature-not-implemented") def sendIqError(self, to, fro, ID, xmlns, etype, condition): """ Sends an IQ error response. See the XMPP RFC for details on the fields. """ #debug.log("Sending IQ Error: %r %r %r %r %r" % (to,fro,xmlns,etype,condition)) LogEvent(INFO) el = Element((None, "iq")) el.attributes["to"] = to el.attributes["from"] = fro if ID: el.attributes["id"] = ID el.attributes["type"] = "error" error = el.addElement("error") error.attributes["type"] = etype error.attributes["code"] = str(utils.errorCodeMap[condition]) cond = error.addElement(condition) self.pytrans.send(el) def addHandler(self, ns, handler, prefix=0): """ Adds a namespace handler to the handler pool. If prefix is true, match at beginning of namespace. """ LogEvent(INFO) if prefix: self.prefixhandlers[ns] = handler else: self.handlers[ns] = handler