mirror of
				https://github.com/python/cpython.git
				synced 2025-11-02 22:51:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1309 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			1309 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#! /usr/bin/env python
 | 
						|
 | 
						|
"""Support module for CGI (Common Gateway Interface) scripts.
 | 
						|
 | 
						|
This module defines a number of utilities for use by CGI scripts
 | 
						|
written in Python.
 | 
						|
 | 
						|
 | 
						|
Introduction
 | 
						|
------------
 | 
						|
 | 
						|
A CGI script is invoked by an HTTP server, usually to process user
 | 
						|
input submitted through an HTML <FORM> or <ISINPUT> element.
 | 
						|
 | 
						|
Most often, CGI scripts live in the server's special cgi-bin
 | 
						|
directory.  The HTTP server places all sorts of information about the
 | 
						|
request (such as the client's hostname, the requested URL, the query
 | 
						|
string, and lots of other goodies) in the script's shell environment,
 | 
						|
executes the script, and sends the script's output back to the client.
 | 
						|
 | 
						|
The script's input is connected to the client too, and sometimes the
 | 
						|
form data is read this way; at other times the form data is passed via
 | 
						|
the "query string" part of the URL.  This module (cgi.py) is intended
 | 
						|
to take care of the different cases and provide a simpler interface to
 | 
						|
the Python script.  It also provides a number of utilities that help
 | 
						|
in debugging scripts, and the latest addition is support for file
 | 
						|
uploads from a form (if your browser supports it -- Grail 0.3 and
 | 
						|
Netscape 2.0 do).
 | 
						|
 | 
						|
The output of a CGI script should consist of two sections, separated
 | 
						|
by a blank line.  The first section contains a number of headers,
 | 
						|
telling the client what kind of data is following.  Python code to
 | 
						|
generate a minimal header section looks like this:
 | 
						|
 | 
						|
	print "Content-type: text/html"	# HTML is following
 | 
						|
	print				# blank line, end of headers
 | 
						|
 | 
						|
The second section is usually HTML, which allows the client software
 | 
						|
to display nicely formatted text with header, in-line images, etc.
 | 
						|
Here's Python code that prints a simple piece of HTML:
 | 
						|
 | 
						|
	print "<TITLE>CGI script output</TITLE>"
 | 
						|
	print "<H1>This is my first CGI script</H1>"
 | 
						|
	print "Hello, world!"
 | 
						|
 | 
						|
(It may not be fully legal HTML according to the letter of the
 | 
						|
standard, but any browser will understand it.)
 | 
						|
 | 
						|
 | 
						|
Using the cgi module
 | 
						|
--------------------
 | 
						|
 | 
						|
Begin by writing "import cgi".  Don't use "from cgi import *" -- the
 | 
						|
module defines all sorts of names for its own use or for backward 
 | 
						|
compatibility that you don't want in your namespace.
 | 
						|
 | 
						|
It's best to use the FieldStorage class.  The other classes define in this 
 | 
						|
module are provided mostly for backward compatibility.  Instantiate it 
 | 
						|
exactly once, without arguments.  This reads the form contents from 
 | 
						|
standard input or the environment (depending on the value of various 
 | 
						|
environment variables set according to the CGI standard).  Since it may 
 | 
						|
consume standard input, it should be instantiated only once.
 | 
						|
 | 
						|
The FieldStorage instance can be accessed as if it were a Python 
 | 
						|
dictionary.  For instance, the following code (which assumes that the 
 | 
						|
Content-type header and blank line have already been printed) checks that 
 | 
						|
the fields "name" and "addr" are both set to a non-empty string:
 | 
						|
 | 
						|
	form = cgi.FieldStorage()
 | 
						|
	form_ok = 0
 | 
						|
	if form.has_key("name") and form.has_key("addr"):
 | 
						|
		if form["name"].value != "" and form["addr"].value != "":
 | 
						|
			form_ok = 1
 | 
						|
	if not form_ok:
 | 
						|
		print "<H1>Error</H1>"
 | 
						|
		print "Please fill in the name and addr fields."
 | 
						|
		return
 | 
						|
	...further form processing here...
 | 
						|
 | 
						|
Here the fields, accessed through form[key], are themselves instances
 | 
						|
of FieldStorage (or MiniFieldStorage, depending on the form encoding).
 | 
						|
 | 
						|
If the submitted form data contains more than one field with the same
 | 
						|
name, the object retrieved by form[key] is not a (Mini)FieldStorage
 | 
						|
instance but a list of such instances.  If you expect this possibility
 | 
						|
(i.e., when your HTML form comtains multiple fields with the same
 | 
						|
name), use the type() function to determine whether you have a single
 | 
						|
instance or a list of instances.  For example, here's code that
 | 
						|
concatenates any number of username fields, separated by commas:
 | 
						|
 | 
						|
	username = form["username"]
 | 
						|
	if type(username) is type([]):
 | 
						|
		# Multiple username fields specified
 | 
						|
		usernames = ""
 | 
						|
		for item in username:
 | 
						|
			if usernames:
 | 
						|
				# Next item -- insert comma
 | 
						|
				usernames = usernames + "," + item.value
 | 
						|
			else:
 | 
						|
				# First item -- don't insert comma
 | 
						|
				usernames = item.value
 | 
						|
	else:
 | 
						|
		# Single username field specified
 | 
						|
		usernames = username.value
 | 
						|
 | 
						|
If a field represents an uploaded file, the value attribute reads the 
 | 
						|
entire file in memory as a string.  This may not be what you want.  You can 
 | 
						|
test for an uploaded file by testing either the filename attribute or the 
 | 
						|
file attribute.  You can then read the data at leasure from the file 
 | 
						|
attribute:
 | 
						|
 | 
						|
	fileitem = form["userfile"]
 | 
						|
	if fileitem.file:
 | 
						|
		# It's an uploaded file; count lines
 | 
						|
		linecount = 0
 | 
						|
		while 1:
 | 
						|
			line = fileitem.file.readline()
 | 
						|
			if not line: break
 | 
						|
			linecount = linecount + 1
 | 
						|
 | 
						|
The file upload draft standard entertains the possibility of uploading
 | 
						|
multiple files from one field (using a recursive multipart/*
 | 
						|
encoding).  When this occurs, the item will be a dictionary-like
 | 
						|
FieldStorage item.  This can be determined by testing its type
 | 
						|
attribute, which should have the value "multipart/form-data" (or
 | 
						|
perhaps another string beginning with "multipart/").  It this case, it
 | 
						|
can be iterated over recursively just like the top-level form object.
 | 
						|
 | 
						|
When a form is submitted in the "old" format (as the query string or as a 
 | 
						|
single data part of type application/x-www-form-urlencoded), the items 
 | 
						|
will actually be instances of the class MiniFieldStorage.  In this case,
 | 
						|
the list, file and filename attributes are always None.
 | 
						|
 | 
						|
 | 
						|
Old classes
 | 
						|
-----------
 | 
						|
 | 
						|
These classes, present in earlier versions of the cgi module, are still 
 | 
						|
supported for backward compatibility.  New applications should use the
 | 
						|
FieldStorage class.
 | 
						|
 | 
						|
SvFormContentDict: single value form content as dictionary; assumes each 
 | 
						|
field name occurs in the form only once.
 | 
						|
 | 
						|
FormContentDict: multiple value form content as dictionary (the form
 | 
						|
items are lists of values).  Useful if your form contains multiple
 | 
						|
fields with the same name.
 | 
						|
 | 
						|
Other classes (FormContent, InterpFormContentDict) are present for
 | 
						|
backwards compatibility with really old applications only.  If you still 
 | 
						|
use these and would be inconvenienced when they disappeared from a next 
 | 
						|
version of this module, drop me a note.
 | 
						|
 | 
						|
 | 
						|
Functions
 | 
						|
---------
 | 
						|
 | 
						|
These are useful if you want more control, or if you want to employ
 | 
						|
some of the algorithms implemented in this module in other
 | 
						|
circumstances.
 | 
						|
 | 
						|
parse(fp, [environ, [keep_blank_values, [strict_parsing]]]): parse a
 | 
						|
form into a Python dictionary.
 | 
						|
 | 
						|
parse_qs(qs, [keep_blank_values, [strict_parsing]]): parse a query
 | 
						|
string (data of type application/x-www-form-urlencoded).
 | 
						|
 | 
						|
parse_multipart(fp, pdict): parse input of type multipart/form-data (for 
 | 
						|
file uploads).
 | 
						|
 | 
						|
parse_header(string): parse a header like Content-type into a main
 | 
						|
value and a dictionary of parameters.
 | 
						|
 | 
						|
test(): complete test program.
 | 
						|
 | 
						|
print_environ(): format the shell environment in HTML.
 | 
						|
 | 
						|
print_form(form): format a form in HTML.
 | 
						|
 | 
						|
print_environ_usage(): print a list of useful environment variables in
 | 
						|
HTML.
 | 
						|
 | 
						|
escape(): convert the characters "&", "<" and ">" to HTML-safe
 | 
						|
sequences.  Use this if you need to display text that might contain
 | 
						|
such characters in HTML.  To translate URLs for inclusion in the HREF
 | 
						|
attribute of an <A> tag, use urllib.quote().
 | 
						|
 | 
						|
log(fmt, ...): write a line to a log file; see docs for initlog().
 | 
						|
 | 
						|
 | 
						|
Caring about security
 | 
						|
---------------------
 | 
						|
 | 
						|
There's one important rule: if you invoke an external program (e.g.
 | 
						|
via the os.system() or os.popen() functions), make very sure you don't
 | 
						|
pass arbitrary strings received from the client to the shell.  This is
 | 
						|
a well-known security hole whereby clever hackers anywhere on the web
 | 
						|
can exploit a gullible CGI script to invoke arbitrary shell commands.
 | 
						|
Even parts of the URL or field names cannot be trusted, since the
 | 
						|
request doesn't have to come from your form!
 | 
						|
 | 
						|
To be on the safe side, if you must pass a string gotten from a form
 | 
						|
to a shell command, you should make sure the string contains only
 | 
						|
alphanumeric characters, dashes, underscores, and periods.
 | 
						|
 | 
						|
 | 
						|
Installing your CGI script on a Unix system
 | 
						|
-------------------------------------------
 | 
						|
 | 
						|
Read the documentation for your HTTP server and check with your local
 | 
						|
system administrator to find the directory where CGI scripts should be
 | 
						|
installed; usually this is in a directory cgi-bin in the server tree.
 | 
						|
 | 
						|
Make sure that your script is readable and executable by "others"; the
 | 
						|
Unix file mode should be 755 (use "chmod 755 filename").  Make sure
 | 
						|
that the first line of the script contains "#!" starting in column 1
 | 
						|
followed by the pathname of the Python interpreter, for instance:
 | 
						|
 | 
						|
	#! /usr/local/bin/python
 | 
						|
 | 
						|
Make sure the Python interpreter exists and is executable by "others".
 | 
						|
 | 
						|
(Note that it's probably not a good idea to use #! /usr/bin/env python
 | 
						|
here, since the Python interpreter may not be on the default path
 | 
						|
given to CGI scripts!!!)
 | 
						|
 | 
						|
Make sure that any files your script needs to read or write are
 | 
						|
readable or writable, respectively, by "others" -- their mode should
 | 
						|
be 644 for readable and 666 for writable.  This is because, for
 | 
						|
security reasons, the HTTP server executes your script as user
 | 
						|
"nobody", without any special privileges.  It can only read (write,
 | 
						|
execute) files that everybody can read (write, execute).  The current
 | 
						|
directory at execution time is also different (it is usually the
 | 
						|
server's cgi-bin directory) and the set of environment variables is
 | 
						|
also different from what you get at login.  in particular, don't count
 | 
						|
on the shell's search path for executables ($PATH) or the Python
 | 
						|
module search path ($PYTHONPATH) to be set to anything interesting.
 | 
						|
 | 
						|
If you need to load modules from a directory which is not on Python's
 | 
						|
default module search path, you can change the path in your script,
 | 
						|
before importing other modules, e.g.:
 | 
						|
 | 
						|
	import sys
 | 
						|
	sys.path.insert(0, "/usr/home/joe/lib/python")
 | 
						|
	sys.path.insert(0, "/usr/local/lib/python")
 | 
						|
 | 
						|
(This way, the directory inserted last will be searched first!)
 | 
						|
 | 
						|
Instructions for non-Unix systems will vary; check your HTTP server's
 | 
						|
documentation (it will usually have a section on CGI scripts).
 | 
						|
 | 
						|
 | 
						|
Testing your CGI script
 | 
						|
-----------------------
 | 
						|
 | 
						|
Unfortunately, a CGI script will generally not run when you try it
 | 
						|
from the command line, and a script that works perfectly from the
 | 
						|
command line may fail mysteriously when run from the server.  There's
 | 
						|
one reason why you should still test your script from the command
 | 
						|
line: if it contains a syntax error, the python interpreter won't
 | 
						|
execute it at all, and the HTTP server will most likely send a cryptic
 | 
						|
error to the client.
 | 
						|
 | 
						|
Assuming your script has no syntax errors, yet it does not work, you
 | 
						|
have no choice but to read the next section:
 | 
						|
 | 
						|
 | 
						|
Debugging CGI scripts
 | 
						|
---------------------
 | 
						|
 | 
						|
First of all, check for trivial installation errors -- reading the
 | 
						|
section above on installing your CGI script carefully can save you a
 | 
						|
lot of time.  If you wonder whether you have understood the
 | 
						|
installation procedure correctly, try installing a copy of this module
 | 
						|
file (cgi.py) as a CGI script.  When invoked as a script, the file
 | 
						|
will dump its environment and the contents of the form in HTML form.
 | 
						|
Give it the right mode etc, and send it a request.  If it's installed
 | 
						|
in the standard cgi-bin directory, it should be possible to send it a
 | 
						|
request by entering a URL into your browser of the form:
 | 
						|
 | 
						|
	http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home
 | 
						|
 | 
						|
If this gives an error of type 404, the server cannot find the script
 | 
						|
-- perhaps you need to install it in a different directory.  If it
 | 
						|
gives another error (e.g.  500), there's an installation problem that
 | 
						|
you should fix before trying to go any further.  If you get a nicely
 | 
						|
formatted listing of the environment and form content (in this
 | 
						|
example, the fields should be listed as "addr" with value "At Home"
 | 
						|
and "name" with value "Joe Blow"), the cgi.py script has been
 | 
						|
installed correctly.  If you follow the same procedure for your own
 | 
						|
script, you should now be able to debug it.
 | 
						|
 | 
						|
The next step could be to call the cgi module's test() function from
 | 
						|
your script: replace its main code with the single statement
 | 
						|
 | 
						|
	cgi.test()
 | 
						|
	
 | 
						|
This should produce the same results as those gotten from installing
 | 
						|
the cgi.py file itself.
 | 
						|
 | 
						|
When an ordinary Python script raises an unhandled exception
 | 
						|
(e.g. because of a typo in a module name, a file that can't be opened,
 | 
						|
etc.), the Python interpreter prints a nice traceback and exits.
 | 
						|
While the Python interpreter will still do this when your CGI script
 | 
						|
raises an exception, most likely the traceback will end up in one of
 | 
						|
the HTTP server's log file, or be discarded altogether.
 | 
						|
 | 
						|
Fortunately, once you have managed to get your script to execute
 | 
						|
*some* code, it is easy to catch exceptions and cause a traceback to
 | 
						|
be printed.  The test() function below in this module is an example.
 | 
						|
Here are the rules:
 | 
						|
 | 
						|
	1. Import the traceback module (before entering the
 | 
						|
	   try-except!)
 | 
						|
	
 | 
						|
	2. Make sure you finish printing the headers and the blank
 | 
						|
	   line early
 | 
						|
	
 | 
						|
	3. Assign sys.stderr to sys.stdout
 | 
						|
	
 | 
						|
	3. Wrap all remaining code in a try-except statement
 | 
						|
	
 | 
						|
	4. In the except clause, call traceback.print_exc()
 | 
						|
 | 
						|
For example:
 | 
						|
 | 
						|
	import sys
 | 
						|
	import traceback
 | 
						|
	print "Content-type: text/html"
 | 
						|
	print
 | 
						|
	sys.stderr = sys.stdout
 | 
						|
	try:
 | 
						|
		...your code here...
 | 
						|
	except:
 | 
						|
		print "\n\n<PRE>"
 | 
						|
		traceback.print_exc()
 | 
						|
 | 
						|
Notes: The assignment to sys.stderr is needed because the traceback
 | 
						|
prints to sys.stderr.  The print "\n\n<PRE>" statement is necessary to
 | 
						|
disable the word wrapping in HTML.
 | 
						|
 | 
						|
If you suspect that there may be a problem in importing the traceback
 | 
						|
module, you can use an even more robust approach (which only uses
 | 
						|
built-in modules):
 | 
						|
 | 
						|
	import sys
 | 
						|
	sys.stderr = sys.stdout
 | 
						|
	print "Content-type: text/plain"
 | 
						|
	print
 | 
						|
	...your code here...
 | 
						|
 | 
						|
This relies on the Python interpreter to print the traceback.  The
 | 
						|
content type of the output is set to plain text, which disables all
 | 
						|
HTML processing.  If your script works, the raw HTML will be displayed
 | 
						|
by your client.  If it raises an exception, most likely after the
 | 
						|
first two lines have been printed, a traceback will be displayed.
 | 
						|
Because no HTML interpretation is going on, the traceback will
 | 
						|
readable.
 | 
						|
 | 
						|
When all else fails, you may want to insert calls to log() to your
 | 
						|
program or even to a copy of the cgi.py file.  Note that this requires
 | 
						|
you to set cgi.logfile to the name of a world-writable file before the
 | 
						|
first call to log() is made!
 | 
						|
 | 
						|
Good luck!
 | 
						|
 | 
						|
 | 
						|
Common problems and solutions
 | 
						|
-----------------------------
 | 
						|
 | 
						|
- Most HTTP servers buffer the output from CGI scripts until the
 | 
						|
script is completed.  This means that it is not possible to display a
 | 
						|
progress report on the client's display while the script is running.
 | 
						|
 | 
						|
- Check the installation instructions above.
 | 
						|
 | 
						|
- Check the HTTP server's log files.  ("tail -f logfile" in a separate
 | 
						|
window may be useful!)
 | 
						|
 | 
						|
- Always check a script for syntax errors first, by doing something
 | 
						|
like "python script.py".
 | 
						|
 | 
						|
- When using any of the debugging techniques, don't forget to add
 | 
						|
"import sys" to the top of the script.
 | 
						|
 | 
						|
- When invoking external programs, make sure they can be found.
 | 
						|
Usually, this means using absolute path names -- $PATH is usually not
 | 
						|
set to a very useful value in a CGI script.
 | 
						|
 | 
						|
- When reading or writing external files, make sure they can be read
 | 
						|
or written by every user on the system.
 | 
						|
 | 
						|
- Don't try to give a CGI script a set-uid mode.  This doesn't work on
 | 
						|
most systems, and is a security liability as well.
 | 
						|
 | 
						|
 | 
						|
History
 | 
						|
-------
 | 
						|
 | 
						|
Michael McLay started this module.  Steve Majewski changed the
 | 
						|
interface to SvFormContentDict and FormContentDict.  The multipart
 | 
						|
parsing was inspired by code submitted by Andreas Paepcke.  Guido van
 | 
						|
Rossum rewrote, reformatted and documented the module and is currently
 | 
						|
responsible for its maintenance.
 | 
						|
 | 
						|
 | 
						|
XXX The module is getting pretty heavy with all those docstrings.
 | 
						|
Perhaps there should be a slimmed version that doesn't contain all those 
 | 
						|
backwards compatible and debugging classes and functions?
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
# " <== Emacs font-lock de-bogo-kludgificocity
 | 
						|
 | 
						|
__version__ = "2.1"
 | 
						|
 | 
						|
 | 
						|
# Imports
 | 
						|
# =======
 | 
						|
 | 
						|
import string
 | 
						|
import sys
 | 
						|
import os
 | 
						|
 | 
						|
 | 
						|
# Logging support
 | 
						|
# ===============
 | 
						|
 | 
						|
logfile = ""		# Filename to log to, if not empty
 | 
						|
logfp = None		# File object to log to, if not None
 | 
						|
 | 
						|
def initlog(*allargs):
 | 
						|
    """Write a log message, if there is a log file.
 | 
						|
 | 
						|
    Even though this function is called initlog(), you should always
 | 
						|
    use log(); log is a variable that is set either to initlog
 | 
						|
    (initially), to dolog (once the log file has been opened), or to
 | 
						|
    nolog (when logging is disabled).
 | 
						|
 | 
						|
    The first argument is a format string; the remaining arguments (if
 | 
						|
    any) are arguments to the % operator, so e.g.
 | 
						|
        log("%s: %s", "a", "b")
 | 
						|
    will write "a: b" to the log file, followed by a newline.
 | 
						|
 | 
						|
    If the global logfp is not None, it should be a file object to
 | 
						|
    which log data is written.
 | 
						|
 | 
						|
    If the global logfp is None, the global logfile may be a string
 | 
						|
    giving a filename to open, in append mode.  This file should be
 | 
						|
    world writable!!!  If the file can't be opened, logging is
 | 
						|
    silently disabled (since there is no safe place where we could
 | 
						|
    send an error message).
 | 
						|
 | 
						|
    """
 | 
						|
    global logfp, log
 | 
						|
    if logfile and not logfp:
 | 
						|
	try:
 | 
						|
	    logfp = open(logfile, "a")
 | 
						|
	except IOError:
 | 
						|
	    pass
 | 
						|
    if not logfp:
 | 
						|
	log = nolog
 | 
						|
    else:
 | 
						|
	log = dolog
 | 
						|
    apply(log, allargs)
 | 
						|
 | 
						|
def dolog(fmt, *args):
 | 
						|
    """Write a log message to the log file.  See initlog() for docs."""
 | 
						|
    logfp.write(fmt%args + "\n")
 | 
						|
 | 
						|
def nolog(*allargs):
 | 
						|
    """Dummy function, assigned to log when logging is disabled."""
 | 
						|
    pass
 | 
						|
 | 
						|
log = initlog		# The current logging function
 | 
						|
 | 
						|
 | 
						|
# Parsing functions
 | 
						|
# =================
 | 
						|
 | 
						|
def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
 | 
						|
    """Parse a query in the environment or from a file (default stdin)
 | 
						|
 | 
						|
        Arguments, all optional:
 | 
						|
 | 
						|
        fp              : file pointer; default: sys.stdin
 | 
						|
 | 
						|
	environ         : environment dictionary; default: os.environ
 | 
						|
 | 
						|
        keep_blank_values: flag indicating whether blank values in
 | 
						|
            URL encoded forms should be treated as blank strings.  
 | 
						|
            A true value inicates that blanks should be retained as 
 | 
						|
            blank strings.  The default false value indicates that
 | 
						|
	    blank values are to be ignored and treated as if they were
 | 
						|
	    not included.
 | 
						|
 | 
						|
	strict_parsing: flag indicating what to do with parsing errors.
 | 
						|
	    If false (the default), errors are silently ignored.
 | 
						|
	    If true, errors raise a ValueError exception.
 | 
						|
    """
 | 
						|
    if not fp:
 | 
						|
	fp = sys.stdin
 | 
						|
    if not environ.has_key('REQUEST_METHOD'):
 | 
						|
	environ['REQUEST_METHOD'] = 'GET'	# For testing stand-alone
 | 
						|
    if environ['REQUEST_METHOD'] == 'POST':
 | 
						|
	ctype, pdict = parse_header(environ['CONTENT_TYPE'])
 | 
						|
	if ctype == 'multipart/form-data':
 | 
						|
	    return parse_multipart(fp, pdict)
 | 
						|
	elif ctype == 'application/x-www-form-urlencoded':
 | 
						|
	    clength = string.atoi(environ['CONTENT_LENGTH'])
 | 
						|
	    qs = fp.read(clength)
 | 
						|
	else:
 | 
						|
	    qs = ''			# Unknown content-type
 | 
						|
	if environ.has_key('QUERY_STRING'): 
 | 
						|
	    if qs: qs = qs + '&'
 | 
						|
	    qs = qs + environ['QUERY_STRING']
 | 
						|
	elif sys.argv[1:]: 
 | 
						|
	    if qs: qs = qs + '&'
 | 
						|
	    qs = qs + sys.argv[1]
 | 
						|
	environ['QUERY_STRING'] = qs	# XXX Shouldn't, really
 | 
						|
    elif environ.has_key('QUERY_STRING'):
 | 
						|
	qs = environ['QUERY_STRING']
 | 
						|
    else:
 | 
						|
	if sys.argv[1:]:
 | 
						|
	    qs = sys.argv[1]
 | 
						|
	else:
 | 
						|
	    qs = ""
 | 
						|
	environ['QUERY_STRING'] = qs	# XXX Shouldn't, really
 | 
						|
    return parse_qs(qs, keep_blank_values, strict_parsing)
 | 
						|
 | 
						|
 | 
						|
def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
 | 
						|
    """Parse a query given as a string argument.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
 | 
						|
	qs: URL-encoded query string to be parsed
 | 
						|
 | 
						|
        keep_blank_values: flag indicating whether blank values in
 | 
						|
            URL encoded queries should be treated as blank strings.  
 | 
						|
            A true value inicates that blanks should be retained as 
 | 
						|
            blank strings.  The default false value indicates that
 | 
						|
	    blank values are to be ignored and treated as if they were
 | 
						|
	    not included.
 | 
						|
 | 
						|
	strict_parsing: flag indicating what to do with parsing errors.
 | 
						|
	    If false (the default), errors are silently ignored.
 | 
						|
	    If true, errors raise a ValueError exception.
 | 
						|
    """
 | 
						|
    import urllib, regsub
 | 
						|
    name_value_pairs = string.splitfields(qs, '&')
 | 
						|
    dict = {}
 | 
						|
    for name_value in name_value_pairs:
 | 
						|
	nv = string.splitfields(name_value, '=')
 | 
						|
	if len(nv) != 2:
 | 
						|
	    if strict_parsing:
 | 
						|
		raise ValueError, "bad query field: %s" % `name_value`
 | 
						|
	    continue
 | 
						|
	name = nv[0]
 | 
						|
	value = urllib.unquote(regsub.gsub('+', ' ', nv[1]))
 | 
						|
        if len(value) or keep_blank_values:
 | 
						|
	    if dict.has_key (name):
 | 
						|
		dict[name].append(value)
 | 
						|
	    else:
 | 
						|
		dict[name] = [value]
 | 
						|
    return dict
 | 
						|
 | 
						|
 | 
						|
def parse_multipart(fp, pdict):
 | 
						|
    """Parse multipart input.
 | 
						|
 | 
						|
    Arguments:
 | 
						|
    fp   : input file
 | 
						|
    pdict: dictionary containing other parameters of conten-type header
 | 
						|
 | 
						|
    Returns a dictionary just like parse_qs(): keys are the field names, each 
 | 
						|
    value is a list of values for that field.  This is easy to use but not 
 | 
						|
    much good if you are expecting megabytes to be uploaded -- in that case, 
 | 
						|
    use the FieldStorage class instead which is much more flexible.  Note 
 | 
						|
    that content-type is the raw, unparsed contents of the content-type 
 | 
						|
    header.
 | 
						|
    
 | 
						|
    XXX This does not parse nested multipart parts -- use FieldStorage for 
 | 
						|
    that.
 | 
						|
    
 | 
						|
    XXX This should really be subsumed by FieldStorage altogether -- no 
 | 
						|
    point in having two implementations of the same parsing algorithm.
 | 
						|
 | 
						|
    """
 | 
						|
    import mimetools
 | 
						|
    if pdict.has_key('boundary'):
 | 
						|
	boundary = pdict['boundary']
 | 
						|
    else:
 | 
						|
	boundary = ""
 | 
						|
    nextpart = "--" + boundary
 | 
						|
    lastpart = "--" + boundary + "--"
 | 
						|
    partdict = {}
 | 
						|
    terminator = ""
 | 
						|
 | 
						|
    while terminator != lastpart:
 | 
						|
	bytes = -1
 | 
						|
	data = None
 | 
						|
	if terminator:
 | 
						|
	    # At start of next part.  Read headers first.
 | 
						|
	    headers = mimetools.Message(fp)
 | 
						|
	    clength = headers.getheader('content-length')
 | 
						|
	    if clength:
 | 
						|
		try:
 | 
						|
		    bytes = string.atoi(clength)
 | 
						|
		except string.atoi_error:
 | 
						|
		    pass
 | 
						|
	    if bytes > 0:
 | 
						|
		data = fp.read(bytes)
 | 
						|
	    else:
 | 
						|
		data = ""
 | 
						|
	# Read lines until end of part.
 | 
						|
	lines = []
 | 
						|
	while 1:
 | 
						|
	    line = fp.readline()
 | 
						|
	    if not line:
 | 
						|
		terminator = lastpart # End outer loop
 | 
						|
		break
 | 
						|
	    if line[:2] == "--":
 | 
						|
		terminator = string.strip(line)
 | 
						|
		if terminator in (nextpart, lastpart):
 | 
						|
		    break
 | 
						|
	    lines.append(line)
 | 
						|
	# Done with part.
 | 
						|
	if data is None:
 | 
						|
	    continue
 | 
						|
	if bytes < 0:
 | 
						|
	    if lines:
 | 
						|
		# Strip final line terminator
 | 
						|
		line = lines[-1]
 | 
						|
		if line[-2:] == "\r\n":
 | 
						|
		    line = line[:-2]
 | 
						|
		elif line[-1:] == "\n":
 | 
						|
		    line = line[:-1]
 | 
						|
		lines[-1] = line
 | 
						|
		data = string.joinfields(lines, "")
 | 
						|
	line = headers['content-disposition']
 | 
						|
	if not line:
 | 
						|
	    continue
 | 
						|
	key, params = parse_header(line)
 | 
						|
	if key != 'form-data':
 | 
						|
	    continue
 | 
						|
	if params.has_key('name'):
 | 
						|
	    name = params['name']
 | 
						|
	else:
 | 
						|
	    continue
 | 
						|
	if partdict.has_key(name):
 | 
						|
	    partdict[name].append(data)
 | 
						|
	else:
 | 
						|
	    partdict[name] = [data]
 | 
						|
 | 
						|
    return partdict
 | 
						|
 | 
						|
 | 
						|
def parse_header(line):
 | 
						|
    """Parse a Content-type like header.
 | 
						|
 | 
						|
    Return the main content-type and a dictionary of options.
 | 
						|
 | 
						|
    """
 | 
						|
    plist = map(string.strip, string.splitfields(line, ';'))
 | 
						|
    key = string.lower(plist[0])
 | 
						|
    del plist[0]
 | 
						|
    pdict = {}
 | 
						|
    for p in plist:
 | 
						|
	i = string.find(p, '=')
 | 
						|
	if i >= 0:
 | 
						|
	    name = string.lower(string.strip(p[:i]))
 | 
						|
	    value = string.strip(p[i+1:])
 | 
						|
	    if len(value) >= 2 and value[0] == value[-1] == '"':
 | 
						|
		value = value[1:-1]
 | 
						|
	    pdict[name] = value
 | 
						|
    return key, pdict
 | 
						|
 | 
						|
 | 
						|
# Classes for field storage
 | 
						|
# =========================
 | 
						|
 | 
						|
class MiniFieldStorage:
 | 
						|
 | 
						|
    """Like FieldStorage, for use when no file uploads are possible."""
 | 
						|
 | 
						|
    # Dummy attributes
 | 
						|
    filename = None
 | 
						|
    list = None
 | 
						|
    type = None
 | 
						|
    file = None
 | 
						|
    type_options = {}
 | 
						|
    disposition = None
 | 
						|
    disposition_options = {}
 | 
						|
    headers = {}
 | 
						|
 | 
						|
    def __init__(self, name, value):
 | 
						|
	"""Constructor from field name and value."""
 | 
						|
	from StringIO import StringIO
 | 
						|
	self.name = name
 | 
						|
	self.value = value
 | 
						|
        # self.file = StringIO(value)
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
	"""Return printable representation."""
 | 
						|
	return "MiniFieldStorage(%s, %s)" % (`self.name`, `self.value`)
 | 
						|
 | 
						|
 | 
						|
class FieldStorage:
 | 
						|
 | 
						|
    """Store a sequence of fields, reading multipart/form-data.
 | 
						|
 | 
						|
    This class provides naming, typing, files stored on disk, and
 | 
						|
    more.  At the top level, it is accessible like a dictionary, whose
 | 
						|
    keys are the field names.  (Note: None can occur as a field name.)
 | 
						|
    The items are either a Python list (if there's multiple values) or
 | 
						|
    another FieldStorage or MiniFieldStorage object.  If it's a single
 | 
						|
    object, it has the following attributes:
 | 
						|
 | 
						|
    name: the field name, if specified; otherwise None
 | 
						|
 | 
						|
    filename: the filename, if specified; otherwise None; this is the
 | 
						|
	client side filename, *not* the file name on which it is
 | 
						|
	stored (that's a temporary file you don't deal with)
 | 
						|
 | 
						|
    value: the value as a *string*; for file uploads, this
 | 
						|
	transparently reads the file every time you request the value
 | 
						|
 | 
						|
    file: the file(-like) object from which you can read the data;
 | 
						|
	None if the data is stored a simple string
 | 
						|
 | 
						|
    type: the content-type, or None if not specified
 | 
						|
 | 
						|
    type_options: dictionary of options specified on the content-type
 | 
						|
	line
 | 
						|
 | 
						|
    disposition: content-disposition, or None if not specified
 | 
						|
 | 
						|
    disposition_options: dictionary of corresponding options
 | 
						|
 | 
						|
    headers: a dictionary(-like) object (sometimes rfc822.Message or a
 | 
						|
	subclass thereof) containing *all* headers
 | 
						|
 | 
						|
    The class is subclassable, mostly for the purpose of overriding
 | 
						|
    the make_file() method, which is called internally to come up with
 | 
						|
    a file open for reading and writing.  This makes it possible to
 | 
						|
    override the default choice of storing all files in a temporary
 | 
						|
    directory and unlinking them as soon as they have been opened.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, fp=None, headers=None, outerboundary="",
 | 
						|
		 environ=os.environ, keep_blank_values=0, strict_parsing=0):
 | 
						|
	"""Constructor.  Read multipart/* until last part.
 | 
						|
 | 
						|
	Arguments, all optional:
 | 
						|
 | 
						|
	fp      	: file pointer; default: sys.stdin
 | 
						|
 | 
						|
	headers 	: header dictionary-like object; default:
 | 
						|
	    taken from environ as per CGI spec
 | 
						|
 | 
						|
        outerboundary   : terminating multipart boundary
 | 
						|
	    (for internal use only)
 | 
						|
 | 
						|
	environ         : environment dictionary; default: os.environ
 | 
						|
 | 
						|
        keep_blank_values: flag indicating whether blank values in
 | 
						|
            URL encoded forms should be treated as blank strings.  
 | 
						|
            A true value inicates that blanks should be retained as 
 | 
						|
            blank strings.  The default false value indicates that
 | 
						|
	    blank values are to be ignored and treated as if they were
 | 
						|
	    not included.
 | 
						|
 | 
						|
	strict_parsing: flag indicating what to do with parsing errors.
 | 
						|
	    If false (the default), errors are silently ignored.
 | 
						|
	    If true, errors raise a ValueError exception.
 | 
						|
 | 
						|
	"""
 | 
						|
	method = None
 | 
						|
	self.keep_blank_values = keep_blank_values
 | 
						|
	self.strict_parsing = strict_parsing
 | 
						|
	if environ.has_key('REQUEST_METHOD'):
 | 
						|
	    method = string.upper(environ['REQUEST_METHOD'])
 | 
						|
	if not fp and method == 'GET':
 | 
						|
	    qs = None
 | 
						|
	    if environ.has_key('QUERY_STRING'):
 | 
						|
		qs = environ['QUERY_STRING']
 | 
						|
	    from StringIO import StringIO
 | 
						|
	    fp = StringIO(qs or "")
 | 
						|
	    if headers is None:
 | 
						|
		headers = {'content-type':
 | 
						|
			   "application/x-www-form-urlencoded"}
 | 
						|
	if headers is None:
 | 
						|
	    headers = {}
 | 
						|
	    if environ.has_key('CONTENT_TYPE'):
 | 
						|
		headers['content-type'] = environ['CONTENT_TYPE']
 | 
						|
	    if environ.has_key('CONTENT_LENGTH'):
 | 
						|
		headers['content-length'] = environ['CONTENT_LENGTH']
 | 
						|
	self.fp = fp or sys.stdin
 | 
						|
	self.headers = headers
 | 
						|
	self.outerboundary = outerboundary
 | 
						|
 | 
						|
	# Process content-disposition header
 | 
						|
	cdisp, pdict = "", {}
 | 
						|
	if self.headers.has_key('content-disposition'):
 | 
						|
	    cdisp, pdict = parse_header(self.headers['content-disposition'])
 | 
						|
	self.disposition = cdisp
 | 
						|
	self.disposition_options = pdict
 | 
						|
	self.name = None
 | 
						|
	if pdict.has_key('name'):
 | 
						|
	    self.name = pdict['name']
 | 
						|
	self.filename = None
 | 
						|
	if pdict.has_key('filename'):
 | 
						|
	    self.filename = pdict['filename']
 | 
						|
 | 
						|
	# Process content-type header
 | 
						|
	ctype, pdict = "text/plain", {}
 | 
						|
	if self.headers.has_key('content-type'):
 | 
						|
	    ctype, pdict = parse_header(self.headers['content-type'])
 | 
						|
	self.type = ctype
 | 
						|
	self.type_options = pdict
 | 
						|
	self.innerboundary = ""
 | 
						|
	if pdict.has_key('boundary'):
 | 
						|
	    self.innerboundary = pdict['boundary']
 | 
						|
	clen = -1
 | 
						|
	if self.headers.has_key('content-length'):
 | 
						|
	    try:
 | 
						|
		clen = string.atoi(self.headers['content-length'])
 | 
						|
	    except:
 | 
						|
		pass
 | 
						|
	self.length = clen
 | 
						|
 | 
						|
	self.list = self.file = None
 | 
						|
	self.done = 0
 | 
						|
	self.lines = []
 | 
						|
	if ctype == 'application/x-www-form-urlencoded':
 | 
						|
	    self.read_urlencoded()
 | 
						|
	elif ctype[:10] == 'multipart/':
 | 
						|
	    self.read_multi()
 | 
						|
	else:
 | 
						|
	    self.read_single()
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
	"""Return a printable representation."""
 | 
						|
	return "FieldStorage(%s, %s, %s)" % (
 | 
						|
		`self.name`, `self.filename`, `self.value`)
 | 
						|
 | 
						|
    def __getattr__(self, name):
 | 
						|
	if name != 'value':
 | 
						|
	    raise AttributeError, name
 | 
						|
	if self.file:
 | 
						|
	    self.file.seek(0)
 | 
						|
	    value = self.file.read()
 | 
						|
	    self.file.seek(0)
 | 
						|
	elif self.list is not None:
 | 
						|
	    value = self.list
 | 
						|
	else:
 | 
						|
	    value = None
 | 
						|
	return value
 | 
						|
 | 
						|
    def __getitem__(self, key):
 | 
						|
	"""Dictionary style indexing."""
 | 
						|
	if self.list is None:
 | 
						|
	    raise TypeError, "not indexable"
 | 
						|
	found = []
 | 
						|
	for item in self.list:
 | 
						|
	    if item.name == key: found.append(item)
 | 
						|
	if not found:
 | 
						|
	    raise KeyError, key
 | 
						|
	if len(found) == 1:
 | 
						|
	    return found[0]
 | 
						|
	else:
 | 
						|
	    return found
 | 
						|
 | 
						|
    def keys(self):
 | 
						|
	"""Dictionary style keys() method."""
 | 
						|
	if self.list is None:
 | 
						|
	    raise TypeError, "not indexable"
 | 
						|
	keys = []
 | 
						|
	for item in self.list:
 | 
						|
	    if item.name not in keys: keys.append(item.name)
 | 
						|
	return keys
 | 
						|
 | 
						|
    def has_key(self, key):
 | 
						|
	"""Dictionary style has_key() method."""
 | 
						|
	if self.list is None:
 | 
						|
	    raise TypeError, "not indexable"
 | 
						|
	for item in self.list:
 | 
						|
	    if item.name == key: return 1
 | 
						|
	return 0
 | 
						|
 | 
						|
    def __len__(self):
 | 
						|
	"""Dictionary style len(x) support."""
 | 
						|
	return len(self.keys())
 | 
						|
 | 
						|
    def read_urlencoded(self):
 | 
						|
	"""Internal: read data in query string format."""
 | 
						|
	qs = self.fp.read(self.length)
 | 
						|
	dict = parse_qs(qs, self.keep_blank_values, self.strict_parsing)
 | 
						|
	self.list = []
 | 
						|
	for key, valuelist in dict.items():
 | 
						|
	    for value in valuelist:
 | 
						|
		self.list.append(MiniFieldStorage(key, value))
 | 
						|
	self.skip_lines()
 | 
						|
 | 
						|
    def read_multi(self):
 | 
						|
	"""Internal: read a part that is itself multipart."""
 | 
						|
	import rfc822
 | 
						|
	self.list = []
 | 
						|
	part = self.__class__(self.fp, {}, self.innerboundary)
 | 
						|
	# Throw first part away
 | 
						|
	while not part.done:
 | 
						|
	    headers = rfc822.Message(self.fp)
 | 
						|
	    part = self.__class__(self.fp, headers, self.innerboundary)
 | 
						|
	    self.list.append(part)
 | 
						|
	self.skip_lines()
 | 
						|
 | 
						|
    def read_single(self):
 | 
						|
	"""Internal: read an atomic part."""
 | 
						|
	if self.length >= 0:
 | 
						|
	    self.read_binary()
 | 
						|
	    self.skip_lines()
 | 
						|
	else:
 | 
						|
	    self.read_lines()
 | 
						|
	self.file.seek(0)
 | 
						|
 | 
						|
    bufsize = 8*1024		# I/O buffering size for copy to file
 | 
						|
 | 
						|
    def read_binary(self):
 | 
						|
	"""Internal: read binary data."""
 | 
						|
	self.file = self.make_file('b')
 | 
						|
	todo = self.length
 | 
						|
	if todo >= 0:
 | 
						|
	    while todo > 0:
 | 
						|
		data = self.fp.read(min(todo, self.bufsize))
 | 
						|
		if not data:
 | 
						|
		    self.done = -1
 | 
						|
		    break
 | 
						|
		self.file.write(data)
 | 
						|
		todo = todo - len(data)
 | 
						|
 | 
						|
    def read_lines(self):
 | 
						|
	"""Internal: read lines until EOF or outerboundary."""
 | 
						|
	self.file = self.make_file('')
 | 
						|
	if self.outerboundary:
 | 
						|
	    self.read_lines_to_outerboundary()
 | 
						|
	else:
 | 
						|
	    self.read_lines_to_eof()
 | 
						|
 | 
						|
    def read_lines_to_eof(self):
 | 
						|
	"""Internal: read lines until EOF."""
 | 
						|
	while 1:
 | 
						|
	    line = self.fp.readline()
 | 
						|
	    if not line:
 | 
						|
		self.done = -1
 | 
						|
		break
 | 
						|
	    self.lines.append(line)
 | 
						|
	    self.file.write(line)
 | 
						|
 | 
						|
    def read_lines_to_outerboundary(self):
 | 
						|
	"""Internal: read lines until outerboundary."""
 | 
						|
	next = "--" + self.outerboundary
 | 
						|
	last = next + "--"
 | 
						|
	delim = ""
 | 
						|
	while 1:
 | 
						|
	    line = self.fp.readline()
 | 
						|
	    if not line:
 | 
						|
		self.done = -1
 | 
						|
		break
 | 
						|
	    self.lines.append(line)
 | 
						|
	    if line[:2] == "--":
 | 
						|
		strippedline = string.strip(line)
 | 
						|
		if strippedline == next:
 | 
						|
		    break
 | 
						|
		if strippedline == last:
 | 
						|
		    self.done = 1
 | 
						|
		    break
 | 
						|
	    odelim = delim
 | 
						|
	    if line[-2:] == "\r\n":
 | 
						|
		delim = "\r\n"
 | 
						|
		line = line[:-2]
 | 
						|
	    elif line[-1] == "\n":
 | 
						|
		delim = "\n"
 | 
						|
		line = line[:-1]
 | 
						|
	    else:
 | 
						|
		delim = ""
 | 
						|
	    self.file.write(odelim + line)
 | 
						|
 | 
						|
    def skip_lines(self):
 | 
						|
	"""Internal: skip lines until outer boundary if defined."""
 | 
						|
	if not self.outerboundary or self.done:
 | 
						|
	    return
 | 
						|
	next = "--" + self.outerboundary
 | 
						|
	last = next + "--"
 | 
						|
	while 1:
 | 
						|
	    line = self.fp.readline()
 | 
						|
	    if not line:
 | 
						|
		self.done = -1
 | 
						|
		break
 | 
						|
	    self.lines.append(line)
 | 
						|
	    if line[:2] == "--":
 | 
						|
		strippedline = string.strip(line)
 | 
						|
		if strippedline == next:
 | 
						|
		    break
 | 
						|
		if strippedline == last:
 | 
						|
		    self.done = 1
 | 
						|
		    break
 | 
						|
 | 
						|
    def make_file(self, binary):
 | 
						|
	"""Overridable: return a readable & writable file.
 | 
						|
 | 
						|
	The file will be used as follows:
 | 
						|
	- data is written to it
 | 
						|
	- seek(0)
 | 
						|
	- data is read from it
 | 
						|
 | 
						|
	The 'binary' argument is 'b' if the file should be created in
 | 
						|
	binary mode (on non-Unix systems), '' otherwise.
 | 
						|
 | 
						|
	This version opens a temporary file for reading and writing,
 | 
						|
	and immediately deletes (unlinks) it.  The trick (on Unix!) is
 | 
						|
	that the file can still be used, but it can't be opened by
 | 
						|
	another process, and it will automatically be deleted when it
 | 
						|
	is closed or when the current process terminates.
 | 
						|
 | 
						|
	If you want a more permanent file, you derive a class which
 | 
						|
	overrides this method.  If you want a visible temporary file
 | 
						|
	that is nevertheless automatically deleted when the script
 | 
						|
	terminates, try defining a __del__ method in a derived class
 | 
						|
	which unlinks the temporary files you have created.
 | 
						|
 | 
						|
	"""
 | 
						|
	import tempfile
 | 
						|
	tfn = tempfile.mktemp()
 | 
						|
	f = open(tfn, "w%s+" % binary)
 | 
						|
	os.unlink(tfn)
 | 
						|
	return f
 | 
						|
 | 
						|
 | 
						|
