summaryrefslogtreecommitdiffstats
path: root/Lib/cgi.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/cgi.py')
-rwxr-xr-xLib/cgi.py277
1 files changed, 272 insertions, 5 deletions
diff --git a/Lib/cgi.py b/Lib/cgi.py
index d4e5bff..fd4ce32 100755
--- a/Lib/cgi.py
+++ b/Lib/cgi.py
@@ -32,8 +32,8 @@ by a blank line. The first section contains a number of headers,
telling the client what kind of data is following. Python code to
generate a minimal header section looks like this:
- print "Content-type: text/html" # HTML is following
- print # blank line, end of headers
+ print "Content-type: text/html" # HTML is following
+ print # blank line, end of headers
The second section is usually HTML, which allows the client software
to display nicely formatted text with header, in-line images, etc.
@@ -503,6 +503,272 @@ def parse_header(line):
return key, pdict
+# Classes for field storage
+# =========================
+
+class MiniFieldStorage:
+
+ """Internal: dummy FieldStorage, used with query string format."""
+
+ def __init__(self, name, value):
+ """Constructor from field name and value."""
+ self.name = name
+ self.value = value
+ from StringIO import StringIO
+ self.filename = None
+ self.list = None
+ self.file = StringIO(value)
+
+ def __repr__(self):
+ """Return printable representation."""
+ return "MiniFieldStorage(%s, %s)" % (`self.name`,
+ `self.value`)
+
+
+class FieldStorage:
+
+ """Store a sequence of fields, reading multipart/form-data."""
+
+ def __init__(self, fp=None, headers=None, outerboundary=""):
+ """Constructor. Read multipart/* until last part."""
+ method = None
+ if environ.has_key('REQUEST_METHOD'):
+ method = string.upper(environ['REQUEST_METHOD'])
+ if not fp and method == 'GET':
+ qs = None
+ if environ.has_key('QUERY_STRING'):
+ qs = environ['QUERY_STRING']
+ from StringIO import StringIO
+ fp = StringIO(qs or "")
+ if headers is None:
+ headers = {'content-type':
+ "application/x-www-form-urlencoded"}
+ if headers is None:
+ headers = {}
+ if environ.has_key('CONTENT_TYPE'):
+ headers['content-type'] = environ['CONTENT_TYPE']
+ if environ.has_key('CONTENT_LENGTH'):
+ headers['content-length'] = environ['CONTENT_LENGTH']
+ self.fp = fp or sys.stdin
+ self.headers = headers
+ self.outerboundary = outerboundary
+
+ # Process content-disposition header
+ cdisp, pdict = "", {}
+ if self.headers.has_key('content-disposition'):
+ cdisp, pdict = parse_header(self.headers['content-disposition'])
+ self.disposition = cdisp
+ self.disposition_options = pdict
+ self.name = None
+ if pdict.has_key('name'):
+ self.name = pdict['name']
+ self.filename = None
+ if pdict.has_key('filename'):
+ self.filename = pdict['filename']
+
+ # Process content-type header
+ ctype, pdict = "text/plain", {}
+ if self.headers.has_key('content-type'):
+ ctype, pdict = parse_header(self.headers['content-type'])
+ self.type = ctype
+ self.type_options = pdict
+ self.innerboundary = ""
+ if pdict.has_key('boundary'):
+ self.innerboundary = pdict['boundary']
+ clen = -1
+ if self.headers.has_key('content-length'):
+ try:
+ clen = string.atoi(self.headers['content-length'])
+ except:
+ pass
+ self.length = clen
+
+ self.list = self.file = None
+ self.done = 0
+ self.lines = []
+ if ctype == 'application/x-www-form-urlencoded':
+ self.read_urlencoded()
+ elif ctype[:10] == 'multipart/':
+ self.read_multi()
+ else:
+ self.read_single()
+
+ def __repr__(self):
+ """Return a printable representation."""
+ return "FieldStorage(%s, %s, %s)" % (
+ `self.name`, `self.filename`, `self.value`)
+
+ def __getattr__(self, name):
+ if name != 'value':
+ raise AttributeError, name
+ if self.file:
+ self.file.seek(0)
+ value = self.file.read()
+ self.file.seek(0)
+ elif self.list is not None:
+ value = self.list
+ else:
+ value = None
+ return value
+
+ def __getitem__(self, key):
+ """Dictionary style indexing."""
+ if self.list is None:
+ raise TypeError, "not indexable"
+ found = []
+ for item in self.list:
+ if item.name == key: found.append(item)
+ if not found:
+ raise KeyError, key
+ return found
+
+ def keys(self):
+ """Dictionary style keys() method."""
+ if self.list is None:
+ raise TypeError, "not indexable"
+ keys = []
+ for item in self.list:
+ if item.name not in keys: keys.append(item.name)
+ return keys
+
+ def read_urlencoded(self):
+ """Internal: read data in query string format."""
+ qs = self.fp.read(self.length)
+ dict = parse_qs(qs)
+ self.list = []
+ for key, valuelist in dict.items():
+ for value in valuelist:
+ self.list.append(MiniFieldStorage(key, value))
+ self.skip_lines()
+
+ def read_multi(self):
+ """Internal: read a part that is itself multipart."""
+ import rfc822
+ self.list = []
+ part = self.__class__(self.fp, {}, self.innerboundary)
+ # Throw first part away
+ while not part.done:
+ headers = rfc822.Message(self.fp)
+ part = self.__class__(self.fp, headers, self.innerboundary)
+ self.list.append(part)
+ self.skip_lines()
+
+ def read_single(self):
+ """Internal: read an atomic part."""
+ if self.length >= 0:
+ self.read_binary()
+ self.skip_lines()
+ else:
+ self.read_lines()
+ self.file.seek(0)
+
+ bufsize = 8*1024 # I/O buffering size for copy to file
+
+ def read_binary(self):
+ """Internal: read binary data."""
+ self.file = self.make_file('b')
+ todo = self.length
+ if todo >= 0:
+ while todo > 0:
+ data = self.fp.read(min(todo, self.bufsize))
+ if not data:
+ self.done = -1
+ break
+ self.file.write(data)
+ todo = todo - len(data)
+
+ def read_lines(self):
+ """Internal: read lines until EOF or outerboundary."""
+ self.file = self.make_file('')
+ if self.outerboundary:
+ self.read_lines_to_outerboundary()
+ else:
+ self.read_lines_to_eof()
+
+ def read_lines_to_eof(self):
+ """Internal: read lines until EOF."""
+ while 1:
+ line = self.fp.readline()
+ if not line:
+ self.done = -1
+ break
+ self.lines.append(line)
+ if line[-2:] == '\r\n':
+ line = line[:-2] + '\n'
+ self.file.write(line)
+
+ def read_lines_to_outerboundary(self):
+ """Internal: read lines until outerboundary."""
+ next = "--" + self.outerboundary
+ last = next + "--"
+ delim = ""
+ while 1:
+ line = self.fp.readline()
+ if not line:
+ self.done = -1
+ break
+ self.lines.append(line)
+ if line[:2] == "--":
+ strippedline = string.strip(line)
+ if strippedline == next:
+ break
+ if strippedline == last:
+ self.done = 1
+ break
+ if line[-2:] == "\r\n":
+ line = line[:-2]
+ elif line[-1] == "\n":
+ line = line[:-1]
+ self.file.write(delim + line)
+ delim = "\n"
+
+ def skip_lines(self):
+ """Internal: skip lines until outer boundary if defined."""
+ if not self.outerboundary or self.done:
+ return
+ next = "--" + self.outerboundary
+ last = next + "--"
+ while 1:
+ line = self.fp.readline()
+ if not line:
+ self.done = -1
+ break
+ self.lines.append(line)
+ if line[:2] == "--":
+ strippedline = string.strip(line)
+ if strippedline == next:
+ break
+ if strippedline == last:
+ self.done = 1
+ break
+
+ def make_file(self, binary):
+ """Overridable: return a readable & writable file.
+
+ The file will be used as follows:
+ - data is written to it
+ - seek(0)
+ - data is read from it
+
+ The 'binary' argument is 'b' if the file should be created in
+ binary mode (on non-Unix systems), '' otherwise.
+
+ The intention is that you can override this method to selectively
+ create a real (temporary) file or use a memory file dependent on
+ the perceived size of the file or the presence of a filename, etc.
+
+ """
+
+ # Prefer ArrayIO over StringIO, if it's available
+ try:
+ from ArrayIO import ArrayIO
+ ioclass = ArrayIO
+ except ImportError:
+ from StringIO import StringIO
+ ioclass = StringIO
+ return ioclass()
+
+
# Main classes
# ============
@@ -636,7 +902,7 @@ def test():
sys.stderr = sys.stdout
try:
print_environ()
- print_form(FormContentDict())
+ print_form(FieldStorage())
print
print "<H3>Current Working Directory:</H3>"
try:
@@ -671,8 +937,9 @@ def print_form(form):
print "<DL>"
for key in keys:
print "<DT>" + escape(key) + ":",
- print "<i>" + escape(`type(form[key])`) + "</i>"
- print "<DD>" + escape(`form[key]`)
+ value = form[key]
+ print "<i>" + escape(`type(value)`) + "</i>"
+ print "<DD>" + escape(`value`)
print "</DL>"
print