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