]> code.delx.au - pymsnt/blobdiff - src/main.py
Hopefully fixed weird exception errors.
[pymsnt] / src / main.py
index 76ca79600d5aebc25677971e93ec94a071d8b5b2..4b4d906022860449743b86ee76ba46884a9418f9 100644 (file)
-# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
 # Licensed for distribution under the GPL version 2, check COPYING for details
 
 # Licensed for distribution under the GPL version 2, check COPYING for details
 
-import utils
-import os
-import os.path
-import shutil
-import time
-import sys
+import os, os.path, time, sys, codecs, getopt
 reload(sys)
 sys.setdefaultencoding("utf-8")
 reload(sys)
 sys.setdefaultencoding("utf-8")
+sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)
+
+# Find the best reactor
+selectWarning = "Unable to install any good reactors (kqueue, epoll, poll).\nWe fell back to using select. You may have scalability problems.\nThis reactor will not support more than 1024 connections at a time."
+try:
+       from twisted.internet import epollreactor as bestreactor
+except:
+       try:
+               from twisted.internet import kqreactor as bestreactor
+       except:
+               try:
+                       from twisted.internet import pollreactor as bestreactor
+               except:
+                       try:
+                               from twisted.internet import selectreactor as bestreactor
+                               print selectWarning
+                       except:
+                               try:
+                                       from twisted.internet import default as bestreactor
+                                       print selectWarning
+                               except:
+                                       print "Unable to find a reactor.\nExiting..."
+                                       sys.exit(1)
+bestreactor.install()
+
+
 
 # Must load config before everything else
 import config
 import xmlconfig
 
 # Must load config before everything else
 import config
 import xmlconfig
-xmlconfig.reloadConfig()
+configFile = "config.xml"
+configOptions = {}
+opts, args = getopt.getopt(sys.argv[1:], "bc:o:dDgtlp:h", ["background", "config=", "option=", "debug", "Debug", "garbage", "traceback", "log=", "pid=", "help"])
+for o, v in opts:
+       if o in ("-c", "--config"):
+               configFile = v
+       elif o in ("-b", "--background"):
+               config.background = True
+       elif o in ("-d", "--debug"):
+               config.debugLevel = "2"
+       elif o in ("-D", "--Debug"):
+               config.debugLevel = "3"
+       elif o in ("-g", "--garbage"):
+               import gc
+               gc.set_debug(gc.DEBUG_LEAK|gc.DEBUG_STATS)
+       elif o in ("-t", "--traceback"):
+               config.debugLevel = "1"
+       elif o in ("-l", "--log"):
+               config.debugFile = v
+       elif o in ("-p", "--pid"):
+               config.pid = v
+       elif o in ("-o", "--option"):
+               var, setting = v.split("=", 2)
+               configOptions[var] = setting
+       elif o in ("-h", "--help"):
+               print "%s [options]" % sys.argv[0]
+               print "   -h                  print this help"
+               print "   -b                  daemonize/background transport"
+               print "   -c <file>           read configuration from this file"
+               print "   -d                  print debugging output"
+               print "   -D                  print extended debugging output"
+               print "   -g                  print garbage collection output"
+               print "   -t                  print debugging only on traceback"
+               print "   -l <file>           write debugging output to file"
+               print "   -p <file>           write process ID to file"
+               print "   -o <var>=<setting>  set config var to setting"
+               sys.exit(0)
+
+xmlconfig.reloadConfig(configFile, configOptions)
+
+if config.reactor:
+       # They picked their own reactor. Lets install it.
+       del sys.modules["twisted.internet.reactor"]
+       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 select(), reactor."
+
 
 from twisted.internet import reactor, task
 from twisted.internet.defer import Deferred
 from tlib.xmlw import Element, jid, component
 from debug import LogEvent, INFO, WARN, ERROR
 
 
 from twisted.internet import reactor, task
 from twisted.internet.defer import Deferred
 from tlib.xmlw import Element, jid, component
 from debug import LogEvent, INFO, WARN, ERROR
 
+import debug
+import utils
 import xdb
 import avatar
 import session
 import xdb
 import avatar
 import session
@@ -32,23 +109,19 @@ import lang
 import legacy
 import housekeep
 
 import legacy
 import housekeep
 
-#import gc
-#gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS)
 
 
 class PyTransport(component.Service):
        def __init__(self):
                LogEvent(INFO)
 
 
 
 class PyTransport(component.Service):
        def __init__(self):
                LogEvent(INFO)
 
