| #!/usr/local/bin/python |
| |
| # XXX TODO |
| # - proper doc strings instead of this rambling dialogue style |
| # - more utilities, e.g. |
| # - print_header(type="test/html", blankline=1) -- print MIME header |
| # - utility to format a nice error message in HTML |
| # - utility to format a Location: ... response, including HTML |
| # - utility to catch errors and display traceback |
| |
| # |
| # A class for wrapping the WWW Forms Common Gateway Interface (CGI) |
| # Michael McLay, NIST mclay@eeel.nist.gov 6/14/94 |
| # |
| # modified by Steve Majewski <sdm7g@Virginia.EDU> 12/5/94 |
| # |
| # now maintained as part of the Python distribution |
| |
| # Several classes to parse the name/value pairs that are passed to |
| # a server's CGI by GET, POST or PUT methods by a WWW FORM. This |
| # module is based on Mike McLay's original cgi.py after discussing |
| # changes with him and others on the comp.lang.python newsgroup, and |
| # at the NIST Python workshop. |
| # |
| # The rationale for changes was: |
| # The original FormContent class was almost, but not quite like |
| # a dictionary object. Besides adding some extra access methods, |
| # it had a values() method with different arguments and semantics |
| # from the standard values() method of a mapping object. Also, |
| # it provided several different access methods that may be necessary |
| # or useful, but made it a little more confusing to figure out how |
| # to use. Also, we wanted to make the most typical cases the simplest |
| # and most convenient access methods. ( Most form fields just return |
| # a single value, and in practice, a lot of code was just assuming |
| # a single value and ignoring all others. On the other hand, the |
| # protocol allows multiple values to be returned. |
| # |
| # The new base class (FormContentDict) is just like a dictionary. |
| # In fact, if you just want a dictionary, all of the stuff that was |
| # in __init__ has been extracted into a cgi.parse() function that will |
| # return the "raw" dictionary, but having a class allows you to customize |
| # it further. |
| # Mike McLay's original FormContent class is reimplemented as a |
| # subclass of FormContentDict. |
| # There are two additional sub-classes, but I'm not yet too sure |
| # whether they are what I want. |
| # |
| |
| import string,regsub,sys,os,urllib |
| # since os.environ may often be used in cgi code, we name it in this module. |
| from os import environ |
| |
| |
| def parse(): |
| """Parse the query passed in the environment or on stdin""" |
| if environ['REQUEST_METHOD'] == 'POST': |
| qs = sys.stdin.read(string.atoi(environ['CONTENT_LENGTH'])) |
| environ['QUERY_STRING'] = qs |
| elif environ.has_key('QUERY_STRING'): |
| qs = environ['QUERY_STRING'] |
| else: |
| environ['QUERY_STRING'] = qs = '' |
| return parse_qs(qs) |
| |
| |
| def parse_qs(qs): |
| """Parse a query given as a string argument""" |
| name_value_pairs = string.splitfields(qs, '&') |
| dict = {} |
| for name_value in name_value_pairs: |
| nv = string.splitfields(name_value, '=') |
| if len(nv) != 2: |
| continue |
| name = nv[0] |
| value = urllib.unquote(regsub.gsub('+',' ',nv[1])) |
| if len(value): |
| if dict.has_key (name): |
| dict[name].append(value) |
| else: |
| dict[name] = [value] |
| return dict |
| |
| |
| |
| # The FormContent constructor creates a dictionary from the name/value pairs |
| # passed through the CGI interface. |
| |
| |
| # |
| # form['key'] |
| # form.__getitem__('key') |
| # form.has_key('key') |
| # form.keys() |
| # form.values() |
| # form.items() |
| # form.dict |
| |
| class FormContentDict: |
| def __init__( self ): |
| self.dict = parse() |
| self.query_string = environ['QUERY_STRING'] |
| def __getitem__(self,key): |
| return self.dict[key] |
| def keys(self): |
| return self.dict.keys() |
| def has_key(self, key): |
| return self.dict.has_key(key) |
| def values(self): |
| return self.dict.values() |
| def items(self): |
| return self.dict.items() |
| def __len__( self ): |
| return len(self.dict) |
| |
| |
| # This is the "strict" single-value expecting version. |
| # IF you only expect a single value for each field, then form[key] |
| # will return that single value ( the [0]-th ), and raise an |
| # IndexError if that expectation is not true. |
| # IF you expect a field to have possible multiple values, than you |
| # can use form.getlist( key ) to get all of the values. |
| # values() and items() are a compromise: they return single strings |
| # where there is a single value, and lists of strings otherwise. |
| |
| class SvFormContentDict(FormContentDict): |
| def __getitem__( self, key ): |
| if len( self.dict[key] ) > 1 : |
| raise IndexError, 'expecting a single value' |
| return self.dict[key][0] |
| def getlist( self, key ): |
| return self.dict[key] |
| def values( self ): |
| lis = [] |
| for each in self.dict.values() : |
| if len( each ) == 1 : |
| lis.append( each[0] ) |
| else: lis.append( each ) |
| return lis |
| def items( self ): |
| lis = [] |
| for key,value in self.dict.items(): |
| if len(value) == 1 : |
| lis.append( (key,value[0]) ) |
| else: lis.append( (key,value) ) |
| return lis |
| |
| |
| # And this sub-class is similar to the above, but it will attempt to |
| # interpret numerical values. This is here as mostly as an example, |
| # but I think the real way to handle typed-data from a form may be |
| # to make an additional table driver parsing stage that has a table |
| # of allowed input patterns and the output conversion types - it |
| # would signal type-errors on parse, not on access. |
| class InterpFormContentDict(SvFormContentDict): |
| def __getitem__( self, key ): |
| v = SvFormContentDict.__getitem__( self, key ) |
| if v[0] in string.digits+'+-.' : |
| try: return string.atoi( v ) |
| except ValueError: |
| try: return string.atof( v ) |
| except ValueError: pass |
| return string.strip(v) |
| def values( self ): |
| lis = [] |
| for key in self.keys(): |
| try: |
| lis.append( self[key] ) |
| except IndexError: |
| lis.append( self.dict[key] ) |
| return lis |
| def items( self ): |
| lis = [] |
| for key in self.keys(): |
| try: |
| lis.append( (key, self[key]) ) |
| except IndexError: |
| lis.append( (key, self.dict[key]) ) |
| return lis |
| |
| |
| # class FormContent parses the name/value pairs that are passed to a |
| # server's CGI by GET, POST, or PUT methods by a WWW FORM. several |
| # specialized FormContent dictionary access methods have been added |
| # for convenience. |
| |
| # function return value |
| # |
| # form.keys() all keys in dictionary |
| # form.has_key('key') test keys existance |
| # form[key] returns list associated with key |
| # form.values('key') key's list (same as form.[key]) |
| # form.indexed_value('key' index) nth element in key's value list |
| # form.value(key) key's unstripped value |
| # form.length(key) number of elements in key's list |
| # form.stripped(key) key's value with whitespace stripped |
| # form.pars() full dictionary |
| |
| |
| |
| class FormContent(FormContentDict): |
| # This is the original FormContent semantics of values, |
| # not the dictionary like semantics. |
| def values(self,key): |
| if self.dict.has_key(key):return self.dict[key] |
| else: return None |
| def indexed_value(self,key, location): |
| if self.dict.has_key(key): |
| if len (self.dict[key]) > location: |
| return self.dict[key][location] |
| else: return None |
| else: return None |
| def value(self,key): |
| if self.dict.has_key(key):return self.dict[key][0] |
| else: return None |
| def length(self,key): |
| return len (self.dict[key]) |
| def stripped(self,key): |
| if self.dict.has_key(key):return string.strip(self.dict[key][0]) |
| else: return None |
| def pars(self): |
| return self.dict |
| |
| |
| |
| |
| |
| |
| def print_environ_usage(): |
| print """ |
| <H3>These operating system environment variables could have been |
| set:</H3> <UL> |
| <LI>AUTH_TYPE |
| <LI>CONTENT_LENGTH |
| <LI>CONTENT_TYPE |
| <LI>DATE_GMT |
| <LI>DATE_LOCAL |
| <LI>DOCUMENT_NAME |
| <LI>DOCUMENT_ROOT |
| <LI>DOCUMENT_URI |
| <LI>GATEWAY_INTERFACE |
| <LI>LAST_MODIFIED |
| <LI>PATH |
| <LI>PATH_INFO |
| <LI>PATH_TRANSLATED |
| <LI>QUERY_STRING |
| <LI>REMOTE_ADDR |
| <LI>REMOTE_HOST |
| <LI>REMOTE_IDENT |
| <LI>REMOTE_USER |
| <LI>REQUEST_METHOD |
| <LI>SCRIPT_NAME |
| <LI>SERVER_NAME |
| <LI>SERVER_PORT |
| <LI>SERVER_PROTOCOL |
| <LI>SERVER_ROOT |
| <LI>SERVER_SOFTWARE |
| </UL> |
| """ |
| |
| def print_environ(): |
| skeys = environ.keys() |
| skeys.sort() |
| print '<h3> The following environment variables ' \ |
| 'were set by the CGI script: </h3>' |
| print '<dl>' |
| for key in skeys: |
| print '<dt>', escape(key), '<dd>', escape(environ[key]) |
| print '</dl>' |
| |
| def print_form( form ): |
| skeys = form.keys() |
| skeys.sort() |
| print '<h3> The following name/value pairs ' \ |
| 'were entered in the form: </h3>' |
| print '<dl>' |
| for key in skeys: |
| print '<dt>', escape(key), ':', |
| print '<i>', escape(`type(form[key])`), '</i>', |
| print '<dd>', escape(`form[key]`) |
| print '</dl>' |
| |
| def escape( s ): |
| s = regsub.gsub('&', '&', s) # Must be done first |
| s = regsub.gsub('<', '<', s) |
| s = regsub.gsub('>', '>', s) |
| return s |
| |
| def test( what ): |
| label = escape(str(what)) |
| print 'Content-type: text/html\n\n' |
| print '<HEADER>\n<TITLE>' + label + '</TITLE>\n</HEADER>\n' |
| print '<BODY>\n' |
| print "<H1>" + label +"</H1>\n" |
| form = what() |
| print_form( form ) |
| print_environ() |
| print_environ_usage() |
| print '</body>' |
| |
| if __name__ == '__main__' : |
| test_classes = ( FormContent, FormContentDict, SvFormContentDict, InterpFormContentDict ) |
| test( test_classes[0] ) # by default, test compatibility with |
| # old version, change index to test others. |