# Backwards Compatibility Classes
 | 
						|
# ===============================
 | 
						|
 | 
						|
class FormContentDict:
 | 
						|
    """Basic (multiple values per field) form content as dictionary.
 | 
						|
 | 
						|
    form = FormContentDict()
 | 
						|
 | 
						|
    form[key] -> [value, value, ...]
 | 
						|
    form.has_key(key) -> Boolean
 | 
						|
    form.keys() -> [key, key, ...]
 | 
						|
    form.values() -> [[val, val, ...], [val, val, ...], ...]
 | 
						|
    form.items() ->  [(key, [val, val, ...]), (key, [val, val, ...]), ...]
 | 
						|
    form.dict == {key: [val, val, ...], ...}
 | 
						|
 | 
						|
    """
 | 
						|
    def __init__(self, environ=os.environ):
 | 
						|
        self.dict = parse(environ=environ)
 | 
						|
	self.query_string = environ['QUERY_STRING']
 | 
						|
    def __getitem__(self,key):
 | 
						|
	return self.dict[key]
 | 
						|
    def keys(self):
 | 
						|
	return self.dict.keys()
 | 
						|
    def has_key(self, key):
 | 
						|
	return self.dict.has_key(key)
 | 
						|
    def values(self):
 | 
						|
	return self.dict.values()
 | 
						|
    def items(self):
 | 
						|
	return self.dict.items() 
 | 
						|
    def __len__( self ):
 | 
						|
	return len(self.dict)
 | 
						|
 | 
						|
 | 
						|
