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