Mercurial > p > roundup > code
comparison bin/roundup-server @ 23:0eea7628adb2
More Grande Splite stuff
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Sun, 22 Jul 2001 11:15:45 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 22:d64c32a499f9 | 23:0eea7628adb2 |
|---|---|
| 1 #!/usr/bin/python | |
| 2 """ HTTP Server that serves roundup. | |
| 3 | |
| 4 Stolen from CGIHTTPServer | |
| 5 | |
| 6 $Id: roundup-server,v 1.1 2001-07-22 11:15:45 richard Exp $ | |
| 7 | |
| 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." | |
| 13 | |
| 14 __version__ = "0.1" | |
| 15 | |
| 16 __all__ = ["RoundupRequestHandler"] | |
| 17 | |
| 18 import os, urllib, StringIO, traceback, cgi, binascii, string | |
| 19 import BaseHTTPServer | |
| 20 import SimpleHTTPServer | |
| 21 | |
| 22 # Roundup modules of use here | |
| 23 from roundup import cgitb, cgi_client | |
| 24 | |
| 25 # These are here temporarily until I get a real reload system in place | |
| 26 from roundup import date, hyperdb, hyper_bsddb, roundupdb, htmltemplate | |
| 27 | |
| 28 # | |
| 29 ## Configuration | |
| 30 # | |
| 31 | |
| 32 # This indicates where the Roundup instance lives | |
| 33 ROUNDUPS = { | |
| 34 'test': '/tmp/roundup_test', | |
| 35 } | |
| 36 | |
| 37 # Where to log debugging information to. Use an instance of DevNull if you | |
| 38 # don't want to log anywhere. | |
| 39 # TODO: actually use this stuff | |
| 40 #class DevNull: | |
| 41 # def write(self, info): | |
| 42 # pass | |
| 43 #LOG = open('/var/log/roundup.cgi.log', 'a') | |
| 44 #LOG = DevNull() | |
| 45 | |
| 46 # | |
| 47 ## end configuration | |
| 48 # | |
| 49 | |
| 50 | |
| 51 class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): | |
| 52 def send_head(self): | |
| 53 """Version of send_head that support CGI scripts""" | |
| 54 # TODO: actually do the HEAD ... | |
| 55 return self.run_cgi() | |
| 56 | |
| 57 def run_cgi(self): | |
| 58 """ Execute the CGI command. Wrap an innner call in an error | |
| 59 handler so all errors can be caught. | |
| 60 """ | |
| 61 save_stdin = sys.stdin | |
| 62 sys.stdin = self.rfile | |
| 63 try: | |
| 64 self.inner_run_cgi() | |
| 65 except cgi_client.Unauthorised: | |
| 66 self.wfile.write('Content-Type: text/html\n') | |
| 67 self.wfile.write('Status: 403\n') | |
| 68 self.wfile.write('Unauthorised') | |
| 69 except: | |
| 70 try: | |
| 71 reload(cgitb) | |
| 72 self.wfile.write("Content-Type: text/html\n\n") | |
| 73 self.wfile.write(cgitb.breaker()) | |
| 74 self.wfile.write(cgitb.html()) | |
| 75 except: | |
| 76 self.wfile.write("Content-Type: text/html\n\n") | |
| 77 self.wfile.write("<pre>") | |
| 78 s = StringIO.StringIO() | |
| 79 traceback.print_exc(None, s) | |
| 80 self.wfile.write(cgi.escape(s.getvalue())) | |
| 81 self.wfile.write("</pre>\n") | |
| 82 sys.stdin = save_stdin | |
| 83 | |
| 84 def inner_run_cgi(self): | |
| 85 ''' This is the inner part of the CGI handling | |
| 86 ''' | |
| 87 | |
| 88 rest = self.path | |
| 89 i = rest.rfind('?') | |
| 90 if i >= 0: | |
| 91 rest, query = rest[:i], rest[i+1:] | |
| 92 else: | |
| 93 query = '' | |
| 94 | |
| 95 # figure the instance | |
| 96 if rest == '/': | |
| 97 raise ValueError, 'No instance specified' | |
| 98 l_path = string.split(rest, '/') | |
| 99 instance = urllib.unquote(l_path[1]) | |
| 100 if ROUNDUPS.has_key(instance): | |
| 101 instance_home = ROUNDUPS[instance] | |
| 102 module_path, instance = os.path.split(instance_home) | |
| 103 sys.path.insert(0, module_path) | |
| 104 try: | |
| 105 instance = __import__(instance) | |
| 106 finally: | |
| 107 del sys.path[0] | |
| 108 else: | |
| 109 raise ValueError, 'No such instance "%s"'%instance | |
| 110 | |
| 111 # figure out what the rest of the path is | |
| 112 if len(l_path) > 2: | |
| 113 rest = '/'.join(l_path[2:]) | |
| 114 else: | |
| 115 rest = '/' | |
| 116 | |
| 117 # Set up the CGI environment | |
| 118 env = {} | |
| 119 env['REQUEST_METHOD'] = self.command | |
| 120 env['PATH_INFO'] = urllib.unquote(rest) | |
| 121 if query: | |
| 122 env['QUERY_STRING'] = query | |
| 123 host = self.address_string() | |
| 124 if self.headers.typeheader is None: | |
| 125 env['CONTENT_TYPE'] = self.headers.type | |
| 126 else: | |
| 127 env['CONTENT_TYPE'] = self.headers.typeheader | |
| 128 length = self.headers.getheader('content-length') | |
| 129 if length: | |
| 130 env['CONTENT_LENGTH'] = length | |
| 131 co = filter(None, self.headers.getheaders('cookie')) | |
| 132 if co: | |
| 133 env['HTTP_COOKIE'] = ', '.join(co) | |
| 134 env['SCRIPT_NAME'] = '' | |
| 135 env['SERVER_NAME'] = self.server.server_name | |
| 136 env['SERVER_PORT'] = str(self.server.server_port) | |
| 137 | |
| 138 decoded_query = query.replace('+', ' ') | |
| 139 | |
| 140 # if root, setuid to nobody | |
| 141 # TODO why isn't this done much earlier? - say, in main()? | |
| 142 if not os.getuid(): | |
| 143 nobody = nobody_uid() | |
| 144 os.setuid(nobody) | |
| 145 | |
| 146 # reload all modules | |
| 147 # TODO check for file timestamp changes and dependencies | |
| 148 reload(date) | |
| 149 reload(hyperdb) | |
| 150 reload(roundupdb) | |
| 151 reload(htmltemplate) | |
| 152 reload(cgi_client) | |
| 153 sys.path.insert(0, module_path) | |
| 154 try: | |
| 155 reload(instance) | |
| 156 finally: | |
| 157 del sys.path[0] | |
| 158 | |
| 159 # initialise the roundupdb, check for auth | |
| 160 db = instance.open('admin') | |
| 161 message = 'Unauthorised' | |
| 162 auth = self.headers.getheader('authorization') | |
| 163 if auth: | |
| 164 l = binascii.a2b_base64(auth.split(' ')[1]).split(':') | |
| 165 user = l[0] | |
| 166 password = None | |
| 167 if len(l) > 1: | |
| 168 password = l[1] | |
| 169 try: | |
| 170 uid = db.user.lookup(user) | |
| 171 except KeyError: | |
| 172 auth = None | |
| 173 message = 'Username not recognised' | |
| 174 else: | |
| 175 if password != db.user.get(uid, 'password'): | |
| 176 message = 'Incorrect password' | |
| 177 auth = None | |
| 178 db.close() | |
| 179 del db | |
| 180 if not auth: | |
| 181 self.send_response(401) | |
| 182 self.send_header('Content-Type', 'text/html') | |
| 183 self.send_header('WWW-Authenticate', 'basic realm="Roundup"') | |
| 184 self.end_headers() | |
| 185 self.wfile.write(message) | |
| 186 return | |
| 187 | |
| 188 self.send_response(200, "Script output follows") | |
| 189 | |
| 190 # do the roundup thang | |
| 191 db = instance.open(user) | |
| 192 client = instance.Client(self.wfile, db, env, user) | |
| 193 client.main() | |
| 194 do_POST = run_cgi | |
| 195 | |
| 196 nobody = None | |
| 197 | |
| 198 def nobody_uid(): | |
| 199 """Internal routine to get nobody's uid""" | |
| 200 global nobody | |
| 201 if nobody: | |
| 202 return nobody | |
| 203 try: | |
| 204 import pwd | |
| 205 except ImportError: | |
| 206 return -1 | |
| 207 try: | |
| 208 nobody = pwd.getpwnam('nobody')[2] | |
| 209 except KeyError: | |
| 210 nobody = 1 + max(map(lambda x: x[2], pwd.getpwall())) | |
| 211 return nobody | |
| 212 | |
| 213 if __name__ == '__main__': | |
| 214 # TODO make this configurable again? command-line seems ok to me... | |
| 215 address = ('', 8080) | |
| 216 httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler) | |
| 217 print 'Roundup server started on', address | |
| 218 httpd.serve_forever() | |
| 219 | |
| 220 # | |
| 221 # $Log: not supported by cvs2svn $ | |
| 222 # | |
| 223 |
