blob: dd5bee6a9d1a32bb1e3b3b0780c9436aafb227d3 [file] [log] [blame]
Guido van Rossum152f9d91997-02-18 16:55:33 +00001#! /usr/local/bin/python
Guido van Rossum1c9daa81995-09-18 21:52:37 +00002
Guido van Rossum72755611996-03-06 07:20:06 +00003"""Support module for CGI (Common Gateway Interface) scripts.
Guido van Rossum1c9daa81995-09-18 21:52:37 +00004
Guido van Rossum7aee3841996-03-07 18:00:44 +00005This module defines a number of utilities for use by CGI scripts
6written in Python.
Guido van Rossum72755611996-03-06 07:20:06 +00007"""
8
Jeremy Hyltonc253d9a2000-08-03 20:57:44 +00009# XXX Perhaps there should be a slimmed version that doesn't contain
10# all those backwards compatible and debugging classes and functions?
Guido van Rossum98d9fd32000-02-28 15:12:25 +000011
12# History
13# -------
14#
15# Michael McLay started this module. Steve Majewski changed the
16# interface to SvFormContentDict and FormContentDict. The multipart
17# parsing was inspired by code submitted by Andreas Paepcke. Guido van
18# Rossum rewrote, reformatted and documented the module and is currently
19# responsible for its maintenance.
20#
21
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +000022__version__ = "2.4"
Guido van Rossum0147db01996-03-09 03:16:04 +000023
Guido van Rossum72755611996-03-06 07:20:06 +000024
25# Imports
26# =======
27
28import string
Guido van Rossum72755611996-03-06 07:20:06 +000029import sys
30import os
Guido van Rossuma5e9fb61997-08-12 18:18:13 +000031import urllib
Guido van Rossuma5e9fb61997-08-12 18:18:13 +000032import mimetools
33import rfc822
Moshe Zadkaa1a4b592000-08-25 21:47:56 +000034import UserDict
Guido van Rossuma5e9fb61997-08-12 18:18:13 +000035from StringIO import StringIO
Guido van Rossum72755611996-03-06 07:20:06 +000036
Guido van Rossumc204c701996-09-05 19:07:11 +000037
38# Logging support
39# ===============
40
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000041logfile = "" # Filename to log to, if not empty
42logfp = None # File object to log to, if not None
Guido van Rossumc204c701996-09-05 19:07:11 +000043
44def initlog(*allargs):
45 """Write a log message, if there is a log file.
46
47 Even though this function is called initlog(), you should always
48 use log(); log is a variable that is set either to initlog
49 (initially), to dolog (once the log file has been opened), or to
50 nolog (when logging is disabled).
51
52 The first argument is a format string; the remaining arguments (if
53 any) are arguments to the % operator, so e.g.
54 log("%s: %s", "a", "b")
55 will write "a: b" to the log file, followed by a newline.
56
57 If the global logfp is not None, it should be a file object to
58 which log data is written.
59
60 If the global logfp is None, the global logfile may be a string
61 giving a filename to open, in append mode. This file should be
62 world writable!!! If the file can't be opened, logging is
63 silently disabled (since there is no safe place where we could
64 send an error message).
65
66 """
67 global logfp, log
68 if logfile and not logfp:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000069 try:
70 logfp = open(logfile, "a")
71 except IOError:
72 pass
Guido van Rossumc204c701996-09-05 19:07:11 +000073 if not logfp:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000074 log = nolog
Guido van Rossumc204c701996-09-05 19:07:11 +000075 else:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000076 log = dolog
Guido van Rossumc204c701996-09-05 19:07:11 +000077 apply(log, allargs)
78
79def dolog(fmt, *args):
80 """Write a log message to the log file. See initlog() for docs."""
81 logfp.write(fmt%args + "\n")
82
83def nolog(*allargs):
84 """Dummy function, assigned to log when logging is disabled."""
85 pass
86
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000087log = initlog # The current logging function
Guido van Rossumc204c701996-09-05 19:07:11 +000088
89
Guido van Rossum72755611996-03-06 07:20:06 +000090# Parsing functions
91# =================
92
Guido van Rossumad164711997-05-13 19:03:23 +000093# Maximum input we will accept when REQUEST_METHOD is POST
94# 0 ==> unlimited input
95maxlen = 0
96
Guido van Rossume08c04c1996-11-11 19:29:11 +000097def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
Guido van Rossum773ab271996-07-23 03:46:24 +000098 """Parse a query in the environment or from a file (default stdin)
99
100 Arguments, all optional:
101
102 fp : file pointer; default: sys.stdin
103
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000104 environ : environment dictionary; default: os.environ
Guido van Rossum773ab271996-07-23 03:46:24 +0000105
106 keep_blank_values: flag indicating whether blank values in
107 URL encoded forms should be treated as blank strings.
Thomas Wouters7e474022000-07-16 12:04:32 +0000108 A true value indicates that blanks should be retained as
Guido van Rossum773ab271996-07-23 03:46:24 +0000109 blank strings. The default false value indicates that
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000110 blank values are to be ignored and treated as if they were
111 not included.
Guido van Rossume08c04c1996-11-11 19:29:11 +0000112
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000113 strict_parsing: flag indicating what to do with parsing errors.
114 If false (the default), errors are silently ignored.
115 If true, errors raise a ValueError exception.
Guido van Rossum773ab271996-07-23 03:46:24 +0000116 """
Guido van Rossum7aee3841996-03-07 18:00:44 +0000117 if not fp:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000118 fp = sys.stdin
Guido van Rossum7aee3841996-03-07 18:00:44 +0000119 if not environ.has_key('REQUEST_METHOD'):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000120 environ['REQUEST_METHOD'] = 'GET' # For testing stand-alone
Guido van Rossum7aee3841996-03-07 18:00:44 +0000121 if environ['REQUEST_METHOD'] == 'POST':
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000122 ctype, pdict = parse_header(environ['CONTENT_TYPE'])
123 if ctype == 'multipart/form-data':
124 return parse_multipart(fp, pdict)
125 elif ctype == 'application/x-www-form-urlencoded':
126 clength = string.atoi(environ['CONTENT_LENGTH'])
127 if maxlen and clength > maxlen:
128 raise ValueError, 'Maximum content length exceeded'
129 qs = fp.read(clength)
130 else:
131 qs = '' # Unknown content-type
132 if environ.has_key('QUERY_STRING'):
133 if qs: qs = qs + '&'
134 qs = qs + environ['QUERY_STRING']
135 elif sys.argv[1:]:
136 if qs: qs = qs + '&'
137 qs = qs + sys.argv[1]
138 environ['QUERY_STRING'] = qs # XXX Shouldn't, really
Guido van Rossum7aee3841996-03-07 18:00:44 +0000139 elif environ.has_key('QUERY_STRING'):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000140 qs = environ['QUERY_STRING']
Guido van Rossum7aee3841996-03-07 18:00:44 +0000141 else:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000142 if sys.argv[1:]:
143 qs = sys.argv[1]
144 else:
145 qs = ""
146 environ['QUERY_STRING'] = qs # XXX Shouldn't, really
Guido van Rossume08c04c1996-11-11 19:29:11 +0000147 return parse_qs(qs, keep_blank_values, strict_parsing)
Guido van Rossume7808771995-08-07 20:12:09 +0000148
149
Guido van Rossume08c04c1996-11-11 19:29:11 +0000150def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
151 """Parse a query given as a string argument.
Guido van Rossum773ab271996-07-23 03:46:24 +0000152
153 Arguments:
154
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000155 qs: URL-encoded query string to be parsed
Guido van Rossum773ab271996-07-23 03:46:24 +0000156
157 keep_blank_values: flag indicating whether blank values in
158 URL encoded queries should be treated as blank strings.
Thomas Wouters7e474022000-07-16 12:04:32 +0000159 A true value indicates that blanks should be retained as
Guido van Rossum773ab271996-07-23 03:46:24 +0000160 blank strings. The default false value indicates that
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000161 blank values are to be ignored and treated as if they were
162 not included.
Guido van Rossume08c04c1996-11-11 19:29:11 +0000163
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000164 strict_parsing: flag indicating what to do with parsing errors.
165 If false (the default), errors are silently ignored.
166 If true, errors raise a ValueError exception.
Guido van Rossum773ab271996-07-23 03:46:24 +0000167 """
Guido van Rossum7aee3841996-03-07 18:00:44 +0000168 dict = {}
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000169 for name, value in parse_qsl(qs, keep_blank_values, strict_parsing):
Moshe Zadkaa1a4b592000-08-25 21:47:56 +0000170 if dict.has_key(name):
171 dict[name].append(value)
172 else:
173 dict[name] = [value]
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000174 return dict
175
176def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
177 """Parse a query given as a string argument.
178
Jeremy Hyltonafde7e22000-09-15 20:06:57 +0000179 Arguments:
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000180
Jeremy Hyltonafde7e22000-09-15 20:06:57 +0000181 qs: URL-encoded query string to be parsed
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000182
Jeremy Hyltonafde7e22000-09-15 20:06:57 +0000183 keep_blank_values: flag indicating whether blank values in
184 URL encoded queries should be treated as blank strings. A
185 true value indicates that blanks should be retained as blank
186 strings. The default false value indicates that blank values
187 are to be ignored and treated as if they were not included.
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000188
Jeremy Hyltonafde7e22000-09-15 20:06:57 +0000189 strict_parsing: flag indicating what to do with parsing errors. If
190 false (the default), errors are silently ignored. If true,
191 errors raise a ValueError exception.
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000192
Jeremy Hyltonafde7e22000-09-15 20:06:57 +0000193 Returns a list, as G-d intended.
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000194 """
Jeremy Hyltonafde7e22000-09-15 20:06:57 +0000195 pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
196 r = []
197 for name_value in pairs:
198 nv = name_value.split('=', 1)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000199 if len(nv) != 2:
200 if strict_parsing:
201 raise ValueError, "bad query field: %s" % `name_value`
202 continue
Moshe Zadkaa1a4b592000-08-25 21:47:56 +0000203 if len(nv[1]) or keep_blank_values:
204 name = urllib.unquote(string.replace(nv[0], '+', ' '))
205 value = urllib.unquote(string.replace(nv[1], '+', ' '))
206 r.append((name, value))
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000207
208 return r
Guido van Rossum9a22de11995-01-12 12:29:47 +0000209
210
Guido van Rossum0147db01996-03-09 03:16:04 +0000211def parse_multipart(fp, pdict):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000212 """Parse multipart input.
Guido van Rossum9a22de11995-01-12 12:29:47 +0000213
Guido van Rossum7aee3841996-03-07 18:00:44 +0000214 Arguments:
215 fp : input file
Guido van Rossum7aee3841996-03-07 18:00:44 +0000216 pdict: dictionary containing other parameters of conten-type header
Guido van Rossum72755611996-03-06 07:20:06 +0000217
Guido van Rossum0147db01996-03-09 03:16:04 +0000218 Returns a dictionary just like parse_qs(): keys are the field names, each
219 value is a list of values for that field. This is easy to use but not
220 much good if you are expecting megabytes to be uploaded -- in that case,
221 use the FieldStorage class instead which is much more flexible. Note
222 that content-type is the raw, unparsed contents of the content-type
223 header.
224
225 XXX This does not parse nested multipart parts -- use FieldStorage for
226 that.
227
228 XXX This should really be subsumed by FieldStorage altogether -- no
229 point in having two implementations of the same parsing algorithm.
Guido van Rossum72755611996-03-06 07:20:06 +0000230
Guido van Rossum7aee3841996-03-07 18:00:44 +0000231 """
Guido van Rossum7aee3841996-03-07 18:00:44 +0000232 if pdict.has_key('boundary'):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000233 boundary = pdict['boundary']
Guido van Rossum7aee3841996-03-07 18:00:44 +0000234 else:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000235 boundary = ""
Guido van Rossum7aee3841996-03-07 18:00:44 +0000236 nextpart = "--" + boundary
237 lastpart = "--" + boundary + "--"
238 partdict = {}
239 terminator = ""
240
241 while terminator != lastpart:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000242 bytes = -1
243 data = None
244 if terminator:
245 # At start of next part. Read headers first.
246 headers = mimetools.Message(fp)
247 clength = headers.getheader('content-length')
248 if clength:
249 try:
250 bytes = string.atoi(clength)
251 except string.atoi_error:
252 pass
253 if bytes > 0:
254 if maxlen and bytes > maxlen:
255 raise ValueError, 'Maximum content length exceeded'
256 data = fp.read(bytes)
257 else:
258 data = ""
259 # Read lines until end of part.
260 lines = []
261 while 1:
262 line = fp.readline()
263 if not line:
264 terminator = lastpart # End outer loop
265 break
266 if line[:2] == "--":
267 terminator = string.strip(line)
268 if terminator in (nextpart, lastpart):
269 break
270 lines.append(line)
271 # Done with part.
272 if data is None:
273 continue
274 if bytes < 0:
275 if lines:
276 # Strip final line terminator
277 line = lines[-1]
278 if line[-2:] == "\r\n":
279 line = line[:-2]
280 elif line[-1:] == "\n":
281 line = line[:-1]
282 lines[-1] = line
283 data = string.joinfields(lines, "")
284 line = headers['content-disposition']
285 if not line:
286 continue
287 key, params = parse_header(line)
288 if key != 'form-data':
289 continue
290 if params.has_key('name'):
291 name = params['name']
292 else:
293 continue
294 if partdict.has_key(name):
295 partdict[name].append(data)
296 else:
297 partdict[name] = [data]
Guido van Rossum72755611996-03-06 07:20:06 +0000298
Guido van Rossum7aee3841996-03-07 18:00:44 +0000299 return partdict
Guido van Rossum9a22de11995-01-12 12:29:47 +0000300
301
Guido van Rossum72755611996-03-06 07:20:06 +0000302def parse_header(line):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000303 """Parse a Content-type like header.
304
305 Return the main content-type and a dictionary of options.
306
307 """
308 plist = map(string.strip, string.splitfields(line, ';'))
309 key = string.lower(plist[0])
310 del plist[0]
311 pdict = {}
312 for p in plist:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000313 i = string.find(p, '=')
314 if i >= 0:
315 name = string.lower(string.strip(p[:i]))
316 value = string.strip(p[i+1:])
317 if len(value) >= 2 and value[0] == value[-1] == '"':
318 value = value[1:-1]
319 pdict[name] = value
Guido van Rossum7aee3841996-03-07 18:00:44 +0000320 return key, pdict
Guido van Rossum72755611996-03-06 07:20:06 +0000321
322
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000323# Classes for field storage
324# =========================
325
326class MiniFieldStorage:
327
Guido van Rossum0147db01996-03-09 03:16:04 +0000328 """Like FieldStorage, for use when no file uploads are possible."""
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000329
Guido van Rossum7aee3841996-03-07 18:00:44 +0000330 # Dummy attributes
331 filename = None
332 list = None
333 type = None
Guido van Rossum773ab271996-07-23 03:46:24 +0000334 file = None
Guido van Rossum4032c2c1996-03-09 04:04:35 +0000335 type_options = {}
Guido van Rossum7aee3841996-03-07 18:00:44 +0000336 disposition = None
337 disposition_options = {}
338 headers = {}
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000339
Guido van Rossum7aee3841996-03-07 18:00:44 +0000340 def __init__(self, name, value):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000341 """Constructor from field name and value."""
342 self.name = name
343 self.value = value
Guido van Rossum773ab271996-07-23 03:46:24 +0000344 # self.file = StringIO(value)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000345
346 def __repr__(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000347 """Return printable representation."""
348 return "MiniFieldStorage(%s, %s)" % (`self.name`, `self.value`)
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000349
350
351class FieldStorage:
352
Guido van Rossum7aee3841996-03-07 18:00:44 +0000353 """Store a sequence of fields, reading multipart/form-data.
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000354
Guido van Rossum7aee3841996-03-07 18:00:44 +0000355 This class provides naming, typing, files stored on disk, and
356 more. At the top level, it is accessible like a dictionary, whose
357 keys are the field names. (Note: None can occur as a field name.)
358 The items are either a Python list (if there's multiple values) or
359 another FieldStorage or MiniFieldStorage object. If it's a single
360 object, it has the following attributes:
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000361
Guido van Rossum7aee3841996-03-07 18:00:44 +0000362 name: the field name, if specified; otherwise None
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000363
Guido van Rossum7aee3841996-03-07 18:00:44 +0000364 filename: the filename, if specified; otherwise None; this is the
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000365 client side filename, *not* the file name on which it is
366 stored (that's a temporary file you don't deal with)
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000367
Guido van Rossum7aee3841996-03-07 18:00:44 +0000368 value: the value as a *string*; for file uploads, this
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000369 transparently reads the file every time you request the value
Guido van Rossum7aee3841996-03-07 18:00:44 +0000370
371 file: the file(-like) object from which you can read the data;
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000372 None if the data is stored a simple string
Guido van Rossum7aee3841996-03-07 18:00:44 +0000373
374 type: the content-type, or None if not specified
375
376 type_options: dictionary of options specified on the content-type
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000377 line
Guido van Rossum7aee3841996-03-07 18:00:44 +0000378
379 disposition: content-disposition, or None if not specified
380
381 disposition_options: dictionary of corresponding options
382
383 headers: a dictionary(-like) object (sometimes rfc822.Message or a
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000384 subclass thereof) containing *all* headers
Guido van Rossum7aee3841996-03-07 18:00:44 +0000385
386 The class is subclassable, mostly for the purpose of overriding
387 the make_file() method, which is called internally to come up with
388 a file open for reading and writing. This makes it possible to
389 override the default choice of storing all files in a temporary
390 directory and unlinking them as soon as they have been opened.
391
392 """
393
Guido van Rossum773ab271996-07-23 03:46:24 +0000394 def __init__(self, fp=None, headers=None, outerboundary="",
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000395 environ=os.environ, keep_blank_values=0, strict_parsing=0):
396 """Constructor. Read multipart/* until last part.
Guido van Rossum7aee3841996-03-07 18:00:44 +0000397
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000398 Arguments, all optional:
Guido van Rossum7aee3841996-03-07 18:00:44 +0000399
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000400 fp : file pointer; default: sys.stdin
Guido van Rossumb1b4f941998-05-08 19:55:51 +0000401 (not used when the request method is GET)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000402
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000403 headers : header dictionary-like object; default:
404 taken from environ as per CGI spec
Guido van Rossum7aee3841996-03-07 18:00:44 +0000405
Guido van Rossum773ab271996-07-23 03:46:24 +0000406 outerboundary : terminating multipart boundary
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000407 (for internal use only)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000408
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000409 environ : environment dictionary; default: os.environ
Guido van Rossum773ab271996-07-23 03:46:24 +0000410
411 keep_blank_values: flag indicating whether blank values in
412 URL encoded forms should be treated as blank strings.
Thomas Wouters7e474022000-07-16 12:04:32 +0000413 A true value indicates that blanks should be retained as
Guido van Rossum773ab271996-07-23 03:46:24 +0000414 blank strings. The default false value indicates that
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000415 blank values are to be ignored and treated as if they were
416 not included.
Guido van Rossum773ab271996-07-23 03:46:24 +0000417
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000418 strict_parsing: flag indicating what to do with parsing errors.
419 If false (the default), errors are silently ignored.
420 If true, errors raise a ValueError exception.
Guido van Rossume08c04c1996-11-11 19:29:11 +0000421
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000422 """
423 method = 'GET'
424 self.keep_blank_values = keep_blank_values
425 self.strict_parsing = strict_parsing
426 if environ.has_key('REQUEST_METHOD'):
427 method = string.upper(environ['REQUEST_METHOD'])
Guido van Rossum01852831998-06-25 02:40:17 +0000428 if method == 'GET' or method == 'HEAD':
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000429 if environ.has_key('QUERY_STRING'):
430 qs = environ['QUERY_STRING']
431 elif sys.argv[1:]:
432 qs = sys.argv[1]
433 else:
434 qs = ""
435 fp = StringIO(qs)
436 if headers is None:
437 headers = {'content-type':
438 "application/x-www-form-urlencoded"}
439 if headers is None:
Guido van Rossumcff311a1998-06-11 14:06:59 +0000440 headers = {}
441 if method == 'POST':
442 # Set default content-type for POST to what's traditional
443 headers['content-type'] = "application/x-www-form-urlencoded"
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000444 if environ.has_key('CONTENT_TYPE'):
445 headers['content-type'] = environ['CONTENT_TYPE']
446 if environ.has_key('CONTENT_LENGTH'):
447 headers['content-length'] = environ['CONTENT_LENGTH']
448 self.fp = fp or sys.stdin
449 self.headers = headers
450 self.outerboundary = outerboundary
Guido van Rossum7aee3841996-03-07 18:00:44 +0000451
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000452 # Process content-disposition header
453 cdisp, pdict = "", {}
454 if self.headers.has_key('content-disposition'):
455 cdisp, pdict = parse_header(self.headers['content-disposition'])
456 self.disposition = cdisp
457 self.disposition_options = pdict
458 self.name = None
459 if pdict.has_key('name'):
460 self.name = pdict['name']
461 self.filename = None
462 if pdict.has_key('filename'):
463 self.filename = pdict['filename']
Guido van Rossum7aee3841996-03-07 18:00:44 +0000464
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000465 # Process content-type header
Barry Warsaw302331a1999-01-08 17:42:03 +0000466 #
467 # Honor any existing content-type header. But if there is no
468 # content-type header, use some sensible defaults. Assume
469 # outerboundary is "" at the outer level, but something non-false
470 # inside a multi-part. The default for an inner part is text/plain,
471 # but for an outer part it should be urlencoded. This should catch
472 # bogus clients which erroneously forget to include a content-type
473 # header.
474 #
475 # See below for what we do if there does exist a content-type header,
476 # but it happens to be something we don't understand.
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000477 if self.headers.has_key('content-type'):
478 ctype, pdict = parse_header(self.headers['content-type'])
Guido van Rossumce900de1999-06-02 18:44:22 +0000479 elif self.outerboundary or method != 'POST':
Barry Warsaw302331a1999-01-08 17:42:03 +0000480 ctype, pdict = "text/plain", {}
481 else:
482 ctype, pdict = 'application/x-www-form-urlencoded', {}
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000483 self.type = ctype
484 self.type_options = pdict
485 self.innerboundary = ""
486 if pdict.has_key('boundary'):
487 self.innerboundary = pdict['boundary']
488 clen = -1
489 if self.headers.has_key('content-length'):
490 try:
491 clen = string.atoi(self.headers['content-length'])
492 except:
493 pass
494 if maxlen and clen > maxlen:
495 raise ValueError, 'Maximum content length exceeded'
496 self.length = clen
Guido van Rossum7aee3841996-03-07 18:00:44 +0000497
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000498 self.list = self.file = None
499 self.done = 0
500 self.lines = []
501 if ctype == 'application/x-www-form-urlencoded':
502 self.read_urlencoded()
503 elif ctype[:10] == 'multipart/':
Guido van Rossumf5745001998-10-20 14:43:02 +0000504 self.read_multi(environ, keep_blank_values, strict_parsing)
Barry Warsaw302331a1999-01-08 17:42:03 +0000505 else:
Guido van Rossum60a3bd81999-06-11 18:26:09 +0000506 self.read_single()
Guido van Rossum7aee3841996-03-07 18:00:44 +0000507
508 def __repr__(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000509 """Return a printable representation."""
510 return "FieldStorage(%s, %s, %s)" % (
511 `self.name`, `self.filename`, `self.value`)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000512
513 def __getattr__(self, name):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000514 if name != 'value':
515 raise AttributeError, name
516 if self.file:
517 self.file.seek(0)
518 value = self.file.read()
519 self.file.seek(0)
520 elif self.list is not None:
521 value = self.list
522 else:
523 value = None
524 return value
Guido van Rossum7aee3841996-03-07 18:00:44 +0000525
526 def __getitem__(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000527 """Dictionary style indexing."""
528 if self.list is None:
529 raise TypeError, "not indexable"
530 found = []
531 for item in self.list:
532 if item.name == key: found.append(item)
533 if not found:
534 raise KeyError, key
535 if len(found) == 1:
536 return found[0]
537 else:
538 return found
Guido van Rossum7aee3841996-03-07 18:00:44 +0000539
Moshe Zadkaa1a4b592000-08-25 21:47:56 +0000540 def getvalue(self, key, default=None):
541 """Dictionary style get() method, including 'value' lookup."""
542 if self.has_key(key):
543 value = self[key]
544 if type(value) is type([]):
545 return map(lambda v: v.value, value)
546 else:
547 return value.value
548 else:
549 return default
550
Guido van Rossum7aee3841996-03-07 18:00:44 +0000551 def keys(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000552 """Dictionary style keys() method."""
553 if self.list is None:
554 raise TypeError, "not indexable"
555 keys = []
556 for item in self.list:
557 if item.name not in keys: keys.append(item.name)
558 return keys
Guido van Rossum7aee3841996-03-07 18:00:44 +0000559
Guido van Rossum0147db01996-03-09 03:16:04 +0000560 def has_key(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000561 """Dictionary style has_key() method."""
562 if self.list is None:
563 raise TypeError, "not indexable"
564 for item in self.list:
565 if item.name == key: return 1
566 return 0
Guido van Rossum0147db01996-03-09 03:16:04 +0000567
Guido van Rossum88b85d41997-01-11 19:21:33 +0000568 def __len__(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000569 """Dictionary style len(x) support."""
570 return len(self.keys())
Guido van Rossum88b85d41997-01-11 19:21:33 +0000571
Guido van Rossum7aee3841996-03-07 18:00:44 +0000572 def read_urlencoded(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000573 """Internal: read data in query string format."""
574 qs = self.fp.read(self.length)
Guido van Rossum1946f0d1999-06-04 17:54:39 +0000575 self.list = list = []
576 for key, value in parse_qsl(qs, self.keep_blank_values,
577 self.strict_parsing):
578 list.append(MiniFieldStorage(key, value))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000579 self.skip_lines()
Guido van Rossum7aee3841996-03-07 18:00:44 +0000580
Guido van Rossum030d2ec1998-12-09 22:16:46 +0000581 FieldStorageClass = None
582
Guido van Rossumf5745001998-10-20 14:43:02 +0000583 def read_multi(self, environ, keep_blank_values, strict_parsing):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000584 """Internal: read a part that is itself multipart."""
585 self.list = []
Guido van Rossum030d2ec1998-12-09 22:16:46 +0000586 klass = self.FieldStorageClass or self.__class__
587 part = klass(self.fp, {}, self.innerboundary,
588 environ, keep_blank_values, strict_parsing)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000589 # Throw first part away
590 while not part.done:
591 headers = rfc822.Message(self.fp)
Guido van Rossum030d2ec1998-12-09 22:16:46 +0000592 part = klass(self.fp, headers, self.innerboundary,
593 environ, keep_blank_values, strict_parsing)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000594 self.list.append(part)
595 self.skip_lines()
Guido van Rossum7aee3841996-03-07 18:00:44 +0000596
597 def read_single(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000598 """Internal: read an atomic part."""
599 if self.length >= 0:
600 self.read_binary()
601 self.skip_lines()
602 else:
603 self.read_lines()
604 self.file.seek(0)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000605
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000606 bufsize = 8*1024 # I/O buffering size for copy to file
Guido van Rossum7aee3841996-03-07 18:00:44 +0000607
608 def read_binary(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000609 """Internal: read binary data."""
610 self.file = self.make_file('b')
611 todo = self.length
612 if todo >= 0:
613 while todo > 0:
614 data = self.fp.read(min(todo, self.bufsize))
615 if not data:
616 self.done = -1
617 break
618 self.file.write(data)
619 todo = todo - len(data)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000620
621 def read_lines(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000622 """Internal: read lines until EOF or outerboundary."""
623 self.file = self.make_file('')
624 if self.outerboundary:
625 self.read_lines_to_outerboundary()
626 else:
627 self.read_lines_to_eof()
Guido van Rossum7aee3841996-03-07 18:00:44 +0000628
629 def read_lines_to_eof(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000630 """Internal: read lines until EOF."""
631 while 1:
632 line = self.fp.readline()
633 if not line:
634 self.done = -1
635 break
636 self.lines.append(line)
637 self.file.write(line)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000638
639 def read_lines_to_outerboundary(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000640 """Internal: read lines until outerboundary."""
641 next = "--" + self.outerboundary
642 last = next + "--"
643 delim = ""
644 while 1:
645 line = self.fp.readline()
646 if not line:
647 self.done = -1
648 break
649 self.lines.append(line)
650 if line[:2] == "--":
651 strippedline = string.strip(line)
652 if strippedline == next:
653 break
654 if strippedline == last:
655 self.done = 1
656 break
657 odelim = delim
658 if line[-2:] == "\r\n":
659 delim = "\r\n"
660 line = line[:-2]
661 elif line[-1] == "\n":
662 delim = "\n"
663 line = line[:-1]
664 else:
665 delim = ""
666 self.file.write(odelim + line)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000667
668 def skip_lines(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000669 """Internal: skip lines until outer boundary if defined."""
670 if not self.outerboundary or self.done:
671 return
672 next = "--" + self.outerboundary
673 last = next + "--"
674 while 1:
675 line = self.fp.readline()
676 if not line:
677 self.done = -1
678 break
679 self.lines.append(line)
680 if line[:2] == "--":
681 strippedline = string.strip(line)
682 if strippedline == next:
683 break
684 if strippedline == last:
685 self.done = 1
686 break
Guido van Rossum7aee3841996-03-07 18:00:44 +0000687
Guido van Rossuma5e9fb61997-08-12 18:18:13 +0000688 def make_file(self, binary=None):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000689 """Overridable: return a readable & writable file.
Guido van Rossum7aee3841996-03-07 18:00:44 +0000690
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000691 The file will be used as follows:
692 - data is written to it
693 - seek(0)
694 - data is read from it
Guido van Rossum7aee3841996-03-07 18:00:44 +0000695
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000696 The 'binary' argument is unused -- the file is always opened
697 in binary mode.
Guido van Rossum7aee3841996-03-07 18:00:44 +0000698
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000699 This version opens a temporary file for reading and writing,
700 and immediately deletes (unlinks) it. The trick (on Unix!) is
701 that the file can still be used, but it can't be opened by
702 another process, and it will automatically be deleted when it
703 is closed or when the current process terminates.
Guido van Rossum4032c2c1996-03-09 04:04:35 +0000704
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000705 If you want a more permanent file, you derive a class which
706 overrides this method. If you want a visible temporary file
707 that is nevertheless automatically deleted when the script
708 terminates, try defining a __del__ method in a derived class
709 which unlinks the temporary files you have created.
Guido van Rossum7aee3841996-03-07 18:00:44 +0000710
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000711 """
712 import tempfile
713 return tempfile.TemporaryFile("w+b")
714
Guido van Rossum243ddcd1996-03-07 06:33:07 +0000715
716
Guido van Rossum4032c2c1996-03-09 04:04:35 +0000717# Backwards Compatibility Classes
718# ===============================
Guido van Rossum9a22de11995-01-12 12:29:47 +0000719
Moshe Zadkaa1a4b592000-08-25 21:47:56 +0000720class FormContentDict(UserDict.UserDict):
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000721 """Form content as dictionary with a list of values per field.
Guido van Rossum72755611996-03-06 07:20:06 +0000722
Guido van Rossum7aee3841996-03-07 18:00:44 +0000723 form = FormContentDict()
724
725 form[key] -> [value, value, ...]
726 form.has_key(key) -> Boolean
727 form.keys() -> [key, key, ...]
728 form.values() -> [[val, val, ...], [val, val, ...], ...]
729 form.items() -> [(key, [val, val, ...]), (key, [val, val, ...]), ...]
730 form.dict == {key: [val, val, ...], ...}
731
732 """
Guido van Rossum773ab271996-07-23 03:46:24 +0000733 def __init__(self, environ=os.environ):
Moshe Zadkaa1a4b592000-08-25 21:47:56 +0000734 self.dict = self.data = parse(environ=environ)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000735 self.query_string = environ['QUERY_STRING']
Guido van Rossum9a22de11995-01-12 12:29:47 +0000736
737
Guido van Rossum9a22de11995-01-12 12:29:47 +0000738class SvFormContentDict(FormContentDict):
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000739 """Form content as dictionary expecting a single value per field.
Guido van Rossum7aee3841996-03-07 18:00:44 +0000740
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000741 If you only expect a single value for each field, then form[key]
Guido van Rossum7aee3841996-03-07 18:00:44 +0000742 will return that single value. It will raise an IndexError if
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000743 that expectation is not true. If you expect a field to have
Guido van Rossum7aee3841996-03-07 18:00:44 +0000744 possible multiple values, than you can use form.getlist(key) to
745 get all of the values. values() and items() are a compromise:
746 they return single strings where there is a single value, and
747 lists of strings otherwise.
748
749 """
750 def __getitem__(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000751 if len(self.dict[key]) > 1:
752 raise IndexError, 'expecting a single value'
753 return self.dict[key][0]
Guido van Rossum7aee3841996-03-07 18:00:44 +0000754 def getlist(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000755 return self.dict[key]
Guido van Rossum7aee3841996-03-07 18:00:44 +0000756 def values(self):
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000757 result = []
758 for value in self.dict.values():
759 if len(value) == 1:
760 result.append(value[0])
761 else: result.append(value)
762 return result
Guido van Rossum7aee3841996-03-07 18:00:44 +0000763 def items(self):
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000764 result = []
765 for key, value in self.dict.items():
766 if len(value) == 1:
767 result.append((key, value[0]))
768 else: result.append((key, value))
769 return result
Guido van Rossum9a22de11995-01-12 12:29:47 +0000770
771
Guido van Rossum9a22de11995-01-12 12:29:47 +0000772class InterpFormContentDict(SvFormContentDict):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000773 """This class is present for backwards compatibility only."""
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000774 def __getitem__(self, key):
775 v = SvFormContentDict.__getitem__(self, key)
776 if v[0] in string.digits + '+-.':
777 try: return string.atoi(v)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000778 except ValueError:
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000779 try: return string.atof(v)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000780 except ValueError: pass
781 return string.strip(v)
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000782 def values(self):
783 result = []
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000784 for key in self.keys():
785 try:
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000786 result.append(self[key])
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000787 except IndexError:
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000788 result.append(self.dict[key])
789 return result
790 def items(self):
791 result = []
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000792 for key in self.keys():
793 try:
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000794 result.append((key, self[key]))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000795 except IndexError:
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000796 result.append((key, self.dict[key]))
797 return result
Guido van Rossum9a22de11995-01-12 12:29:47 +0000798
799
Guido van Rossum9a22de11995-01-12 12:29:47 +0000800class FormContent(FormContentDict):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000801 """This class is present for backwards compatibility only."""
Guido van Rossum0147db01996-03-09 03:16:04 +0000802 def values(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000803 if self.dict.has_key(key) :return self.dict[key]
804 else: return None
Guido van Rossum0147db01996-03-09 03:16:04 +0000805 def indexed_value(self, key, location):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000806 if self.dict.has_key(key):
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000807 if len(self.dict[key]) > location:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000808 return self.dict[key][location]
809 else: return None
810 else: return None
Guido van Rossum0147db01996-03-09 03:16:04 +0000811 def value(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000812 if self.dict.has_key(key): return self.dict[key][0]
813 else: return None
Guido van Rossum0147db01996-03-09 03:16:04 +0000814 def length(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000815 return len(self.dict[key])
Guido van Rossum0147db01996-03-09 03:16:04 +0000816 def stripped(self, key):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000817 if self.dict.has_key(key): return string.strip(self.dict[key][0])
818 else: return None
Guido van Rossum7aee3841996-03-07 18:00:44 +0000819 def pars(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000820 return self.dict
Guido van Rossum9a22de11995-01-12 12:29:47 +0000821
822
Guido van Rossum72755611996-03-06 07:20:06 +0000823# Test/debug code
824# ===============
Guido van Rossum9a22de11995-01-12 12:29:47 +0000825
Guido van Rossum773ab271996-07-23 03:46:24 +0000826def test(environ=os.environ):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000827 """Robust test CGI script, usable as main program.
Guido van Rossum9a22de11995-01-12 12:29:47 +0000828
Guido van Rossum7aee3841996-03-07 18:00:44 +0000829 Write minimal HTTP headers and dump all information provided to
830 the script in HTML form.
831
832 """
833 import traceback
834 print "Content-type: text/html"
835 print
836 sys.stderr = sys.stdout
837 try:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000838 form = FieldStorage() # Replace with other classes to test those
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000839 print_directory()
840 print_arguments()
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000841 print_form(form)
842 print_environ(environ)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000843 print_environ_usage()
844 def f():
845 exec "testing print_exception() -- <I>italics?</I>"
846 def g(f=f):
847 f()
848 print "<H3>What follows is a test, not an actual exception:</H3>"
849 g()
Guido van Rossum7aee3841996-03-07 18:00:44 +0000850 except:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000851 print_exception()
Guido van Rossumf85de8a1996-08-20 20:22:39 +0000852
Guido van Rossum57d51f22000-09-16 21:16:01 +0000853 print "<H1>Second try with a small maxlen...</H1>"
854
Guido van Rossumad164711997-05-13 19:03:23 +0000855 global maxlen
856 maxlen = 50
857 try:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000858 form = FieldStorage() # Replace with other classes to test those
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000859 print_directory()
860 print_arguments()
Guido van Rossuma3c6a8a2000-09-19 04:11:46 +0000861 print_form(form)
862 print_environ(environ)
Guido van Rossumad164711997-05-13 19:03:23 +0000863 except:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000864 print_exception()
Guido van Rossumad164711997-05-13 19:03:23 +0000865
Guido van Rossumf85de8a1996-08-20 20:22:39 +0000866def print_exception(type=None, value=None, tb=None, limit=None):
867 if type is None:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000868 type, value, tb = sys.exc_info()
Guido van Rossumf85de8a1996-08-20 20:22:39 +0000869 import traceback
870 print
871 print "<H3>Traceback (innermost last):</H3>"
872 list = traceback.format_tb(tb, limit) + \
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000873 traceback.format_exception_only(type, value)
Guido van Rossumf85de8a1996-08-20 20:22:39 +0000874 print "<PRE>%s<B>%s</B></PRE>" % (
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000875 escape(string.join(list[:-1], "")),
876 escape(list[-1]),
877 )
Guido van Rossumf15d1591997-09-29 23:22:12 +0000878 del tb
Guido van Rossum9a22de11995-01-12 12:29:47 +0000879
Guido van Rossum773ab271996-07-23 03:46:24 +0000880def print_environ(environ=os.environ):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000881 """Dump the shell environment as HTML."""
882 keys = environ.keys()
883 keys.sort()
884 print
Guido van Rossum503e50b1996-05-28 22:57:20 +0000885 print "<H3>Shell Environment:</H3>"
Guido van Rossum7aee3841996-03-07 18:00:44 +0000886 print "<DL>"
887 for key in keys:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000888 print "<DT>", escape(key), "<DD>", escape(environ[key])
Guido van Rossum7aee3841996-03-07 18:00:44 +0000889 print "</DL>"
890 print
Guido van Rossum72755611996-03-06 07:20:06 +0000891
892def print_form(form):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000893 """Dump the contents of a form as HTML."""
894 keys = form.keys()
895 keys.sort()
896 print
Guido van Rossum503e50b1996-05-28 22:57:20 +0000897 print "<H3>Form Contents:</H3>"
Guido van Rossum57d51f22000-09-16 21:16:01 +0000898 if not keys:
899 print "<P>No form fields."
Guido van Rossum7aee3841996-03-07 18:00:44 +0000900 print "<DL>"
901 for key in keys:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000902 print "<DT>" + escape(key) + ":",
903 value = form[key]
904 print "<i>" + escape(`type(value)`) + "</i>"
905 print "<DD>" + escape(`value`)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000906 print "</DL>"
907 print
908
909def print_directory():
910 """Dump the current directory as HTML."""
911 print
912 print "<H3>Current Working Directory:</H3>"
913 try:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000914 pwd = os.getcwd()
Guido van Rossum7aee3841996-03-07 18:00:44 +0000915 except os.error, msg:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000916 print "os.error:", escape(str(msg))
Guido van Rossum7aee3841996-03-07 18:00:44 +0000917 else:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000918 print escape(pwd)
Guido van Rossum7aee3841996-03-07 18:00:44 +0000919 print
Guido van Rossum9a22de11995-01-12 12:29:47 +0000920
Guido van Rossuma8738a51996-03-14 21:30:28 +0000921def print_arguments():
922 print
Guido van Rossum503e50b1996-05-28 22:57:20 +0000923 print "<H3>Command Line Arguments:</H3>"
Guido van Rossuma8738a51996-03-14 21:30:28 +0000924 print
925 print sys.argv
926 print
927
Guido van Rossum9a22de11995-01-12 12:29:47 +0000928def print_environ_usage():
Guido van Rossum7aee3841996-03-07 18:00:44 +0000929 """Dump a list of environment variables used by CGI as HTML."""
930 print """
Guido van Rossum72755611996-03-06 07:20:06 +0000931<H3>These environment variables could have been set:</H3>
932<UL>
Guido van Rossum9a22de11995-01-12 12:29:47 +0000933<LI>AUTH_TYPE
934<LI>CONTENT_LENGTH
935<LI>CONTENT_TYPE
936<LI>DATE_GMT
937<LI>DATE_LOCAL
938<LI>DOCUMENT_NAME
939<LI>DOCUMENT_ROOT
940<LI>DOCUMENT_URI
941<LI>GATEWAY_INTERFACE
942<LI>LAST_MODIFIED
943<LI>PATH
944<LI>PATH_INFO
945<LI>PATH_TRANSLATED
946<LI>QUERY_STRING
947<LI>REMOTE_ADDR
948<LI>REMOTE_HOST
949<LI>REMOTE_IDENT
950<LI>REMOTE_USER
951<LI>REQUEST_METHOD
952<LI>SCRIPT_NAME
953<LI>SERVER_NAME
954<LI>SERVER_PORT
955<LI>SERVER_PROTOCOL
956<LI>SERVER_ROOT
957<LI>SERVER_SOFTWARE
958</UL>
Guido van Rossum7aee3841996-03-07 18:00:44 +0000959In addition, HTTP headers sent by the server may be passed in the
960environment as well. Here are some common variable names:
961<UL>
962<LI>HTTP_ACCEPT
963<LI>HTTP_CONNECTION
964<LI>HTTP_HOST
965<LI>HTTP_PRAGMA
966<LI>HTTP_REFERER
967<LI>HTTP_USER_AGENT
968</UL>
Guido van Rossum9a22de11995-01-12 12:29:47 +0000969"""
970
Guido van Rossum9a22de11995-01-12 12:29:47 +0000971
Guido van Rossum72755611996-03-06 07:20:06 +0000972# Utilities
973# =========
Guido van Rossum9a22de11995-01-12 12:29:47 +0000974
Guido van Rossum64c66201997-07-19 20:11:53 +0000975def escape(s, quote=None):
Guido van Rossum7aee3841996-03-07 18:00:44 +0000976 """Replace special characters '&', '<' and '>' by SGML entities."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000977 s = string.replace(s, "&", "&amp;") # Must be done first!
Guido van Rossum00f9fea1997-12-24 21:18:41 +0000978 s = string.replace(s, "<", "&lt;")
979 s = string.replace(s, ">", "&gt;",)
Guido van Rossum64c66201997-07-19 20:11:53 +0000980 if quote:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000981 s = string.replace(s, '"', "&quot;")
Guido van Rossum7aee3841996-03-07 18:00:44 +0000982 return s
Guido van Rossum9a22de11995-01-12 12:29:47 +0000983
Guido van Rossum9a22de11995-01-12 12:29:47 +0000984
Guido van Rossum72755611996-03-06 07:20:06 +0000985# Invoke mainline
986# ===============
987
988# Call test() when this file is run as a script (not imported as a module)
989if __name__ == '__main__':
Guido van Rossum7aee3841996-03-07 18:00:44 +0000990 test()