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