Home | History | Annotate | Download | only in Lib
      1 #! /usr/local/bin/python
      2 
      3 # NOTE: the above "/usr/local/bin/python" is NOT a mistake.  It is
      4 # intentionally NOT "/usr/bin/env python".  On many systems
      5 # (e.g. Solaris), /usr/local/bin is not in $PATH as passed to CGI
      6 # scripts, and /usr/local/bin is the default directory where Python is
      7 # installed, so /usr/bin/env would be unable to find python.  Granted,
      8 # binary installations by Linux vendors often install Python in
      9 # /usr/bin.  So let those vendors patch cgi.py to match their choice
     10 # of installation.
     11 
     12 """Support module for CGI (Common Gateway Interface) scripts.
     13 
     14 This module defines a number of utilities for use by CGI scripts
     15 written in Python.
     16 """
     17 
     18 # XXX Perhaps there should be a slimmed version that doesn't contain
     19 # all those backwards compatible and debugging classes and functions?
     20 
     21 # History
     22 # -------
     23 #
     24 # Michael McLay started this module.  Steve Majewski changed the
     25 # interface to SvFormContentDict and FormContentDict.  The multipart
     26 # parsing was inspired by code submitted by Andreas Paepcke.  Guido van
     27 # Rossum rewrote, reformatted and documented the module and is currently
     28 # responsible for its maintenance.
     29 #
     30 
     31 __version__ = "2.6"
     32 
     33 
     34 # Imports
     35 # =======
     36 
     37 from operator import attrgetter
     38 import sys
     39 import os
     40 import UserDict
     41 import urlparse
     42 
     43 from warnings import filterwarnings, catch_warnings, warn
     44 with catch_warnings():
     45     if sys.py3kwarning:
     46         filterwarnings("ignore", ".*mimetools has been removed",
     47                        DeprecationWarning)
     48         filterwarnings("ignore", ".*rfc822 has been removed",
     49                        DeprecationWarning)
     50     import mimetools
     51     import rfc822
     52 
     53 try:
     54     from cStringIO import StringIO
     55 except ImportError:
     56     from StringIO import StringIO
     57 
     58 __all__ = ["MiniFieldStorage", "FieldStorage", "FormContentDict",
     59            "SvFormContentDict", "InterpFormContentDict", "FormContent",
     60            "parse", "parse_qs", "parse_qsl", "parse_multipart",
     61            "parse_header", "print_exception", "print_environ",
     62            "print_form", "print_directory", "print_arguments",
     63            "print_environ_usage", "escape"]
     64 
     65 # Logging support
     66 # ===============
     67 
     68 logfile = ""            # Filename to log to, if not empty
     69 logfp = None            # File object to log to, if not None
     70 
     71 def initlog(*allargs):
     72     """Write a log message, if there is a log file.
     73 
     74     Even though this function is called initlog(), you should always
     75     use log(); log is a variable that is set either to initlog
     76     (initially), to dolog (once the log file has been opened), or to
     77     nolog (when logging is disabled).
     78 
     79     The first argument is a format string; the remaining arguments (if
     80     any) are arguments to the % operator, so e.g.
     81         log("%s: %s", "a", "b")
     82     will write "a: b" to the log file, followed by a newline.
     83 
     84     If the global logfp is not None, it should be a file object to
     85     which log data is written.
     86 
     87     If the global logfp is None, the global logfile may be a string
     88     giving a filename to open, in append mode.  This file should be
     89     world writable!!!  If the file can't be opened, logging is
     90     silently disabled (since there is no safe place where we could
     91     send an error message).
     92 
     93     """
     94     global logfp, log
     95     if logfile and not logfp:
     96         try:
     97             logfp = open(logfile, "a")
     98         except IOError:
     99             pass
    100     if not logfp:
    101         log = nolog
    102     else:
    103         log = dolog
    104     log(*allargs)
    105 
    106 def dolog(fmt, *args):
    107     """Write a log message to the log file.  See initlog() for docs."""
    108     logfp.write(fmt%args + "\n")
    109 
    110 def nolog(*allargs):
    111     """Dummy function, assigned to log when logging is disabled."""
    112     pass
    113 
    114 log = initlog           # The current logging function
    115 
    116 
    117 # Parsing functions
    118 # =================
    119 
    120 # Maximum input we will accept when REQUEST_METHOD is POST
    121 # 0 ==> unlimited input
    122 maxlen = 0
    123 
    124 def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
    125     """Parse a query in the environment or from a file (default stdin)
    126 
    127         Arguments, all optional:
    128 
    129         fp              : file pointer; default: sys.stdin
    130 
    131         environ         : environment dictionary; default: os.environ
    132 
    133         keep_blank_values: flag indicating whether blank values in
    134             percent-encoded forms should be treated as blank strings.
    135             A true value indicates that blanks should be retained as
    136             blank strings.  The default false value indicates that
    137             blank values are to be ignored and treated as if they were
    138             not included.
    139 
    140         strict_parsing: flag indicating what to do with parsing errors.
    141             If false (the default), errors are silently ignored.
    142             If true, errors raise a ValueError exception.
    143     """
    144     if fp is None:
    145         fp = sys.stdin
    146     if not 'REQUEST_METHOD' in environ:
    147         environ['REQUEST_METHOD'] = 'GET'       # For testing stand-alone
    148     if environ['REQUEST_METHOD'] == 'POST':
    149         ctype, pdict = parse_header(environ['CONTENT_TYPE'])
    150         if ctype == 'multipart/form-data':
    151             return parse_multipart(fp, pdict)
    152         elif ctype == 'application/x-www-form-urlencoded':
    153             clength = int(environ['CONTENT_LENGTH'])
    154             if maxlen and clength > maxlen:
    155                 raise ValueError, 'Maximum content length exceeded'
    156             qs = fp.read(clength)
    157         else:
    158             qs = ''                     # Unknown content-type
    159         if 'QUERY_STRING' in environ:
    160             if qs: qs = qs + '&'
    161             qs = qs + environ['QUERY_STRING']
    162         elif sys.argv[1:]:
    163             if qs: qs = qs + '&'
    164             qs = qs + sys.argv[1]
    165         environ['QUERY_STRING'] = qs    # XXX Shouldn't, really
    166     elif 'QUERY_STRING' in environ:
    167         qs = environ['QUERY_STRING']
    168     else:
    169         if sys.argv[1:]:
    170             qs = sys.argv[1]
    171         else:
    172             qs = ""
    173         environ['QUERY_STRING'] = qs    # XXX Shouldn't, really
    174     return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
    175 
    176 
    177 # parse query string function called from urlparse,
    178 # this is done in order to maintain backward compatibility.
    179 
    180 def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
    181     """Parse a query given as a string argument."""
    182     warn("cgi.parse_qs is deprecated, use urlparse.parse_qs instead",
    183          PendingDeprecationWarning, 2)
    184     return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
    185 
    186 
    187 def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
    188     """Parse a query given as a string argument."""
    189     warn("cgi.parse_qsl is deprecated, use urlparse.parse_qsl instead",
    190          PendingDeprecationWarning, 2)
    191     return urlparse.parse_qsl(qs, keep_blank_values, strict_parsing)
    192 
    193 def parse_multipart(fp, pdict):
    194     """Parse multipart input.
    195 
    196     Arguments:
    197     fp   : input file
    198     pdict: dictionary containing other parameters of content-type header
    199 
    200     Returns a dictionary just like parse_qs(): keys are the field names, each
    201     value is a list of values for that field.  This is easy to use but not
    202     much good if you are expecting megabytes to be uploaded -- in that case,
    203     use the FieldStorage class instead which is much more flexible.  Note
    204     that content-type is the raw, unparsed contents of the content-type
    205     header.
    206 
    207     XXX This does not parse nested multipart parts -- use FieldStorage for
    208     that.
    209 
    210     XXX This should really be subsumed by FieldStorage altogether -- no
    211     point in having two implementations of the same parsing algorithm.
    212     Also, FieldStorage protects itself better against certain DoS attacks
    213     by limiting the size of the data read in one chunk.  The API here
    214     does not support that kind of protection.  This also affects parse()
    215     since it can call parse_multipart().
    216 
    217     """
    218     boundary = ""
    219     if 'boundary' in pdict:
    220         boundary = pdict['boundary']
    221     if not valid_boundary(boundary):
    222         raise ValueError,  ('Invalid boundary in multipart form: %r'
    223                             % (boundary,))
    224 
    225     nextpart = "--" + boundary
    226     lastpart = "--" + boundary + "--"
    227     partdict = {}
    228     terminator = ""
    229 
    230     while terminator != lastpart:
    231         bytes = -1
    232         data = None
    233         if terminator:
    234             # At start of next part.  Read headers first.
    235             headers = mimetools.Message(fp)
    236             clength = headers.getheader('content-length')
    237             if clength:
    238                 try:
    239                     bytes = int(clength)
    240                 except ValueError:
    241                     pass
    242             if bytes > 0:
    243                 if maxlen and bytes > maxlen:
    244                     raise ValueError, 'Maximum content length exceeded'
    245                 data = fp.read(bytes)
    246             else:
    247                 data = ""
    248         # Read lines until end of part.
    249         lines = []
    250         while 1:
    251             line = fp.readline()
    252             if not line:
    253                 terminator = lastpart # End outer loop
    254                 break
    255             if line[:2] == "--":
    256                 terminator = line.strip()
    257                 if terminator in (nextpart, lastpart):
    258                     break
    259             lines.append(line)
    260         # Done with part.
    261         if data is None:
    262             continue
    263         if bytes < 0:
    264             if lines:
    265                 # Strip final line terminator
    266                 line = lines[-1]
    267                 if line[-2:] == "\r\n":
    268                     line = line[:-2]
    269                 elif line[-1:] == "\n":
    270                     line = line[:-1]
    271                 lines[-1] = line
    272                 data = "".join(lines)
    273         line = headers['content-disposition']
    274         if not line:
    275             continue
    276         key, params = parse_header(line)
    277         if key != 'form-data':
    278             continue
    279         if 'name' in params:
    280             name = params['name']
    281         else:
    282             continue
    283         if name in partdict:
    284             partdict[name].append(data)
    285         else:
    286             partdict[name] = [data]
    287 
    288     return partdict
    289 
    290 
    291 def _parseparam(s):
    292     while s[:1] == ';':
    293         s = s[1:]
    294         end = s.find(';')
    295         while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
    296             end = s.find(';', end + 1)
    297         if end < 0:
    298             end = len(s)
    299         f = s[:end]
    300         yield f.strip()
    301         s = s[end:]
    302 
    303 def parse_header(line):
    304     """Parse a Content-type like header.
    305 
    306     Return the main content-type and a dictionary of options.
    307 
    308     """
    309     parts = _parseparam(';' + line)
    310     key = parts.next()
    311     pdict = {}
    312     for p in parts:
    313         i = p.find('=')
    314         if i >= 0:
    315             name = p[:i].strip().lower()
    316             value = p[i+1:].strip()
    317             if len(value) >= 2 and value[0] == value[-1] == '"':
    318                 value = value[1:-1]
    319                 value = value.replace('\\\\', '\\').replace('\\"', '"')
    320             pdict[name] = value
    321     return key, pdict
    322 
    323 
    324 # Classes for field storage
    325 # =========================
    326 
    327 class MiniFieldStorage:
    328 
    329     """Like FieldStorage, for use when no file uploads are possible."""
    330 
    331     # Dummy attributes
    332     filename = None
    333     list = None
    334     type = None
    335     file = None
    336     type_options = {}
    337     disposition = None
    338     disposition_options = {}
    339     headers = {}
    340 
    341     def __init__(self, name, value):
    342         """Constructor from field name and value."""
    343         self.name = name
    344         self.value = value
    345         # self.file = StringIO(value)
    346 
    347     def __repr__(self):
    348         """Return printable representation."""
    349         return "MiniFieldStorage(%r, %r)" % (self.name, self.value)
    350 
    351 
    352 class FieldStorage:
    353 
    354     """Store a sequence of fields, reading multipart/form-data.
    355 
    356     This class provides naming, typing, files stored on disk, and
    357     more.  At the top level, it is accessible like a dictionary, whose
    358     keys are the field names.  (Note: None can occur as a field name.)
    359     The items are either a Python list (if there's multiple values) or
    360     another FieldStorage or MiniFieldStorage object.  If it's a single
    361     object, it has the following attributes:
    362 
    363     name: the field name, if specified; otherwise None
    364 
    365     filename: the filename, if specified; otherwise None; this is the
    366         client side filename, *not* the file name on which it is
    367         stored (that's a temporary file you don't deal with)
    368 
    369     value: the value as a *string*; for file uploads, this
    370         transparently reads the file every time you request the value
    371 
    372     file: the file(-like) object from which you can read the data;
    373         None if the data is stored a simple string
    374 
    375     type: the content-type, or None if not specified
    376 
    377     type_options: dictionary of options specified on the content-type
    378         line
    379 
    380     disposition: content-disposition, or None if not specified
    381 
    382     disposition_options: dictionary of corresponding options
    383 
    384     headers: a dictionary(-like) object (sometimes rfc822.Message or a
    385         subclass thereof) containing *all* headers
    386 
    387     The class is subclassable, mostly for the purpose of overriding
    388     the make_file() method, which is called internally to come up with
    389     a file open for reading and writing.  This makes it possible to
    390     override the default choice of storing all files in a temporary
    391     directory and unlinking them as soon as they have been opened.
    392 
    393     """
    394 
    395     def __init__(self, fp=None, headers=None, outerboundary="",
    396                  environ=os.environ, keep_blank_values=0, strict_parsing=0):
    397         """Constructor.  Read multipart/* until last part.
    398 
    399         Arguments, all optional:
    400 
    401         fp              : file pointer; default: sys.stdin
    402             (not used when the request method is GET)
    403 
    404         headers         : header dictionary-like object; default:
    405             taken from environ as per CGI spec
    406 
    407         outerboundary   : terminating multipart boundary
    408             (for internal use only)
    409 
    410         environ         : environment dictionary; default: os.environ
    411 
    412         keep_blank_values: flag indicating whether blank values in
    413             percent-encoded forms should be treated as blank strings.
    414             A true value indicates that blanks should be retained as
    415             blank strings.  The default false value indicates that
    416             blank values are to be ignored and treated as if they were
    417             not included.
    418 
    419         strict_parsing: flag indicating what to do with parsing errors.
    420             If false (the default), errors are silently ignored.
    421             If true, errors raise a ValueError exception.
    422 
    423         """
    424         method = 'GET'
    425         self.keep_blank_values = keep_blank_values
    426         self.strict_parsing = strict_parsing
    427         if 'REQUEST_METHOD' in environ:
    428             method = environ['REQUEST_METHOD'].upper()
    429         self.qs_on_post = None
    430         if method == 'GET' or method == 'HEAD':
    431             if 'QUERY_STRING' in environ:
    432                 qs = environ['QUERY_STRING']
    433             elif sys.argv[1:]:
    434                 qs = sys.argv[1]
    435             else:
    436                 qs = ""
    437             fp = StringIO(qs)
    438             if headers is None:
    439                 headers = {'content-type':
    440                            "application/x-www-form-urlencoded"}
    441         if headers is None:
    442             headers = {}
    443             if method == 'POST':
    444                 # Set default content-type for POST to what's traditional
    445                 headers['content-type'] = "application/x-www-form-urlencoded"
    446             if 'CONTENT_TYPE' in environ:
    447                 headers['content-type'] = environ['CONTENT_TYPE']
    448             if 'QUERY_STRING' in environ:
    449                 self.qs_on_post = environ['QUERY_STRING']
    450             if 'CONTENT_LENGTH' in environ:
    451                 headers['content-length'] = environ['CONTENT_LENGTH']
    452         self.fp = fp or sys.stdin
    453         self.headers = headers
    454         self.outerboundary = outerboundary
    455 
    456         # Process content-disposition header
    457         cdisp, pdict = "", {}
    458         if 'content-disposition' in self.headers:
    459             cdisp, pdict = parse_header(self.headers['content-disposition'])
    460         self.disposition = cdisp
    461         self.disposition_options = pdict
    462         self.name = None
    463         if 'name' in pdict:
    464             self.name = pdict['name']
    465         self.filename = None
    466         if 'filename' in pdict:
    467             self.filename = pdict['filename']
    468 
    469         # Process content-type header
    470         #
    471         # Honor any existing content-type header.  But if there is no
    472         # content-type header, use some sensible defaults.  Assume
    473         # outerboundary is "" at the outer level, but something non-false
    474         # inside a multi-part.  The default for an inner part is text/plain,
    475         # but for an outer part it should be urlencoded.  This should catch
    476         # bogus clients which erroneously forget to include a content-type
    477         # header.
    478         #
    479         # See below for what we do if there does exist a content-type header,
    480         # but it happens to be something we don't understand.
    481         if 'content-type' in self.headers:
    482             ctype, pdict = parse_header(self.headers['content-type'])
    483         elif self.outerboundary or method != 'POST':
    484             ctype, pdict = "text/plain", {}
    485         else:
    486             ctype, pdict = 'application/x-www-form-urlencoded', {}
    487         self.type = ctype
    488         self.type_options = pdict
    489         self.innerboundary = ""
    490         if 'boundary' in pdict:
    491             self.innerboundary = pdict['boundary']
    492         clen = -1
    493         if 'content-length' in self.headers:
    494             try:
    495                 clen = int(self.headers['content-length'])
    496             except ValueError:
    497                 pass
    498             if maxlen and clen > maxlen:
    499                 raise ValueError, 'Maximum content length exceeded'
    500         self.length = clen
    501 
    502         self.list = self.file = None
    503         self.done = 0
    504         if ctype == 'application/x-www-form-urlencoded':
    505             self.read_urlencoded()
    506         elif ctype[:10] == 'multipart/':
    507             self.read_multi(environ, keep_blank_values, strict_parsing)
    508         else:
    509             self.read_single()
    510 
    511     def __repr__(self):
    512         """Return a printable representation."""
    513         return "FieldStorage(%r, %r, %r)" % (
    514                 self.name, self.filename, self.value)
    515 
    516     def __iter__(self):
    517         return iter(self.keys())
    518 
    519     def __getattr__(self, name):
    520         if name != 'value':
    521             raise AttributeError, name
    522         if self.file:
    523             self.file.seek(0)
    524             value = self.file.read()
    525             self.file.seek(0)
    526         elif self.list is not None:
    527             value = self.list
    528         else:
    529             value = None
    530         return value
    531 
    532     def __getitem__(self, key):
    533         """Dictionary style indexing."""
    534         if self.list is None:
    535             raise TypeError, "not indexable"
    536         found = []
    537         for item in self.list:
    538             if item.name == key: found.append(item)
    539         if not found:
    540             raise KeyError, key
    541         if len(found) == 1:
    542             return found[0]
    543         else:
    544             return found
    545 
    546     def getvalue(self, key, default=None):
    547         """Dictionary style get() method, including 'value' lookup."""
    548         if key in self:
    549             value = self[key]
    550             if type(value) is type([]):
    551                 return map(attrgetter('value'), value)
    552             else:
    553                 return value.value
    554         else:
    555             return default
    556 
    557     def getfirst(self, key, default=None):
    558         """ Return the first value received."""
    559         if key in self:
    560             value = self[key]
    561             if type(value) is type([]):
    562                 return value[0].value
    563             else:
    564                 return value.value
    565         else:
    566             return default
    567 
    568     def getlist(self, key):
    569         """ Return list of received values."""
    570         if key in self:
    571             value = self[key]
    572             if type(value) is type([]):
    573                 return map(attrgetter('value'), value)
    574             else:
    575                 return [value.value]
    576         else:
    577             return []
    578 
    579     def keys(self):
    580         """Dictionary style keys() method."""
    581         if self.list is None:
    582             raise TypeError, "not indexable"
    583         return list(set(item.name for item in self.list))
    584 
    585     def has_key(self, key):
    586         """Dictionary style has_key() method."""
    587         if self.list is None:
    588             raise TypeError, "not indexable"
    589         return any(item.name == key for item in self.list)
    590 
    591     def __contains__(self, key):
    592         """Dictionary style __contains__ method."""
    593         if self.list is None:
    594             raise TypeError, "not indexable"
    595         return any(item.name == key for item in self.list)
    596 
    597     def __len__(self):
    598         """Dictionary style len(x) support."""
    599         return len(self.keys())
    600 
    601     def __nonzero__(self):
    602         return bool(self.list)
    603 
    604     def read_urlencoded(self):
    605         """Internal: read data in query string format."""
    606         qs = self.fp.read(self.length)
    607         if self.qs_on_post:
    608             qs += '&' + self.qs_on_post
    609         self.list = list = []
    610         for key, value in urlparse.parse_qsl(qs, self.keep_blank_values,
    611                                             self.strict_parsing):
    612             list.append(MiniFieldStorage(key, value))
    613         self.skip_lines()
    614 
    615     FieldStorageClass = None
    616 
    617     def read_multi(self, environ, keep_blank_values, strict_parsing):
    618         """Internal: read a part that is itself multipart."""
    619         ib = self.innerboundary
    620         if not valid_boundary(ib):
    621             raise ValueError, 'Invalid boundary in multipart form: %r' % (ib,)
    622         self.list = []
    623         if self.qs_on_post:
    624             for key, value in urlparse.parse_qsl(self.qs_on_post,
    625                                 self.keep_blank_values, self.strict_parsing):
    626                 self.list.append(MiniFieldStorage(key, value))
    627             FieldStorageClass = None
    628 
    629         klass = self.FieldStorageClass or self.__class__
    630         part = klass(self.fp, {}, ib,
    631                      environ, keep_blank_values, strict_parsing)
    632         # Throw first part away
    633         while not part.done:
    634             headers = rfc822.Message(self.fp)
    635             part = klass(self.fp, headers, ib,
    636                          environ, keep_blank_values, strict_parsing)
    637             self.list.append(part)
    638         self.skip_lines()
    639 
    640     def read_single(self):
    641         """Internal: read an atomic part."""
    642         if self.length >= 0:
    643             self.read_binary()
    644             self.skip_lines()
    645         else:
    646             self.read_lines()
    647         self.file.seek(0)
    648 
    649     bufsize = 8*1024            # I/O buffering size for copy to file
    650 
    651     def read_binary(self):
    652         """Internal: read binary data."""
    653         self.file = self.make_file('b')
    654         todo = self.length
    655         if todo >= 0:
    656             while todo > 0:
    657                 data = self.fp.read(min(todo, self.bufsize))
    658                 if not data:
    659                     self.done = -1
    660                     break
    661                 self.file.write(data)
    662                 todo = todo - len(data)
    663 
    664     def read_lines(self):
    665         """Internal: read lines until EOF or outerboundary."""
    666         self.file = self.__file = StringIO()
    667         if self.outerboundary:
    668             self.read_lines_to_outerboundary()
    669         else:
    670             self.read_lines_to_eof()
    671 
    672     def __write(self, line):
    673         if self.__file is not None:
    674             if self.__file.tell() + len(line) > 1000:
    675                 self.file = self.make_file('')
    676                 self.file.write(self.__file.getvalue())
    677                 self.__file = None
    678         self.file.write(line)
    679 
    680     def read_lines_to_eof(self):
    681         """Internal: read lines until EOF."""
    682         while 1:
    683             line = self.fp.readline(1<<16)
    684             if not line:
    685                 self.done = -1
    686                 break
    687             self.__write(line)
    688 
    689     def read_lines_to_outerboundary(self):
    690         """Internal: read lines until outerboundary."""
    691         next = "--" + self.outerboundary
    692         last = next + "--"
    693         delim = ""
    694         last_line_lfend = True
    695         while 1:
    696             line = self.fp.readline(1<<16)
    697             if not line:
    698                 self.done = -1
    699                 break
    700             if delim == "\r":
    701                 line = delim + line
    702                 delim = ""
    703             if line[:2] == "--" and last_line_lfend:
    704                 strippedline = line.strip()
    705                 if strippedline == next:
    706                     break
    707                 if strippedline == last:
    708                     self.done = 1
    709                     break
    710             odelim = delim
    711             if line[-2:] == "\r\n":
    712                 delim = "\r\n"
    713                 line = line[:-2]
    714                 last_line_lfend = True
    715             elif line[-1] == "\n":
    716                 delim = "\n"
    717                 line = line[:-1]
    718                 last_line_lfend = True
    719             elif line[-1] == "\r":
    720                 # We may interrupt \r\n sequences if they span the 2**16
    721                 # byte boundary
    722                 delim = "\r"
    723                 line = line[:-1]
    724                 last_line_lfend = False
    725             else:
    726                 delim = ""
    727                 last_line_lfend = False
    728             self.__write(odelim + line)
    729 
    730     def skip_lines(self):
    731         """Internal: skip lines until outer boundary if defined."""
    732         if not self.outerboundary or self.done:
    733             return
    734         next = "--" + self.outerboundary
    735         last = next + "--"
    736         last_line_lfend = True
    737         while 1:
    738             line = self.fp.readline(1<<16)
    739             if not line:
    740                 self.done = -1
    741                 break
    742             if line[:2] == "--" and last_line_lfend:
    743                 strippedline = line.strip()
    744                 if strippedline == next:
    745                     break
    746                 if strippedline == last:
    747                     self.done = 1
    748                     break
    749             last_line_lfend = line.endswith('\n')
    750 
    751     def make_file(self, binary=None):
    752         """Overridable: return a readable & writable file.
    753 
    754         The file will be used as follows:
    755         - data is written to it
    756         - seek(0)
    757         - data is read from it
    758 
    759         The 'binary' argument is unused -- the file is always opened
    760         in binary mode.
    761 
    762         This version opens a temporary file for reading and writing,
    763         and immediately deletes (unlinks) it.  The trick (on Unix!) is
    764         that the file can still be used, but it can't be opened by
    765         another process, and it will automatically be deleted when it
    766         is closed or when the current process terminates.
    767 
    768         If you want a more permanent file, you derive a class which
    769         overrides this method.  If you want a visible temporary file
    770         that is nevertheless automatically deleted when the script
    771         terminates, try defining a __del__ method in a derived class
    772         which unlinks the temporary files you have created.
    773 
    774         """
    775         import tempfile
    776         return tempfile.TemporaryFile("w+b")
    777 
    778 
    779 
    780 # Backwards Compatibility Classes
    781 # ===============================
    782 
    783 class FormContentDict(UserDict.UserDict):
    784     """Form content as dictionary with a list of values per field.
    785 
    786     form = FormContentDict()
    787 
    788     form[key] -> [value, value, ...]
    789     key in form -> Boolean
    790     form.keys() -> [key, key, ...]
    791     form.values() -> [[val, val, ...], [val, val, ...], ...]
    792     form.items() ->  [(key, [val, val, ...]), (key, [val, val, ...]), ...]
    793     form.dict == {key: [val, val, ...], ...}
    794 
    795     """
    796     def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0):
    797         self.dict = self.data = parse(environ=environ,
    798                                       keep_blank_values=keep_blank_values,
    799                                       strict_parsing=strict_parsing)
    800         self.query_string = environ['QUERY_STRING']
    801 
    802 
    803 class SvFormContentDict(FormContentDict):
    804     """Form content as dictionary expecting a single value per field.
    805 
    806     If you only expect a single value for each field, then form[key]
    807     will return that single value.  It will raise an IndexError if
    808     that expectation is not true.  If you expect a field to have
    809     possible multiple values, than you can use form.getlist(key) to
    810     get all of the values.  values() and items() are a compromise:
    811     they return single strings where there is a single value, and
    812     lists of strings otherwise.
    813 
    814     """
    815     def __getitem__(self, key):
    816         if len(self.dict[key]) > 1:
    817             raise IndexError, 'expecting a single value'
    818         return self.dict[key][0]
    819     def getlist(self, key):
    820         return self.dict[key]
    821     def values(self):
    822         result = []
    823         for value in self.dict.values():
    824             if len(value) == 1:
    825                 result.append(value[0])
    826             else: result.append(value)
    827         return result
    828     def items(self):
    829         result = []
    830         for key, value in self.dict.items():
    831             if len(value) == 1:
    832                 result.append((key, value[0]))
    833             else: result.append((key, value))
    834         return result
    835 
    836 
    837 class InterpFormContentDict(SvFormContentDict):
    838     """This class is present for backwards compatibility only."""
    839     def __getitem__(self, key):
    840         v = SvFormContentDict.__getitem__(self, key)
    841         if v[0] in '0123456789+-.':
    842             try: return int(v)
    843             except ValueError:
    844                 try: return float(v)
    845                 except ValueError: pass
    846         return v.strip()
    847     def values(self):
    848         result = []
    849         for key in self.keys():
    850             try:
    851                 result.append(self[key])
    852             except IndexError:
    853                 result.append(self.dict[key])
    854         return result
    855     def items(self):
    856         result = []
    857         for key in self.keys():
    858             try:
    859                 result.append((key, self[key]))
    860             except IndexError:
    861                 result.append((key, self.dict[key]))
    862         return result
    863 
    864 
    865 class FormContent(FormContentDict):
    866     """This class is present for backwards compatibility only."""
    867     def values(self, key):
    868         if key in self.dict :return self.dict[key]
    869         else: return None
    870     def indexed_value(self, key, location):
    871         if key in self.dict:
    872             if len(self.dict[key]) > location:
    873                 return self.dict[key][location]
    874             else: return None
    875         else: return None
    876     def value(self, key):
    877         if key in self.dict: return self.dict[key][0]
    878         else: return None
    879     def length(self, key):
    880         return len(self.dict[key])
    881     def stripped(self, key):
    882         if key in self.dict: return self.dict[key][0].strip()
    883         else: return None
    884     def pars(self):
    885         return self.dict
    886 
    887 
    888 # Test/debug code
    889 # ===============
    890 
    891 def test(environ=os.environ):
    892     """Robust test CGI script, usable as main program.
    893 
    894     Write minimal HTTP headers and dump all information provided to
    895     the script in HTML form.
    896 
    897     """
    898     print "Content-type: text/html"
    899     print
    900     sys.stderr = sys.stdout
    901     try:
    902         form = FieldStorage()   # Replace with other classes to test those
    903         print_directory()
    904         print_arguments()
    905         print_form(form)
    906         print_environ(environ)
    907         print_environ_usage()
    908         def f():
    909             exec "testing print_exception() -- <I>italics?</I>"
    910         def g(f=f):
    911             f()
    912         print "<H3>What follows is a test, not an actual exception:</H3>"
    913         g()
    914     except:
    915         print_exception()
    916 
    917     print "<H1>Second try with a small maxlen...</H1>"
    918 
    919     global maxlen
    920     maxlen = 50
    921     try:
    922         form = FieldStorage()   # Replace with other classes to test those
    923         print_directory()
    924         print_arguments()
    925         print_form(form)
    926         print_environ(environ)
    927     except:
    928         print_exception()
    929 
    930 def print_exception(type=None, value=None, tb=None, limit=None):
    931     if type is None:
    932         type, value, tb = sys.exc_info()
    933     import traceback
    934     print
    935     print "<H3>Traceback (most recent call last):</H3>"
    936     list = traceback.format_tb(tb, limit) + \
    937            traceback.format_exception_only(type, value)
    938     print "<PRE>%s<B>%s</B></PRE>" % (
    939         escape("".join(list[:-1])),
    940         escape(list[-1]),
    941         )
    942     del tb
    943 
    944 def print_environ(environ=os.environ):
    945     """Dump the shell environment as HTML."""
    946     keys = environ.keys()
    947     keys.sort()
    948     print
    949     print "<H3>Shell Environment:</H3>"
    950     print "<DL>"
    951     for key in keys:
    952         print "<DT>", escape(key), "<DD>", escape(environ[key])
    953     print "</DL>"
    954     print
    955 
    956 def print_form(form):
    957     """Dump the contents of a form as HTML."""
    958     keys = form.keys()
    959     keys.sort()
    960     print
    961     print "<H3>Form Contents:</H3>"
    962     if not keys:
    963         print "<P>No form fields."
    964     print "<DL>"
    965     for key in keys:
    966         print "<DT>" + escape(key) + ":",
    967         value = form[key]
    968         print "<i>" + escape(repr(type(value))) + "</i>"
    969         print "<DD>" + escape(repr(value))
    970     print "</DL>"
    971     print
    972 
    973 def print_directory():
    974     """Dump the current directory as HTML."""
    975     print
    976     print "<H3>Current Working Directory:</H3>"
    977     try:
    978         pwd = os.getcwd()
    979     except os.error, msg:
    980         print "os.error:", escape(str(msg))
    981     else:
    982         print escape(pwd)
    983     print
    984 
    985 def print_arguments():
    986     print
    987     print "<H3>Command Line Arguments:</H3>"
    988     print
    989     print sys.argv
    990     print
    991 
    992 def print_environ_usage():
    993     """Dump a list of environment variables used by CGI as HTML."""
    994     print """
    995 <H3>These environment variables could have been set:</H3>
    996 <UL>
    997 <LI>AUTH_TYPE
    998 <LI>CONTENT_LENGTH
    999 <LI>CONTENT_TYPE
   1000 <LI>DATE_GMT
   1001 <LI>DATE_LOCAL
   1002 <LI>DOCUMENT_NAME
   1003 <LI>DOCUMENT_ROOT
   1004 <LI>DOCUMENT_URI
   1005 <LI>GATEWAY_INTERFACE
   1006 <LI>LAST_MODIFIED
   1007 <LI>PATH
   1008 <LI>PATH_INFO
   1009 <LI>PATH_TRANSLATED
   1010 <LI>QUERY_STRING
   1011 <LI>REMOTE_ADDR
   1012 <LI>REMOTE_HOST
   1013 <LI>REMOTE_IDENT
   1014 <LI>REMOTE_USER
   1015 <LI>REQUEST_METHOD
   1016 <LI>SCRIPT_NAME
   1017 <LI>SERVER_NAME
   1018 <LI>SERVER_PORT
   1019 <LI>SERVER_PROTOCOL
   1020 <LI>SERVER_ROOT
   1021 <LI>SERVER_SOFTWARE
   1022 </UL>
   1023 In addition, HTTP headers sent by the server may be passed in the
   1024 environment as well.  Here are some common variable names:
   1025 <UL>
   1026 <LI>HTTP_ACCEPT
   1027 <LI>HTTP_CONNECTION
   1028 <LI>HTTP_HOST
   1029 <LI>HTTP_PRAGMA
   1030 <LI>HTTP_REFERER
   1031 <LI>HTTP_USER_AGENT
   1032 </UL>
   1033 """
   1034 
   1035 
   1036 # Utilities
   1037 # =========
   1038 
   1039 def escape(s, quote=None):
   1040     '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
   1041     If the optional flag quote is true, the quotation mark character (")
   1042     is also translated.'''
   1043     s = s.replace("&", "&amp;") # Must be done first!
   1044     s = s.replace("<", "&lt;")
   1045     s = s.replace(">", "&gt;")
   1046     if quote:
   1047         s = s.replace('"', "&quot;")
   1048     return s
   1049 
   1050 def valid_boundary(s, _vb_pattern="^[ -~]{0,200}[!-~]$"):
   1051     import re
   1052     return re.match(_vb_pattern, s)
   1053 
   1054 # Invoke mainline
   1055 # ===============
   1056 
   1057 # Call test() when this file is run as a script (not imported as a module)
   1058 if __name__ == '__main__':
   1059     test()
   1060