blob: 3862cb548bcbb2e42fec7f526b960ce7924aaf6a [file] [log] [blame]
Guido van Rossum9a22de11995-01-12 12:29:47 +00001#!/usr/local/bin/python
2#
3# A class for wrapping the WWW Forms Common Gateway Interface (CGI)
4# Michael McLay, NIST mclay@eeel.nist.gov 6/14/94
5#
6# modified by Steve Majewski <sdm7g@Virginia.EDU> 12/5/94
7#
8
9# Several classes to parse the name/value pairs that are passed to
10# a server's CGI by GET, POST or PUT methods by a WWW FORM. This
11# module is based on Mike McLay's original cgi.py after discussing
12# changes with him and others on the comp.lang.python newsgroup, and
13# at the NIST Python workshop.
14#
15# The rationale for changes was:
16# The original FormContent class was almost, but not quite like
17# a dictionary object. Besides adding some extra access methods,
18# it had a values() method with different arguments and semantics
19# from the standard values() method of a mapping object. Also,
20# it provided several different access methods that may be necessary
21# or useful, but made it a little more confusing to figure out how
22# to use. Also, we wanted to make the most typical cases the simplest
23# and most convenient access methods. ( Most form fields just return
24# a single value, and in practice, a lot of code was just assuming
25# a single value and ignoring all others. On the other hand, the
26# protocol allows multiple values to be returned.
27#
28# The new base class (FormContentDict) is just like a dictionary.
29# In fact, if you just want a dictionary, all of the stuff that was
30# in __init__ has been extracted into a cgi.parse() function that will
31# return the "raw" dictionary, but having a class allows you to customize
32# it further.
33# Mike McLay's original FormContent class is reimplemented as a
34# subclass of FormContentDict.
35# There are two additional sub-classes, but I'm not yet too sure
36# whether they are what I want.
37#
38
39import string,regsub,sys,os,urllib
40# since os.environ may often be used in cgi code, we name it in this module.
41from os import environ
42
43
44def parse():
45 if environ['REQUEST_METHOD'] == 'POST':
46 qs = sys.stdin.read(string.atoi(environ['CONTENT_LENGTH']))
47 environ['QUERY_STRING'] = qs
48 else:
49 qs = environ['QUERY_STRING']
50 name_value_pairs = string.splitfields(qs, '&')
51 dict = {}
52 for name_value in name_value_pairs:
53 nv = string.splitfields(name_value, '=')
54 if len(nv) != 2:
55 continue
56 name = nv[0]
57 value = urllib.unquote(regsub.gsub('+',' ',nv[1]))
58 if len(value):
59 if dict.has_key (name):
60 dict[name].append(value)
61 else:
62 dict[name] = [value]
63 return dict
64
65
66
67# The FormContent constructor creates a dictionary from the name/value pairs
68# passed through the CGI interface.
69
70
71#
72# form['key']
73# form.__getitem__('key')
74# form.has_key('key')
75# form.keys()
76# form.values()
77# form.items()
78# form.dict
79
80class FormContentDict:
81 def __init__( self ):
82 self.dict = parse()
83 self.query_string = environ['QUERY_STRING']
84 def __getitem__(self,key):
85 return self.dict[key]
86 def keys(self):
87 return self.dict.keys()
88 def has_key(self, key):
89 return self.dict.has_key(key)
90 def values(self):
91 return self.dict.values()
92 def items(self):
93 return self.dict.items()
94 def __len__( self ):
95 return len(self.dict)
96
97
98# This is the "strict" single-value expecting version.
99# IF you only expect a single value for each field, then form[key]
100# will return that single value ( the [0]-th ), and raise an
101# IndexError if that expectation is not true.
102# IF you expect a field to have possible multiple values, than you
103# can use form.getlist( key ) to get all of the values.
104# values() and items() are a compromise: they return single strings
105# where there is a single value, and lists of strings otherwise.
106
107class SvFormContentDict(FormContentDict):
108 def __getitem__( self, key ):
109 if len( self.dict[key] ) > 1 :
110 raise IndexError, 'expecting a single value'
111 return self.dict[key][0]
112 def getlist( self, key ):
113 return self.dict[key]
114 def values( self ):
115 lis = []
116 for each in self.dict.values() :
117 if len( each ) == 1 :
118 lis.append( each[0] )
119 else: lis.append( each )
120 return lis
121 def items( self ):
122 lis = []
123 for key,value in self.dict.items():
124 if len(value) == 1 :
125 lis.append( (key,value[0]) )
126 else: lis.append( (key,value) )
127 return lis
128
129
130# And this sub-class is similar to the above, but it will attempt to
131# interpret numerical values. This is here as mostly as an example,
132# but I think the real way to handle typed-data from a form may be
133# to make an additional table driver parsing stage that has a table
134# of allowed input patterns and the output conversion types - it
135# would signal type-errors on parse, not on access.
136class InterpFormContentDict(SvFormContentDict):
137 def __getitem__( self, key ):
138 v = SvFormContentDict.__getitem__( self, key )
139 if v[0] in string.digits+'+-.' :
140 try: return string.atoi( v )
141 except ValueError:
142 try: return string.atof( v )
143 except ValueError: pass
144 return string.strip(v)
145 def values( self ):
146 lis = []
147 for key in self.keys():
148 try:
149 lis.append( self[key] )
150 except IndexError:
151 lis.append( self.dict[key] )
152 return lis
153 def items( self ):
154 lis = []
155 for key in self.keys():
156 try:
157 lis.append( (key, self[key]) )
158 except IndexError:
159 lis.append( (key, self.dict[key]) )
160 return lis
161
162
163# class FormContent parses the name/value pairs that are passed to a
164# server's CGI by GET, POST, or PUT methods by a WWW FORM. several
165# specialized FormContent dictionary access methods have been added
166# for convenience.
167
168# function return value
169#
170# form.keys() all keys in dictionary
171# form.has_key('key') test keys existance
172# form[key] returns list associated with key
173# form.values('key') key's list (same as form.[key])
174# form.indexed_value('key' index) nth element in key's value list
175# form.value(key) key's unstripped value
176# form.length(key) number of elements in key's list
177# form.stripped(key) key's value with whitespace stripped
178# form.pars() full dictionary
179
180
181
182class FormContent(FormContentDict):
183# This is the original FormContent semantics of values,
184# not the dictionary like semantics.
185 def values(self,key):
186 if self.dict.has_key(key):return self.dict[key]
187 else: return None
188 def indexed_value(self,key, location):
189 if self.dict.has_key(key):
190 if len (self.dict[key]) > location:
191 return self.dict[key][location]
192 else: return None
193 else: return None
194 def value(self,key):
195 if self.dict.has_key(key):return self.dict[key][0]
196 else: return None
197 def length(self,key):
198 return len (self.dict[key])
199 def stripped(self,key):
200 if self.dict.has_key(key):return string.strip(self.dict[key][0])
201 else: return None
202 def pars(self):
203 return self.dict
204
205
206
207
208
209
210def print_environ_usage():
211 print """
212<H3>These operating system environment variables could have been
213set:</H3> <UL>
214<LI>AUTH_TYPE
215<LI>CONTENT_LENGTH
216<LI>CONTENT_TYPE
217<LI>DATE_GMT
218<LI>DATE_LOCAL
219<LI>DOCUMENT_NAME
220<LI>DOCUMENT_ROOT
221<LI>DOCUMENT_URI
222<LI>GATEWAY_INTERFACE
223<LI>LAST_MODIFIED
224<LI>PATH
225<LI>PATH_INFO
226<LI>PATH_TRANSLATED
227<LI>QUERY_STRING
228<LI>REMOTE_ADDR
229<LI>REMOTE_HOST
230<LI>REMOTE_IDENT
231<LI>REMOTE_USER
232<LI>REQUEST_METHOD
233<LI>SCRIPT_NAME
234<LI>SERVER_NAME
235<LI>SERVER_PORT
236<LI>SERVER_PROTOCOL
237<LI>SERVER_ROOT
238<LI>SERVER_SOFTWARE
239</UL>
240"""
241
242def print_environ():
243 skeys = environ.keys()
244 skeys.sort()
Guido van Rossumeb9e9d21995-02-27 13:16:11 +0000245 print '<h3> The following environment variables ' \
246 'were set by the CGI script: </h3>'
Guido van Rossum9a22de11995-01-12 12:29:47 +0000247 print '<dl>'
248 for key in skeys:
Guido van Rossumeb9e9d21995-02-27 13:16:11 +0000249 print '<dt>', escape(key), '<dd>', escape(environ[key])
Guido van Rossum9a22de11995-01-12 12:29:47 +0000250 print '</dl>'
251
252def print_form( form ):
Guido van Rossum9a22de11995-01-12 12:29:47 +0000253 skeys = form.keys()
254 skeys.sort()
Guido van Rossumeb9e9d21995-02-27 13:16:11 +0000255 print '<h3> The following name/value pairs ' \
256 'were entered in the form: </h3>'
257 print '<dl>'
Guido van Rossum9a22de11995-01-12 12:29:47 +0000258 for key in skeys:
Guido van Rossumeb9e9d21995-02-27 13:16:11 +0000259 print '<dt>', escape(key), ':',
260 print '<i>', escape(`type(form[key])`), '</i>',
Guido van Rossumdcce73a1995-03-14 17:22:28 +0000261 print '<dd>', escape(`form[key]`)
Guido van Rossum9a22de11995-01-12 12:29:47 +0000262 print '</dl>'
263
264def escape( s ):
Guido van Rossumdcce73a1995-03-14 17:22:28 +0000265 s = regsub.gsub('&', '&amp;', s) # Must be done first
266 s = regsub.gsub('<', '&lt;', s)
267 s = regsub.gsub('>', '&gt;', s)
Guido van Rossumeb9e9d21995-02-27 13:16:11 +0000268 return s
Guido van Rossum9a22de11995-01-12 12:29:47 +0000269
270def test( what ):
271 label = escape(str(what))
272 print 'Content-type: text/html\n\n'
273 print '<HEADER>\n<TITLE>' + label + '</TITLE>\n</HEADER>\n'
274 print '<BODY>\n'
275 print "<H1>" + label +"</H1>\n"
276 form = what()
277 print_form( form )
278 print_environ()
279 print_environ_usage()
280 print '</body>'
281
282if __name__ == '__main__' :
283 test_classes = ( FormContent, FormContentDict, SvFormContentDict, InterpFormContentDict )
284 test( test_classes[0] ) # by default, test compatibility with
285 # old version, change index to test others.