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