symbian-qemu-0.9.1-12/python-2.6.1/Lib/CGIHTTPServer.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """CGI-savvy HTTP Server.
       
     2 
       
     3 This module builds on SimpleHTTPServer by implementing GET and POST
       
     4 requests to cgi-bin scripts.
       
     5 
       
     6 If the os.fork() function is not present (e.g. on Windows),
       
     7 os.popen2() is used as a fallback, with slightly altered semantics; if
       
     8 that function is not present either (e.g. on Macintosh), only Python
       
     9 scripts are supported, and they are executed by the current process.
       
    10 
       
    11 In all cases, the implementation is intentionally naive -- all
       
    12 requests are executed sychronously.
       
    13 
       
    14 SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
       
    15 -- it may execute arbitrary Python code or external programs.
       
    16 
       
    17 Note that status code 200 is sent prior to execution of a CGI script, so
       
    18 scripts cannot send other status codes such as 302 (redirect).
       
    19 """
       
    20 
       
    21 
       
    22 __version__ = "0.4"
       
    23 
       
    24 __all__ = ["CGIHTTPRequestHandler"]
       
    25 
       
    26 import os
       
    27 import sys
       
    28 import urllib
       
    29 import BaseHTTPServer
       
    30 import SimpleHTTPServer
       
    31 import select
       
    32 
       
    33 
       
    34 class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
       
    35 
       
    36     """Complete HTTP server with GET, HEAD and POST commands.
       
    37 
       
    38     GET and HEAD also support running CGI scripts.
       
    39 
       
    40     The POST command is *only* implemented for CGI scripts.
       
    41 
       
    42     """
       
    43 
       
    44     # Determine platform specifics
       
    45     have_fork = hasattr(os, 'fork')
       
    46     have_popen2 = hasattr(os, 'popen2')
       
    47     have_popen3 = hasattr(os, 'popen3')
       
    48 
       
    49     # Make rfile unbuffered -- we need to read one line and then pass
       
    50     # the rest to a subprocess, so we can't use buffered input.
       
    51     rbufsize = 0
       
    52 
       
    53     def do_POST(self):
       
    54         """Serve a POST request.
       
    55 
       
    56         This is only implemented for CGI scripts.
       
    57 
       
    58         """
       
    59 
       
    60         if self.is_cgi():
       
    61             self.run_cgi()
       
    62         else:
       
    63             self.send_error(501, "Can only POST to CGI scripts")
       
    64 
       
    65     def send_head(self):
       
    66         """Version of send_head that support CGI scripts"""
       
    67         if self.is_cgi():
       
    68             return self.run_cgi()
       
    69         else:
       
    70             return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
       
    71 
       
    72     def is_cgi(self):
       
    73         """Test whether self.path corresponds to a CGI script,
       
    74         and return a boolean.
       
    75 
       
    76         This function sets self.cgi_info to a tuple (dir, rest)
       
    77         when it returns True, where dir is the directory part before
       
    78         the CGI script name.  Note that rest begins with a
       
    79         slash if it is not empty.
       
    80 
       
    81         The default implementation tests whether the path
       
    82         begins with one of the strings in the list
       
    83         self.cgi_directories (and the next character is a '/'
       
    84         or the end of the string).
       
    85         """
       
    86 
       
    87         path = self.path
       
    88 
       
    89         for x in self.cgi_directories:
       
    90             i = len(x)
       
    91             if path[:i] == x and (not path[i:] or path[i] == '/'):
       
    92                 self.cgi_info = path[:i], path[i+1:]
       
    93                 return True
       
    94         return False
       
    95 
       
    96     cgi_directories = ['/cgi-bin', '/htbin']
       
    97 
       
    98     def is_executable(self, path):
       
    99         """Test whether argument path is an executable file."""
       
   100         return executable(path)
       
   101 
       
   102     def is_python(self, path):
       
   103         """Test whether argument path is a Python script."""
       
   104         head, tail = os.path.splitext(path)
       
   105         return tail.lower() in (".py", ".pyw")
       
   106 
       
   107     def run_cgi(self):
       
   108         """Execute a CGI script."""
       
   109         path = self.path
       
   110         dir, rest = self.cgi_info
       
   111 
       
   112         i = path.find('/', len(dir) + 1)
       
   113         while i >= 0:
       
   114             nextdir = path[:i]
       
   115             nextrest = path[i+1:]
       
   116 
       
   117             scriptdir = self.translate_path(nextdir)
       
   118             if os.path.isdir(scriptdir):
       
   119                 dir, rest = nextdir, nextrest
       
   120                 i = path.find('/', len(dir) + 1)
       
   121             else:
       
   122                 break
       
   123 
       
   124         # find an explicit query string, if present.
       
   125         i = rest.rfind('?')
       
   126         if i >= 0:
       
   127             rest, query = rest[:i], rest[i+1:]
       
   128         else:
       
   129             query = ''
       
   130 
       
   131         # dissect the part after the directory name into a script name &
       
   132         # a possible additional path, to be stored in PATH_INFO.
       
   133         i = rest.find('/')
       
   134         if i >= 0:
       
   135             script, rest = rest[:i], rest[i:]
       
   136         else:
       
   137             script, rest = rest, ''
       
   138 
       
   139         scriptname = dir + '/' + script
       
   140         scriptfile = self.translate_path(scriptname)
       
   141         if not os.path.exists(scriptfile):
       
   142             self.send_error(404, "No such CGI script (%r)" % scriptname)
       
   143             return
       
   144         if not os.path.isfile(scriptfile):
       
   145             self.send_error(403, "CGI script is not a plain file (%r)" %
       
   146                             scriptname)
       
   147             return
       
   148         ispy = self.is_python(scriptname)
       
   149         if not ispy:
       
   150             if not (self.have_fork or self.have_popen2 or self.have_popen3):
       
   151                 self.send_error(403, "CGI script is not a Python script (%r)" %
       
   152                                 scriptname)
       
   153                 return
       
   154             if not self.is_executable(scriptfile):
       
   155                 self.send_error(403, "CGI script is not executable (%r)" %
       
   156                                 scriptname)
       
   157                 return
       
   158 
       
   159         # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
       
   160         # XXX Much of the following could be prepared ahead of time!
       
   161         env = {}
       
   162         env['SERVER_SOFTWARE'] = self.version_string()
       
   163         env['SERVER_NAME'] = self.server.server_name
       
   164         env['GATEWAY_INTERFACE'] = 'CGI/1.1'
       
   165         env['SERVER_PROTOCOL'] = self.protocol_version
       
   166         env['SERVER_PORT'] = str(self.server.server_port)
       
   167         env['REQUEST_METHOD'] = self.command
       
   168         uqrest = urllib.unquote(rest)
       
   169         env['PATH_INFO'] = uqrest
       
   170         env['PATH_TRANSLATED'] = self.translate_path(uqrest)
       
   171         env['SCRIPT_NAME'] = scriptname
       
   172         if query:
       
   173             env['QUERY_STRING'] = query
       
   174         host = self.address_string()
       
   175         if host != self.client_address[0]:
       
   176             env['REMOTE_HOST'] = host
       
   177         env['REMOTE_ADDR'] = self.client_address[0]
       
   178         authorization = self.headers.getheader("authorization")
       
   179         if authorization:
       
   180             authorization = authorization.split()
       
   181             if len(authorization) == 2:
       
   182                 import base64, binascii
       
   183                 env['AUTH_TYPE'] = authorization[0]
       
   184                 if authorization[0].lower() == "basic":
       
   185                     try:
       
   186                         authorization = base64.decodestring(authorization[1])
       
   187                     except binascii.Error:
       
   188                         pass
       
   189                     else:
       
   190                         authorization = authorization.split(':')
       
   191                         if len(authorization) == 2:
       
   192                             env['REMOTE_USER'] = authorization[0]
       
   193         # XXX REMOTE_IDENT
       
   194         if self.headers.typeheader is None:
       
   195             env['CONTENT_TYPE'] = self.headers.type
       
   196         else:
       
   197             env['CONTENT_TYPE'] = self.headers.typeheader
       
   198         length = self.headers.getheader('content-length')
       
   199         if length:
       
   200             env['CONTENT_LENGTH'] = length
       
   201         referer = self.headers.getheader('referer')
       
   202         if referer:
       
   203             env['HTTP_REFERER'] = referer
       
   204         accept = []
       
   205         for line in self.headers.getallmatchingheaders('accept'):
       
   206             if line[:1] in "\t\n\r ":
       
   207                 accept.append(line.strip())
       
   208             else:
       
   209                 accept = accept + line[7:].split(',')
       
   210         env['HTTP_ACCEPT'] = ','.join(accept)
       
   211         ua = self.headers.getheader('user-agent')
       
   212         if ua:
       
   213             env['HTTP_USER_AGENT'] = ua
       
   214         co = filter(None, self.headers.getheaders('cookie'))
       
   215         if co:
       
   216             env['HTTP_COOKIE'] = ', '.join(co)
       
   217         # XXX Other HTTP_* headers
       
   218         # Since we're setting the env in the parent, provide empty
       
   219         # values to override previously set values
       
   220         for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
       
   221                   'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
       
   222             env.setdefault(k, "")
       
   223         os.environ.update(env)
       
   224 
       
   225         self.send_response(200, "Script output follows")
       
   226 
       
   227         decoded_query = query.replace('+', ' ')
       
   228 
       
   229         if self.have_fork:
       
   230             # Unix -- fork as we should
       
   231             args = [script]
       
   232             if '=' not in decoded_query:
       
   233                 args.append(decoded_query)
       
   234             nobody = nobody_uid()
       
   235             self.wfile.flush() # Always flush before forking
       
   236             pid = os.fork()
       
   237             if pid != 0:
       
   238                 # Parent
       
   239                 pid, sts = os.waitpid(pid, 0)
       
   240                 # throw away additional data [see bug #427345]
       
   241                 while select.select([self.rfile], [], [], 0)[0]:
       
   242                     if not self.rfile.read(1):
       
   243                         break
       
   244                 if sts:
       
   245                     self.log_error("CGI script exit status %#x", sts)
       
   246                 return
       
   247             # Child
       
   248             try:
       
   249                 try:
       
   250                     os.setuid(nobody)
       
   251                 except os.error:
       
   252                     pass
       
   253                 os.dup2(self.rfile.fileno(), 0)
       
   254                 os.dup2(self.wfile.fileno(), 1)
       
   255                 os.execve(scriptfile, args, os.environ)
       
   256             except:
       
   257                 self.server.handle_error(self.request, self.client_address)
       
   258                 os._exit(127)
       
   259 
       
   260         elif self.have_popen2 or self.have_popen3:
       
   261             # Windows -- use popen2 or popen3 to create a subprocess
       
   262             import shutil
       
   263             if self.have_popen3:
       
   264                 popenx = os.popen3
       
   265             else:
       
   266                 popenx = os.popen2
       
   267             cmdline = scriptfile
       
   268             if self.is_python(scriptfile):
       
   269                 interp = sys.executable
       
   270                 if interp.lower().endswith("w.exe"):
       
   271                     # On Windows, use python.exe, not pythonw.exe
       
   272                     interp = interp[:-5] + interp[-4:]
       
   273                 cmdline = "%s -u %s" % (interp, cmdline)
       
   274             if '=' not in query and '"' not in query:
       
   275                 cmdline = '%s "%s"' % (cmdline, query)
       
   276             self.log_message("command: %s", cmdline)
       
   277             try:
       
   278                 nbytes = int(length)
       
   279             except (TypeError, ValueError):
       
   280                 nbytes = 0
       
   281             files = popenx(cmdline, 'b')
       
   282             fi = files[0]
       
   283             fo = files[1]
       
   284             if self.have_popen3:
       
   285                 fe = files[2]
       
   286             if self.command.lower() == "post" and nbytes > 0:
       
   287                 data = self.rfile.read(nbytes)
       
   288                 fi.write(data)
       
   289             # throw away additional data [see bug #427345]
       
   290             while select.select([self.rfile._sock], [], [], 0)[0]:
       
   291                 if not self.rfile._sock.recv(1):
       
   292                     break
       
   293             fi.close()
       
   294             shutil.copyfileobj(fo, self.wfile)
       
   295             if self.have_popen3:
       
   296                 errors = fe.read()
       
   297                 fe.close()
       
   298                 if errors:
       
   299                     self.log_error('%s', errors)
       
   300             sts = fo.close()
       
   301             if sts:
       
   302                 self.log_error("CGI script exit status %#x", sts)
       
   303             else:
       
   304                 self.log_message("CGI script exited OK")
       
   305 
       
   306         else:
       
   307             # Other O.S. -- execute script in this process
       
   308             save_argv = sys.argv
       
   309             save_stdin = sys.stdin
       
   310             save_stdout = sys.stdout
       
   311             save_stderr = sys.stderr
       
   312             try:
       
   313                 save_cwd = os.getcwd()
       
   314                 try:
       
   315                     sys.argv = [scriptfile]
       
   316                     if '=' not in decoded_query:
       
   317                         sys.argv.append(decoded_query)
       
   318                     sys.stdout = self.wfile
       
   319                     sys.stdin = self.rfile
       
   320                     execfile(scriptfile, {"__name__": "__main__"})
       
   321                 finally:
       
   322                     sys.argv = save_argv
       
   323                     sys.stdin = save_stdin
       
   324                     sys.stdout = save_stdout
       
   325                     sys.stderr = save_stderr
       
   326                     os.chdir(save_cwd)
       
   327             except SystemExit, sts:
       
   328                 self.log_error("CGI script exit status %s", str(sts))
       
   329             else:
       
   330                 self.log_message("CGI script exited OK")
       
   331 
       
   332 
       
   333 nobody = None
       
   334 
       
   335 def nobody_uid():
       
   336     """Internal routine to get nobody's uid"""
       
   337     global nobody
       
   338     if nobody:
       
   339         return nobody
       
   340     try:
       
   341         import pwd
       
   342     except ImportError:
       
   343         return -1
       
   344     try:
       
   345         nobody = pwd.getpwnam('nobody')[2]
       
   346     except KeyError:
       
   347         nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
       
   348     return nobody
       
   349 
       
   350 
       
   351 def executable(path):
       
   352     """Test for executable file."""
       
   353     try:
       
   354         st = os.stat(path)
       
   355     except os.error:
       
   356         return False
       
   357     return st.st_mode & 0111 != 0
       
   358 
       
   359 
       
   360 def test(HandlerClass = CGIHTTPRequestHandler,
       
   361          ServerClass = BaseHTTPServer.HTTPServer):
       
   362     SimpleHTTPServer.test(HandlerClass, ServerClass)
       
   363 
       
   364 
       
   365 if __name__ == '__main__':
       
   366     test()