class SvFormContentDict(FormContentDict):
 | 
						|
    """Strict single-value expecting form content as dictionary.
 | 
						|
 | 
						|
    IF you only expect a single value for each field, then form[key]
 | 
						|
    will return that single value.  It will raise an IndexError if
 | 
						|
    that expectation is not true.  IF you expect a field to have
 | 
						|
    possible multiple values, than you can use form.getlist(key) to
 | 
						|
    get all of the values.  values() and items() are a compromise:
 | 
						|
    they return single strings where there is a single value, and
 | 
						|
    lists of strings otherwise.
 | 
						|
 | 
						|
    """
 | 
						|
    def __getitem__(self, key):
 | 
						|
	if len(self.dict[key]) > 1: 
 | 
						|
	    raise IndexError, 'expecting a single value' 
 | 
						|
	return self.dict[key][0]
 | 
						|
    def getlist(self, key):
 | 
						|
	return self.dict[key]
 | 
						|
    def values(self):
 | 
						|
	lis = []
 | 
						|
	for each in self.dict.values(): 
 | 
						|
	    if len( each ) == 1 : 
 | 
						|
		lis.append(each[0])
 | 
						|
	    else: lis.append(each)
 | 
						|
	return lis
 | 
						|
    def items(self):
 | 
						|
	lis = []
 | 
						|
	for key,value in self.dict.items():
 | 
						|
	    if len(value) == 1 :
 | 
						|
		lis.append((key, value[0]))
 | 
						|
	    else:	lis.append((key, value))
 | 
						|
	return lis
 | 
						|
 | 
						|
 | 
						|
