home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pytho152.zip / emx / lib / python1.5 / CGIHTTPServer.py < prev    next >
Text File  |  2000-08-10  |  6KB  |  201 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.             if self.headers.typeheader is None:
  137.                 env['CONTENT_TYPE'] = self.headers.type
  138.             else:
  139.                 env['CONTENT_TYPE'] = self.headers.typeheader
  140.             length = self.headers.getheader('content-length')
  141.             if length:
  142.                 env['CONTENT_LENGTH'] = length
  143.             accept = []
  144.             for line in self.headers.getallmatchingheaders('accept'):
  145.                 if line[:1] in string.whitespace:
  146.                     accept.append(string.strip(line))
  147.                 else:
  148.                     accept = accept + string.split(line[7:], ',')
  149.             env['HTTP_ACCEPT'] = string.joinfields(accept, ',')
  150.             ua = self.headers.getheader('user-agent')
  151.             if ua:
  152.                 env['HTTP_USER_AGENT'] = ua
  153.             # XXX Other HTTP_* headers
  154.             decoded_query = string.replace(query, '+', ' ')
  155.             try:
  156.                 os.setuid(nobody)
  157.             except os.error:
  158.                 pass
  159.             os.dup2(self.rfile.fileno(), 0)
  160.             os.dup2(self.wfile.fileno(), 1)
  161.             print scriptfile, script, decoded_query
  162.             os.execve(scriptfile,
  163.                       [script, decoded_query],
  164.                       env)
  165.         except:
  166.             self.server.handle_error(self.request, self.client_address)
  167.             os._exit(127)
  168.  
  169.  
  170. nobody = None
  171.  
  172. def nobody_uid():
  173.     """Internal routine to get nobody's uid"""
  174.     global nobody
  175.     if nobody:
  176.         return nobody
  177.     import pwd
  178.     try:
  179.         nobody = pwd.getpwnam('nobody')[2]
  180.     except pwd.error:
  181.         nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
  182.     return nobody
  183.  
  184.  
  185. def executable(path):
  186.     """Test for executable file."""
  187.     try:
  188.         st = os.stat(path)
  189.     except os.error:
  190.         return 0
  191.     return st[0] & 0111 != 0
  192.  
  193.  
  194. def test(HandlerClass = CGIHTTPRequestHandler,
  195.          ServerClass = BaseHTTPServer.HTTPServer):
  196.     SimpleHTTPServer.test(HandlerClass, ServerClass)
  197.  
  198.  
  199. if __name__ == '__main__':
  200.     test()
  201.