Code

Use abspath() from os.path, it's been there since 1.5.2.
[roundup.git] / roundup / scripts / roundup_server.py
index 9f3a26a2c0248e587aa8aec165ebb25717086c57..97b6a5d203f957ae1c22fdcbd5b8904733696a8d 100644 (file)
 # 
 """ HTTP Server that serves roundup.
 
-$Id: roundup_server.py,v 1.17 2003-01-13 02:44:42 richard Exp $
+$Id: roundup_server.py,v 1.30 2003-10-25 11:20:17 jlgijsbers Exp $
 """
 
 # python version check
 from roundup import version_check
 
 import sys, os, urllib, StringIO, traceback, cgi, binascii, getopt, imp
-import BaseHTTPServer
+import BaseHTTPServer, socket
 
 # Roundup modules of use here
 from roundup.cgi import cgitb, client
@@ -40,25 +40,28 @@ from roundup.i18n import _
 # Make sure the NAME part doesn't include any url-unsafe characters like 
 # spaces, as these confuse the cookie handling in browsers like IE.
 TRACKER_HOMES = {
-    'bar': '/tmp/bar',
+#    'example': '/path/to/example',
 }
 
 ROUNDUP_USER = None
 
 
-# Where to log debugging information to. Use an instance of DevNull if you
-# don't want to log anywhere.
-# TODO: actually use this stuff
-#class DevNull:
-#    def write(self, info):
-#        pass
-#LOG = open('/var/log/roundup.cgi.log', 'a')
-#LOG = DevNull()
-
 #
 ##  end configuration
 #
 
+import zlib, base64
+favico = zlib.decompress(base64.decodestring('''
+eJyVUk2IQVEUfn4yaRYjibdQZiVba/ZE2djIUmHWFjaKGVmIlY2iFMVG2ViQhXqFSP6iFFJvw4uF
+LGdWd743mpeMn+a88917Oue7955z3qEoET6FQkHx8iahKDV2A8B7XgERRf/EKMSUzyf8ypbbnnQy
+mWBdr9eVSkVw3tJGoxGNRpvNZigUyufzWPv9Pvwcx0UiERj7/V4g73Y7j8fTarWMRmO73U4kEkKI
+YZhardbr9eLxuOD0+/2ZTMZisYjFYpqmU6kU799uN5tNMBg8HA7ZbPY8GaTh8/mEipRKpclk0ul0
+NpvNarUmk0mWZS/yr9frcrmc+iMOh+NWydPp1Ov1SiSSc344HL7fKKfTiSN2u12tVqOcxWJxn6/V
+ag0GAwxkrlKp5vP5fT7ulMlk6XRar9dLpVIUXi6Xb5Hxa1wul0ajKZVKsVjM7XYXCoVOp3OVPJvN
+AoFAtVo1m825XO7hSODOYrH4kHbxxGAwwODBGI/H6DBs5LNara7yl8slGjIcDsHpdrunU6PRCAP2
+r3fPdUcIYeyEfLSAJ0LeAUZHCAt8Al/8/kLIEWDB5YDj0wm8fAP6fVfo
+'''.strip()))
+
 class RoundupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
     TRACKER_HOMES = TRACKER_HOMES
     ROUNDUP_USER = ROUNDUP_USER
@@ -86,14 +89,14 @@ class RoundupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
                 self.wfile.write(cgitb.breaker())
                 self.wfile.write(cgitb.html())
             except:
-                self.wfile.write("<pre>")
                 s = StringIO.StringIO()
                 traceback.print_exc(None, s)
+                self.wfile.write("<pre>")
                 self.wfile.write(cgi.escape(s.getvalue()))
                 self.wfile.write("</pre>\n")
         sys.stdin = save_stdin
 
-    do_GET = do_POST = do_HEAD = send_head = run_cgi
+    do_GET = do_POST = run_cgi
 
     def index(self):
         ''' Print up an index of the available trackers
@@ -115,19 +118,36 @@ class RoundupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
     def inner_run_cgi(self):
         ''' This is the inner part of the CGI handling
         '''
-
         rest = self.path
+
+        if rest == '/favicon.ico':
+            raise client.NotFound
+
         i = rest.rfind('?')
         if i >= 0:
             rest, query = rest[:i], rest[i+1:]
         else:
             query = ''
 
-        # figure the tracker
+        # no tracker - spit out the index
         if rest == '/':
             return self.index()
+
+        # figure the tracker
         l_path = rest.split('/')
         tracker_name = urllib.unquote(l_path[1])
+
+        # handle missing trailing '/'
+        if len(l_path) == 2:
+            self.send_response(301)
+            # redirect - XXX https??
+            protocol = 'http'
+            url = '%s://%s%s/'%(protocol, self.headers['host'], self.path)
+            self.send_header('Location', url)
+            self.end_headers()
+            self.wfile.write('Moved Permanently')
+            return
+
         if self.TRACKER_HOMES.has_key(tracker_name):
             tracker_home = self.TRACKER_HOMES[tracker_name]
             tracker = roundup.instance.open(tracker_home)
@@ -170,18 +190,33 @@ class RoundupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
         c = tracker.Client(tracker, self, env)
         c.main()
 
+    LOG_IPADDRESS = 1
+    def address_string(self):
+        if self.LOG_IPADDRESS:
+            return self.client_address[0]
+        else:
+            host, port = self.client_address
+            return socket.getfqdn(host)
+
 def usage(message=''):
     if message:
         message = _('Error: %(error)s\n\n')%{'error': message}
     print _('''%(message)sUsage:
