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