mirror of https://github.com/python/cpython
304 lines
9.0 KiB
Python
Executable File
304 lines
9.0 KiB
Python
Executable File
#!/usr/local/bin/python
|
|
|
|
# XXX TODO
|
|
# - proper doc strings instead of this rambling dialogue style
|
|
# - more utilities, e.g.
|
|
# - print_header(type="test/html", blankline=1) -- print MIME header
|
|
# - utility to format a nice error message in HTML
|
|
# - utility to format a Location: ... response, including HTML
|
|
# - utility to catch errors and display traceback
|
|
|
|
#
|
|
# A class for wrapping the WWW Forms Common Gateway Interface (CGI)
|
|
# Michael McLay, NIST mclay@eeel.nist.gov 6/14/94
|
|
#
|
|
# modified by Steve Majewski <sdm7g@Virginia.EDU> 12/5/94
|
|
#
|
|
# now maintained as part of the Python distribution
|
|
|
|
# Several classes to parse the name/value pairs that are passed to
|
|
# a server's CGI by GET, POST or PUT methods by a WWW FORM. This
|
|
# module is based on Mike McLay's original cgi.py after discussing
|
|
# changes with him and others on the comp.lang.python newsgroup, and
|
|
# at the NIST Python workshop.
|
|
#
|
|
# The rationale for changes was:
|
|
# The original FormContent class was almost, but not quite like
|
|
# a dictionary object. Besides adding some extra access methods,
|
|
# it had a values() method with different arguments and semantics
|
|
# from the standard values() method of a mapping object. Also,
|
|
# it provided several different access methods that may be necessary
|
|
# or useful, but made it a little more confusing to figure out how
|
|
# to use. Also, we wanted to make the most typical cases the simplest
|
|
# and most convenient access methods. ( Most form fields just return
|
|
# a single value, and in practice, a lot of code was just assuming
|
|
# a single value and ignoring all others. On the other hand, the
|
|
# protocol allows multiple values to be returned.
|
|
#
|
|
# The new base class (FormContentDict) is just like a dictionary.
|
|
# In fact, if you just want a dictionary, all of the stuff that was
|
|
# in __init__ has been extracted into a cgi.parse() function that will
|
|
# return the "raw" dictionary, but having a class allows you to customize
|
|
# it further.
|
|
# Mike McLay's original FormContent class is reimplemented as a
|
|
# subclass of FormContentDict.
|
|
# There are two additional sub-classes, but I'm not yet too sure
|
|
# whether they are what I want.
|
|
#
|
|
|
|
import string,regsub,sys,os,urllib
|
|
# since os.environ may often be used in cgi code, we name it in this module.
|
|
from os import environ
|
|
|
|
|
|
def parse():
|
|
"""Parse the query passed in the environment or on stdin"""
|
|
if environ['REQUEST_METHOD'] == 'POST':
|
|
qs = sys.stdin.read(string.atoi(environ['CONTENT_LENGTH']))
|
|
environ['QUERY_STRING'] = qs
|
|
elif environ.has_key('QUERY_STRING'):
|
|
qs = environ['QUERY_STRING']
|
|
else:
|
|
environ['QUERY_STRING'] = qs = ''
|
|
return parse_qs(qs)
|
|
|
|
|
|
def parse_qs(qs):
|
|
"""Parse a query given as a string argument"""
|
|
name_value_pairs = string.splitfields(qs, '&')
|
|
dict = {}
|
|
for name_value in name_value_pairs:
|
|
nv = string.splitfields(name_value, '=')
|
|
if len(nv) != 2:
|
|
continue
|
|
name = nv[0]
|
|
value = urllib.unquote(regsub.gsub('+',' ',nv[1]))
|
|
if len(value):
|
|
if dict.has_key (name):
|
|
dict[name].append(value)
|
|
else:
|
|
dict[name] = [value]
|
|
return dict
|
|
|
|
|
|
|
|
# The FormContent constructor creates a dictionary from the name/value pairs
|
|
# passed through the CGI interface.
|
|
|
|
|
|
#
|
|
# form['key']
|
|
# form.__getitem__('key')
|
|
# form.has_key('key')
|
|
# form.keys()
|
|
# form.values()
|
|
# form.items()
|
|
# form.dict
|
|
|
|
class FormContentDict:
|
|
def __init__( self ):
|
|
self.dict = parse()
|
|
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)
|
|
|
|
|
|
# This is the "strict" single-value expecting version.
|
|
# IF you only expect a single value for each field, then form[key]
|
|
# will return that single value ( the [0]-th ), and 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.
|
|
|
|
class SvFormContentDict(FormContentDict):
|
|
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
|
|
|
|
|
|
# And this sub-class is similar to the above, but it will attempt to
|
|
# interpret numerical values. This is here as mostly as an example,
|
|
# but I think the real way to handle typed-data from a form may be
|
|
# to make an additional table driver parsing stage that has a table
|
|
# of allowed input patterns and the output conversion types - it
|
|
# would signal type-errors on parse, not on access.
|
|
class InterpFormContentDict(SvFormContentDict):
|
|
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 parses the name/value pairs that are passed to a
|
|
# server's CGI by GET, POST, or PUT methods by a WWW FORM. several
|
|
# specialized FormContent dictionary access methods have been added
|
|
# for convenience.
|
|
|
|
# function return value
|
|
#
|
|
# form.keys() all keys in dictionary
|
|
# form.has_key('key') test keys existance
|
|
# form[key] returns list associated with key
|
|
# form.values('key') key's list (same as form.[key])
|
|
# form.indexed_value('key' index) nth element in key's value list
|
|
# form.value(key) key's unstripped value
|
|
# form.length(key) number of elements in key's list
|
|
# form.stripped(key) key's value with whitespace stripped
|
|
# form.pars() full dictionary
|
|
|
|
|
|
|
|
class FormContent(FormContentDict):
|
|
# This is the original FormContent semantics of values,
|
|
# not the dictionary like semantics.
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_environ_usage():
|
|
print """
|
|
<H3>These operating system 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>
|
|
"""
|
|
|
|
def print_environ():
|
|
skeys = environ.keys()
|
|
skeys.sort()
|
|
print '<h3> The following environment variables ' \
|
|
'were set by the CGI script: </h3>'
|
|
print '<dl>'
|
|
for key in skeys:
|
|
print '<dt>', escape(key), '<dd>', escape(environ[key])
|
|
print '</dl>'
|
|
|
|
def print_form( form ):
|
|
skeys = form.keys()
|
|
skeys.sort()
|
|
print '<h3> The following name/value pairs ' \
|
|
'were entered in the form: </h3>'
|
|
print '<dl>'
|
|
for key in skeys:
|
|
print '<dt>', escape(key), ':',
|
|
print '<i>', escape(`type(form[key])`), '</i>',
|
|
print '<dd>', escape(`form[key]`)
|
|
print '</dl>'
|
|
|
|
def escape( s ):
|
|
s = regsub.gsub('&', '&', s) # Must be done first
|
|
s = regsub.gsub('<', '<', s)
|
|
s = regsub.gsub('>', '>', s)
|
|
return s
|
|
|
|
def test( what ):
|
|
label = escape(str(what))
|
|
print 'Content-type: text/html\n\n'
|
|
print '<HEADER>\n<TITLE>' + label + '</TITLE>\n</HEADER>\n'
|
|
print '<BODY>\n'
|
|
print "<H1>" + label +"</H1>\n"
|
|
form = what()
|
|
print_form( form )
|
|
print_environ()
|
|
print_environ_usage()
|
|
print '</body>'
|
|
|
|
if __name__ == '__main__' :
|
|
test_classes = ( FormContent, FormContentDict, SvFormContentDict, InterpFormContentDict )
|
|
test( test_classes[0] ) # by default, test compatibility with
|
|
# old version, change index to test others.
|