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