class InterpFormContentDict(SvFormContentDict):
 | 
						|
    """This class is present for backwards compatibility only.""" 
 | 
						|
    def __getitem__( self, key ):
 | 
						|
	v = SvFormContentDict.__getitem__( self, key )
 | 
						|
	if v[0] in string.digits+'+-.' : 
 | 
						|
	    try:  return  string.atoi( v ) 
 | 
						|
	    except ValueError:
 | 
						|
		try:	return string.atof( v )
 | 
						|
		except ValueError: pass
 | 
						|
	return string.strip(v)
 | 
						|
    def values( self ):
 | 
						|
	lis = [] 
 | 
						|
	for key in self.keys():
 | 
						|
	    try:
 | 
						|
		lis.append( self[key] )
 | 
						|
	    except IndexError:
 | 
						|
		lis.append( self.dict[key] )
 | 
						|
	return lis
 | 
						|
    def items( self ):
 | 
						|
	lis = [] 
 | 
						|
	for key in self.keys():
 | 
						|
	    try:
 | 
						|
		lis.append( (key, self[key]) )
 | 
						|
	    except IndexError:
 | 
						|
		lis.append( (key, self.dict[key]) )
 | 
						|
	return lis
 | 
						|
 | 
						|
 | 
						|
class FormContent(FormContentDict):
 | 
						|
    """This class is present for backwards compatibility only.""" 
 | 
						|
    def values(self, key):
 | 
						|
	if self.dict.has_key(key) :return self.dict[key]
 | 
						|
	else: return None
 | 
						|
    def indexed_value(self, key, location):
 | 
						|
	if self.dict.has_key(key):
 | 
						|
	    if len (self.dict[key]) > location:
 | 
						|
		return self.dict[key][location]
 | 
						|
	    else: return None
 | 
						|
	else: return None
 | 
						|
    def value(self, key):
 | 
						|
	if self.dict.has_key(key): return self.dict[key][0]
 | 
						|
	else: return None
 | 
						|
    def length(self, key):
 | 
						|
	return len(self.dict[key])
 | 
						|
    def stripped(self, key):
 | 
						|
	if self.dict.has_key(key): return string.strip(self.dict[key][0])
 | 
						|
	else: return None
 | 
						|
    def pars(self):
 | 
						|
	return self.dict
 | 
						|
 | 
						|
 | 
						|
