[Python-checkins] CVS: python/dist/src/Lib CGIHTTPServer.py,1.13,1.14

Guido van Rossum python-dev@python.org
Mon, 18 Sep 2000 21:01:05 -0700


Update of /cvsroot/python/python/dist/src/Lib
In directory slayer.i.sourceforge.net:/tmp/cvs-serv4667

Modified Files:
	CGIHTTPServer.py 
Log Message:
An honest attempt to make this work on Unix, Windows, and even
Macintosh (the latter untested).

This closes Bug #110839.


Index: CGIHTTPServer.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/CGIHTTPServer.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -r1.13 -r1.14
*** CGIHTTPServer.py	2000/09/01 03:27:34	1.13
--- CGIHTTPServer.py	2000/09/19 04:01:01	1.14
***************
*** 4,17 ****
  requests to cgi-bin scripts.
  
! If the os.fork() function is not present, this module will not work;
! SystemError will be raised instead.
  
  """
  
  
! __version__ = "0.3"
  
  
  import os
  import string
  import urllib
--- 4,26 ----
  requests to cgi-bin scripts.
  
! If the os.fork() function is not present (e.g. on Windows),
! os.popen2() is used as a fallback, with slightly altered semantics; if
! that function is not present either (e.g. on Macintosh), only Python
! scripts are supported, and they are executed by the current process.
  
+ In all cases, the implementation is intentionally naive -- all
+ requests are executed sychronously.
+ 
+ SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
+ -- it may execute arbitrary Python code or external programs.
+ 
  """
  
  
! __version__ = "0.4"
  
  
  import os
+ import sys
  import string
  import urllib
***************
*** 20,29 ****
  
  
- try:
-     os.fork
- except AttributeError:
-     raise SystemError, __name__ + " requires os.fork()"
- 
- 
  class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  
--- 29,32 ----
***************
*** 36,39 ****
--- 39,46 ----
      """
  
+     # Determine platform specifics
+     have_fork = hasattr(os, 'fork')
+     have_popen2 = hasattr(os, 'popen2')
+ 
      # Make rfile unbuffered -- we need to read one line and then pass
      # the rest to a subprocess, so we can't use buffered input.
***************
*** 60,66 ****
  
      def is_cgi(self):
!         """test whether PATH corresponds to a CGI script.
  
!         Return a tuple (dir, rest) if PATH requires running a
          CGI script, None if not.  Note that rest begins with a
          slash if it is not empty.
--- 67,73 ----
  
      def is_cgi(self):
