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