Home | History | Annotate | Download | only in python2.7
      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 compatiblity.
    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 line[:2] == "--" and last_line_lfend:
    701                 strippedline = line.strip()
    702                 if strippedline == next:
    703                     break
    704                 if strippedline == last:
    705                     self.done = 1
    706                     break
    707             odelim = delim
    708             if line[-2:] == "\r\n":
    709                 delim = "\r\n"
    710                 line = line[:-2]
    711                 last_line_lfend = True
    712             elif line[-1] == "\n":
    713                 delim = "\n"
    714                 line = line[:-1]
    715                 last_line_lfend = True
    716             else:
    717                 delim = ""
    718                 last_line_lfend = False
    719             self.__write(odelim + line)
    720 
    721     def skip_lines(self):
    722         """Internal: skip lines until outer boundary if defined."""
    723         if not self.outerboundary or self.done:
    724             return
    725         next = "--" + self.outerboundary
    726         last = next + "--"
    727         last_line_lfend = True
    728         while 1:
    729             line = self.fp.readline(1<<16)
    730             if not line:
    731                 self.done = -1
    732                 break
    733             if line[:2] == "--" and last_line_lfend:
    734                 strippedline = line.strip()
    735                 if strippedline == next:
    736                     break
    737                 if strippedline == last:
    738                     self.done = 1
    739                     break
    740             last_line_lfend = line.endswith('\n')
    741 
    742     def make_file(self, binary=None):
    743         """Overridable: return a readable & writable file.
    744 
    745         The file will be used as follows:
    746         - data is written to it
    747         - seek(0)
    748         - data is read from it
    749 
    750         The 'binary' argument is unused -- the file is always opened
    751         in binary mode.
    752 
    753         This version opens a temporary file for reading and writing,
    754         and immediately deletes (unlinks) it.  The trick (on Unix!) is
    755         that the file can still be used, but it can't be opened by
    756         another process, and it will automatically be deleted when it
    757         is closed or when the current process terminates.
    758 
    759         If you want a more permanent file, you derive a class which
    760         overrides this method.  If you want a visible temporary file
    761         that is nevertheless automatically deleted when the script
    762         terminates, try defining a __del__ method in a derived class
    763         which unlinks the temporary files you have created.
    764 
    765         """
    766         import tempfile
    767         return tempfile.TemporaryFile("w+b")
    768 
    769 
    770 
    771 # Backwards Compatibility Classes
    772 # ===============================
    773 
    774 class FormContentDict(UserDict.UserDict):
    775     """Form content as dictionary with a list of values per field.
    776 
    777     form = FormContentDict()
    778 
    779     form[key] -> [value, value, ...]
    780     key in form -> Boolean
    781     form.keys() -> [key, key, ...]
    782     form.values() -> [[val, val, ...], [val, val, ...], ...]
    783     form.items() ->  [(key, [val, val, ...]), (key, [val, val, ...]), ...]
    784     form.dict == {key: [val, val, ...], ...}
    785 
    786     """
    787     def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0):
    788         self.dict = self.data = parse(environ=environ,
    789                                       keep_blank_values=keep_blank_values,
    790                                       strict_parsing=strict_parsing)
    791         self.query_string = environ['QUERY_STRING']
    792 
    793 
    794 class SvFormContentDict(FormContentDict):
    795     """Form content as dictionary expecting a single value per field.
    796 
    797     If you only expect a single value for each field, then form[key]
    798     will return that single value.  It will raise an IndexError if
    799     that expectation is not true.  If you expect a field to have
    800     possible multiple values, than you can use form.getlist(key) to
    801     get all of the values.  values() and items() are a compromise:
    802     they return single strings where there is a single value, and
    803     lists of strings otherwise.
    804 
    805     """
    806     def __getitem__(self, key):
    807         if len(self.dict[key]) > 1:
    808             raise IndexError, 'expecting a single value'
    809         return self.dict[key][0]
    810     def getlist(self, key):
    811         return self.dict[key]
    812     def values(self):
    813         result = []
    814         for value in self.dict.values():
    815             if len(value) == 1:
    816                 result.append(value[0])
    817             else: result.append(value)
    818         return result
    819     def items(self):
    820         result = []
    821         for key, value in self.dict.items():
    822             if len(value) == 1:
    823                 result.append((key, value[0]))
    824             else: result.append((key, value))
    825         return result
    826 
    827 
    828 class InterpFormContentDict(SvFormContentDict):
    829     """This class is present for backwards compatibility only."""
    830     def __getitem__(self, key):
    831         v = SvFormContentDict.__getitem__(self, key)
    832         if v[0] in '0123456789+-.':
    833             try: return int(v)
    834             except ValueError:
    835                 try: return float(v)
    836                 except ValueError: pass
    837         return v.strip()
    838     def values(self):
    839         result = []
    840         for key in self.keys():
    841             try:
    842                 result.append(self[key])
    843             except IndexError:
    844                 result.append(self.dict[key])
    845         return result
    846     def items(self):
    847         result = []
    848         for key in self.keys():
    849             try:
    850                 result.append((key, self[key]))
    851             except IndexError:
    852                 result.append((key, self.dict[key]))
    853         return result
    854 
    855 
    856 class FormContent(FormContentDict):
    857     """This class is present for backwards compatibility only."""
    858     def values(self, key):
    859         if key in self.dict :return self.dict[key]
    860         else: return None
    861     def indexed_value(self, key, location):
    862         if key in self.dict:
    863             if len(self.dict[key]) > location:
    864                 return self.dict[key][location]
    865             else: return None
    866         else: return None
    867     def value(self, key):
    868         if key in self.dict: return self.dict[key][0]
    869         else: return None
    870     def length(self, key):
    871         return len(self.dict[key])
    872     def stripped(self, key):
    873         if key in self.dict: return self.dict[key][0].strip()
    874         else: return None
    875     def pars(self):
    876         return self.dict
    877 
    878 
    879 # Test/debug code
    880 # ===============
    881 
    882 def test(environ=os.environ):
    883     """Robust test CGI script, usable as main program.
    884 
    885     Write minimal HTTP headers and dump all information provided to
    886     the script in HTML form.
    887 
    888     """
    889     print "Content-type: text/html"
    890     print
    891     sys.stderr = sys.stdout
    892     try:
    893         form = FieldStorage()   # Replace with other classes to test those
    894         print_directory()
    895         print_arguments()
    896         print_form(form)
    897         print_environ(environ)
    898         print_environ_usage()
    899         def f():
    900             exec "testing print_exception() -- <I>italics?</I>"
    901         def g(f=f):
    902             f()
    903         print "<H3>What follows is a test, not an actual exception:</H3>"
    904         g()
    905     except:
    906         print_exception()
    907 
    908     print "<H1>Second try with a small maxlen...</H1>"
    909 
    910     global maxlen
    911     maxlen = 50
    912     try:
    913         form = FieldStorage()   # Replace with other classes to test those
    914         print_directory()
    915         print_arguments()
    916         print_form(form)
    917         print_environ(environ)
    918     except:
    919         print_exception()
    920 
    921 def print_exception(type=None, value=None, tb=None, limit=None):
    922     if type is None:
    923         type, value, tb = sys.exc_info()
    924     import traceback
    925     print
    926     print "<H3>Traceback (most recent call last):</H3>"
    927     list = traceback.format_tb(tb, limit) + \
    928            traceback.format_exception_only(type, value)
    929     print "<PRE>%s<B>%s</B></PRE>" % (
    930         escape("".join(list[:-1])),
    931         escape(list[-1]),
    932         )
    933     del tb
    934 
    935 def print_environ(environ=os.environ):
    936     """Dump the shell environment as HTML."""
    937     keys = environ.keys()
    938     keys.sort()
    939     print
    940     print "<H3>Shell Environment:</H3>"
    941     print "<DL>"
    942     for key in keys:
    943         print "<DT>", escape(key), "<DD>", escape(environ[key])
    944     print "</DL>"
    945     print
    946 
    947 def print_form(form):
    948     """Dump the contents of a form as HTML."""
    949     keys = form.keys()
    950     keys.sort()
    951     print
    952     print "<H3>Form Contents:</H3>"
    953     if not keys:
    954         print "<P>No form fields."
    955     print "<DL>"
    956     for key in keys:
    957         print "<DT>" + escape(key) + ":",
    958         value = form[key]
    959         print "<i>" + escape(repr(type(value))) + "</i>"
    960         print "<DD>" + escape(repr(value))
    961     print "</DL>"
    962     print
    963 
    964 def print_directory():
    965     """Dump the current directory as HTML."""
    966     print
    967     print "<H3>Current Working Directory:</H3>"
    968     try:
    969         pwd = os.getcwd()
    970     except os.error, msg:
    971         print "os.error:", escape(str(msg))
    972     else:
    973         print escape(pwd)
    974     print
    975 
    976 def print_arguments():
    977     print
    978     print "<H3>Command Line Arguments:</H3>"
    979     print
    980     print sys.argv
    981     print
    982 
    983 def print_environ_usage():
    984     """Dump a list of environment variables used by CGI as HTML."""
    985     print """
    986 <H3>These environment variables could have been set:</H3>
    987 <UL>
    988 <LI>AUTH_TYPE
    989 <LI>CONTENT_LENGTH
    990 <LI>CONTENT_TYPE
    991 <LI>DATE_GMT
    992 <LI>DATE_LOCAL
    993 <LI>DOCUMENT_NAME
    994 <LI>DOCUMENT_ROOT
    995 <LI>DOCUMENT_URI
    996 <LI>GATEWAY_INTERFACE
    997 <LI>LAST_MODIFIED
    998 <LI>PATH
    999 <LI>PATH_INFO
   1000 <LI>PATH_TRANSLATED
   1001 <LI>QUERY_STRING
   1002 <LI>REMOTE_ADDR
   1003 <LI>REMOTE_HOST
   1004 <LI>REMOTE_IDENT
   1005 <LI>REMOTE_USER
   1006 <LI>REQUEST_METHOD
   1007 <LI>SCRIPT_NAME
   1008 <LI>SERVER_NAME
   1009 <LI>SERVER_PORT
   1010 <LI>SERVER_PROTOCOL
   1011 <LI>SERVER_ROOT
   1012 <LI>SERVER_SOFTWARE
   1013 </UL>
   1014 In addition, HTTP headers sent by the server may be passed in the
   1015 environment as well.  Here are some common variable names:
   1016 <UL>
   1017 <LI>HTTP_ACCEPT
   1018 <LI>HTTP_CONNECTION
   1019 <LI>HTTP_HOST
   1020 <LI>HTTP_PRAGMA
   1021 <LI>HTTP_REFERER
   1022 <LI>HTTP_USER_AGENT
   1023 </UL>
   1024 """
   1025 
   1026 
   1027 # Utilities
   1028 # =========
   1029 
   1030 def escape(s, quote=None):
   1031     '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
   1032     If the optional flag quote is true, the quotation mark character (")
   1033     is also translated.'''
   1034     s = s.replace("&", "&amp;") # Must be done first!
   1035     s = s.replace("<", "&lt;")
   1036     s = s.replace(">", "&gt;")
   1037     if quote:
   1038         s = s.replace('"', "&quot;")
   1039     return s
   1040 
   1041 def valid_boundary(s, _vb_pattern="^[ -~]{0,200}[!-~]$"):
   1042     import re
   1043     return re.match(_vb_pattern, s)
   1044 
   1045 # Invoke mainline
   1046 # ===============
   1047 
   1048 # Call test() when this file is run as a script (not imported as a module)
   1049 if __name__ == '__main__':
   1050     test()
   1051