# Test/debug code
 | 
						|
# ===============
 | 
						|
 | 
						|
def test(environ=os.environ):
 | 
						|
    """Robust test CGI script, usable as main program.
 | 
						|
 | 
						|
    Write minimal HTTP headers and dump all information provided to
 | 
						|
    the script in HTML form.
 | 
						|
 | 
						|
    """
 | 
						|
    import traceback
 | 
						|
    print "Content-type: text/html"
 | 
						|
    print
 | 
						|
    sys.stderr = sys.stdout
 | 
						|
    try:
 | 
						|
	form = FieldStorage()	# Replace with other classes to test those
 | 
						|
	print_form(form)
 | 
						|
        print_environ(environ)
 | 
						|
	print_directory()
 | 
						|
	print_arguments()
 | 
						|
	print_environ_usage()
 | 
						|
	def f():
 | 
						|
	    exec "testing print_exception() -- <I>italics?</I>"
 | 
						|
	def g(f=f):
 | 
						|
	    f()
 | 
						|
	print "<H3>What follows is a test, not an actual exception:</H3>"
 | 
						|
	g()
 | 
						|
    except:
 | 
						|
	print_exception()
 | 
						|
 | 
						|
def print_exception(type=None, value=None, tb=None, limit=None):
 | 
						|
    if type is None:
 | 
						|
	type, value, tb = sys.exc_type, sys.exc_value, sys.exc_traceback
 | 
						|
    import traceback
 | 
						|
    print
 | 
						|
    print "<H3>Traceback (innermost last):</H3>"
 | 
						|
    list = traceback.format_tb(tb, limit) + \
 | 
						|
	   traceback.format_exception_only(type, value)
 | 
						|
    print "<PRE>%s<B>%s</B></PRE>" % (
 | 
						|
	escape(string.join(list[:-1], "")),
 | 
						|
	escape(list[-1]),
 | 
						|
	)
 | 
						|
 | 
						|
