mirror of https://github.com/BOINC/boinc.git
185 lines
6.8 KiB
Python
Executable File
185 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# $Id$
|
|
# cgi/php web server
|
|
|
|
import BaseHTTPServer, CGIHTTPServer
|
|
import sys, os, urllib, select
|
|
|
|
php_path = None
|
|
possible_php_paths = [ '/usr/lib/cgi-bin/php4',
|
|
'PROGRAM_PATH/fake_php.py' ]
|
|
def setup_php(program_path):
|
|
global php_path
|
|
for p in possible_php_paths:
|
|
p = p.replace('PROGRAM_PATH', program_path)
|
|
if os.path.exists(p):
|
|
php_path = p
|
|
return
|
|
raise Exception("No php binary found - not even fake_php.py (program_path=%s) !"%program_path)
|
|
|
|
class PHPHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
|
|
def is_cgi(self):
|
|
if os.path.split(self.path)[1] == '':
|
|
index_php = os.path.join(self.path, 'index.php')
|
|
if os.path.exists(self.translate_path(index_php)):
|
|
self.path = index_php
|
|
if self.path.find('.php') != -1:
|
|
self.cgi_info = os.path.split(self.path)
|
|
return True
|
|
|
|
for p in self.cgi_directories:
|
|
p = os.path.join(p,'')
|
|
if self.path.startswith(p):
|
|
self.cgi_info = os.path.split(self.path)
|
|
return True
|
|
return False
|
|
|
|
def run_cgi(self):
|
|
"""Execute a CGI script."""
|
|
dir, rest = self.cgi_info
|
|
i = rest.rfind('?')
|
|
if i >= 0:
|
|
rest, query = rest[:i], rest[i+1:]
|
|
else:
|
|
query = ''
|
|
i = rest.find('/')
|
|
if i >= 0:
|
|
script, rest = rest[:i], rest[i:]
|
|
else:
|
|
script, rest = rest, ''
|
|
scriptname = dir + '/' + script
|
|
is_php = script.endswith('.php')
|
|
# print "#### cgi_info=%s,dir=%s,rest=%s,script=%s,scriptname=%s,is_php=%s"%(self.cgi_info,dir,rest,script,scriptname,is_php)
|
|
if is_php:
|
|
if not php_path: raise Exception('php_path not set')
|
|
scriptfile = php_path
|
|
sourcefile = self.translate_path(scriptname)
|
|
else:
|
|
scriptfile = self.translate_path(scriptname)
|
|
if not os.path.exists(scriptfile):
|
|
self.send_error(404, "No such CGI script (%s)" % `scriptname`)
|
|
return
|
|
if not os.path.isfile(scriptfile):
|
|
self.send_error(403, "CGI script is not a plain file (%s)" %
|
|
`scriptname`)
|
|
return
|
|
ispy = self.is_python(scriptname)
|
|
if not ispy:
|
|
if not (self.have_fork or self.have_popen2 or self.have_popen3):
|
|
self.send_error(403, "CGI script is not a Python script (%s)" %
|
|
`scriptname`)
|
|
return
|
|
if not self.is_executable(scriptfile):
|
|
self.send_error(403, "CGI script is not executable (%s)" %
|
|
`scriptname`)
|
|
return
|
|
|
|
# Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
|
|
# XXX Much of the following could be prepared ahead of time!
|
|
env = {}
|
|
env['DOCUMENT_ROOT'] = os.getcwd()
|
|
env['SERVER_SOFTWARE'] = self.version_string()
|
|
env['SERVER_NAME'] = self.server.server_name
|
|
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
|
|
env['SERVER_PROTOCOL'] = self.protocol_version
|
|
env['SERVER_PORT'] = str(self.server.server_port)
|
|
env['REQUEST_METHOD'] = self.command
|
|
uqrest = urllib.unquote(self.cgi_info[1])
|
|
env['REQUEST_URI'] = self.path
|
|
# env['PATH_INFO'] = uqrest
|
|
# env['PATH_TRANSLATED'] = self.translate_path(uqrest)
|
|
env['SCRIPT_NAME'] = scriptname
|
|
env['SCRIPT_FILENAME'] = self.translate_path(scriptname)
|
|
if query:
|
|
env['QUERY_STRING'] = query
|
|
host = self.address_string()
|
|
if host != self.client_address[0]:
|
|
env['REMOTE_HOST'] = host
|
|
env['REMOTE_ADDR'] = self.client_address[0]
|
|
env['REDIRECT_STATUS'] = '1' # for php
|
|
# XXX AUTH_TYPE
|
|
# XXX REMOTE_USER
|
|
# XXX REMOTE_IDENT
|
|
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
|
|
accept = []
|
|
for line in self.headers.getallmatchingheaders('accept'):
|
|
if line[:1] in "\t\n\r ":
|
|
accept.append(line.strip())
|
|
else:
|
|
accept = accept + line[7:].split(',')
|
|
env['HTTP_ACCEPT'] = ','.join(accept)
|
|
ua = self.headers.getheader('user-agent')
|
|
if ua:
|
|
env['HTTP_USER_AGENT'] = ua
|
|
co = filter(None, self.headers.getheaders('cookie'))
|
|
if co:
|
|
env['HTTP_COOKIE'] = ', '.join(co)
|
|
# XXX Other HTTP_* headers
|
|
if not self.have_fork:
|
|
# Since we're setting the env in the parent, provide empty
|
|
# values to override previously set values
|
|
for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
|
|
'HTTP_USER_AGENT', 'HTTP_COOKIE'):
|
|
env.setdefault(k, "")
|
|
os.environ.update(env)
|
|
|
|
self.send_response(200, "Script output follows")
|
|
|
|
decoded_query = query.replace('+', ' ')
|
|
|
|
if self.have_fork:
|
|
# Unix -- fork as we should
|
|
if is_php:
|
|
args = [php_path, sourcefile]
|
|
else:
|
|
args = [script]
|
|
if '=' not in decoded_query:
|
|
args.append(decoded_query)
|
|
nobody = CGIHTTPServer.nobody_uid()
|
|
self.wfile.flush() # Always flush before forking
|
|
pid = os.fork()
|
|
if pid != 0:
|
|
# Parent
|
|
pid, sts = os.waitpid(pid, 0)
|
|
# throw away additional data [see bug #427345]
|
|
while select.select([self.rfile], [], [], 0)[0]:
|
|
try:
|
|
if not self.rfile.read(1):
|
|
break
|
|
except:
|
|
break
|
|
if sts:
|
|
self.log_error("CGI script exit status %#x", sts)
|
|
return
|
|
# Child
|
|
try:
|
|
try:
|
|
os.setuid(nobody)
|
|
except os.error:
|
|
pass
|
|
os.dup2(self.rfile.fileno(), 0)
|
|
os.dup2(self.wfile.fileno(), 1)
|
|
os.chdir(self.translate_path(dir)) # KC
|
|
os.execve(scriptfile, args, os.environ)
|
|
except:
|
|
self.server.handle_error(self.request, self.client_address)
|
|
os._exit(127)
|
|
|
|
else:
|
|
raise SystemExit('need fork()')
|
|
|
|
def serve(bind='localhost', port=8000, handler=PHPHTTPRequestHandler):
|
|
httpd = BaseHTTPServer.HTTPServer((bind,port), handler)
|
|
httpd.serve_forever()
|
|
|
|
if __name__ == '__main__':
|
|
setup_php(os.path.realpath(os.path.dirname(sys.argv[0])))
|
|
serve()
|