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