source: titan/mediathek/localhoster/lib/python2.7/cgi.py

Last change on this file was 40661, checked in by obi, 5 years ago

reset

File size: 33.5 KB
Line 
1#! /usr/bin/env python
2
3"""Support module for CGI (Common Gateway Interface) scripts.
4
5This module defines a number of utilities for use by CGI scripts
6written in Python.
7"""
8
9# XXX Perhaps there should be a slimmed version that doesn't contain
10# all those backwards compatible and debugging classes and functions?
11
12# History
13# -------
14#
15# Michael McLay started this module.  Steve Majewski changed the
16# interface to SvFormContentDict and FormContentDict.  The multipart
17# parsing was inspired by code submitted by Andreas Paepcke.  Guido van
18# Rossum rewrote, reformatted and documented the module and is currently
19# responsible for its maintenance.
20#
21
22__version__ = "2.6"
23
24
25# Imports
26# =======
27
28from operator import attrgetter
29import sys
30import os
31import UserDict
32import urlparse
33
34from warnings import filterwarnings, catch_warnings, warn
35with catch_warnings():
36    if sys.py3kwarning:
37        filterwarnings("ignore", ".*mimetools has been removed",
38                       DeprecationWarning)
39        filterwarnings("ignore", ".*rfc822 has been removed",
40                       DeprecationWarning)
41    import mimetools
42    import rfc822
43
44try:
45    from cStringIO import StringIO
46except ImportError:
47    from StringIO import StringIO
48
49__all__ = ["MiniFieldStorage", "FieldStorage", "FormContentDict",
50           "SvFormContentDict", "InterpFormContentDict", "FormContent",
51           "parse", "parse_qs", "parse_qsl", "parse_multipart",
52           "parse_header", "print_exception", "print_environ",
53           "print_form", "print_directory", "print_arguments",
54           "print_environ_usage", "escape"]
55
56# Logging support
57# ===============
58
59logfile = ""            # Filename to log to, if not empty
60logfp = None            # File object to log to, if not None
61
62def initlog(*allargs):
63    """Write a log message, if there is a log file.
64
65    Even though this function is called initlog(), you should always
66    use log(); log is a variable that is set either to initlog
67    (initially), to dolog (once the log file has been opened), or to
68    nolog (when logging is disabled).
69
70    The first argument is a format string; the remaining arguments (if
71    any) are arguments to the % operator, so e.g.
72        log("%s: %s", "a", "b")
73    will write "a: b" to the log file, followed by a newline.
74
75    If the global logfp is not None, it should be a file object to
76    which log data is written.
77
78    If the global logfp is None, the global logfile may be a string
79    giving a filename to open, in append mode.  This file should be
80    world writable!!!  If the file can't be opened, logging is
81    silently disabled (since there is no safe place where we could
82    send an error message).
83
84    """
85    global logfp, log
86    if logfile and not logfp:
87        try:
88            logfp = open(logfile, "a")
89        except IOError:
90            pass
91    if not logfp:
92        log = nolog
93    else:
94        log = dolog
95    log(*allargs)
96
97def dolog(fmt, *args):
98    """Write a log message to the log file.  See initlog() for docs."""
99    logfp.write(fmt%args + "\n")
100
101def nolog(*allargs):
102    """Dummy function, assigned to log when logging is disabled."""
103    pass
104
105log = initlog           # The current logging function
106
107
108# Parsing functions
109# =================
110
111# Maximum input we will accept when REQUEST_METHOD is POST
112# 0 ==> unlimited input
113maxlen = 0
114
115def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
116    """Parse a query in the environment or from a file (default stdin)
117
118        Arguments, all optional:
119
120        fp              : file pointer; default: sys.stdin
121
122        environ         : environment dictionary; default: os.environ
123
124        keep_blank_values: flag indicating whether blank values in
125            percent-encoded forms should be treated as blank strings.
126            A true value indicates that blanks should be retained as
127            blank strings.  The default false value indicates that
128            blank values are to be ignored and treated as if they were
129            not included.
130
131        strict_parsing: flag indicating what to do with parsing errors.
132            If false (the default), errors are silently ignored.
133            If true, errors raise a ValueError exception.
134    """
135    if fp is None:
136        fp = sys.stdin
137    if not 'REQUEST_METHOD' in environ:
138        environ['REQUEST_METHOD'] = 'GET'       # For testing stand-alone
139    if environ['REQUEST_METHOD'] == 'POST':
140        ctype, pdict = parse_header(environ['CONTENT_TYPE'])
141        if ctype == 'multipart/form-data':
142            return parse_multipart(fp, pdict)
143        elif ctype == 'application/x-www-form-urlencoded':
144            clength = int(environ['CONTENT_LENGTH'])
145            if maxlen and clength > maxlen:
146                raise ValueError, 'Maximum content length exceeded'
147            qs = fp.read(clength)
148        else:
149            qs = ''                     # Unknown content-type
150        if 'QUERY_STRING' in environ:
151            if qs: qs = qs + '&'
152            qs = qs + environ['QUERY_STRING']
153        elif sys.argv[1:]:
154            if qs: qs = qs + '&'
155            qs = qs + sys.argv[1]
156        environ['QUERY_STRING'] = qs    # XXX Shouldn't, really
157    elif 'QUERY_STRING' in environ:
158        qs = environ['QUERY_STRING']
159    else:
160        if sys.argv[1:]:
161            qs = sys.argv[1]
162        else:
163            qs = ""
164        environ['QUERY_STRING'] = qs    # XXX Shouldn't, really
165    return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
166
167
168# parse query string function called from urlparse,
169# this is done in order to maintain backward compatiblity.
170
171def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
172    """Parse a query given as a string argument."""
173    warn("cgi.parse_qs is deprecated, use urlparse.parse_qs instead",
174         PendingDeprecationWarning, 2)
175    return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
176
177
178def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
179    """Parse a query given as a string argument."""
180    warn("cgi.parse_qsl is deprecated, use urlparse.parse_qsl instead",
181         PendingDeprecationWarning, 2)
182    return urlparse.parse_qsl(qs, keep_blank_values, strict_parsing)
183
184def parse_multipart(fp, pdict):
185    """Parse multipart input.
186
187    Arguments:
188    fp   : input file
189    pdict: dictionary containing other parameters of content-type header
190
191    Returns a dictionary just like parse_qs(): keys are the field names, each
192    value is a list of values for that field.  This is easy to use but not
193    much good if you are expecting megabytes to be uploaded -- in that case,
194    use the FieldStorage class instead which is much more flexible.  Note
195    that content-type is the raw, unparsed contents of the content-type
196    header.
197
198    XXX This does not parse nested multipart parts -- use FieldStorage for
199    that.
200
201    XXX This should really be subsumed by FieldStorage altogether -- no
202    point in having two implementations of the same parsing algorithm.
203    Also, FieldStorage protects itself better against certain DoS attacks
204    by limiting the size of the data read in one chunk.  The API here
205    does not support that kind of protection.  This also affects parse()
206    since it can call parse_multipart().
207
208    """
209    boundary = ""
210    if 'boundary' in pdict:
211        boundary = pdict['boundary']
212    if not valid_boundary(boundary):
213        raise ValueError,  ('Invalid boundary in multipart form: %r'
214                            % (boundary,))
215
216    nextpart = "--" + boundary
217    lastpart = "--" + boundary + "--"
218    partdict = {}
219    terminator = ""
220
221    while terminator != lastpart:
222        bytes = -1
223        data = None
224        if terminator:
225            # At start of next part.  Read headers first.
226            headers = mimetools.Message(fp)
227            clength = headers.getheader('content-length')
228            if clength:
229                try:
230                    bytes = int(clength)
231                except ValueError:
232                    pass
233            if bytes > 0:
234                if maxlen and bytes > maxlen:
235                    raise ValueError, 'Maximum content length exceeded'
236                data = fp.read(bytes)
237            else:
238                data = ""
239        # Read lines until end of part.
240        lines = []
241        while 1:
242            line = fp.readline()
243            if not line:
244                terminator = lastpart # End outer loop
245                break
246            if line[:2] == "--":
247                terminator = line.strip()
248                if terminator in (nextpart, lastpart):
249                    break
250            lines.append(line)
251        # Done with part.
252        if data is None:
253            continue
254        if bytes < 0:
255            if lines:
256                # Strip final line terminator
257                line = lines[-1]
258                if line[-2:] == "\r\n":
259                    line = line[:-2]
260                elif line[-1:] == "\n":
261                    line = line[:-1]
262                lines[-1] = line
263                data = "".join(lines)
264        line = headers['content-disposition']
265        if not line:
266            continue
267        key, params = parse_header(line)
268        if key != 'form-data':
269            continue
270        if 'name' in params:
271            name = params['name']
272        else:
273            continue
274        if name in partdict:
275            partdict[name].append(data)
276        else:
277            partdict[name] = [data]
278
279    return partdict
280
281
282def _parseparam(s):
283    while s[:1] == ';':
284        s = s[1:]
285        end = s.find(';')
286        while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
287            end = s.find(';', end + 1)
288        if end < 0:
289            end = len(s)
290        f = s[:end]
291        yield f.strip()
292        s = s[end:]
293
294def parse_header(line):
295    """Parse a Content-type like header.
296
297    Return the main content-type and a dictionary of options.
298
299    """
300    parts = _parseparam(';' + line)
301    key = parts.next()
302    pdict = {}
303    for p in parts:
304        i = p.find('=')
305        if i >= 0:
306            name = p[:i].strip().lower()
307            value = p[i+1:].strip()
308            if len(value) >= 2 and value[0] == value[-1] == '"':
309                value = value[1:-1]
310                value = value.replace('\\\\', '\\').replace('\\"', '"')
311            pdict[name] = value
312    return key, pdict
313
314
315# Classes for field storage
316# =========================
317
318class MiniFieldStorage:
319
320    """Like FieldStorage, for use when no file uploads are possible."""
321
322    # Dummy attributes
323    filename = None
324    list = None
325    type = None
326    file = None
327    type_options = {}
328    disposition = None
329    disposition_options = {}
330    headers = {}
331
332    def __init__(self, name, value):
333        """Constructor from field name and value."""
334        self.name = name
335        self.value = value
336        # self.file = StringIO(value)
337
338    def __repr__(self):
339        """Return printable representation."""
340        return "MiniFieldStorage(%r, %r)" % (self.name, self.value)
341
342
343class FieldStorage:
344
345    """Store a sequence of fields, reading multipart/form-data.
346
347    This class provides naming, typing, files stored on disk, and
348    more.  At the top level, it is accessible like a dictionary, whose
349    keys are the field names.  (Note: None can occur as a field name.)
350    The items are either a Python list (if there's multiple values) or
351    another FieldStorage or MiniFieldStorage object.  If it's a single
352    object, it has the following attributes:
353
354    name: the field name, if specified; otherwise None
355
356    filename: the filename, if specified; otherwise None; this is the
357        client side filename, *not* the file name on which it is
358        stored (that's a temporary file you don't deal with)
359
360    value: the value as a *string*; for file uploads, this
361        transparently reads the file every time you request the value
362
363    file: the file(-like) object from which you can read the data;
364        None if the data is stored a simple string
365
366    type: the content-type, or None if not specified
367
368    type_options: dictionary of options specified on the content-type
369        line
370
371    disposition: content-disposition, or None if not specified
372
373    disposition_options: dictionary of corresponding options
374
375    headers: a dictionary(-like) object (sometimes rfc822.Message or a
376        subclass thereof) containing *all* headers
377
378    The class is subclassable, mostly for the purpose of overriding
379    the make_file() method, which is called internally to come up with
380    a file open for reading and writing.  This makes it possible to
381    override the default choice of storing all files in a temporary
382    directory and unlinking them as soon as they have been opened.
383
384    """
385
386    def __init__(self, fp=None, headers=None, outerboundary="",
387                 environ=os.environ, keep_blank_values=0, strict_parsing=0):
388        """Constructor.  Read multipart/* until last part.
389
390        Arguments, all optional:
391
392        fp              : file pointer; default: sys.stdin
393            (not used when the request method is GET)
394
395        headers         : header dictionary-like object; default:
396            taken from environ as per CGI spec
397
398        outerboundary   : terminating multipart boundary
399            (for internal use only)
400
401        environ         : environment dictionary; default: os.environ
402
403        keep_blank_values: flag indicating whether blank values in
404            percent-encoded forms should be treated as blank strings.
405            A true value indicates that blanks should be retained as
406            blank strings.  The default false value indicates that
407            blank values are to be ignored and treated as if they were
408            not included.
409
410        strict_parsing: flag indicating what to do with parsing errors.
411            If false (the default), errors are silently ignored.
412            If true, errors raise a ValueError exception.
413
414        """
415        method = 'GET'
416        self.keep_blank_values = keep_blank_values
417        self.strict_parsing = strict_parsing
418        if 'REQUEST_METHOD' in environ:
419            method = environ['REQUEST_METHOD'].upper()
420        self.qs_on_post = None
421        if method == 'GET' or method == 'HEAD':
422            if 'QUERY_STRING' in environ:
423                qs = environ['QUERY_STRING']
424            elif sys.argv[1:]:
425                qs = sys.argv[1]
426            else:
427                qs = ""
428            fp = StringIO(qs)
429            if headers is None:
430                headers = {'content-type':
431                           "application/x-www-form-urlencoded"}
432        if headers is None:
433            headers = {}
434            if method == 'POST':
435                # Set default content-type for POST to what's traditional
436                headers['content-type'] = "application/x-www-form-urlencoded"
437            if 'CONTENT_TYPE' in environ:
438                headers['content-type'] = environ['CONTENT_TYPE']
439            if 'QUERY_STRING' in environ:
440                self.qs_on_post = environ['QUERY_STRING']
441            if 'CONTENT_LENGTH' in environ:
442                headers['content-length'] = environ['CONTENT_LENGTH']
443        self.fp = fp or sys.stdin
444        self.headers = headers
445        self.outerboundary = outerboundary
446
447        # Process content-disposition header
448        cdisp, pdict = "", {}
449        if 'content-disposition' in self.headers:
450            cdisp, pdict = parse_header(self.headers['content-disposition'])
451        self.disposition = cdisp
452        self.disposition_options = pdict
453        self.name = None
454        if 'name' in pdict:
455            self.name = pdict['name']
456        self.filename = None
457        if 'filename' in pdict:
458            self.filename = pdict['filename']
459
460        # Process content-type header
461        #
462        # Honor any existing content-type header.  But if there is no
463        # content-type header, use some sensible defaults.  Assume
464        # outerboundary is "" at the outer level, but something non-false
465        # inside a multi-part.  The default for an inner part is text/plain,
466        # but for an outer part it should be urlencoded.  This should catch
467        # bogus clients which erroneously forget to include a content-type
468        # header.
469        #
470        # See below for what we do if there does exist a content-type header,
471        # but it happens to be something we don't understand.
472        if 'content-type' in self.headers:
473            ctype, pdict = parse_header(self.headers['content-type'])
474        elif self.outerboundary or method != 'POST':
475            ctype, pdict = "text/plain", {}
476        else:
477            ctype, pdict = 'application/x-www-form-urlencoded', {}
478        self.type = ctype
479        self.type_options = pdict
480        self.innerboundary = ""
481        if 'boundary' in pdict:
482            self.innerboundary = pdict['boundary']
483        clen = -1
484        if 'content-length' in self.headers:
485            try:
486                clen = int(self.headers['content-length'])
487            except ValueError:
488                pass
489            if maxlen and clen > maxlen:
490                raise ValueError, 'Maximum content length exceeded'
491        self.length = clen
492
493        self.list = self.file = None
494        self.done = 0
495        if ctype == 'application/x-www-form-urlencoded':
496            self.read_urlencoded()
497        elif ctype[:10] == 'multipart/':
498            self.read_multi(environ, keep_blank_values, strict_parsing)
499        else:
500            self.read_single()
501
502    def __repr__(self):
503        """Return a printable representation."""
504        return "FieldStorage(%r, %r, %r)" % (
505                self.name, self.filename, self.value)
506
507    def __iter__(self):
508        return iter(self.keys())
509
510    def __getattr__(self, name):
511        if name != 'value':
512            raise AttributeError, name
513        if self.file:
514            self.file.seek(0)
515            value = self.file.read()
516            self.file.seek(0)
517        elif self.list is not None:
518            value = self.list
519        else:
520            value = None
521        return value
522
523    def __getitem__(self, key):
524        """Dictionary style indexing."""
525        if self.list is None:
526            raise TypeError, "not indexable"
527        found = []
528        for item in self.list:
529            if item.name == key: found.append(item)
530        if not found:
531            raise KeyError, key
532        if len(found) == 1:
533            return found[0]
534        else:
535            return found
536
537    def getvalue(self, key, default=None):
538        """Dictionary style get() method, including 'value' lookup."""
539        if key in self:
540            value = self[key]
541            if type(value) is type([]):
542                return map(attrgetter('value'), value)
543            else:
544                return value.value
545        else:
546            return default
547
548    def getfirst(self, key, default=None):
549        """ Return the first value received."""
550        if key in self:
551            value = self[key]
552            if type(value) is type([]):
553                return value[0].value
554            else:
555                return value.value
556        else:
557            return default
558
559    def getlist(self, key):
560        """ Return list of received values."""
561        if key in self:
562            value = self[key]
563            if type(value) is type([]):
564                return map(attrgetter('value'), value)
565            else:
566                return [value.value]
567        else:
568            return []
569
570    def keys(self):
571        """Dictionary style keys() method."""
572        if self.list is None:
573            raise TypeError, "not indexable"
574        return list(set(item.name for item in self.list))
575
576    def has_key(self, key):
577        """Dictionary style has_key() method."""
578        if self.list is None:
579            raise TypeError, "not indexable"
580        return any(item.name == key for item in self.list)
581
582    def __contains__(self, key):
583        """Dictionary style __contains__ method."""
584        if self.list is None:
585            raise TypeError, "not indexable"
586        return any(item.name == key for item in self.list)
587
588    def __len__(self):
589        """Dictionary style len(x) support."""
590        return len(self.keys())
591
592    def __nonzero__(self):
593        return bool(self.list)
594
595    def read_urlencoded(self):
596        """Internal: read data in query string format."""
597        qs = self.fp.read(self.length)
598        if self.qs_on_post:
599            qs += '&' + self.qs_on_post
600        self.list = list = []
601        for key, value in urlparse.parse_qsl(qs, self.keep_blank_values,
602                                            self.strict_parsing):
603            list.append(MiniFieldStorage(key, value))
604        self.skip_lines()
605
606    FieldStorageClass = None
607
608    def read_multi(self, environ, keep_blank_values, strict_parsing):
609        """Internal: read a part that is itself multipart."""
610        ib = self.innerboundary
611        if not valid_boundary(ib):
612            raise ValueError, 'Invalid boundary in multipart form: %r' % (ib,)
613        self.list = []
614        if self.qs_on_post:
615            for key, value in urlparse.parse_qsl(self.qs_on_post,
616                                self.keep_blank_values, self.strict_parsing):
617                self.list.append(MiniFieldStorage(key, value))
618            FieldStorageClass = None
619
620        klass = self.FieldStorageClass or self.__class__
621        part = klass(self.fp, {}, ib,
622                     environ, keep_blank_values, strict_parsing)
623        # Throw first part away
624        while not part.done:
625            headers = rfc822.Message(self.fp)
626            part = klass(self.fp, headers, ib,
627                         environ, keep_blank_values, strict_parsing)
628            self.list.append(part)
629        self.skip_lines()
630
631    def read_single(self):
632        """Internal: read an atomic part."""
633        if self.length >= 0:
634            self.read_binary()
635            self.skip_lines()
636        else:
637            self.read_lines()
638        self.file.seek(0)
639
640    bufsize = 8*1024            # I/O buffering size for copy to file
641
642    def read_binary(self):
643        """Internal: read binary data."""
644        self.file = self.make_file('b')
645        todo = self.length
646        if todo >= 0:
647            while todo > 0:
648                data = self.fp.read(min(todo, self.bufsize))
649                if not data:
650                    self.done = -1
651                    break
652                self.file.write(data)
653                todo = todo - len(data)
654
655    def read_lines(self):
656        """Internal: read lines until EOF or outerboundary."""
657        self.file = self.__file = StringIO()
658        if self.outerboundary:
659            self.read_lines_to_outerboundary()
660        else:
661            self.read_lines_to_eof()
662
663    def __write(self, line):
664        if self.__file is not None:
665            if self.__file.tell() + len(line) > 1000:
666                self.file = self.make_file('')
667                self.file.write(self.__file.getvalue())
668                self.__file = None
669        self.file.write(line)
670
671    def read_lines_to_eof(self):
672        """Internal: read lines until EOF."""
673        while 1:
674            line = self.fp.readline(1<<16)
675            if not line:
676                self.done = -1
677                break
678            self.__write(line)
679
680    def read_lines_to_outerboundary(self):
681        """Internal: read lines until outerboundary."""
682        next = "--" + self.outerboundary
683        last = next + "--"
684        delim = ""
685        last_line_lfend = True
686        while 1:
687            line = self.fp.readline(1<<16)
688            if not line:
689                self.done = -1
690                break
691            if delim == "\r":
692                line = delim + line
693                delim = ""
694            if line[:2] == "--" and last_line_lfend:
695                strippedline = line.strip()
696                if strippedline == next:
697                    break
698                if strippedline == last:
699                    self.done = 1
700                    break
701            odelim = delim
702            if line[-2:] == "\r\n":
703                delim = "\r\n"
704                line = line[:-2]
705                last_line_lfend = True
706            elif line[-1] == "\n":
707                delim = "\n"
708                line = line[:-1]
709                last_line_lfend = True
710            elif line[-1] == "\r":
711                # We may interrupt \r\n sequences if they span the 2**16
712                # byte boundary
713                delim = "\r"
714                line = line[:-1]
715                last_line_lfend = False
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
774class 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
794class 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
828class 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
856class 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
882def 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
921def 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
935def 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
947def 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
964def 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
976def print_arguments():
977    print
978    print "<H3>Command Line Arguments:</H3>"
979    print
980    print sys.argv
981    print
982
983def 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>
1014In addition, HTTP headers sent by the server may be passed in the
1015environment 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
1030def 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
1041def 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)
1049if __name__ == '__main__':
1050    test()
Note: See TracBrowser for help on using the repository browser.