-               # Do any auto-update stuff
-               housekeep.init()
-               
                # Discovery, as well as some builtin features
                self.discovery = disco.ServerDiscovery(self)
                # Discovery, as well as some builtin features
                self.discovery = disco.ServerDiscovery(self)
-               self.discovery.addIdentity("gateway", legacy.id, legacy.name, config.jid)
-               self.discovery.addIdentity("conference", "text", legacy.name + " Chatrooms", config.jid)
+               self.discovery.addIdentity("gateway", legacy.id, config.discoName, config.jid)
+               self.discovery.addIdentity("conference", "text", config.discoName + " 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.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.discovery.addIdentity("client", "pc", "MSN Messenger", "USER")
                
                self.xdb = xdb.XDB(config.jid, legacy.mangle)
                self.avatarCache = avatar.AvatarCache()
                
                self.xdb = xdb.XDB(config.jid, legacy.mangle)
                self.avatarCache = avatar.AvatarCache()
@@ -60,7 +133,9 @@ class PyTransport(component.Service):
                self.vCardFactory = misciq.VCardFactory(self)
                self.iqAvatarFactor = misciq.IqAvatarFactory(self)
                self.connectUsers = misciq.ConnectUsers(self)
                self.vCardFactory = misciq.VCardFactory(self)
                self.iqAvatarFactor = misciq.IqAvatarFactory(self)
                self.connectUsers = misciq.ConnectUsers(self)
-               if config.ftJabberPort: self.ftSOCKS5 = ft.Proxy65(int(config.ftJabberPort))
+               if config.ftJabberPort:
+                       self.ftSOCKS5Receive = ft.Proxy65(int(config.ftJabberPort))
+                       self.ftSOCKS5Send = misciq.Socks5FileTransfer(self)
                if config.ftOOBPort:
                        self.ftOOBReceive = ft.FileTransferOOBReceive(int(config.ftOOBPort))
                        self.ftOOBSend = misciq.FileTransferOOBSend(self)
                if config.ftOOBPort:
                        self.ftOOBReceive = ft.FileTransferOOBReceive(int(config.ftOOBPort))
                        self.ftOOBSend = misciq.FileTransferOOBSend(self)
@@ -77,8 +152,8 @@ class PyTransport(component.Service):
                # Message IDs
                self.messageID = 0
                
                # Message IDs
                self.messageID = 0
                
-               self.loopCall = task.LoopingCall(self.loopCall)
-               self.loopCall.start(60.0)
+               self.loopTask = task.LoopingCall(self.loopFunc)
+               self.loopTask.start(60.0)
 
        def removeMe(self):
                LogEvent(INFO)
 
        def removeMe(self):
                LogEvent(INFO)
@@ -101,7 +176,7 @@ class PyTransport(component.Service):
        def reserveID(self, ID):
                self.reservedIDs.append(ID)
        
        def reserveID(self, ID):
                self.reservedIDs.append(ID)
        
-       def loopCall(self):
+       def loopFunc(self):
                numsessions = len(self.sessions)
 
                #if config.debugOn and numsessions > 0:
                numsessions = len(self.sessions)
 
                #if config.debugOn and numsessions > 0:
@@ -116,12 +191,12 @@ class PyTransport(component.Service):
                        oldDict = self.sessions.copy()
                        self.sessions = {}
                        for key in oldDict:
                        oldDict = self.sessions.copy()
                        self.sessions = {}
                        for key in oldDict:
-                               session = oldDict[key]
-                               if not session.alive:
+                               s = oldDict[key]
+                               if not s.alive:
                                        LogEvent(WARN, "", "Ghost session found.")
                                        # Don't add it to the new dictionary. Effectively removing it
                                else:
                                        LogEvent(WARN, "", "Ghost session found.")
                                        # Don't add it to the new dictionary. Effectively removing it
                                else:
-                                       self.sessions[key] = session
+                                       self.sessions[key] = s
        
        def componentConnected(self, xmlstream):
                LogEvent(INFO)
        
        def componentConnected(self, xmlstream):
                LogEvent(INFO)
@@ -165,8 +240,12 @@ class PyTransport(component.Service):
                        LogEvent(WARN, "", "Failed stringprep.")
                        return
                mtype = el.getAttribute("type")
                        LogEvent(WARN, "", "Failed stringprep.")
                        return
                mtype = el.getAttribute("type")
-               if self.sessions.has_key(froj.userhost()):
-                       self.sessions[froj.userhost()].onMessage(el)
+               s = self.sessions.get(froj.userhost(), None)
+               if mtype == "error" and s:
+                       LogEvent(INFO, s.jabberID, "Removing session because of message type=error")
+                       s.removeMe()
+               elif s:
+                       s.onMessage(el)
                elif mtype != "error":
                        to = el.getAttribute("to")
                        ulang = utils.getLang(el)
                elif mtype != "error":
                        to = el.getAttribute("to")
                        ulang = utils.getLang(el)
@@ -187,8 +266,13 @@ class PyTransport(component.Service):
                        LogEvent(WARN, "", "Failed stringprep.")
                        return
 
                        LogEvent(WARN, "", "Failed stringprep.")
                        return
 
-               if self.sessions.has_key(froj.userhost()):
-                       self.sessions[froj.userhost()].onPresence(el)
+               ptype = el.getAttribute("type")
+               s = self.sessions.get(froj.userhost())
+               if ptype == "error" and s:
+                       LogEvent(INFO, s.jabberID, "Removing session because of message type=error")
+                       s.removeMe()
+               elif s:
+                       s.onPresence(el)
                else:
                        ulang = utils.getLang(el)
                        ptype = el.getAttribute("type")
                else:
                        ulang = utils.getLang(el)
                        ptype = el.getAttribute("type")
@@ -232,24 +316,48 @@ class PyTransport(component.Service):
 
 class App:
        def __init__(self):
 
 class App:
        def __init__(self):
+               # Check for any other instances
+               if config.pid and os.name != "posix":
+                       config.pid = ""
+               if config.pid:
+                       twistd.checkPID(config.pid)
+
+               # Do any auto-update stuff
+               housekeep.init()
+               
+               # Daemonise the process and write the PID file
+               if config.background and os.name == "posix":
+                       twistd.daemonize()
+               if config.pid:
+                       self.writePID()
+
+               # Initialise debugging, and do the other half of SIGHUPstuff
+               debug.reloadConfig()
+               legacy.reloadConfig()
+
+               # Start the service
                jid = config.jid
                jid = config.jid
-               if config.compjid: jid = config.compjid
+               if config.useXCP and 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()
                reactor.addSystemEventTrigger('before', 'shutdown', self.shuttingDown)
        
                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()
                reactor.addSystemEventTrigger('before', 'shutdown', self.shuttingDown)
        
-       def alreadyRunning(self):
-               print "There is already a transport instance running with this configuration."
-               print "Exiting..."
-               sys.exit(1)
-       
+       def writePID(self):
+               # Create a PID file
+               pid = str(os.getpid())
+               pf = open(config.pid, "w")
+               pf.write("%s\n" % pid)
+               pf.close()
+
        def shuttingDown(self):
                self.transportSvc.removeMe()
                # Keep the transport running for another 3 seconds
                def cb(ignored=None):
        def shuttingDown(self):
                self.transportSvc.removeMe()
                # Keep the transport running for another 3 seconds
                def cb(ignored=None):
-                       pass
+                       if config.pid:
+                               twistd.removePID(config.pid)
                d = Deferred()
                d.addCallback(cb)
                reactor.callLater(3.0, d.callback, None)
                d = Deferred()
                d.addCallback(cb)
                reactor.callLater(3.0, d.callback, None)
@@ -258,15 +366,26 @@ class App:
 
 
 def SIGHUPstuff(*args):
 
 
 def SIGHUPstuff(*args):
-       xmlconfig.reloadConfig()
+       global configFile, configOptions
+       xmlconfig.reloadConfig(configFile, configOptions)
+       if config.pid and os.name != "posix":
+               config.pid = ""
+       debug.reloadConfig()
+       legacy.reloadConfig()
 
 if os.name == "posix":
        import signal
        # Set SIGHUP to reload the config file & close & open debug file
        signal.signal(signal.SIGHUP, SIGHUPstuff)
 
 if os.name == "posix":
        import signal
        # Set SIGHUP to reload the config file & close & open debug file
        signal.signal(signal.SIGHUP, SIGHUPstuff)
+       # Load some scripts for PID and daemonising
+       from twisted.scripts import twistd
 
 
 
 
-if __name__ == "__main__":
+def main():
+       # Create the application
        app = App()
        reactor.run()
 
        app = App()
        reactor.run()
 
+if __name__ == "__main__":
+       main()
+