home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Lib / CGIHTTPServer.py < prev    next >
Text File  |  1997-12-24  |  5KB  |  198 lines

  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. """
  7.  
  8.  
  9. __version__ = "0.3"
  10.  
  11.  
  12. import os
  13. import sys
  14. import time
  15. import socket
  16. import string
  17. import urllib
  18. import BaseHTTPServer
  19. import SimpleHTTPServer
  20.  
  21.  
  22. class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  23.  
  24.     """Complete HTTP server with GET, HEAD and POST commands.
  25.  
  26.     GET and HEAD also support running CGI scripts.
  27.  
  28.     The POST command is *only* implemented for CGI scripts.
  29.  
  30.     """
  31.  
  32.     def do_POST(self):
  33.     """Serve a POST request.
  34.  
  35.     This is only implemented for CGI scripts.
  36.  
  37.     """
  38.  
  39.     if self.is_cgi():
  40.         self.run_cgi()
  41.     else:
  42.         self.send_error(501, "Can only POST to CGI scripts")
  43.  
  44.     def send_head(self):
  45.     """Version of send_head that support CGI scripts"""
  46.     if self.is_cgi():
  47.         return self.run_cgi()
  48.     else:
  49.         return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
  50.  
  51.     def is_cgi(self):
  52.     """test whether PATH corresponds to a CGI script.
  53.  
  54.     Return a tuple (dir, rest) if PATH requires running a
  55.     CGI script, None if not.  Note that rest begins with a
  56.     slash if it is not empty.
  57.  
  58.     The default implementation tests whether the path
  59.     begins with one of the strings in the list
  60.     self.cgi_directories (and the next character is a '/'
  61.     or the end of the string).
  62.  
  63.     """
  64.  
  65.     path = self.path
  66.  
  67.     for x in self.cgi_directories:
  68.         i = len(x)
  69.         if path[:i] == x and (not path[i:] or path[i] == '/'):
  70.         self.cgi_info = path[:i], path[i+1:]
  71.         return 1
  72.     return 0
  73.  
  74.     cgi_directories = ['/cgi-bin', '/htbin']
  75.  
  76.     def run_cgi(self):
  77.     """Execute a CGI script."""
  78.     dir, rest = self.cgi_info
  79.     i = string.rfind(rest, '?')
  80.     if i >= 0:
  81.         rest, query = rest[:i], rest[i+1:]
  82.     else:
  83.         query = ''
  84.     i = string.find(rest, '/')
  85.     if i >= 0:
  86.         script, rest = rest[:i], rest[i:]
  87.     else:
  88.         script, rest = rest, ''
  89.     scriptname = dir + '/' + script
  90.     scriptfile = self.translate_path(scriptname)
  91.     if not os.path.exists(scriptfile):
  92.         self.send_error(404, "No such CGI script (%s)" % `scriptname`)
  93.         return
  94.     if not os.path.isfile(scriptfile):
  95.         self.send_error(403, "CGI script is not a plain file (%s)" %
  96.                 `scriptname`)
  97.         return
  98.     if not executable(scriptfile):
  99.         self.send_error(403, "CGI script is not executable (%s)" %
  100.                 `scriptname`)
  101.         return
  102.     nobody = nobody_uid()
  103.     self.send_response(200, "Script output follows")
  104.     self.wfile.flush() # Always flush before forking
  105.     pid = os.fork()
  106.     if pid != 0:
  107.         # Parent
  108.         pid, sts = os.waitpid(pid, 0)
  109.         if sts:
  110.         self.log_error("CGI script exit status x%x" % sts)
  111.         return
  112.     # Child
  113.     try:
  114.         # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
  115.         # XXX Much of the following could be prepared ahead of time!
  116.         env = {}
  117.         env['SERVER_SOFTWARE'] = self.version_string()
  118.         env['SERVER_NAME'] = self.server.server_name
  119.         env['GATEWAY_INTERFACE'] = 'CGI/1.1'
  120.         env['SERVER_PROTOCOL'] = self.protocol_version
  121.         env['SERVER_PORT'] = str(self.server.server_port)
  122.         env['REQUEST_METHOD'] = self.command
  123.         uqrest = urllib.unquote(rest)
  124.         env['PATH_INFO'] = uqrest
  125.         env['PATH_TRANSLATED'] = self.translate_path(uqrest)
  126.         env['SCRIPT_NAME'] = scriptname
  127.         if query:
  128.         env['QUERY_STRING'] = query
  129.         host = self.address_string()
  130.         if host != self.client_address[0]:
  131.         env['REMOTE_HOST'] = host
  132.         env['REMOTE_ADDR'] = self.client_address[0]
  133.         # AUTH_TYPE
  134.         # REMOTE_USER
  135.         # REMOTE_IDENT
  136.         env['CONTENT_TYPE'] = self.headers.type
  137.         length = self.headers.getheader('content-length')
  138.         if length:
  139.         env['CONTENT_LENGTH'] = length
  140.         accept = []
  141.         for line in self.headers.getallmatchingheaders('accept'):
  142.         if line[:1] in string.whitespace:
  143.             accept.append(string.strip(line))
  144.         else:
  145.             accept = accept + string.split(line[7:])
  146.         env['HTTP_ACCEPT'] = string.joinfields(accept, ',')
  147.         ua = self.headers.getheader('user-agent')
  148.         if ua:
  149.         env['HTTP_USER_AGENT'] = ua
  150.         # XXX Other HTTP_* headers
  151.         decoded_query = string.replace(query, '+', ' ')
  152.         try:
  153.         os.setuid(nobody)
  154.         except os.error:
  155.         pass
  156.         os.dup2(self.rfile.fileno(), 0)
  157.         os.dup2(self.wfile.fileno(), 1)
  158.         print scriptfile, script, decoded_query
  159.         os.execve(scriptfile,
  160.               [script, decoded_query],
  161.               env)
  162.     except:
  163.         self.server.handle_error(self.request, self.client_address)
  164.         os._exit(127)
  165.  
  166.  
  167. nobody = None
  168.  
  169. def nobody_uid():
  170.     """Internal routine to get nobody's uid"""
  171.     global nobody
  172.     if nobody:
  173.     return nobody
  174.     import pwd
  175.     try:
  176.     nobody = pwd.getpwnam('nobody')[2]
  177.     except pwd.error:
  178.     nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
  179.     return nobody
  180.  
  181.  
  182. def executable(path):
  183.     """Test for executable file."""
  184.     try:
  185.     st = os.stat(path)
  186.     except os.error:
  187.     return 0
  188.     return st[0] & 0111 != 0
  189.  
  190.  
  191. def test(HandlerClass = CGIHTTPRequestHandler,
  192.      ServerClass = BaseHTTPServer.HTTPServer):
  193.     SimpleHTTPServer.test(HandlerClass, ServerClass)
  194.  
  195.  
  196. if __name__ == '__main__':
  197.     test()
  198.