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