!         """Test whether self.path corresponds to a CGI script.
  
!         Return a tuple (dir, rest) if self.path requires running a
          CGI script, None if not.  Note that rest begins with a
          slash if it is not empty.
***************
*** 84,87 ****
--- 91,103 ----
      cgi_directories = ['/cgi-bin', '/htbin']
  
+     def is_executable(self, path):
+         """Test whether argument path is an executable file."""
+         return executable(path)
+ 
+     def is_python(self, path):
+         """Test whether argument path is a Python script."""
+         head, tail = os.path.splitext(path)
+         return tail.lower() in (".py", ".pyw")
+ 
      def run_cgi(self):
          """Execute a CGI script."""
***************
*** 105,182 ****
              self.send_error(403, "CGI script is not a plain file (%s)" %
                              `scriptname`)
-             return
-         if not executable(scriptfile):
-             self.send_error(403, "CGI script is not executable (%s)" %
-                             `scriptname`)
              return
!         nobody = nobody_uid()
          self.send_response(200, "Script output follows")
!         self.wfile.flush() # Always flush before forking
!         pid = os.fork()
!         if pid != 0:
!             # Parent
!             pid, sts = os.waitpid(pid, 0)
              if sts:
!                 self.log_error("CGI script exit status x%x" % sts)
!             return
!         # Child
!         try:
!             # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
!             # XXX Much of the following could be prepared ahead of time!
!             env = {}
!             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(rest)
!             env['PATH_INFO'] = uqrest
!             env['PATH_TRANSLATED'] = self.translate_path(uqrest)
!             env['SCRIPT_NAME'] = 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]
!             # AUTH_TYPE
!             # REMOTE_USER
!             # 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 string.whitespace:
!                     accept.append(string.strip(line))
!                 else:
!                     accept = accept + string.split(line[7:], ',')
!             env['HTTP_ACCEPT'] = string.joinfields(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'] = string.join(co, ', ')
!             # XXX Other HTTP_* headers
!             decoded_query = string.replace(query, '+', ' ')
              try:
!                 os.setuid(nobody)
!             except os.error:
!                 pass
!             os.dup2(self.rfile.fileno(), 0)
!             os.dup2(self.wfile.fileno(), 1)
!             print scriptfile, script, decoded_query
!             os.execve(scriptfile,
!                       [script, decoded_query],
!                       env)
!         except:
!             self.server.handle_error(self.request, self.client_address)
!             os._exit(127)
  
  
--- 121,271 ----
              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):
!                 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['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(rest)
!         env['PATH_INFO'] = uqrest
!         env['PATH_TRANSLATED'] = self.translate_path(uqrest)
!         env['SCRIPT_NAME'] = 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]
!         # 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 string.whitespace:
!                 accept.append(string.strip(line))
!             else:
!                 accept = accept + string.split(line[7:], ',')
!         env['HTTP_ACCEPT'] = string.joinfields(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'] = string.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, "")
! 
          self.send_response(200, "Script output follows")
! 
!         decoded_query = string.replace(query, '+', ' ')
! 
!         if self.have_fork:
!             # Unix -- fork as we should
!             args = [script]
!             if '=' not in decoded_query:
!                 args.append(decoded_query)
!             nobody = nobody_uid()
!             self.wfile.flush() # Always flush before forking
!             pid = os.fork()
!             if pid != 0:
!                 # Parent
!                 pid, sts = os.waitpid(pid, 0)
!                 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.execve(scriptfile, args, env)
!             except:
!                 self.server.handle_error(self.request, self.client_address)
!                 os._exit(127)
! 
!         elif self.have_popen2:
!             # Windows -- use popen2 to create a subprocess
!             import shutil
!             os.environ.update(env)
!             cmdline = scriptfile
!             if self.is_python(scriptfile):
!                 interp = sys.executable
!                 if interp.lower().endswith("w.exe"):
!                     # On Windows, use python.exe, not python.exe
!                     interp = interp[:-5] = interp[-4:]
!                 cmdline = "%s %s" % (interp, cmdline)
!             if '=' not in query and '"' not in query:
!                 cmdline = '%s "%s"' % (cmdline, query)
!             self.log_error("command: %s", cmdline)
!             try:
!                 nbytes = int(length)
!             except:
!                 nbytes = 0
!             fi, fo = os.popen2(cmdline)
!             if self.command.lower() == "post" and nbytes > 0:
!                 data = self.rfile.read(nbytes)
!                 fi.write(data)
!             fi.close()
!             shutil.copyfileobj(fo, self.wfile)
!             sts = fo.close()
              if sts:
!                 self.log_error("CGI script exit status %#x", sts)
              else:
!                 self.log_error("CGI script exited OK")
! 
!         else:
!             # Other O.S. -- execute script in this process
!             os.environ.update(env)
!             save_argv = sys.argv
!             save_stdin = sys.stdin
!             save_stdout = sys.stdout
!             save_stderr = sys.stderr
              try:
!                 try:
!                     sys.argv = [scriptfile]
!                     if '=' not in decoded_query:
!                         sys.argv.append(decoded_query)
!                     sys.stdout = self.wfile
!                     sys.stdin = self.rfile
!                     execfile(scriptfile, {"__name__": "__main__"})
!                 finally:
!                     sys.argv = save_argv
!                     sys.stdin = save_stdin
!                     sys.stdout = save_stdout
!                     sys.stderr = save_stderr
!             except SystemExit, sts:
!                 self.log_error("CGI script exit status %s", str(sts))
!             else:
!                 self.log_error("CGI script exited OK")
  
  
***************
*** 188,192 ****
      if nobody:
          return nobody
!     import pwd
      try:
          nobody = pwd.getpwnam('nobody')[2]
--- 277,284 ----
      if nobody:
          return nobody
!     try:
!         import pwd
!     except ImportError:
!         return -1
      try:
          nobody = pwd.getpwnam('nobody')[2]