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