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