Code

HTTP_HOST and HTTP_PORT config options.
[roundup.git] / server.py
1 #!/usr/bin/python
2 """ HTTP Server that serves roundup.
4 Stolen from CGIHTTPServer
6 $Id: server.py,v 1.4 2001-07-19 10:43:01 anthonybaxter Exp $
8 """
9 import sys
10 if int(sys.version[0]) < 2:
11     print "Content-Type: text/plain\n"
12     print "Roundup requires Python 2.0 or newer."
14 __version__ = "0.1"
16 __all__ = ["CGIHTTPRequestHandler"]
18 import os, urllib, StringIO, traceback, cgi, binascii
19 import BaseHTTPServer
20 import SimpleHTTPServer
21 import date, hyperdb, template, roundupdb, roundup_cgi
22 import cgitb
24 class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
25     def send_head(self):
26         """Version of send_head that support CGI scripts"""
27         return self.run_cgi()
29     def run_cgi(self):
30         """Execute a CGI script."""
31         rest = self.path
32         i = rest.rfind('?')
33         if i >= 0:
34             rest, query = rest[:i], rest[i+1:]
35         else:
36             query = ''
38         # Set up the CGI environment
39         env = {}
40         env['REQUEST_METHOD'] = self.command
41         env['PATH_INFO'] = urllib.unquote(rest)
42         if query:
43             env['QUERY_STRING'] = query
44         host = self.address_string()
45         if self.headers.typeheader is None:
46             env['CONTENT_TYPE'] = self.headers.type
47         else:
48             env['CONTENT_TYPE'] = self.headers.typeheader
49         length = self.headers.getheader('content-length')
50         if length:
51             env['CONTENT_LENGTH'] = length
52         co = filter(None, self.headers.getheaders('cookie'))
53         if co:
54             env['HTTP_COOKIE'] = ', '.join(co)
55         env['SCRIPT_NAME'] = ''
56         env['SERVER_NAME'] = self.server.server_name
57         env['SERVER_PORT'] = str(self.server.server_port)
59         decoded_query = query.replace('+', ' ')
61         # if root, setuid to nobody
62         if not os.getuid():
63             nobody = nobody_uid()
64             os.setuid(nobody)
66         # TODO check for file timestamp changes
67         reload(date)
68         reload(hyperdb)
69         reload(roundupdb)
70         reload(template)
71         reload(roundup_cgi)
73         # initialise the roundupdb, check for auth
74         db = roundupdb.openDB('db', 'admin')
75         message = 'Unauthorised'
76         auth = self.headers.getheader('authorization')
77         if auth:
78             l = binascii.a2b_base64(auth.split(' ')[1]).split(':')
79             user = l[0]
80             password = None
81             if len(l) > 1:
82                 password = l[1]
83             try:
84                 uid = db.user.lookup(user)
85             except KeyError:
86                 auth = None
87                 message = 'Username not recognised'
88             else:
89                 if password != db.user.get(uid, 'password'):
90                     message = 'Incorrect password'
91                     auth = None
92         db.close()
93         del db
94         if not auth:
95             self.send_response(401)
96             self.send_header('Content-Type', 'text/html')
97             self.send_header('WWW-Authenticate', 'basic realm="Roundup"')
98             self.end_headers()
99             self.wfile.write(message)
100             return
102         self.send_response(200, "Script output follows")
104         # do the roundup thang
105         save_stdin = sys.stdin
106         try:
107             sys.stdin = self.rfile
108             client = roundup_cgi.Client(self.wfile, env, user)
109             client.main()
110         except roundup_cgi.Unauthorised:
111             self.wfile.write('Content-Type: text/html\n')
112             self.wfile.write('Status: 403\n')
113             self.wfile.write('Unauthorised')
114         except:
115             try:
116                 reload(cgitb)
117                 self.wfile.write(cgitb.breaker())
118                 self.wfile.write(cgitb.html())
119             except:
120                 self.wfile.write("Content-Type: text/html\n\n")
121                 self.wfile.write("<pre>")
122                 s = StringIO.StringIO()
123                 traceback.print_exc(None, s)
124                 self.wfile.write(cgi.escape(s.getvalue()))
125                 self.wfile.write("</pre>\n")
126         sys.stdin = save_stdin
127     do_POST = run_cgi
130 nobody = None
132 def nobody_uid():
133     """Internal routine to get nobody's uid"""
134     global nobody
135     if nobody:
136         return nobody
137     try:
138         import pwd
139     except ImportError:
140         return -1
141     try:
142         nobody = pwd.getpwnam('nobody')[2]
143     except KeyError:
144         nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
145     return nobody
147 def main():
148     from config import HTTP_HOST, HTTP_PORT
149     address = (HTTP_HOST, HTTP_PORT)
150     httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler)
151     print 'Roundup server started on', address
152     httpd.serve_forever()
154 if __name__ == '__main__':
155     main()
158 # $Log: not supported by cvs2svn $
159 # Revision 1.3  2001/07/19 06:27:07  anthonybaxter
160 # fixing (manually) the (dollarsign)Log(dollarsign) entries caused by
161 # my using the magic (dollarsign)Id(dollarsign) and (dollarsign)Log(dollarsign)
162 # strings in a commit message. I'm a twonk.
164 # Also broke the help string in two.
166 # Revision 1.2  2001/07/19 05:52:22  anthonybaxter
167 # Added CVS keywords Id and Log to all python files.