def print_environ(environ=os.environ):
 | 
						|
    """Dump the shell environment as HTML."""
 | 
						|
    keys = environ.keys()
 | 
						|
    keys.sort()
 | 
						|
    print
 | 
						|
    print "<H3>Shell Environment:</H3>"
 | 
						|
    print "<DL>"
 | 
						|
    for key in keys:
 | 
						|
	print "<DT>", escape(key), "<DD>", escape(environ[key])
 | 
						|
    print "</DL>" 
 | 
						|
    print
 | 
						|
 | 
						|
def print_form(form):
 | 
						|
    """Dump the contents of a form as HTML."""
 | 
						|
    keys = form.keys()
 | 
						|
    keys.sort()
 | 
						|
    print
 | 
						|
    print "<H3>Form Contents:</H3>"
 | 
						|
    print "<DL>"
 | 
						|
    for key in keys:
 | 
						|
	print "<DT>" + escape(key) + ":",
 | 
						|
	value = form[key]
 | 
						|
	print "<i>" + escape(`type(value)`) + "</i>"
 | 
						|
	print "<DD>" + escape(`value`)
 | 
						|
    print "</DL>"
 | 
						|
    print
 | 
						|
 | 
						|
def print_directory():
 | 
						|
    """Dump the current directory as HTML."""
 | 
						|
    print
 | 
						|
    print "<H3>Current Working Directory:</H3>"
 | 
						|
    try:
 | 
						|
	pwd = os.getcwd()
 | 
						|
    except os.error, msg:
 | 
						|
	print "os.error:", escape(str(msg))
 | 
						|
    else:
 | 
						|
	print escape(pwd)
 | 
						|
    print
 | 
						|
 | 
						|
