1#! /usr/local/bin/python 2 3# NOTE: the above "/usr/local/bin/python" is NOT a mistake. It is 4# intentionally NOT "/usr/bin/env python". On many systems 5# (e.g. Solaris), /usr/local/bin is not in $PATH as passed to CGI 6# scripts, and /usr/local/bin is the default directory where Python is 7# installed, so /usr/bin/env would be unable to find python. Granted, 8# binary installations by Linux vendors often install Python in 9# /usr/bin. So let those vendors patch cgi.py to match their choice 10# of installation. 11 12"""Support module for CGI (Common Gateway Interface) scripts. 13 14This module defines a number of utilities for use by CGI scripts 15written in Python. 16""" 17 18# History 19# ------- 20# 21# Michael McLay started this module. Steve Majewski changed the 22# interface to SvFormContentDict and FormContentDict. The multipart 23# parsing was inspired by code submitted by Andreas Paepcke. Guido van 24# Rossum rewrote, reformatted and documented the module and is currently 25# responsible for its maintenance. 26# 27 28__version__ = "2.6" 29 30 31# Imports 32# ======= 33 34from io import StringIO, BytesIO, TextIOWrapper 35from collections.abc import Mapping 36import sys 37import os 38import urllib.parse 39from email.parser import FeedParser 40from email.message import Message 41import html 42import locale 43import tempfile 44 45__all__ = ["MiniFieldStorage", "FieldStorage", "parse", "parse_multipart", 46 "parse_header", "test", "print_exception", "print_environ", 47 "print_form", "print_directory", "print_arguments", 48 "print_environ_usage"] 49 50# Logging support 51# =============== 52 53logfile = "" # Filename to log to, if not empty 54logfp = None # File object to log to, if not None 55 56def initlog(*allargs): 57 """Write a log message, if there is a log file. 58 59 Even though this function is called initlog(), you should always 60 use log(); log is a variable that is set either to initlog 61 (initially), to dolog (once the log file has been opened), or to 62 nolog (when logging is disabled). 63 64 The first argument is a format string; the remaining arguments (if 65 any) are arguments to the % operator, so e.g. 66 log("%s: %s", "a", "b") 67 will write "a: b" to the log file, followed by a newline. 68 69 If the global logfp is not None, it should be a file object to 70 which log data is written. 71 72 If the global logfp is None, the global logfile may be a string 73 giving a filename to open, in append mode. This file should be 74 world writable!!! If the file can't be opened, logging is 75 silently disabled (since there is no safe place where we could 76 send an error message). 77 78 """ 79 global log, logfile, logfp 80 if logfile and not logfp: 81 try: 82 logfp = open(logfile, "a") 83 except OSError: 84 pass 85 if not logfp: 86 log = nolog 87 else: 88 log = dolog 89 log(*allargs) 90 91def dolog(fmt, *args): 92 """Write a log message to the log file. See initlog() for docs.""" 93 logfp.write(fmt%args + "\n") 94 95def nolog(*allargs): 96 """Dummy function, assigned to log when logging is disabled.""" 97 pass 98 99def closelog(): 100 """Close the log file.""" 101 global log, logfile, logfp 102 logfile = '' 103 if logfp: 104 logfp.close() 105 logfp = None 106 log = initlog 107 108log = initlog # The current logging function 109 110 111# Parsing functions 112# ================= 113 114# Maximum input we will accept when REQUEST_METHOD is POST 115# 0 ==> unlimited input 116maxlen = 0 117 118def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): 119 """Parse a query in the environment or from a file (default stdin) 120 121 Arguments, all optional: 122 123 fp : file pointer; default: sys.stdin.buffer 124 125 environ : environment dictionary; default: os.environ 126 127 keep_blank_values: flag indicating whether blank values in 128 percent-encoded forms should be treated as blank strings. 129 A true value indicates that blanks should be retained as 130 blank strings. The default false value indicates that 131 blank values are to be ignored and treated as if they were 132 not included. 133 134 strict_parsing: flag indicating what to do with parsing errors. 135 If false (the default), errors are silently ignored. 136 If true, errors raise a ValueError exception. 137 """ 138 if fp is None: 139 fp = sys.stdin 140 141 # field keys and values (except for files) are returned as strings 142 # an encoding is required to decode the bytes read from self.fp 143 if hasattr(fp,'encoding'): 144 encoding = fp.encoding 145 else: 146 encoding = 'latin-1' 147 148 # fp.read() must return bytes 149 if isinstance(fp, TextIOWrapper): 150 fp = fp.buffer 151 152 if not 'REQUEST_METHOD' in environ: 153 environ['REQUEST_METHOD'] = 'GET' # For testing stand-alone 154 if environ['REQUEST_METHOD'] == 'POST': 155 ctype, pdict = parse_header(environ['CONTENT_TYPE']) 156 if ctype == 'multipart/form-data': 157 return parse_multipart(fp, pdict) 158 elif ctype == 'application/x-www-form-urlencoded': 159 clength = int(environ['CONTENT_LENGTH']) 160 if maxlen and clength > maxlen: 161 raise ValueError('Maximum content length exceeded') 162 qs = fp.read(clength).decode(encoding) 163 else: 164 qs = '' # Unknown content-type 165 if 'QUERY_STRING' in environ: 166 if qs: qs = qs + '&' 167 qs = qs + environ['QUERY_STRING'] 168 elif sys.argv[1:]: 169 if qs: qs = qs + '&' 170 qs = qs + sys.argv[1] 171 environ['QUERY_STRING'] = qs # XXX Shouldn't, really 172 elif 'QUERY_STRING' in environ: 173 qs = environ['QUERY_STRING'] 174 else: 175 if sys.argv[1:]: 176 qs = sys.argv[1] 177 else: 178 qs = "" 179 environ['QUERY_STRING'] = qs # XXX Shouldn't, really 180 return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, 181 encoding=encoding) 182 183 184def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): 185 """Parse multipart input. 186 187 Arguments: 188 fp : input file 189 pdict: dictionary containing other parameters of content-type header 190 encoding, errors: request encoding and error handler, passed to 191 FieldStorage 192 193 Returns a dictionary just like parse_qs(): keys are the field names, each 194 value is a list of values for that field. For non-file fields, the value 195 is a list of strings. 196 """ 197 # RFC 2026, Section 5.1 : The "multipart" boundary delimiters are always 198 # represented as 7bit US-ASCII. 199 boundary = pdict['boundary'].decode('ascii') 200 ctype = "multipart/form-data; boundary={}".format(boundary) 201 headers = Message() 202 headers.set_type(ctype) 203 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() 997