#!/usr/bin/python """ HTTP Server that serves roundup. Stolen from CGIHTTPServer $Id: roundup-server,v 1.2 2001-07-23 04:05:05 anthonybaxter Exp $ """ import sys if int(sys.version[0]) < 2: print "Content-Type: text/plain\n" print "Roundup requires Python 2.0 or newer." sys.exit(0) __version__ = "0.1" __all__ = ["RoundupRequestHandler"] import os, urllib, StringIO, traceback, cgi, binascii, string import BaseHTTPServer import SimpleHTTPServer # Roundup modules of use here from roundup import cgitb, cgi_client # These are here temporarily until I get a real reload system in place from roundup import date, hyperdb, hyper_bsddb, roundupdb, htmltemplate # ## Configuration # # This indicates where the Roundup instance lives ROUNDUPS = { 'test': '/tmp/roundup_test', } # 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 # class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def send_head(self): """Version of send_head that support CGI scripts""" # TODO: actually do the HEAD ... return self.run_cgi() def run_cgi(self): """ Execute the CGI command. Wrap an innner call in an error handler so all errors can be caught. """ save_stdin = sys.stdin sys.stdin = self.rfile try: self.inner_run_cgi() except cgi_client.Unauthorised: self.wfile.write('Content-Type: text/html\n') self.wfile.write('Status: 403\n') self.wfile.write('Unauthorised') except: try: reload(cgitb) self.wfile.write("Content-Type: text/html\n\n") self.wfile.write(cgitb.breaker()) self.wfile.write(cgitb.html()) except: self.wfile.write("Content-Type: text/html\n\n") self.wfile.write("
")
                s = StringIO.StringIO()
                traceback.print_exc(None, s)
                self.wfile.write(cgi.escape(s.getvalue()))
                self.wfile.write("
\n") sys.stdin = save_stdin def inner_run_cgi(self): ''' This is the inner part of the CGI handling ''' rest = self.path i = rest.rfind('?') if i >= 0: rest, query = rest[:i], rest[i+1:] else: query = '' # figure the instance if rest == '/': raise ValueError, 'No instance specified' l_path = string.split(rest, '/') instance = urllib.unquote(l_path[1]) if ROUNDUPS.has_key(instance): instance_home = ROUNDUPS[instance] module_path, instance = os.path.split(instance_home) sys.path.insert(0, module_path) try: instance = __import__(instance) finally: del sys.path[0] else: raise ValueError, 'No such instance "%s"'%instance # figure out what the rest of the path is if len(l_path) > 2: rest = '/'.join(l_path[2:]) else: rest = '/' # Set up the CGI environment env = {} env['REQUEST_METHOD'] = self.command env['PATH_INFO'] = urllib.unquote(rest) if query: env['QUERY_STRING'] = query host = self.address_string() if self.headers.typeheader is None: env['CONTENT_TYPE'] = self.headers.type else: env['CONTENT_TYPE'] = self.headers.typeheader length = self.headers.getheader('content-length') if length: env['CONTENT_LENGTH'] = length co = filter(None, self.headers.getheaders('cookie')) if co: env['HTTP_COOKIE'] = ', '.join(co) env['SCRIPT_NAME'] = '' env['SERVER_NAME'] = self.server.server_name env['SERVER_PORT'] = str(self.server.server_port) decoded_query = query.replace('+', ' ') # if root, setuid to nobody # TODO why isn't this done much earlier? - say, in main()? if not os.getuid(): nobody = nobody_uid() os.setuid(nobody) # reload all modules # TODO check for file timestamp changes and dependencies reload(date) reload(hyperdb) reload(roundupdb) reload(htmltemplate) reload(cgi_client) sys.path.insert(0, module_path) try: reload(instance) finally: del sys.path[0] # initialise the roundupdb, check for auth db = instance.open('admin') message = 'Unauthorised' auth = self.headers.getheader('authorization') if auth: l = binascii.a2b_base64(auth.split(' ')[1]).split(':') user = l[0] password = None if len(l) > 1: password = l[1] try: uid = db.user.lookup(user) except KeyError: auth = None message = 'Username not recognised' else: if password != db.user.get(uid, 'password'): message = 'Incorrect password' auth = None db.close() del db if not auth: self.send_response(401) self.send_header('Content-Type', 'text/html') self.send_header('WWW-Authenticate', 'basic realm="Roundup"') self.end_headers() self.wfile.write(message) return self.send_response(200, "Script output follows") # do the roundup thang db = instance.open(user) client = instance.Client(self.wfile, db, env, user) client.main() do_POST = run_cgi nobody = None def nobody_uid(): """Internal routine to get nobody's uid""" global nobody if nobody: return nobody try: import pwd except ImportError: return -1 try: nobody = pwd.getpwnam('nobody')[2] except KeyError: nobody = 1 + max(map(lambda x: x[2], pwd.getpwall())) return nobody if __name__ == '__main__': # TODO make this configurable again? command-line seems ok to me... address = ('', 8080) httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler) print 'Roundup server started on', address httpd.serve_forever() # # $Log: not supported by cvs2svn $ # Revision 1.1 2001/07/23 03:46:48 richard # moving the bin files to facilitate out-of-the-boxness # # Revision 1.1 2001/07/22 11:15:45 richard # More Grande Splite stuff # #