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