# 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 import sys import config import legacy from debug import LogEvent, INFO, WARN, ERROR import lang import globals class ServiceDiscovery: """ Handles Service Discovery (aka DISCO) support """ def __init__ (self, pytrans): LogEvent(INFO) self.pytrans = pytrans self.identities = {} self.features = {} self.nodes = {} self.userInfoHandlers = [] self.userItemHandlers = [] self.addFeature(globals.DISCO, None, config.jid) self.addFeature(globals.DISCO, None, "USER") self.pytrans.iq.addHandler(globals.DISCO, self.incomingIq, prefix=1) def addIdentity(self, category, ctype, name, jid): """ Adds an identity to this JID's discovery profile. If jid == "USER" then AIM users will get this identity. """ #debug.log("ServerDiscovery: Adding identity \"%r\" \"%r\" \"%r\" \"%r\"" % (category, ctype, name, jid)) LogEvent(INFO) if not self.identities.has_key(jid): self.identities[jid] = [] self.identities[jid].append((category, ctype, name)) def addFeature(self, var, handler, jid): """ Adds a feature to this JID's discovery profile. If jid == "USER" then AIM users will get this feature. """ #debug.log("ServerDiscovery: Adding feature support \"%r\" \"%r\" \"%r\"" % (var, handler, jid)) LogEvent(INFO) if not self.features.has_key(jid): self.features[jid] = [] self.features[jid].append(var) if handler: self.pytrans.iq.addHandler(var, handler) def addNode(self, node, handler, name, jid, rootnode): """ Adds a node to this JID's discovery profile. If jid == "USER" then AIM users will get this node. """ #debug.log("ServerDiscovery: Adding node item \"%r\" \"%r\" \"%r\" \"%r\" \"%r\"" % (node, handler, name, jid, rootnode)) LogEvent(INFO) if not self.nodes.has_key(jid): self.nodes[jid] = {} self.nodes[jid][node] = (handler, name, rootnode) def addUserItemHandler(self, handler): """ Adds an extra handler to disco items requests directed at users """ LogEvent(INFO) self.userItemHandlers.append(handler) def addUserInfoHandler(self, handler): """ Adds an extra handler to disco info requests directed at users """ LogEvent(INFO) self.userInfoHandlers.append(handler) def incomingIq(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") LogEvent(INFO, msg="Looking for handler") for query in el.elements(): xmlns = query.uri node = query.getAttribute("node") if xmlns.startswith(globals.DISCO) and node: if self.nodes.has_key(to) and self.nodes[to].has_key(node) and self.nodes[to][node][0] != None: self.nodes[to][node][0](el) return else: # If the node we're browsing wasn't found, fall through and display the root disco self.sendDiscoInfoResponse(to=fro, ID=ID, ulang=ulang, jid=to) return elif xmlns == globals.DISCO_INFO: self.sendDiscoInfoResponse(to=fro, ID=ID, ulang=ulang, jid=to) return elif xmlns == globals.DISCO_ITEMS: self.sendDiscoItemsResponse(to=fro, ID=ID, ulang=ulang, jid=to) return # Still hasn't been handled LogEvent(WARN, msg="Unknown Iq Request") self.pytrans.iq.sendIqError(to=fro, fro=to, ID=ID, xmlns=xmlns, etype="cancel", condition="feature-not-implemented") def sendDiscoInfoResponse(self, to, ID, ulang, jid): """ Send a service discovery disco#info stanza to the given 'to'. 'jid' is the JID that was queried. """ LogEvent(INFO) iq = Element((None, "iq")) iq.attributes["type"] = "result" iq.attributes["from"] = jid iq.attributes["to"] = to if ID: iq.attributes["id"] = ID query = iq.addElement("query") query.attributes["xmlns"] = globals.DISCO_INFO searchjid = jid if jid.find('@') > 0: searchjid = "USER" # Add any identities for (category, ctype, name) in self.identities.get(searchjid, []): LogEvent(INFO, msg="Found identity %r %r %r" % (category, ctype, name)) identity = query.addElement("identity") identity.attributes["category"] = category identity.attributes["type"] = ctype if name: identity.attributes["name"] = name # Add any supported features for var in self.features.get(searchjid, []): LogEvent(INFO, msg="Found feature %r" % (var)) feature = query.addElement("feature") feature.attributes["var"] = var if searchjid == "USER": # Add any user specific identities/features for hndl in self.userInfoHandlers: hndl(jid, query) self.pytrans.send(iq) def sendDiscoItemsResponse(self, to, ID, ulang, jid): """ Send a service discovery disco#items stanza to the given 'to'. 'jid' is the JID that was queried. """ LogEvent(INFO) iq = Element((None, "iq")) iq.attributes["type"] = "result" iq.attributes["from"] = jid iq.attributes["to"] = to if ID: iq.attributes["id"] = ID query = iq.addElement("query") query.attributes["xmlns"] = globals.DISCO_ITEMS searchjid = jid if jid.find('@') > 0: searchjid = "USER" for node in self.nodes.get(searchjid, []): handler, name, rootnode = self.nodes[jid][node] if rootnode: LogEvent(INFO, msg="Found node %r" % (node)) name = lang.get(name, ulang) item = query.addElement("item") item.attributes["jid"] = jid item.attributes["node"] = node item.attributes["name"] = name if searchjid == "USER": # Add any user specific items for hndl in self.userItemHandlers: hndl(jid, query) self.pytrans.send(iq)