Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 243ddcd

Browse files
committed
Added FieldStorage class, which stores parts in files.
(Not documented yet, and the files are currently StringIO instances.)
1 parent 62d9d6e commit 243ddcd

1 file changed

Lines changed: 272 additions & 5 deletions

File tree

Lib/cgi.py

Lines changed: 272 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
telling the client what kind of data is following. Python code to
3333
generate a minimal header section looks like this:
3434
35-
print "Content-type: text/html" # HTML is following
36-
print # blank line, end of headers
35+
print "Content-type: text/html" # HTML is following
36+
print # blank line, end of headers
3737
3838
The second section is usually HTML, which allows the client software
3939
to display nicely formatted text with header, in-line images, etc.
@@ -503,6 +503,272 @@ def parse_header(line):
503503
return key, pdict
504504

505505

506+
# Classes for field storage
507+
# =========================
508+
509+
class MiniFieldStorage:
510+
511+
"""Internal: dummy FieldStorage, used with query string format."""
512+
513+
def __init__(self, name, value):
514+
"""Constructor from field name and value."""
515+
self.name = name
516+
self.value = value
517+
from StringIO import StringIO
518+
self.filename = None
519+
self.list = None
520+
self.file = StringIO(value)
521+
522+
def __repr__(self):
523+
"""Return printable representation."""
524+
return "MiniFieldStorage(%s, %s)" % (`self.name`,
525+
`self.value`)
526+
527+
528+
class FieldStorage:
529+
530+
"""Store a sequence of fields, reading multipart/form-data."""
531+
532+
def __init__(self, fp=None, headers=None, outerboundary=""):
533+
"""Constructor. Read multipart/* until last part."""
534+
method = None
535+
if environ.has_key('REQUEST_METHOD'):
536+
method = string.upper(environ['REQUEST_METHOD'])
537+
if not fp and method == 'GET':
538+
qs = None
539+
if environ.has_key('QUERY_STRING'):
540+
qs = environ['QUERY_STRING']
541+
from StringIO import StringIO
542+
fp = StringIO(qs or "")
543+
if headers is None:
544+
headers = {'content-type':
545+
"application/x-www-form-urlencoded"}
546+
if headers is None:
547+
headers = {}
548+
if environ.has_key('CONTENT_TYPE'):
549+
headers['content-type'] = environ['CONTENT_TYPE']
550+
if environ.has_key('CONTENT_LENGTH'):
551+
headers['content-length'] = environ['CONTENT_LENGTH']
552+
self.fp = fp or sys.stdin
553+
self.headers = headers
554+
self.outerboundary = outerboundary
555+
556+
# Process content-disposition header
557+
cdisp, pdict = "", {}
558+
if self.headers.has_key('content-disposition'):
559+
cdisp, pdict = parse_header(self.headers['content-disposition'])
560+
self.disposition = cdisp
561+
self.disposition_options = pdict
562+
self.name = None
563+
if pdict.has_key('name'):
564+
self.name = pdict['name']
565+
self.filename = None
566+
if pdict.has_key('filename'):
567+
self.filename = pdict['filename']
568+
569+
# Process content-type header
570+
ctype, pdict = "text/plain", {}
571+
if self.headers.has_key('content-type'):
572+
ctype, pdict = parse_header(self.headers['content-type'])
573+
self.type = ctype
574+
self.type_options = pdict
575+
self.innerboundary = ""
576+
if pdict.has_key('boundary'):
577+
self.innerboundary = pdict['boundary']
578+
clen = -1
579+
if self.headers.has_key('content-length'):
580+
try:
581+
clen = string.atoi(self.headers['content-length'])
582+
except:
583+
pass
584+
self.length = clen
585+
586+
self.list = self.file = None
587+
self.done = 0
588+
self.lines = []
589+
if ctype == 'application/x-www-form-urlencoded':
590+
self.read_urlencoded()
591+
elif ctype[:10] == 'multipart/':
592+
self.read_multi()
593+
else:
594+
self.read_single()
595+
596+
def __repr__(self):
597+
"""Return a printable representation."""
598+
return "FieldStorage(%s, %s, %s)" % (
599+
`self.name`, `self.filename`, `self.value`)
600+
601+
def __getattr__(self, name):
602+
if name != 'value':
603+
raise AttributeError, name
604+
if self.file:
605+
self.file.seek(0)
606+
value = self.file.read()
607+
self.file.seek(0)
608+
elif self.list is not None:
609+
value = self.list
610+
else:
611+
value = None
612+
return value
613+
614+
def __getitem__(self, key):
615+
"""Dictionary style indexing."""
616+
if self.list is None:
617+
raise TypeError, "not indexable"
618+
found = []
619+
for item in self.list:
620+
if item.name == key: found.append(item)
621+
if not found:
622+
raise KeyError, key
623+
return found
624+
625+
def keys(self):
626+
"""Dictionary style keys() method."""
627+
if self.list is None:
628+
raise TypeError, "not indexable"
629+
keys = []
630+
for item in self.list:
631+
if item.name not in keys: keys.append(item.name)
632+
return keys
633+
634+
def read_urlencoded(self):
635+
"""Internal: read data in query string format."""
636+
qs = self.fp.read(self.length)
637+
dict = parse_qs(qs)
638+
self.list = []
639+
for key, valuelist in dict.items():
640+
for value in valuelist:
641+
self.list.append(MiniFieldStorage(key, value))
642+
self.skip_lines()
643+
644+
def read_multi(self):
645+
"""Internal: read a part that is itself multipart."""
646+
import rfc822
647+
self.list = []
648+
part = self.__class__(self.fp, {}, self.innerboundary)
649+
# Throw first part away
650+
while not part.done:
651+
headers = rfc822.Message(self.fp)
652+
part = self.__class__(self.fp, headers, self.innerboundary)
653+
self.list.append(part)
654+
self.skip_lines()
655+
656+
def read_single(self):
657+
"""Internal: read an atomic part."""
658+
if self.length >= 0:
659+
self.read_binary()
660+
self.skip_lines()
661+
else:
662+
self.read_lines()
663+
self.file.seek(0)
664+
665+
bufsize = 8*1024 # I/O buffering size for copy to file
666+
667+
def read_binary(self):
668+
"""Internal: read binary data."""
669+
self.file = self.make_file('b')
670+
todo = self.length
671+
if todo >= 0:
672+
while todo > 0:
673+
data = self.fp.read(min(todo, self.bufsize))
674+
if not data:
675+
self.done = -1
676+
break
677+
self.file.write(data)
678+
todo = todo - len(data)
679+
680+
def read_lines(self):
681+
"""Internal: read lines until EOF or outerboundary."""
682+
self.file = self.make_file('')
683+
if self.outerboundary:
684+
self.read_lines_to_outerboundary()
685+
else:
686+
self.read_lines_to_eof()
687+
688+
def read_lines_to_eof(self):
689+
"""Internal: read lines until EOF."""
690+
while 1:
691+
line = self.fp.readline()
692+
if not line:
693+
self.done = -1
694+
break
695+
self.lines.append(line)
696+
if line[-2:] == '\r\n':
697+
line = line[:-2] + '\n'
698+
self.file.write(line)
699+
700+
def read_lines_to_outerboundary(self):
701+
"""Internal: read lines until outerboundary."""
702+
next = "--" + self.outerboundary
703+
last = next + "--"
704+
delim = ""
705+
while 1:
706+
line = self.fp.readline()
707+
if not line:
708+
self.done = -1
709+
break
710+
self.lines.append(line)
711+
if line[:2] == "--":
712+
strippedline = string.strip(line)
713+
if strippedline == next:
714+
break
715+
if strippedline == last:
716+
self.done = 1
717+
break
718+
if line[-2:] == "\r\n":
719+
line = line[:-2]
720+
elif line[-1] == "\n":
721+
line = line[:-1]
722+
self.file.write(delim + line)
723+
delim = "\n"
724+
725+
def skip_lines(self):
726+
"""Internal: skip lines until outer boundary if defined."""
727+
if not self.outerboundary or self.done:
728+
return
729+
next = "--" + self.outerboundary
730+
last = next + "--"
731+
while 1:
732+
line = self.fp.readline()
733+
if not line:
734+
self.done = -1
735+
break
736+
self.lines.append(line)
737+
if line[:2] == "--":
738+
strippedline = string.strip(line)
739+
if strippedline == next:
740+
break
741+
if strippedline == last:
742+
self.done = 1
743+
break
744+
745+
def make_file(self, binary):
746+
"""Overridable: return a readable & writable file.
747+
748+
The file will be used as follows:
749+
- data is written to it
750+
- seek(0)
751+
- data is read from it
752+
753+
The 'binary' argument is 'b' if the file should be created in
754+
binary mode (on non-Unix systems), '' otherwise.
755+
756+
The intention is that you can override this method to selectively
757+
create a real (temporary) file or use a memory file dependent on
758+
the perceived size of the file or the presence of a filename, etc.
759+
760+
"""
761+
762+
# Prefer ArrayIO over StringIO, if it's available
763+
try:
764+
from ArrayIO import ArrayIO
765+
ioclass = ArrayIO
766+
except ImportError:
767+
from StringIO import StringIO
768+
ioclass = StringIO
769+
return ioclass()
770+
771+
506772
# Main classes
507773
# ============
508774

@@ -636,7 +902,7 @@ def test():
636902
sys.stderr = sys.stdout
637903
try:
638904
print_environ()
639-
print_form(FormContentDict())
905+
print_form(FieldStorage())
640906
print
641907
print "<H3>Current Working Directory:</H3>"
642908
try:
@@ -671,8 +937,9 @@ def print_form(form):
671937
print "<DL>"
672938
for key in keys:
673939
print "<DT>" + escape(key) + ":",
674-
print "<i>" + escape(`type(form[key])`) + "</i>"
675-
print "<DD>" + escape(`form[key]`)
940+
value = form[key]
941+
print "<i>" + escape(`type(value)`) + "</i>"
942+
print "<DD>" + escape(`value`)
676943
print "</DL>"
677944
print
678945

0 commit comments

Comments
 (0)