def print_arguments():
 | 
						|
    print
 | 
						|
    print "<H3>Command Line Arguments:</H3>"
 | 
						|
    print
 | 
						|
    print sys.argv
 | 
						|
    print
 | 
						|
 | 
						|
def print_environ_usage():
 | 
						|
    """Dump a list of environment variables used by CGI as HTML."""
 | 
						|
    print """
 | 
						|
<H3>These environment variables could have been set:</H3>
 | 
						|
<UL>
 | 
						|
<LI>AUTH_TYPE
 | 
						|
<LI>CONTENT_LENGTH
 | 
						|
<LI>CONTENT_TYPE
 | 
						|
<LI>DATE_GMT
 | 
						|
<LI>DATE_LOCAL
 | 
						|
<LI>DOCUMENT_NAME
 | 
						|
<LI>DOCUMENT_ROOT
 | 
						|
<LI>DOCUMENT_URI
 | 
						|
<LI>GATEWAY_INTERFACE
 | 
						|
<LI>LAST_MODIFIED
 | 
						|
<LI>PATH
 | 
						|
<LI>PATH_INFO
 | 
						|
<LI>PATH_TRANSLATED
 | 
						|
<LI>QUERY_STRING
 | 
						|
<LI>REMOTE_ADDR
 | 
						|
<LI>REMOTE_HOST
 | 
						|
<LI>REMOTE_IDENT
 | 
						|
<LI>REMOTE_USER
 | 
						|
<LI>REQUEST_METHOD
 | 
						|
<LI>SCRIPT_NAME
 | 
						|
<LI>SERVER_NAME
 | 
						|
<LI>SERVER_PORT
 | 
						|
<LI>SERVER_PROTOCOL
 | 
						|
<LI>SERVER_ROOT
 | 
						|
<LI>SERVER_SOFTWARE
 | 
						|
</UL>
 | 
						|
In addition, HTTP headers sent by the server may be passed in the
 | 
						|
environment as well.  Here are some common variable names:
 | 
						|
<UL>
 | 
						|
<LI>HTTP_ACCEPT
 | 
						|
<LI>HTTP_CONNECTION
 | 
						|
<LI>HTTP_HOST
 | 
						|
<LI>HTTP_PRAGMA
 | 
						|
<LI>HTTP_REFERER
 | 
						|
<LI>HTTP_USER_AGENT
 | 
						|
</UL>
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
# Utilities
 | 
						|
# =========
 | 
						|
 | 
						|
def escape(s):
 | 
						|
    """Replace special characters '&', '<' and '>' by SGML entities."""
 | 
						|
    import regsub
 | 
						|
    s = regsub.gsub("&", "&", s)	# Must be done first!
 | 
						|
    s = regsub.gsub("<", "<", s)
 | 
						|
    s = regsub.gsub(">", ">", s)
 | 
						|
    return s
 | 
						|
 | 
						|
 | 
						|
# Invoke mainline
 | 
						|
# ===============
 | 
						|
 | 
						|
# Call test() when this file is run as a script (not imported as a module)
 | 
						|
if __name__ == '__main__': 
 | 
						|
    test()
 |