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