|
1 #! /usr/local/bin/python |
|
2 |
|
3 # NOTE: the above "/usr/local/bin/python" is NOT a mistake. It is |
|
4 # intentionally NOT "/usr/bin/env python". On many systems |
|
5 # (e.g. Solaris), /usr/local/bin is not in $PATH as passed to CGI |
|
6 # scripts, and /usr/local/bin is the default directory where Python is |
|
7 # installed, so /usr/bin/env would be unable to find python. Granted, |
|
8 # binary installations by Linux vendors often install Python in |
|
9 # /usr/bin. So let those vendors patch cgi.py to match their choice |
|
10 # of installation. |
|
11 |
|
12 """Support module for CGI (Common Gateway Interface) scripts. |
|
13 |
|
14 This module defines a number of utilities for use by CGI scripts |
|
15 written in Python. |
|
16 """ |
|
17 |
|
18 # XXX Perhaps there should be a slimmed version that doesn't contain |
|
19 # all those backwards compatible and debugging classes and functions? |
|
20 |
|
21 # History |
|
22 # ------- |
|
23 # |
|
24 # Michael McLay started this module. Steve Majewski changed the |
|
25 # interface to SvFormContentDict and FormContentDict. The multipart |
|
26 # parsing was inspired by code submitted by Andreas Paepcke. Guido van |
|
27 # Rossum rewrote, reformatted and documented the module and is currently |
|
28 # responsible for its maintenance. |
|
29 # |
|
30 |
|
31 __version__ = "2.6" |
|
32 |
|
33 |
|
34 # Imports |
|
35 # ======= |
|
36 |
|
37 from operator import attrgetter |
|
38 import sys |
|
39 import os |
|
40 import urllib |
|
41 import UserDict |
|
42 import urlparse |
|
43 |
|
44 from warnings import filterwarnings, catch_warnings, warn |
|
45 with catch_warnings(): |
|
46 if sys.py3kwarning: |
|
47 filterwarnings("ignore", ".*mimetools has been removed", |
|
48 DeprecationWarning) |
|
49 import mimetools |
|
50 if sys.py3kwarning: |
|
51 filterwarnings("ignore", ".*rfc822 has been removed", DeprecationWarning) |
|
52 import rfc822 |
|
53 |
|
54 try: |
|
55 from cStringIO import StringIO |
|
56 except ImportError: |
|
57 from StringIO import StringIO |
|
58 |
|
59 __all__ = ["MiniFieldStorage", "FieldStorage", "FormContentDict", |
|
60 "SvFormContentDict", "InterpFormContentDict", "FormContent", |
|
61 "parse", "parse_qs", "parse_qsl", "parse_multipart", |
|
62 "parse_header", "print_exception", "print_environ", |
|
63 "print_form", "print_directory", "print_arguments", |
|
64 "print_environ_usage", "escape"] |
|
65 |
|
66 # Logging support |
|
67 # =============== |
|
68 |
|
69 logfile = "" # Filename to log to, if not empty |
|
70 logfp = None # File object to log to, if not None |
|
71 |
|
72 def initlog(*allargs): |
|
73 """Write a log message, if there is a log file. |
|
74 |
|
75 Even though this function is called initlog(), you should always |
|
76 use log(); log is a variable that is set either to initlog |
|
77 (initially), to dolog (once the log file has been opened), or to |
|
78 nolog (when logging is disabled). |
|
79 |
|
80 The first argument is a format string; the remaining arguments (if |
|
81 any) are arguments to the % operator, so e.g. |
|
82 log("%s: %s", "a", "b") |
|
83 will write "a: b" to the log file, followed by a newline. |
|
84 |
|
85 If the global logfp is not None, it should be a file object to |
|
86 which log data is written. |
|
87 |
|
88 If the global logfp is None, the global logfile may be a string |
|
89 giving a filename to open, in append mode. This file should be |
|
90 world writable!!! If the file can't be opened, logging is |
|
91 silently disabled (since there is no safe place where we could |
|
92 send an error message). |
|
93 |
|
94 """ |
|
95 global logfp, log |
|
96 if logfile and not logfp: |
|
97 try: |
|
98 logfp = open(logfile, "a") |
|
99 except IOError: |
|
100 pass |
|
101 if not logfp: |
|
102 log = nolog |
|
103 else: |
|
104 log = dolog |
|
105 log(*allargs) |
|
106 |
|
107 def dolog(fmt, *args): |
|
108 """Write a log message to the log file. See initlog() for docs.""" |
|
109 logfp.write(fmt%args + "\n") |
|
110 |
|
111 def nolog(*allargs): |
|
112 """Dummy function, assigned to log when logging is disabled.""" |
|
113 pass |
|
114 |
|
115 log = initlog # The current logging function |
|
116 |
|
117 |
|
118 # Parsing functions |
|
119 # ================= |
|
120 |
|
121 # Maximum input we will accept when REQUEST_METHOD is POST |
|
122 # 0 ==> unlimited input |
|
123 maxlen = 0 |
|
124 |
|
125 def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): |
|
126 """Parse a query in the environment or from a file (default stdin) |
|
127 |
|
128 Arguments, all optional: |
|
129 |
|
130 fp : file pointer; default: sys.stdin |
|
131 |
|
132 environ : environment dictionary; default: os.environ |
|
133 |
|
134 keep_blank_values: flag indicating whether blank values in |
|
135 URL encoded forms should be treated as blank strings. |
|
136 A true value indicates that blanks should be retained as |
|
137 blank strings. The default false value indicates that |
|
138 blank values are to be ignored and treated as if they were |
|
139 not included. |
|
140 |
|
141 strict_parsing: flag indicating what to do with parsing errors. |
|
142 If false (the default), errors are silently ignored. |
|
143 If true, errors raise a ValueError exception. |
|
144 """ |
|
145 if fp is None: |
|
146 fp = sys.stdin |
|
147 if not 'REQUEST_METHOD' in environ: |
|
148 environ['REQUEST_METHOD'] = 'GET' # For testing stand-alone |
|
149 if environ['REQUEST_METHOD'] == 'POST': |
|
150 ctype, pdict = parse_header(environ['CONTENT_TYPE']) |
|
151 if ctype == 'multipart/form-data': |
|
152 return parse_multipart(fp, pdict) |
|
153 elif ctype == 'application/x-www-form-urlencoded': |
|
154 clength = int(environ['CONTENT_LENGTH']) |
|
155 if maxlen and clength > maxlen: |
|
156 raise ValueError, 'Maximum content length exceeded' |
|
157 qs = fp.read(clength) |
|
158 else: |
|
159 qs = '' # Unknown content-type |
|
160 if 'QUERY_STRING' in environ: |
|
161 if qs: qs = qs + '&' |
|
162 qs = qs + environ['QUERY_STRING'] |
|
163 elif sys.argv[1:]: |
|
164 if qs: qs = qs + '&' |
|
165 qs = qs + sys.argv[1] |
|
166 environ['QUERY_STRING'] = qs # XXX Shouldn't, really |
|
167 elif 'QUERY_STRING' in environ: |
|
168 qs = environ['QUERY_STRING'] |
|
169 else: |
|
170 if sys.argv[1:]: |
|
171 qs = sys.argv[1] |
|
172 else: |
|
173 qs = "" |
|
174 environ['QUERY_STRING'] = qs # XXX Shouldn't, really |
|
175 return parse_qs(qs, keep_blank_values, strict_parsing) |
|
176 |
|
177 |
|
178 # parse query string function called from urlparse, |
|
179 # this is done in order to maintain backward compatiblity. |
|
180 |
|
181 def parse_qs(qs, keep_blank_values=0, strict_parsing=0): |
|
182 """Parse a query given as a string argument.""" |
|
183 warn("cgi.parse_qs is deprecated, use urlparse.parse_qs \ |
|
184 instead",PendingDeprecationWarning) |
|
185 return urlparse.parse_qs(qs, keep_blank_values, strict_parsing) |
|
186 |
|
187 |
|
188 def parse_qsl(qs, keep_blank_values=0, strict_parsing=0): |
|
189 """Parse a query given as a string argument.""" |
|
190 warn("cgi.parse_qsl is deprecated, use urlparse.parse_qsl instead", |
|
191 PendingDeprecationWarning) |
|
192 return urlparse.parse_qsl(qs, keep_blank_values, strict_parsing) |
|
193 |
|
194 def parse_multipart(fp, pdict): |
|
195 """Parse multipart input. |
|
196 |
|
197 Arguments: |
|
198 fp : input file |
|
199 pdict: dictionary containing other parameters of content-type header |
|
200 |
|
201 Returns a dictionary just like parse_qs(): keys are the field names, each |
|
202 value is a list of values for that field. This is easy to use but not |
|
203 much good if you are expecting megabytes to be uploaded -- in that case, |
|
204 use the FieldStorage class instead which is much more flexible. Note |
|
205 that content-type is the raw, unparsed contents of the content-type |
|
206 header. |
|
207 |
|
208 XXX This does not parse nested multipart parts -- use FieldStorage for |
|
209 that. |
|
210 |
|
211 XXX This should really be subsumed by FieldStorage altogether -- no |
|
212 point in having two implementations of the same parsing algorithm. |
|
213 Also, FieldStorage protects itself better against certain DoS attacks |
|
214 by limiting the size of the data read in one chunk. The API here |
|
215 does not support that kind of protection. This also affects parse() |
|
216 since it can call parse_multipart(). |
|
217 |
|
218 """ |
|
219 boundary = "" |
|
220 if 'boundary' in pdict: |
|
221 boundary = pdict['boundary'] |
|
222 if not valid_boundary(boundary): |
|
223 raise ValueError, ('Invalid boundary in multipart form: %r' |
|
224 % (boundary,)) |
|
225 |
|
226 nextpart = "--" + boundary |
|
227 lastpart = "--" + boundary + "--" |
|
228 partdict = {} |
|
229 terminator = "" |
|
230 |
|
231 while terminator != lastpart: |
|
232 bytes = -1 |
|
233 data = None |
|
234 if terminator: |
|
235 # At start of next part. Read headers first. |
|
236 headers = mimetools.Message(fp) |
|
237 clength = headers.getheader('content-length') |
|
238 if clength: |
|
239 try: |
|
240 bytes = int(clength) |
|
241 except ValueError: |
|
242 pass |
|
243 if bytes > 0: |
|
244 if maxlen and bytes > maxlen: |
|
245 raise ValueError, 'Maximum content length exceeded' |
|
246 data = fp.read(bytes) |
|
247 else: |
|
248 data = "" |
|
249 # Read lines until end of part. |
|
250 lines = [] |
|
251 while 1: |
|
252 line = fp.readline() |
|
253 if not line: |
|
254 terminator = lastpart # End outer loop |
|
255 break |
|
256 if line[:2] == "--": |
|
257 terminator = line.strip() |
|
258 if terminator in (nextpart, lastpart): |
|
259 break |
|
260 lines.append(line) |
|
261 # Done with part. |
|
262 if data is None: |
|
263 continue |
|
264 if bytes < 0: |
|
265 if lines: |
|
266 # Strip final line terminator |
|
267 line = lines[-1] |
|
268 if line[-2:] == "\r\n": |
|
269 line = line[:-2] |
|
270 elif line[-1:] == "\n": |
|
271 line = line[:-1] |
|
272 lines[-1] = line |
|
273 data = "".join(lines) |
|
274 line = headers['content-disposition'] |
|
275 if not line: |
|
276 continue |
|
277 key, params = parse_header(line) |
|
278 if key != 'form-data': |
|
279 continue |
|
280 if 'name' in params: |
|
281 name = params['name'] |
|
282 else: |
|
283 continue |
|
284 if name in partdict: |
|
285 partdict[name].append(data) |
|
286 else: |
|
287 partdict[name] = [data] |
|
288 |
|
289 return partdict |
|
290 |
|
291 |
|
292 def parse_header(line): |
|
293 """Parse a Content-type like header. |
|
294 |
|
295 Return the main content-type and a dictionary of options. |
|
296 |
|
297 """ |
|
298 plist = [x.strip() for x in line.split(';')] |
|
299 key = plist.pop(0).lower() |
|
300 pdict = {} |
|
301 for p in plist: |
|
302 i = p.find('=') |
|
303 if i >= 0: |
|
304 name = p[:i].strip().lower() |
|
305 value = p[i+1:].strip() |
|
306 if len(value) >= 2 and value[0] == value[-1] == '"': |
|
307 value = value[1:-1] |
|
308 value = value.replace('\\\\', '\\').replace('\\"', '"') |
|
309 pdict[name] = value |
|
310 return key, pdict |
|
311 |
|
312 |
|
313 # Classes for field storage |
|
314 # ========================= |
|
315 |
|
316 class MiniFieldStorage: |
|
317 |
|
318 """Like FieldStorage, for use when no file uploads are possible.""" |
|
319 |
|
320 # Dummy attributes |
|
321 filename = None |
|
322 list = None |
|
323 type = None |
|
324 file = None |
|
325 type_options = {} |
|
326 disposition = None |
|
327 disposition_options = {} |
|
328 headers = {} |
|
329 |
|
330 def __init__(self, name, value): |
|
331 """Constructor from field name and value.""" |
|
332 self.name = name |
|
333 self.value = value |
|
334 # self.file = StringIO(value) |
|
335 |
|
336 def __repr__(self): |
|
337 """Return printable representation.""" |
|
338 return "MiniFieldStorage(%r, %r)" % (self.name, self.value) |
|
339 |
|
340 |
|
341 class FieldStorage: |
|
342 |
|
343 """Store a sequence of fields, reading multipart/form-data. |
|
344 |
|
345 This class provides naming, typing, files stored on disk, and |
|
346 more. At the top level, it is accessible like a dictionary, whose |
|
347 keys are the field names. (Note: None can occur as a field name.) |
|
348 The items are either a Python list (if there's multiple values) or |
|
349 another FieldStorage or MiniFieldStorage object. If it's a single |
|
350 object, it has the following attributes: |
|
351 |
|
352 name: the field name, if specified; otherwise None |
|
353 |
|
354 filename: the filename, if specified; otherwise None; this is the |
|
355 client side filename, *not* the file name on which it is |
|
356 stored (that's a temporary file you don't deal with) |
|
357 |
|
358 value: the value as a *string*; for file uploads, this |
|
359 transparently reads the file every time you request the value |
|
360 |
|
361 file: the file(-like) object from which you can read the data; |
|
362 None if the data is stored a simple string |
|
363 |
|
364 type: the content-type, or None if not specified |
|
365 |
|
366 type_options: dictionary of options specified on the content-type |
|
367 line |
|
368 |
|
369 disposition: content-disposition, or None if not specified |
|
370 |
|
371 disposition_options: dictionary of corresponding options |
|
372 |
|
373 headers: a dictionary(-like) object (sometimes rfc822.Message or a |
|
374 subclass thereof) containing *all* headers |
|
375 |
|
376 The class is subclassable, mostly for the purpose of overriding |
|
377 the make_file() method, which is called internally to come up with |
|
378 a file open for reading and writing. This makes it possible to |
|
379 override the default choice of storing all files in a temporary |
|
380 directory and unlinking them as soon as they have been opened. |
|
381 |
|
382 """ |
|
383 |
|
384 def __init__(self, fp=None, headers=None, outerboundary="", |
|
385 environ=os.environ, keep_blank_values=0, strict_parsing=0): |
|
386 """Constructor. Read multipart/* until last part. |
|
387 |
|
388 Arguments, all optional: |
|
389 |
|
390 fp : file pointer; default: sys.stdin |
|
391 (not used when the request method is GET) |
|
392 |
|
393 headers : header dictionary-like object; default: |
|
394 taken from environ as per CGI spec |
|
395 |
|
396 outerboundary : terminating multipart boundary |
|
397 (for internal use only) |
|
398 |
|
399 environ : environment dictionary; default: os.environ |
|
400 |
|
401 keep_blank_values: flag indicating whether blank values in |
|
402 URL encoded forms should be treated as blank strings. |
|
403 A true value indicates that blanks should be retained as |
|
404 blank strings. The default false value indicates that |
|
405 blank values are to be ignored and treated as if they were |
|
406 not included. |
|
407 |
|
408 strict_parsing: flag indicating what to do with parsing errors. |
|
409 If false (the default), errors are silently ignored. |
|
410 If true, errors raise a ValueError exception. |
|
411 |
|
412 """ |
|
413 method = 'GET' |
|
414 self.keep_blank_values = keep_blank_values |
|
415 self.strict_parsing = strict_parsing |
|
416 if 'REQUEST_METHOD' in environ: |
|
417 method = environ['REQUEST_METHOD'].upper() |
|
418 self.qs_on_post = None |
|
419 if method == 'GET' or method == 'HEAD': |
|
420 if 'QUERY_STRING' in environ: |
|
421 qs = environ['QUERY_STRING'] |
|
422 elif sys.argv[1:]: |
|
423 qs = sys.argv[1] |
|
424 else: |
|
425 qs = "" |
|
426 fp = StringIO(qs) |
|
427 if headers is None: |
|
428 headers = {'content-type': |
|
429 "application/x-www-form-urlencoded"} |
|
430 if headers is None: |
|
431 headers = {} |
|
432 if method == 'POST': |
|
433 # Set default content-type for POST to what's traditional |
|
434 headers['content-type'] = "application/x-www-form-urlencoded" |
|
435 if 'CONTENT_TYPE' in environ: |
|
436 headers['content-type'] = environ['CONTENT_TYPE'] |
|
437 if 'QUERY_STRING' in environ: |
|
438 self.qs_on_post = environ['QUERY_STRING'] |
|
439 if 'CONTENT_LENGTH' in environ: |
|
440 headers['content-length'] = environ['CONTENT_LENGTH'] |
|
441 self.fp = fp or sys.stdin |
|
442 self.headers = headers |
|
443 self.outerboundary = outerboundary |
|
444 |
|
445 # Process content-disposition header |
|
446 cdisp, pdict = "", {} |
|
447 if 'content-disposition' in self.headers: |
|
448 cdisp, pdict = parse_header(self.headers['content-disposition']) |
|
449 self.disposition = cdisp |
|
450 self.disposition_options = pdict |
|
451 self.name = None |
|
452 if 'name' in pdict: |
|
453 self.name = pdict['name'] |
|
454 self.filename = None |
|
455 if 'filename' in pdict: |
|
456 self.filename = pdict['filename'] |
|
457 |
|
458 # Process content-type header |
|
459 # |
|
460 # Honor any existing content-type header. But if there is no |
|
461 # content-type header, use some sensible defaults. Assume |
|
462 # outerboundary is "" at the outer level, but something non-false |
|
463 # inside a multi-part. The default for an inner part is text/plain, |
|
464 # but for an outer part it should be urlencoded. This should catch |
|
465 # bogus clients which erroneously forget to include a content-type |
|
466 # header. |
|
467 # |
|
468 # See below for what we do if there does exist a content-type header, |
|
469 # but it happens to be something we don't understand. |
|
470 if 'content-type' in self.headers: |
|
471 ctype, pdict = parse_header(self.headers['content-type']) |
|
472 elif self.outerboundary or method != 'POST': |
|
473 ctype, pdict = "text/plain", {} |
|
474 else: |
|
475 ctype, pdict = 'application/x-www-form-urlencoded', {} |
|
476 self.type = ctype |
|
477 self.type_options = pdict |
|
478 self.innerboundary = "" |
|
479 if 'boundary' in pdict: |
|
480 self.innerboundary = pdict['boundary'] |
|
481 clen = -1 |
|
482 if 'content-length' in self.headers: |
|
483 try: |
|
484 clen = int(self.headers['content-length']) |
|
485 except ValueError: |
|
486 pass |
|
487 if maxlen and clen > maxlen: |
|
488 raise ValueError, 'Maximum content length exceeded' |
|
489 self.length = clen |
|
490 |
|
491 self.list = self.file = None |
|
492 self.done = 0 |
|
493 if ctype == 'application/x-www-form-urlencoded': |
|
494 self.read_urlencoded() |
|
495 elif ctype[:10] == 'multipart/': |
|
496 self.read_multi(environ, keep_blank_values, strict_parsing) |
|
497 else: |
|
498 self.read_single() |
|
499 |
|
500 def __repr__(self): |
|
501 """Return a printable representation.""" |
|
502 return "FieldStorage(%r, %r, %r)" % ( |
|
503 self.name, self.filename, self.value) |
|
504 |
|
505 def __iter__(self): |
|
506 return iter(self.keys()) |
|
507 |
|
508 def __getattr__(self, name): |
|
509 if name != 'value': |
|
510 raise AttributeError, name |
|
511 if self.file: |
|
512 self.file.seek(0) |
|
513 value = self.file.read() |
|
514 self.file.seek(0) |
|
515 elif self.list is not None: |
|
516 value = self.list |
|
517 else: |
|
518 value = None |
|
519 return value |
|
520 |
|
521 def __getitem__(self, key): |
|
522 """Dictionary style indexing.""" |
|
523 if self.list is None: |
|
524 raise TypeError, "not indexable" |
|
525 found = [] |
|
526 for item in self.list: |
|
527 if item.name == key: found.append(item) |
|
528 if not found: |
|
529 raise KeyError, key |
|
530 if len(found) == 1: |
|
531 return found[0] |
|
532 else: |
|
533 return found |
|
534 |
|
535 def getvalue(self, key, default=None): |
|
536 """Dictionary style get() method, including 'value' lookup.""" |
|
537 if key in self: |
|
538 value = self[key] |
|
539 if type(value) is type([]): |
|
540 return map(attrgetter('value'), value) |
|
541 else: |
|
542 return value.value |
|
543 else: |
|
544 return default |
|
545 |
|
546 def getfirst(self, key, default=None): |
|
547 """ Return the first value received.""" |
|
548 if key in self: |
|
549 value = self[key] |
|
550 if type(value) is type([]): |
|
551 return value[0].value |
|
552 else: |
|
553 return value.value |
|
554 else: |
|
555 return default |
|
556 |
|
557 def getlist(self, key): |
|
558 """ Return list of received values.""" |
|
559 if key in self: |
|
560 value = self[key] |
|
561 if type(value) is type([]): |
|
562 return map(attrgetter('value'), value) |
|
563 else: |
|
564 return [value.value] |
|
565 else: |
|
566 return [] |
|
567 |
|
568 def keys(self): |
|
569 """Dictionary style keys() method.""" |
|
570 if self.list is None: |
|
571 raise TypeError, "not indexable" |
|
572 return list(set(item.name for item in self.list)) |
|
573 |
|
574 def has_key(self, key): |
|
575 """Dictionary style has_key() method.""" |
|
576 if self.list is None: |
|
577 raise TypeError, "not indexable" |
|
578 return any(item.name == key for item in self.list) |
|
579 |
|
580 def __contains__(self, key): |
|
581 """Dictionary style __contains__ method.""" |
|
582 if self.list is None: |
|
583 raise TypeError, "not indexable" |
|
584 return any(item.name == key for item in self.list) |
|
585 |
|
586 def __len__(self): |
|
587 """Dictionary style len(x) support.""" |
|
588 return len(self.keys()) |
|
589 |
|
590 def __nonzero__(self): |
|
591 return bool(self.list) |
|
592 |
|
593 def read_urlencoded(self): |
|
594 """Internal: read data in query string format.""" |
|
595 qs = self.fp.read(self.length) |
|
596 if self.qs_on_post: |
|
597 qs += '&' + self.qs_on_post |
|
598 self.list = list = [] |
|
599 for key, value in urlparse.parse_qsl(qs, self.keep_blank_values, |
|
600 self.strict_parsing): |
|
601 list.append(MiniFieldStorage(key, value)) |
|
602 self.skip_lines() |
|
603 |
|
604 FieldStorageClass = None |
|
605 |
|
606 def read_multi(self, environ, keep_blank_values, strict_parsing): |
|
607 """Internal: read a part that is itself multipart.""" |
|
608 ib = self.innerboundary |
|
609 if not valid_boundary(ib): |
|
610 raise ValueError, 'Invalid boundary in multipart form: %r' % (ib,) |
|
611 self.list = [] |
|
612 if self.qs_on_post: |
|
613 for key, value in urlparse.parse_qsl(self.qs_on_post, |
|
614 self.keep_blank_values, self.strict_parsing): |
|
615 self.list.append(MiniFieldStorage(key, value)) |
|
616 FieldStorageClass = None |
|
617 |
|
618 klass = self.FieldStorageClass or self.__class__ |
|
619 part = klass(self.fp, {}, ib, |
|
620 environ, keep_blank_values, strict_parsing) |
|
621 # Throw first part away |
|
622 while not part.done: |
|
623 headers = rfc822.Message(self.fp) |
|
624 part = klass(self.fp, headers, ib, |
|
625 environ, keep_blank_values, strict_parsing) |
|
626 self.list.append(part) |
|
627 self.skip_lines() |
|
628 |
|
629 def read_single(self): |
|
630 """Internal: read an atomic part.""" |
|
631 if self.length >= 0: |
|
632 self.read_binary() |
|
633 self.skip_lines() |
|
634 else: |
|
635 self.read_lines() |
|
636 self.file.seek(0) |
|
637 |
|
638 bufsize = 8*1024 # I/O buffering size for copy to file |
|
639 |
|
640 def read_binary(self): |
|
641 """Internal: read binary data.""" |
|
642 self.file = self.make_file('b') |
|
643 todo = self.length |
|
644 if todo >= 0: |
|
645 while todo > 0: |
|
646 data = self.fp.read(min(todo, self.bufsize)) |
|
647 if not data: |
|
648 self.done = -1 |
|
649 break |
|
650 self.file.write(data) |
|
651 todo = todo - len(data) |
|
652 |
|
653 def read_lines(self): |
|
654 """Internal: read lines until EOF or outerboundary.""" |
|
655 self.file = self.__file = StringIO() |
|
656 if self.outerboundary: |
|
657 self.read_lines_to_outerboundary() |
|
658 else: |
|
659 self.read_lines_to_eof() |
|
660 |
|
661 def __write(self, line): |
|
662 if self.__file is not None: |
|
663 if self.__file.tell() + len(line) > 1000: |
|
664 self.file = self.make_file('') |
|
665 self.file.write(self.__file.getvalue()) |
|
666 self.__file = None |
|
667 self.file.write(line) |
|
668 |
|
669 def read_lines_to_eof(self): |
|
670 """Internal: read lines until EOF.""" |
|
671 while 1: |
|
672 line = self.fp.readline(1<<16) |
|
673 if not line: |
|
674 self.done = -1 |
|
675 break |
|
676 self.__write(line) |
|
677 |
|
678 def read_lines_to_outerboundary(self): |
|
679 """Internal: read lines until outerboundary.""" |
|
680 next = "--" + self.outerboundary |
|
681 last = next + "--" |
|
682 delim = "" |
|
683 last_line_lfend = True |
|
684 while 1: |
|
685 line = self.fp.readline(1<<16) |
|
686 if not line: |
|
687 self.done = -1 |
|
688 break |
|
689 if line[:2] == "--" and last_line_lfend: |
|
690 strippedline = line.strip() |
|
691 if strippedline == next: |
|
692 break |
|
693 if strippedline == last: |
|
694 self.done = 1 |
|
695 break |
|
696 odelim = delim |
|
697 if line[-2:] == "\r\n": |
|
698 delim = "\r\n" |
|
699 line = line[:-2] |
|
700 last_line_lfend = True |
|
701 elif line[-1] == "\n": |
|
702 delim = "\n" |
|
703 line = line[:-1] |
|
704 last_line_lfend = True |
|
705 else: |
|
706 delim = "" |
|
707 last_line_lfend = False |
|
708 self.__write(odelim + line) |
|
709 |
|
710 def skip_lines(self): |
|
711 """Internal: skip lines until outer boundary if defined.""" |
|
712 if not self.outerboundary or self.done: |
|
713 return |
|
714 next = "--" + self.outerboundary |
|
715 last = next + "--" |
|
716 last_line_lfend = True |
|
717 while 1: |
|
718 line = self.fp.readline(1<<16) |
|
719 if not line: |
|
720 self.done = -1 |
|
721 break |
|
722 if line[:2] == "--" and last_line_lfend: |
|
723 strippedline = line.strip() |
|
724 if strippedline == next: |
|
725 break |
|
726 if strippedline == last: |
|
727 self.done = 1 |
|
728 break |
|
729 last_line_lfend = line.endswith('\n') |
|
730 |
|
731 def make_file(self, binary=None): |
|
732 """Overridable: return a readable & writable file. |
|
733 |
|
734 The file will be used as follows: |
|
735 - data is written to it |
|
736 - seek(0) |
|
737 - data is read from it |
|
738 |
|
739 The 'binary' argument is unused -- the file is always opened |
|
740 in binary mode. |
|
741 |
|
742 This version opens a temporary file for reading and writing, |
|
743 and immediately deletes (unlinks) it. The trick (on Unix!) is |
|
744 that the file can still be used, but it can't be opened by |
|
745 another process, and it will automatically be deleted when it |
|
746 is closed or when the current process terminates. |
|
747 |
|
748 If you want a more permanent file, you derive a class which |
|
749 overrides this method. If you want a visible temporary file |
|
750 that is nevertheless automatically deleted when the script |
|
751 terminates, try defining a __del__ method in a derived class |
|
752 which unlinks the temporary files you have created. |
|
753 |
|
754 """ |
|
755 import tempfile |
|
756 return tempfile.TemporaryFile("w+b") |
|
757 |
|
758 |
|
759 |
|
760 # Backwards Compatibility Classes |
|
761 # =============================== |
|
762 |
|
763 class FormContentDict(UserDict.UserDict): |
|
764 """Form content as dictionary with a list of values per field. |
|
765 |
|
766 form = FormContentDict() |
|
767 |
|
768 form[key] -> [value, value, ...] |
|
769 key in form -> Boolean |
|
770 form.keys() -> [key, key, ...] |
|
771 form.values() -> [[val, val, ...], [val, val, ...], ...] |
|
772 form.items() -> [(key, [val, val, ...]), (key, [val, val, ...]), ...] |
|
773 form.dict == {key: [val, val, ...], ...} |
|
774 |
|
775 """ |
|
776 def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0): |
|
777 self.dict = self.data = parse(environ=environ, |
|
778 keep_blank_values=keep_blank_values, |
|
779 strict_parsing=strict_parsing) |
|
780 self.query_string = environ['QUERY_STRING'] |
|
781 |
|
782 |
|
783 class SvFormContentDict(FormContentDict): |
|
784 """Form content as dictionary expecting a single value per field. |
|
785 |
|
786 If you only expect a single value for each field, then form[key] |
|
787 will return that single value. It will raise an IndexError if |
|
788 that expectation is not true. If you expect a field to have |
|
789 possible multiple values, than you can use form.getlist(key) to |
|
790 get all of the values. values() and items() are a compromise: |
|
791 they return single strings where there is a single value, and |
|
792 lists of strings otherwise. |
|
793 |
|
794 """ |
|
795 def __getitem__(self, key): |
|
796 if len(self.dict[key]) > 1: |
|
797 raise IndexError, 'expecting a single value' |
|
798 return self.dict[key][0] |
|
799 def getlist(self, key): |
|
800 return self.dict[key] |
|
801 def values(self): |
|
802 result = [] |
|
803 for value in self.dict.values(): |
|
804 if len(value) == 1: |
|
805 result.append(value[0]) |
|
806 else: result.append(value) |
|
807 return result |
|
808 def items(self): |
|
809 result = [] |
|
810 for key, value in self.dict.items(): |
|
811 if len(value) == 1: |
|
812 result.append((key, value[0])) |
|
813 else: result.append((key, value)) |
|
814 return result |
|
815 |
|
816 |
|
817 class InterpFormContentDict(SvFormContentDict): |
|
818 """This class is present for backwards compatibility only.""" |
|
819 def __getitem__(self, key): |
|
820 v = SvFormContentDict.__getitem__(self, key) |
|
821 if v[0] in '0123456789+-.': |
|
822 try: return int(v) |
|
823 except ValueError: |
|
824 try: return float(v) |
|
825 except ValueError: pass |
|
826 return v.strip() |
|
827 def values(self): |
|
828 result = [] |
|
829 for key in self.keys(): |
|
830 try: |
|
831 result.append(self[key]) |
|
832 except IndexError: |
|
833 result.append(self.dict[key]) |
|
834 return result |
|
835 def items(self): |
|
836 result = [] |
|
837 for key in self.keys(): |
|
838 try: |
|
839 result.append((key, self[key])) |
|
840 except IndexError: |
|
841 result.append((key, self.dict[key])) |
|
842 return result |
|
843 |
|
844 |
|
845 class FormContent(FormContentDict): |
|
846 """This class is present for backwards compatibility only.""" |
|
847 def values(self, key): |
|
848 if key in self.dict :return self.dict[key] |
|
849 else: return None |
|
850 def indexed_value(self, key, location): |
|
851 if key in self.dict: |
|
852 if len(self.dict[key]) > location: |
|
853 return self.dict[key][location] |
|
854 else: return None |
|
855 else: return None |
|
856 def value(self, key): |
|
857 if key in self.dict: return self.dict[key][0] |
|
858 else: return None |
|
859 def length(self, key): |
|
860 return len(self.dict[key]) |
|
861 def stripped(self, key): |
|
862 if key in self.dict: return self.dict[key][0].strip() |
|
863 else: return None |
|
864 def pars(self): |
|
865 return self.dict |
|
866 |
|
867 |
|
868 # Test/debug code |
|
869 # =============== |
|
870 |
|
871 def test(environ=os.environ): |
|
872 """Robust test CGI script, usable as main program. |
|
873 |
|
874 Write minimal HTTP headers and dump all information provided to |
|
875 the script in HTML form. |
|
876 |
|
877 """ |
|
878 print "Content-type: text/html" |
|
879 print |
|
880 sys.stderr = sys.stdout |
|
881 try: |
|
882 form = FieldStorage() # Replace with other classes to test those |
|
883 print_directory() |
|
884 print_arguments() |
|
885 print_form(form) |
|
886 print_environ(environ) |
|
887 print_environ_usage() |
|
888 def f(): |
|
889 exec "testing print_exception() -- <I>italics?</I>" |
|
890 def g(f=f): |
|
891 f() |
|
892 print "<H3>What follows is a test, not an actual exception:</H3>" |
|
893 g() |
|
894 except: |
|
895 print_exception() |
|
896 |
|
897 print "<H1>Second try with a small maxlen...</H1>" |
|
898 |
|
899 global maxlen |
|
900 maxlen = 50 |
|
901 try: |
|
902 form = FieldStorage() # Replace with other classes to test those |
|
903 print_directory() |
|
904 print_arguments() |
|
905 print_form(form) |
|
906 print_environ(environ) |
|
907 except: |
|
908 print_exception() |
|
909 |
|
910 def print_exception(type=None, value=None, tb=None, limit=None): |
|
911 if type is None: |
|
912 type, value, tb = sys.exc_info() |
|
913 import traceback |
|
914 print |
|
915 print "<H3>Traceback (most recent call last):</H3>" |
|
916 list = traceback.format_tb(tb, limit) + \ |
|
917 traceback.format_exception_only(type, value) |
|
918 print "<PRE>%s<B>%s</B></PRE>" % ( |
|
919 escape("".join(list[:-1])), |
|
920 escape(list[-1]), |
|
921 ) |
|
922 del tb |
|
923 |
|
924 def print_environ(environ=os.environ): |
|
925 """Dump the shell environment as HTML.""" |
|
926 keys = environ.keys() |
|
927 keys.sort() |
|
928 print |
|
929 print "<H3>Shell Environment:</H3>" |
|
930 print "<DL>" |
|
931 for key in keys: |
|
932 print "<DT>", escape(key), "<DD>", escape(environ[key]) |
|
933 print "</DL>" |
|
934 print |
|
935 |
|
936 def print_form(form): |
|
937 """Dump the contents of a form as HTML.""" |
|
938 keys = form.keys() |
|
939 keys.sort() |
|
940 print |
|
941 print "<H3>Form Contents:</H3>" |
|
942 if not keys: |
|
943 print "<P>No form fields." |
|
944 print "<DL>" |
|
945 for key in keys: |
|
946 print "<DT>" + escape(key) + ":", |
|
947 value = form[key] |
|
948 print "<i>" + escape(repr(type(value))) + "</i>" |
|
949 print "<DD>" + escape(repr(value)) |
|
950 print "</DL>" |
|
951 print |
|
952 |
|
953 def print_directory(): |
|
954 """Dump the current directory as HTML.""" |
|
955 print |
|
956 print "<H3>Current Working Directory:</H3>" |
|
957 try: |
|
958 pwd = os.getcwd() |
|
959 except os.error, msg: |
|
960 print "os.error:", escape(str(msg)) |
|
961 else: |
|
962 print escape(pwd) |
|
963 print |
|
964 |
|
965 def print_arguments(): |
|
966 print |
|
967 print "<H3>Command Line Arguments:</H3>" |
|
968 print |
|
969 print sys.argv |
|
970 print |
|
971 |
|
972 def print_environ_usage(): |
|
973 """Dump a list of environment variables used by CGI as HTML.""" |
|
974 print """ |
|
975 <H3>These environment variables could have been set:</H3> |
|
976 <UL> |
|
977 <LI>AUTH_TYPE |
|
978 <LI>CONTENT_LENGTH |
|
979 <LI>CONTENT_TYPE |
|
980 <LI>DATE_GMT |
|
981 <LI>DATE_LOCAL |
|
982 <LI>DOCUMENT_NAME |
|
983 <LI>DOCUMENT_ROOT |
|
984 <LI>DOCUMENT_URI |
|
985 <LI>GATEWAY_INTERFACE |
|
986 <LI>LAST_MODIFIED |
|
987 <LI>PATH |
|
988 <LI>PATH_INFO |
|
989 <LI>PATH_TRANSLATED |
|
990 <LI>QUERY_STRING |
|
991 <LI>REMOTE_ADDR |
|
992 <LI>REMOTE_HOST |
|
993 <LI>REMOTE_IDENT |
|
994 <LI>REMOTE_USER |
|
995 <LI>REQUEST_METHOD |
|
996 <LI>SCRIPT_NAME |
|
997 <LI>SERVER_NAME |
|
998 <LI>SERVER_PORT |
|
999 <LI>SERVER_PROTOCOL |
|
1000 <LI>SERVER_ROOT |
|
1001 <LI>SERVER_SOFTWARE |
|
1002 </UL> |
|
1003 In addition, HTTP headers sent by the server may be passed in the |
|
1004 environment as well. Here are some common variable names: |
|
1005 <UL> |
|
1006 <LI>HTTP_ACCEPT |
|
1007 <LI>HTTP_CONNECTION |
|
1008 <LI>HTTP_HOST |
|
1009 <LI>HTTP_PRAGMA |
|
1010 <LI>HTTP_REFERER |
|
1011 <LI>HTTP_USER_AGENT |
|
1012 </UL> |
|
1013 """ |
|
1014 |
|
1015 |
|
1016 # Utilities |
|
1017 # ========= |
|
1018 |
|
1019 def escape(s, quote=None): |
|
1020 '''Replace special characters "&", "<" and ">" to HTML-safe sequences. |
|
1021 If the optional flag quote is true, the quotation mark character (") |
|
1022 is also translated.''' |
|
1023 s = s.replace("&", "&") # Must be done first! |
|
1024 s = s.replace("<", "<") |
|
1025 s = s.replace(">", ">") |
|
1026 if quote: |
|
1027 s = s.replace('"', """) |
|
1028 return s |
|
1029 |
|
1030 def valid_boundary(s, _vb_pattern="^[ -~]{0,200}[!-~]$"): |
|
1031 import re |
|
1032 return re.match(_vb_pattern, s) |
|
1033 |
|
1034 # Invoke mainline |
|
1035 # =============== |
|
1036 |
|
1037 # Call test() when this file is run as a script (not imported as a module) |
|
1038 if __name__ == '__main__': |
|
1039 test() |