]> code.delx.au - pymsnt/blobdiff - src/main.py
Reimport and tags (0.10.1)
[pymsnt] / src / main.py
index 701085034abf45116eec78e646dbb0ed10f193e7..70d0733be5de52809e095975653a4e65ad7201c7 100644 (file)
@@ -1,53 +1,40 @@
-# Copyright 2004 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
 # 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()
 
-