X-Git-Url: https://code.delx.au/pymsnt/blobdiff_plain/c82ae883c8c1a236e80013c734fb56725b3c535c..85e6c0f55bd0d46cad7f94d15b52c7b64dc94c08:/src/main.py diff --git a/src/main.py b/src/main.py index 7010850..70d0733 100644 --- a/src/main.py +++ b/src/main.py @@ -1,53 +1,40 @@ -# Copyright 2004 James Bunton +# Copyright 2004-2005 James Bunton # Licensed for distribution under the GPL version 2, check COPYING for details import utils import os +import os.path import shutil -if(os.name == "posix"): - import signal +import time import sys reload(sys) sys.setdefaultencoding("utf-8") -import types # Must load config before everything else import config import xmlconfig xmlconfig.reloadConfig() -if(config.reactor == "epoll"): - from twisted.internet import epollreactor - epollreactor.install() -elif(config.reactor == "poll"): - from twisted.internet import pollreactor - pollreactor.install() -elif(config.reactor == "kqueue"): - from twisted.internet import kqreactor - kqreactor.install() -elif(len(config.reactor) > 0): - print "Unknown reactor: ", config.reactor, "Using default reactor" - from twisted.internet import reactor, task from twisted.internet.defer import Deferred -import twisted.python.log -if(utils.checkTwisted()): +if utils.checkTwisted(): from twisted.words.protocols.jabber import component, jid from twisted.xish.domish import Element else: from tlib.jabber import component, jid from tlib.domish import Element - +from debug import LogEvent, INFO, WARN, ERROR import xdb +import avatar import session import jabw import disco import register import misciq import lang -import debug import legacy +import housekeep #import gc #gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS) @@ -55,19 +42,30 @@ import legacy class PyTransport(component.Service): def __init__(self): - debug.log("PyTransport: Service starting up") + LogEvent(INFO) + + # Do any auto-update stuff + housekeep.init() # Discovery, as well as some builtin features self.discovery = disco.ServerDiscovery(self) - self.discovery.addIdentity("gateway", legacy.id, legacy.name) - self.discovery.addIdentity("conference", "text", legacy.name + " Chatrooms") - self.discovery.addFeature("http://jabber.org/protocol/muc", None) # So that clients know you can create groupchat rooms on the server + self.discovery.addIdentity("gateway", legacy.id, legacy.name, config.jid) + self.discovery.addIdentity("conference", "text", legacy.name + " Chatrooms", config.jid) + self.discovery.addFeature(disco.XCONFERENCE, None, config.jid) # So that clients know you can create groupchat rooms on the server + self.discovery.addFeature("jabber:iq:conference", None, config.jid) # We don't actually support this, but Psi has a bug where it looks for this instead of the above self.xdb = xdb.XDB(config.jid, legacy.mangle) + self.avatarCache = avatar.AvatarCache() self.registermanager = register.RegisterManager(self) self.gatewayTranslator = misciq.GatewayTranslator(self) self.versionTeller = misciq.VersionTeller(self) self.pingService = misciq.PingService(self) + self.adHocCommands = misciq.AdHocCommands(self) + self.vCardFactory = misciq.VCardFactory(self) + self.iqAvatarFactor = misciq.IqAvatarFactory(self) + self.connectUsers = misciq.ConnectUsers(self) + self.statistics = misciq.Statistics(self) + self.startTime = int(time.time()) self.xmlstream = None self.sessions = {} @@ -79,31 +77,13 @@ class PyTransport(component.Service): # Message IDs self.messageID = 0 - self.loopCheckSessions = task.LoopingCall(self.loopCheckSessionsCall) - self.loopCheckSessions.start(60.0) # call every ten seconds - - # Display active sessions if debug mode is on - if(config.debugOn): - self.loop = task.LoopingCall(self.loopCall) - self.loop.start(60.0) # call every 60 seconds - twisted.python.log.addObserver(self.exceptionLogger) - - + self.loopCall = task.LoopingCall(self.loopCall) + self.loopCall.start(60.0) + def removeMe(self): - debug.log("PyTransport: Service shutting down") - dic = utils.copyDict(self.sessions) - for session in dic: - dic[session].removeMe() - - def exceptionLogger(self, *kwargs): - if(len(config.debugLog) > 0): - kwargs = kwargs[0] - if(kwargs.has_key("failure")): - failure = kwargs["failure"] - failure.printTraceback(debug) # Pass debug as a pretend file object because it implements the write method - if(config.debugLog): - debug.flushDebugSmart() - print "Exception occured! Check the log!" + LogEvent(INFO) + for session in self.sessions.copy(): + self.sessions[session].removeMe() def makeMessageID(self): self.messageID += 1 @@ -112,7 +92,7 @@ class PyTransport(component.Service): def makeID(self): newID = "r" + str(self.lastID) self.lastID += 1 - if(self.reservedIDs.count(newID) > 0): + if self.reservedIDs.count(newID) > 0: # Ack, it's already used.. Try again return self.makeID() else: @@ -122,87 +102,113 @@ class PyTransport(component.Service): self.reservedIDs.append(ID) def loopCall(self): - if(len(self.sessions) > 0): - debug.log("Sessions:") - for key in self.sessions: - debug.log("\t" + self.sessions[key].jabberID) + numsessions = len(self.sessions) + + #if config.debugOn and numsessions > 0: + # print "Sessions:" + # for key in self.sessions: + # print "\t" + self.sessions[key].jabberID - def loopCheckSessionsCall(self): - if(len(self.sessions) > 0): - oldDict = utils.copyDict(self.sessions) + self.statistics.stats["Uptime"] = int(time.time()) - self.startTime + self.statistics.stats["OnlineUsers"] = numsessions + legacy.updateStats(self.statistics) + if numsessions > 0: + oldDict = self.sessions.copy() self.sessions = {} for key in oldDict: session = oldDict[key] - if(not session.alive): - debug.log("Ghost session %s found. This shouldn't happen. Trace" % (session.jabberID)) + if not session.alive: + LogEvent(WARN, "", "Ghost session found.") # Don't add it to the new dictionary. Effectively removing it else: self.sessions[key] = session def componentConnected(self, xmlstream): - debug.log("PyTransport: Connected to main Jabberd server") + LogEvent(INFO) self.xmlstream = xmlstream self.xmlstream.addObserver("/iq", self.discovery.onIq) self.xmlstream.addObserver("/presence", self.onPresence) self.xmlstream.addObserver("/message", self.onMessage) self.xmlstream.addObserver("/route", self.onRouteMessage) + if config.useXCP: + pres = Element((None, "presence")) + pres.attributes["to"] = "presence@-internal" + pres.attributes["from"] = config.compjid + x = pres.addElement("x") + x.attributes["xmlns"] = "http://www.jabber.com/schemas/component-presence.xsd" + x.attributes["xmlns:config"] = "http://www.jabber.com/config" + x.attributes["config:version"] = "1" + x.attributes["protocol-version"] = "1.0" + x.attributes["config-ns"] = legacy.url + "/component" + self.send(pres) def componentDisconnected(self): - debug.log("PyTransport: Disconnected from main Jabberd server") + LogEvent(INFO) self.xmlstream = None def onRouteMessage(self, el): for child in el.elements(): - if(child.name == "message"): + if child.name == "message": self.onMessage(child) - elif(child.name == "presence"): + elif child.name == "presence": + # Ignore any presence broadcasts about other XCP components + if child.getAttribute("to") and child.getAttribute("to").find("@-internal") > 0: return self.onPresence(child) - elif(child.name == "iq"): + elif child.name == "iq": self.discovery.onIq(child) def onMessage(self, el): fro = el.getAttribute("from") - froj = jid.JID(fro) - to = el.getAttribute("to") -# if(to.find('@') < 0): return + try: + froj = jid.JID(fro) + except Exception, e: + LogEvent(WARN, "", "Failed stringprep.") + return mtype = el.getAttribute("type") - ulang = utils.getLang(el) - body = None - for child in el.elements(): - if(child.name == "body"): - body = child.__str__() - if(self.sessions.has_key(froj.userhost())): + if self.sessions.has_key(froj.userhost()): self.sessions[froj.userhost()].onMessage(el) - elif(mtype != "error"): - debug.log("PyTrans: Sending error response to a message outside of session.") + elif mtype != "error": + to = el.getAttribute("to") + ulang = utils.getLang(el) + body = None + for child in el.elements(): + if child.name == "body": + body = child.__str__() + LogEvent(INFO, "", "Sending error response to a message outside of session.") jabw.sendErrorMessage(self, fro, to, "auth", "not-authorized", lang.get(ulang).notLoggedIn, body) def onPresence(self, el): fro = el.getAttribute("from") - ptype = el.getAttribute("type") - froj = jid.JID(fro) to = el.getAttribute("to") - toj = jid.JID(to) - ulang = utils.getLang(el) - if(self.sessions.has_key(froj.userhost())): + try: + froj = jid.JID(fro) + toj = jid.JID(to) + except Exception, e: + LogEvent(WARN, "", "Failed stringprep.") + return + + if self.sessions.has_key(froj.userhost()): self.sessions[froj.userhost()].onPresence(el) else: - if(to.find('@') < 0): + ulang = utils.getLang(el) + ptype = el.getAttribute("type") + if to.find('@') < 0: # If the presence packet is to the transport (not a user) and there isn't already a session - if(el.getAttribute("type") in [None, ""]): # Don't create a session unless they're sending available presence - debug.log("PyTransport: Attempting to create a new session \"%s\"" % (froj.userhost())) + if not el.getAttribute("type"): # Don't create a session unless they're sending available presence + LogEvent(INFO, "", "Attempting to create a new session.") s = session.makeSession(self, froj.userhost(), ulang) - if(s): + if s: + self.statistics.stats["TotalUsers"] += 1 self.sessions[froj.userhost()] = s - debug.log("PyTransport: New session created \"%s\"" % (froj.userhost())) + LogEvent(INFO, "", "New session created.") # Send the first presence s.onPresence(el) else: - debug.log("PyTransport: Failed to create session \"%s\"" % (froj.userhost())) + LogEvent(INFO, "", "Failed to create session") jabw.sendMessage(self, to=froj.userhost(), fro=config.jid, body=lang.get(ulang).notRegistered) - elif(el.getAttribute("type") != "error"): - debug.log("PyTransport: Sending unavailable presence to non-logged in user \"%s\"" % (froj.userhost())) + elif el.getAttribute("type") != "error": + LogEvent(INFO, "", "Sending unavailable presence to non-logged in user.") pres = Element((None, "presence")) pres.attributes["from"] = to pres.attributes["to"] = fro @@ -210,14 +216,14 @@ class PyTransport(component.Service): self.send(pres) return - elif(ptype in ["subscribe", "subscribed", "unsubscribe", "unsubscribed"]): + elif ptype and (ptype.startswith("subscribe") or ptype.startswith("unsubscribe")): # They haven't logged in, and are trying to change subscription to a user # Lets log them in and then do it - debug.log("PyTransport: Attempting to create a session to do subscription stuff %s" % (froj.userhost())) + LogEvent(INFO, "", "Attempting to create a session to do subscription stuff.") s = session.makeSession(self, froj.userhost(), ulang) - if(s): + if s: self.sessions[froj.userhost()] = s - debug.log("PyTransport: New session created \"%s\"" % (froj.userhost())) + LogEvent(INFO, "", "New session created.") # Tell the session there's a new resource s.handleResourcePresence(froj.userhost(), froj.resource, toj.userhost(), toj.resource, 0, None, None, None) # Send this subscription @@ -226,28 +232,9 @@ class PyTransport(component.Service): class App: def __init__(self): - # Check that there isn't already a PID file - if(os.path.isfile(utils.doPath(config.pid))): - pf = open(utils.doPath(config.pid)) - pid = int(str(pf.readline().strip())) - pf.close() - if(os.name == "posix"): - try: - os.kill(pid, signal.SIGHUP) - self.alreadyRunning() - except OSError: - # The process is still up - pass - else: - self.alreadyRunning() - - # Create a PID file - pid = str(os.getpid()) - pf = file(utils.doPath(config.pid), 'w') - pf.write("%s\n" % pid); - pf.close() - - self.c = component.buildServiceManager(config.jid, config.secret, "tcp:%s:%s" % (config.mainServer, config.port)) + jid = config.jid + if config.compjid: jid = config.compjid + self.c = component.buildServiceManager(jid, config.secret, "tcp:%s:%s" % (config.mainServer, config.port)) self.transportSvc = PyTransport() self.transportSvc.setServiceParent(self.c) self.c.startService() @@ -260,8 +247,9 @@ class App: def shuttingDown(self): self.transportSvc.removeMe() + # Keep the transport running for another 3 seconds def cb(ignored=None): - os.remove(utils.doPath(config.pid)) + pass d = Deferred() d.addCallback(cb) reactor.callLater(3.0, d.callback, None) @@ -273,53 +261,13 @@ def SIGHUPstuff(*args): xmlconfig.reloadConfig() debug.reopenFile() -def doSpoolPrepCheck(): - pre = utils.doPath(config.spooldir) + "/" + config.jid + "/" - try: - f = open(pre + "notes_to_myself", "r") - for line in f.readlines(): - if line == "doSpoolPrepCheck\n": - return - f.close() - except IOError: - pass - - # New installation - if not os.path.exists(pre): - os.makedirs(pre) - f = open(pre + "notes_to_myself", "w") - f.write("doSpoolPrepCheck\n") - f.close() - return - - print "Checking spool files and stringprepping any if necessary...", - for file in os.listdir(pre): - if(file == "notes_to_myself"): return - file = file.replace("%", "@") - filej = jid.JID(file).full() - if(file != filej): - file = file.replace("@", "%") - filej = filej.replace("@", "%") - if(os.path.exists(filej)): - print "Need to move", file, "to", filej, "but the latter exists!\nAborting!" - os.exit(1) - else: - shutil.move(utils.doPath(pre + file, pre + filej)) - print "done" - f = open(pre + "notes_to_myself", "a") - f.write("doSpoolPrepCheck\n") - f.close() - - -if(__name__ == "__main__"): +if os.name == "posix": + import signal # Set SIGHUP to reload the config file & close & open debug file - if(os.name == "posix"): - signal.signal(signal.SIGHUP, SIGHUPstuff) + signal.signal(signal.SIGHUP, SIGHUPstuff) - # Check that all the spool files stringprepped - doSpoolPrepCheck() +if __name__ == "__main__": app = App() reactor.run() -