home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 5 / Amiga Tools 5.iso / tools / developer-tools / andere sprachen / python-1.3 / lib / cgi.py < prev    next >
Encoding:
Python Source  |  1996-07-16  |  13.5 KB  |  599 lines

  1. #!/usr/local/bin/python
  2.  
  3. # Support module for CGI (Common Gateway Interface) scripts.
  4. #
  5. # This module defines a number of utilities for use by CGI scripts
  6. # written in Python.
  7. #
  8. # See cgi.py_doc for documented version.
  9.  
  10.  
  11. __version__ = "2.0a3"
  12.  
  13.  
  14. import string
  15. import sys
  16. import os
  17.  
  18. environ = os.environ
  19.  
  20.  
  21. def parse(fp=None):
  22.     if not fp:
  23.     fp = sys.stdin
  24.     if not environ.has_key('REQUEST_METHOD'):
  25.     environ['REQUEST_METHOD'] = 'GET'    # For testing stand-alone
  26.     if environ['REQUEST_METHOD'] == 'POST':
  27.     ctype, pdict = parse_header(environ['CONTENT_TYPE'])
  28.     if ctype == 'multipart/form-data':
  29.         return parse_multipart(fp, pdict)
  30.     elif ctype == 'application/x-www-form-urlencoded':
  31.         clength = string.atoi(environ['CONTENT_LENGTH'])
  32.         qs = fp.read(clength)
  33.     else:
  34.         qs = ''            # Unknown content-type
  35.     environ['QUERY_STRING'] = qs    # XXX Shouldn't, really
  36.     elif environ.has_key('QUERY_STRING'):
  37.     qs = environ['QUERY_STRING']
  38.     else:
  39.     if sys.argv[1:]:
  40.         qs = sys.argv[1]
  41.     else:
  42.         qs = ""
  43.     environ['QUERY_STRING'] = qs    # XXX Shouldn't, really
  44.     return parse_qs(qs)
  45.  
  46.  
  47. def parse_qs(qs):
  48.     import urllib, regsub
  49.     name_value_pairs = string.splitfields(qs, '&')
  50.     dict = {}
  51.     for name_value in name_value_pairs:
  52.     nv = string.splitfields(name_value, '=')
  53.     if len(nv) != 2:
  54.         continue
  55.     name = nv[0]
  56.     value = urllib.unquote(regsub.gsub('+', ' ', nv[1]))
  57.     if len(value):
  58.         if dict.has_key (name):
  59.         dict[name].append(value)
  60.         else:
  61.         dict[name] = [value]
  62.     return dict
  63.  
  64.  
  65. def parse_multipart(fp, pdict):
  66.     import mimetools
  67.     if pdict.has_key('boundary'):
  68.     boundary = pdict['boundary']
  69.     else:
  70.     boundary = ""
  71.     nextpart = "--" + boundary
  72.     lastpart = "--" + boundary + "--"
  73.     partdict = {}
  74.     terminator = ""
  75.  
  76.     while terminator != lastpart:
  77.     bytes = -1
  78.     data = None
  79.     if terminator:
  80.         # At start of next part.  Read headers first.
  81.         headers = mimetools.Message(fp)
  82.         clength = headers.getheader('content-length')
  83.         if clength:
  84.         try:
  85.             bytes = string.atoi(clength)
  86.         except string.atoi_error:
  87.             pass
  88.         if bytes > 0:
  89.         data = fp.read(bytes)
  90.         else:
  91.         data = ""
  92.     # Read lines until end of part.
  93.     lines = []
  94.     while 1:
  95.         line = fp.readline()
  96.         if not line:
  97.         terminator = lastpart # End outer loop
  98.         break
  99.         if line[:2] == "--":
  100.         terminator = string.strip(line)
  101.         if terminator in (nextpart, lastpart):
  102.             break
  103.         if line[-2:] == '\r\n':
  104.         line = line[:-2]
  105.         elif line[-1:] == '\n':
  106.         line = line[:-1]
  107.         lines.append(line)
  108.     # Done with part.
  109.     if data is None:
  110.         continue
  111.     if bytes < 0:
  112.         data = string.joinfields(lines, "\n")
  113.     line = headers['content-disposition']
  114.     if not line:
  115.         continue
  116.     key, params = parse_header(line)
  117.     if key != 'form-data':
  118.         continue
  119.     if params.has_key('name'):
  120.         name = params['name']
  121.     else:
  122.         continue
  123.     if partdict.has_key(name):
  124.         partdict[name].append(data)
  125.     else:
  126.         partdict[name] = [data]
  127.  
  128.     return partdict
  129.  
  130.  
  131. def parse_header(line):
  132.     plist = map(string.strip, string.splitfields(line, ';'))
  133.     key = string.lower(plist[0])
  134.     del plist[0]
  135.     pdict = {}
  136.     for p in plist:
  137.     i = string.find(p, '=')
  138.     if i >= 0:
  139.         name = string.lower(string.strip(p[:i]))
  140.         value = string.strip(p[i+1:])
  141.         if len(value) >= 2 and value[0] == value[-1] == '"':
  142.         value = value[1:-1]
  143.         pdict[name] = value
  144.     return key, pdict
  145.  
  146.  
  147. # Classes for field storage
  148. # =========================
  149.  
  150. class MiniFieldStorage:
  151.  
  152.     # Dummy attributes
  153.     filename = None
  154.     list = None
  155.     type = None
  156.     type_options = {}
  157.     disposition = None
  158.     disposition_options = {}
  159.     headers = {}
  160.  
  161.     def __init__(self, name, value):
  162.     from StringIO import StringIO
  163.     self.name = name
  164.     self.value = value
  165.     self.file = StringIO(value)
  166.  
  167.     def __repr__(self):
  168.     return "MiniFieldStorage(%s, %s)" % (`self.name`, `self.value`)
  169.  
  170.  
  171. class FieldStorage:
  172.  
  173.     def __init__(self, fp=None, headers=None, outerboundary=""):
  174.  
  175.     method = None
  176.     if environ.has_key('REQUEST_METHOD'):
  177.         method = string.upper(environ['REQUEST_METHOD'])
  178.     if not fp and method == 'GET':
  179.         qs = None
  180.         if environ.has_key('QUERY_STRING'):
  181.         qs = environ['QUERY_STRING']
  182.         from StringIO import StringIO
  183.         fp = StringIO(qs or "")
  184.         if headers is None:
  185.         headers = {'content-type':
  186.                "application/x-www-form-urlencoded"}
  187.     if headers is None:
  188.         headers = {}
  189.         if environ.has_key('CONTENT_TYPE'):
  190.         headers['content-type'] = environ['CONTENT_TYPE']
  191.         if environ.has_key('CONTENT_LENGTH'):
  192.         headers['content-length'] = environ['CONTENT_LENGTH']
  193.     self.fp = fp or sys.stdin
  194.     self.headers = headers
  195.     self.outerboundary = outerboundary
  196.  
  197.     # Process content-disposition header
  198.     cdisp, pdict = "", {}
  199.     if self.headers.has_key('content-disposition'):
  200.         cdisp, pdict = parse_header(self.headers['content-disposition'])
  201.     self.disposition = cdisp
  202.     self.disposition_options = pdict
  203.     self.name = None
  204.     if pdict.has_key('name'):
  205.         self.name = pdict['name']
  206.     self.filename = None
  207.     if pdict.has_key('filename'):
  208.         self.filename = pdict['filename']
  209.  
  210.     # Process content-type header
  211.     ctype, pdict = "text/plain", {}
  212.     if self.headers.has_key('content-type'):
  213.         ctype, pdict = parse_header(self.headers['content-type'])
  214.     self.type = ctype
  215.     self.type_options = pdict
  216.     self.innerboundary = ""
  217.     if pdict.has_key('boundary'):
  218.         self.innerboundary = pdict['boundary']
  219.     clen = -1
  220.     if self.headers.has_key('content-length'):
  221.         try:
  222.         clen = string.atoi(self.headers['content-length'])
  223.         except:
  224.         pass
  225.     self.length = clen
  226.  
  227.     self.list = self.file = None
  228.     self.done = 0
  229.     self.lines = []
  230.     if ctype == 'application/x-www-form-urlencoded':
  231.         self.read_urlencoded()
  232.     elif ctype[:10] == 'multipart/':
  233.         self.read_multi()
  234.     else:
  235.         self.read_single()
  236.  
  237.     def __repr__(self):
  238.     return "FieldStorage(%s, %s, %s)" % (
  239.         `self.name`, `self.filename`, `self.value`)
  240.  
  241.     def __getattr__(self, name):
  242.     if name != 'value':
  243.         raise AttributeError, name
  244.     if self.file:
  245.         self.file.seek(0)
  246.         value = self.file.read()
  247.         self.file.seek(0)
  248.     elif self.list is not None:
  249.         value = self.list
  250.     else:
  251.         value = None
  252.     return value
  253.  
  254.     def __getitem__(self, key):
  255.     if self.list is None:
  256.         raise TypeError, "not indexable"
  257.     found = []
  258.     for item in self.list:
  259.         if item.name == key: found.append(item)
  260.     if not found:
  261.         raise KeyError, key
  262.     if len(found) == 1:
  263.         return found[0]
  264.     else:
  265.         return found
  266.  
  267.     def keys(self):
  268.     if self.list is None:
  269.         raise TypeError, "not indexable"
  270.     keys = []
  271.     for item in self.list:
  272.         if item.name not in keys: keys.append(item.name)
  273.     return keys
  274.  
  275.     def has_key(self, key):
  276.     if self.list is None:
  277.         raise TypeError, "not indexable"
  278.     for item in self.list:
  279.         if item.name == key: return 1
  280.     return 0
  281.  
  282.     def read_urlencoded(self):
  283.     qs = self.fp.read(self.length)
  284.     dict = parse_qs(qs)
  285.     self.list = []
  286.     for key, valuelist in dict.items():
  287.         for value in valuelist:
  288.         self.list.append(MiniFieldStorage(key, value))
  289.     self.skip_lines()
  290.  
  291.     def read_multi(self):
  292.     import rfc822
  293.     self.list = []
  294.     part = self.__class__(self.fp, {}, self.innerboundary)
  295.     # Throw first part away
  296.     while not part.done:
  297.         headers = rfc822.Message(self.fp)
  298.         part = self.__class__(self.fp, headers, self.innerboundary)
  299.         self.list.append(part)
  300.     self.skip_lines()
  301.  
  302.     def read_single(self):
  303.     if self.length >= 0:
  304.         self.read_binary()
  305.         self.skip_lines()
  306.     else:
  307.         self.read_lines()
  308.     self.file.seek(0)
  309.  
  310.     bufsize = 8*1024        # I/O buffering size for copy to file
  311.  
  312.     def read_binary(self):
  313.     self.file = self.make_file('b')
  314.     todo = self.length
  315.     if todo >= 0:
  316.         while todo > 0:
  317.         data = self.fp.read(min(todo, self.bufsize))
  318.         if not data:
  319.             self.done = -1
  320.             break
  321.         self.file.write(data)
  322.         todo = todo - len(data)
  323.  
  324.     def read_lines(self):
  325.     self.file = self.make_file('')
  326.     if self.outerboundary:
  327.         self.read_lines_to_outerboundary()
  328.     else:
  329.         self.read_lines_to_eof()
  330.  
  331.     def read_lines_to_eof(self):
  332.     while 1:
  333.         line = self.fp.readline()
  334.         if not line:
  335.         self.done = -1
  336.         break
  337.         self.lines.append(line)
  338.         if line[-2:] == '\r\n':
  339.         line = line[:-2] + '\n'
  340.         self.file.write(line)
  341.  
  342.     def read_lines_to_outerboundary(self):
  343.     next = "--" + self.outerboundary
  344.     last = next + "--"
  345.     delim = ""
  346.     while 1:
  347.         line = self.fp.readline()
  348.         if not line:
  349.         self.done = -1
  350.         break
  351.         self.lines.append(line)
  352.         if line[:2] == "--":
  353.         strippedline = string.strip(line)
  354.         if strippedline == next:
  355.             break
  356.         if strippedline == last:
  357.             self.done = 1
  358.             break
  359.         if line[-2:] == "\r\n":
  360.         line = line[:-2]
  361.         elif line[-1] == "\n":
  362.         line = line[:-1]
  363.         self.file.write(delim + line)
  364.         delim = "\n"
  365.  
  366.     def skip_lines(self):
  367.     if not self.outerboundary or self.done:
  368.         return
  369.     next = "--" + self.outerboundary
  370.     last = next + "--"
  371.     while 1:
  372.         line = self.fp.readline()
  373.         if not line:
  374.         self.done = -1
  375.         break
  376.         self.lines.append(line)
  377.         if line[:2] == "--":
  378.         strippedline = string.strip(line)
  379.         if strippedline == next:
  380.             break
  381.         if strippedline == last:
  382.             self.done = 1
  383.             break
  384.  
  385.     def make_file(self, binary):
  386.     import tempfile
  387.     tfn = tempfile.mktemp()
  388.     f = open(tfn, "w%s+" % binary)
  389.     os.unlink(tfn)
  390.     return f
  391.  
  392.  
  393. # Backwards Compatibility Classes
  394. # ===============================
  395.  
  396. class FormContentDict:
  397.     def __init__( self ):
  398.     self.dict = parse()
  399.     self.query_string = environ['QUERY_STRING']
  400.     def __getitem__(self,key):
  401.     return self.dict[key]
  402.     def keys(self):
  403.     return self.dict.keys()
  404.     def has_key(self, key):
  405.     return self.dict.has_key(key)
  406.     def values(self):
  407.     return self.dict.values()
  408.     def items(self):
  409.     return self.dict.items() 
  410.     def __len__( self ):
  411.     return len(self.dict)
  412.  
  413.  
  414. class SvFormContentDict(FormContentDict):
  415.     def __getitem__(self, key):
  416.     if len(self.dict[key]) > 1: 
  417.         raise IndexError, 'expecting a single value' 
  418.     return self.dict[key][0]
  419.     def getlist(self, key):
  420.     return self.dict[key]
  421.     def values(self):
  422.     lis = []
  423.     for each in self.dict.values(): 
  424.         if len( each ) == 1 : 
  425.         lis.append(each[0])
  426.         else: lis.append(each)
  427.     return lis
  428.     def items(self):
  429.     lis = []
  430.     for key,value in self.dict.items():
  431.         if len(value) == 1 :
  432.         lis.append((key, value[0]))
  433.         else:    lis.append((key, value))
  434.     return lis
  435.  
  436.  
  437. class InterpFormContentDict(SvFormContentDict):
  438.     def __getitem__( self, key ):
  439.     v = SvFormContentDict.__getitem__( self, key )
  440.     if v[0] in string.digits+'+-.' : 
  441.         try:  return  string.atoi( v ) 
  442.         except ValueError:
  443.         try:    return string.atof( v )
  444.         except ValueError: pass
  445.     return string.strip(v)
  446.     def values( self ):
  447.     lis = [] 
  448.     for key in self.keys():
  449.         try:
  450.         lis.append( self[key] )
  451.         except IndexError:
  452.         lis.append( self.dict[key] )
  453.     return lis
  454.     def items( self ):
  455.     lis = [] 
  456.     for key in self.keys():
  457.         try:
  458.         lis.append( (key, self[key]) )
  459.         except IndexError:
  460.         lis.append( (key, self.dict[key]) )
  461.     return lis
  462.  
  463.  
  464. class FormContent(FormContentDict):
  465.     def values(self, key):
  466.     if self.dict.has_key(key) :return self.dict[key]
  467.     else: return None
  468.     def indexed_value(self, key, location):
  469.     if self.dict.has_key(key):
  470.         if len (self.dict[key]) > location:
  471.         return self.dict[key][location]
  472.         else: return None
  473.     else: return None
  474.     def value(self, key):
  475.     if self.dict.has_key(key): return self.dict[key][0]
  476.     else: return None
  477.     def length(self, key):
  478.     return len(self.dict[key])
  479.     def stripped(self, key):
  480.     if self.dict.has_key(key): return string.strip(self.dict[key][0])
  481.     else: return None
  482.     def pars(self):
  483.     return self.dict
  484.  
  485.  
  486. # Test/debug code
  487. # ===============
  488.  
  489. def test():
  490.     import traceback
  491.     print "Content-type: text/html"
  492.     print
  493.     sys.stderr = sys.stdout
  494.     try:
  495.     form = FieldStorage()    # Replace with other classes to test those
  496.     print_form(form)
  497.     print_environ()
  498.     print_directory()
  499.     print_arguments()
  500.     print_environ_usage()
  501.     except:
  502.     print "\n\n<PRE>"    # Turn off HTML word wrap
  503.     traceback.print_exc()
  504.  
  505. def print_environ():
  506.     keys = environ.keys()
  507.     keys.sort()
  508.     print
  509.     print "<H3>Shell environment:</H3>"
  510.     print "<DL>"
  511.     for key in keys:
  512.     print "<DT>", escape(key), "<DD>", escape(environ[key])
  513.     print "</DL>" 
  514.     print
  515.  
  516. def print_form(form):
  517.     keys = form.keys()
  518.     keys.sort()
  519.     print
  520.     print "<H3>Form contents:</H3>"
  521.     print "<DL>"
  522.     for key in keys:
  523.     print "<DT>" + escape(key) + ":",
  524.     value = form[key]
  525.     print "<i>" + escape(`type(value)`) + "</i>"
  526.     print "<DD>" + escape(`value`)
  527.     print "</DL>"
  528.     print
  529.  
  530. def print_directory():
  531.     print
  532.     print "<H3>Current Working Directory:</H3>"
  533.     try:
  534.     pwd = os.getcwd()
  535.     except os.error, msg:
  536.     print "os.error:", escape(str(msg))
  537.     else:
  538.     print escape(pwd)
  539.     print
  540.  
  541. def print_arguments():
  542.     print
  543.     print "<H3>Command line Arguments:</H3>"
  544.     print
  545.     print sys.argv
  546.     print
  547.  
  548. def print_environ_usage():
  549.     print """
  550. <H3>These environment variables could have been set:</H3>
  551. <UL>
  552. <LI>AUTH_TYPE
  553. <LI>CONTENT_LENGTH
  554. <LI>CONTENT_TYPE
  555. <LI>DATE_GMT
  556. <LI>DATE_LOCAL
  557. <LI>DOCUMENT_NAME
  558. <LI>DOCUMENT_ROOT
  559. <LI>DOCUMENT_URI
  560. <LI>GATEWAY_INTERFACE
  561. <LI>LAST_MODIFIED
  562. <LI>PATH
  563. <LI>PATH_INFO
  564. <LI>PATH_TRANSLATED
  565. <LI>QUERY_STRING
  566. <LI>REMOTE_ADDR
  567. <LI>REMOTE_HOST
  568. <LI>REMOTE_IDENT
  569. <LI>REMOTE_USER
  570. <LI>REQUEST_METHOD
  571. <LI>SCRIPT_NAME
  572. <LI>SERVER_NAME
  573. <LI>SERVER_PORT
  574. <LI>SERVER_PROTOCOL
  575. <LI>SERVER_ROOT
  576. <LI>SERVER_SOFTWARE
  577. </UL>
  578. In addition, HTTP headers sent by the server may be passed in the
  579. environment as well.  Here are some common variable names:
  580. <UL>
  581. <LI>HTTP_ACCEPT
  582. <LI>HTTP_CONNECTION
  583. <LI>HTTP_HOST
  584. <LI>HTTP_PRAGMA
  585. <LI>HTTP_REFERER
  586. <LI>HTTP_USER_AGENT
  587. </UL>
  588. """
  589.  
  590. def escape(s):
  591.     import regsub
  592.     s = regsub.gsub("&", "&", s)    # Must be done first!
  593.     s = regsub.gsub("<", "<", s)
  594.     s = regsub.gsub(">", ">", s)
  595.     return s
  596.  
  597. if __name__ == '__main__': 
  598.     test()
  599.