-roundup-server [-n hostname] [-p port] [-l file] [-d file] [name=tracker home]*
+roundup-server [options] [name=tracker home]*
 
+options:
  -n: sets the host name
  -p: sets the port to listen on
+ -u: sets the uid to this user after listening on the port
+ -g: sets the gid to this group after listening on the port
  -l: sets a filename to log to (instead of stdout)
- -d: daemonize, and write the server's PID to the nominated file
+ -d: sets a filename to write server PID to. This option causes the server 
+     to run in the background. Note: on Windows the PID argument is needed,
+     but ignored. The -l option *must* be specified if this option is.
+ -N: log client machine names in access log instead of IP addresses (much
+     slower)
 
- name=tracker home
+name=tracker home:
    Sets the tracker home(s) to use. The name is how the tracker is
    identified in the URL (it's the first part of the URL path). The
    tracker home is the directory that was identified when you did
@@ -228,18 +263,14 @@ def daemonize(pidfile):
     os.dup2(devnull, 1)
     os.dup2(devnull, 2)
 
-def abspath(path):
-    ''' Make the given path an absolute path.
-
-        Code from Zope-Coders posting of 2002-10-06 by GvR.
-    '''
-    if not os.path.isabs(path):
-        path = os.path.join(os.getcwd(), path)
-    return os.path.normpath(path)
-
 def run():
     ''' Script entry point - handle args and figure out what to to.
     '''
+    # time out after a minute if we can
+    import socket
+    if hasattr(socket, 'setdefaulttimeout'):
+        socket.setdefaulttimeout(60)
+
     hostname = ''
     port = 8080
     pidfile = None
@@ -247,18 +278,44 @@ def run():
     try:
         # handle the command-line args
         try:
-            optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:')
+            optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:hN')
         except getopt.GetoptError, e:
             usage(str(e))
 
         user = ROUNDUP_USER
+        group = None
         for (opt, arg) in optlist:
             if opt == '-n': hostname = arg
             elif opt == '-p': port = int(arg)
             elif opt == '-u': user = arg
-            elif opt == '-d': pidfile = abspath(arg)
-            elif opt == '-l': logfile = abspath(arg)
+            elif opt == '-g': group = arg
+            elif opt == '-d': pidfile = os.path.abspath(arg)
+            elif opt == '-l': logfile = os.path.abspath(arg)
             elif opt == '-h': usage()
+            elif opt == '-N': RoundupRequestHandler.LOG_IPADDRESS = 0
+
+        if pidfile and not logfile:
+            raise ValueError, _("logfile *must* be specified if pidfile is")
+  
+        # obtain server before changing user id - allows to use port <
+        # 1024 if started as root
+        address = (hostname, port)
+        httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler)
+
+        if group is not None and hasattr(os, 'getgid'):
+            # if root, setgid to the running user
+            if not os.getgid() and user is not None:
+                try:
+                    import pwd
+                except ImportError:
+                    raise ValueError, _("Can't change groups - no pwd module")
+                try:
+                    gid = pwd.getpwnam(user)[3]
+                except KeyError:
+                    raise ValueError,_("Group %(group)s doesn't exist")%locals()
+                os.setgid(gid)
+            elif os.getgid() and user is not None:
+                print _('WARNING: ignoring "-g" argument, not root')
 
         if hasattr(os, 'getuid'):
             # if root, setuid to the running user
@@ -297,18 +354,20 @@ def run():
 
     # we don't want the cgi module interpreting the command-line args ;)
     sys.argv = sys.argv[:1]
-    address = (hostname, port)
 
-    # fork?
     if pidfile:
-        daemonize(pidfile)
+        if not hasattr(os, 'fork'):
+            print "Sorry, you can't run the server as a daemon on this" \
+                'Operating System'
+            sys.exit(0)
+        else:
+            daemonize(pidfile)
 
     # redirect stdout/stderr to our logfile
     if logfile:
         # appending, unbuffered
         sys.stdout = sys.stderr = open(logfile, 'a', 0)
 
-    httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler)
     print _('Roundup server started on %(address)s')%locals()
     try:
         httpd.serve_forever()