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